Public
Edited
Dec 17, 2021
1 fork
7 stars
Insert cell
Insert cell
Insert cell
svg`<svg viewBox="-100 -100 200 200" style="max-width: 20em">
${repeat(tick(70, 5, "hsl(30, 20%, 70%)"), numMinorTicks * 4 * numMajorTicks)}
${repeat(tick(70, 8, "hsl(30, 20%, 50%)"), numMajorTicks * 4)}
${repeat(`<path d="M 0,-79 l 3,10 l -6,0 z" fill="white" stroke="hsl(30, 20%, 50%)" stroke-width="1"/>`, 4, 45)}
<circle r="70" fill="none" stroke="hsl(30, 20%, 30%)" stroke-width="3" />
<circle r="60" fill="none" stroke="hsl(30, 20%, 60%)" stroke-width="1" />
<circle r="50" fill="none" stroke="hsl(30, 20%, 30%)" stroke-width="2" />
${repeat(`<circle cy="-40" r="1.5" fill="hsl(30, 20%, 50%)"/>`, numDots)}
${repeat(directionMarker(88, 15), 4)}
${repeat(directionMarker(65, 8), 4, 45)}
${repeat(angleMarker(84, 7), 24)}
${repeat(wedge(cardinalLength, cardinalLength * cardinalWedge), 4)}
${repeat(wedge(cardinalLength*0.75, cardinalLength * cardinalWedge * 1.7, 0.3), 4, 45)}
${patternDefs}
</svg>
`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function wedge(radius, width, narrowness=1.0) {
return `
<path d="M 0,0 L ${width*narrowness},${-width} L 0,${-radius} z" fill="url(#shading)" stroke="black" stroke-width="0.5" />
<path d="M 0,0 L 0,${-radius} L ${-width*narrowness},${-width} z" fill="white" stroke="black" stroke-width="0.5" />
`;
}
Insert cell
function tick(radius, length, color) {
return `<path d="M 0,${-radius} l 0,${-length}" fill="none" stroke="${color}" stroke-width="1" />`;
}
Insert cell
function directionMarker(radius, fontSize) { return (angle, _) => {
let label = {0: 'N', 45: 'NE', 90: 'E', 135: 'SE', 180: 'S', 225: 'SW', 270: 'W', 315: 'NW'}[angle] ?? '??';
return `<text y="${-radius}" font-size="${fontSize}" text-anchor="middle" dy="0.4em">${label}</text>`;
};
}
Insert cell
angleMarker = function(radius, fontSize) { return (angle, _) => {
if (angle === 0 || angle === 90 || angle === 180 || angle === 270) return ``;
return `<text y="${-radius}" fill="hsl(30, 20%, 50%)" font-size="${fontSize}" font-family="sans-serif" text-anchor="middle" dy="0.4em">${angle}</text>`;
};
}
Insert cell
function repeat(component, N, initialAngle=0) {
// NOTE: if component is a function, it will be called with (angle, i)
if (N <= 0) return "";
let result = [];
for (let i = 0; i < N; i++) {
let angle = (360 / N) * i + initialAngle;
let el = typeof component === 'function'? component(angle, i) : component;
result.push(`<g transform="rotate(${angle})">${el}</g>`);
}
return result.join("");
}
Insert cell
Insert cell
Insert cell
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