Published
Edited
Jun 10, 2021
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tikzStandardDashPatterns = [
{ name: "Solid", dashLengths: [] },
{ name: "Dotted", dashLengths: [1, 2] },
{ name: "Densely dotted", dashLengths: [1, 1] },
{ name: "Sparsely dotted", dashLengths: [1, 4] },
{ name: "Dashed", dashLengths: [3, 3] },
{ name: "Densely dashed", dashLengths: [3, 1] },
{ name: "Sparsely dashed", dashLengths: [3, 6] },
{ name: "Dashdotted", dashLengths: [3, 2, 1, 2] },
{ name: "Densely dashdotted", dashLengths: [3, 1, 1, 1] },
{ name: "Sparsely dashdotted", dashLengths: [3, 4, 1, 4] }
]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
bonusPatterns = [
generateMorseDashPattern(customText || "Never gonna give you up"),
generateBinaryDashPattern(customText || "Never gonna let you down"),
generateMorseDashPattern("SOS"),
{
name: "Fibonacci",
dashLengths: fib(8).flatMap(x => [x, 1])
}
]
Insert cell
function fib(n) {
const s = Array(n);
s[0] = 1;
s[1] = 1;
for (let i = 2; i < n; ++i) {
s[i] = s[i - 1] + s[i - 2];
}
s.length = n;
return s;
}
Insert cell
fib(10)
Insert cell
Insert cell
maxStrokeWidth = 30
Insert cell
viewof strokeWidth = Inputs.input(8)
Insert cell
viewof capSpaceCompensation = Inputs.input(0.1)
Insert cell
function createStrokeWidthWidget() {
return Inputs.bind(
Inputs.range([0.5, maxStrokeWidth], {
label: "Stroke width",
value: 8,
step: 0.1
}),
viewof strokeWidth
);
}
Insert cell
function createCapSpaceCompensationWidget() {
return Inputs.bind(
Inputs.range([0, 1], {
label: "Line cap space compensation",
value: 0.1,
step: 0.01
}),
viewof capSpaceCompensation
);
}
Insert cell
function createWidgets() {
return html`${createStrokeWidthWidget()} ${createCapSpaceCompensationWidget()}`;
}
Insert cell
Insert cell
function createRoundLineAttributes(
dashLengths,
strokeWidth,
capSpaceCompensation = 0
) {
if (dashLengths.length === 0) {
return `stroke-width=${strokeWidth} stroke-linecap="round"`;
}
const a = 1 - capSpaceCompensation;
const b = -1 + capSpaceCompensation;
const processedDashLengths = dashLengths.map((x, i) => {
// To get the right dash lengths, we need to compensate for the line caps, which extend from the ends of the dashes
const capCompensation = i % 2 ? a : b;
return (Math.max(x + capCompensation, 0) * strokeWidth).toFixed(1);
});

const dashOffset = (capSpaceCompensation * strokeWidth - strokeWidth) / 2;
return `stroke-dasharray="${processedDashLengths.join(
", "
)}" stroke-width=${strokeWidth} stroke-linecap="round" stroke-dashoffset=${dashOffset.toFixed(
1
)}`;
}
Insert cell
function createSquareLineAttributes(dashLengths, strokeWidth) {
if (dashLengths.length === 0) {
return `stroke-width=${strokeWidth}`;
}
const processedDashLengths = dashLengths.map(x =>
(x * strokeWidth).toFixed(1)
);
return `stroke-dasharray="${processedDashLengths.join(
", "
)}" stroke-width=${strokeWidth}`;
}
Insert cell
Insert cell
Insert cell
morseEncodingMap = new Map(morseEncodings.map(d => [d.character, d.encoding]))
Insert cell
function textToMorse(text) {
const missing = morseEncodingMap.get("_");
return text
.split("")
.map(c => morseEncodingMap.get(c.toUpperCase()) || missing);
}
Insert cell
function morseToDashLengths(morse) {
const dashLengths = morse
.split("")
.flatMap(c => (c === "−" ? [3, 1] : [1, 1]));
dashLengths[dashLengths.length - 1] *= 2;
return dashLengths;
}
Insert cell
function textToMorseDashLengths(text) {
const dashLengths = textToMorse(text).flatMap(morseToDashLengths);
dashLengths[dashLengths.length - 1] *= 2;
return dashLengths;
}
Insert cell
function generateMorseDashPattern(text) {
return {
name: `Morse for: ${text}`,
dashLengths: textToMorseDashLengths(text)
};
}
Insert cell
md`---

## Binary`
Insert cell
function textToBinary(text) {
const encoder = new TextEncoder();
const uint8Array = encoder.encode(text);
return Array.from(uint8Array)
.map(x => x.toString(2).padStart(8, "0"))
.join("");
}
Insert cell
function binaryToDashLengths(binaryString) {
const dashLenghts = [];
const characters = binaryString.split("");
if (characters[0] === "0") {
dashLenghts.push(0);
}
let runLength = 1;
let lastDigit = characters[0];
for (let i = 1; i < characters.length; ++i) {
const currentDigit = characters[i];
if (currentDigit === lastDigit) {
runLength += 1;
} else {
dashLenghts.push(runLength);
runLength = 1;
lastDigit = currentDigit;
}
}
dashLenghts.push(runLength);

return dashLenghts;
}
Insert cell
function binaryToDashLengths2(binaryString) {
const dashLengths = binaryString
.split("")
.flatMap(c => (c === "0" ? [1, 1] : [3, 1]));
dashLengths[dashLengths.length - 1] *= 4;

return dashLengths;
}
Insert cell
binaryToDashLengths("1010111")
Insert cell
function textToBinaryDashLengths(text) {
return binaryToDashLengths2(textToBinary(text));
}
Insert cell
function generateBinaryDashPattern(text) {
return {
name: `Binary for: ${text}`,
dashLengths: textToBinaryDashLengths(text)
};
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more