Published
Edited
May 26, 2019
14 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
maxT = Math.log(Math.hypot(width, height) / 2 / a) / b
Insert cell
Insert cell
angles = Array.from(range(nspirals), i =>
i * 2 * Math.PI / nspirals)
Insert cell
Insert cell
Insert cell
Insert cell
spiral = angle => t => cmul([a, 0], cexp([b*t, angle + t]))
// equivalently ⤵
// const r = a * Math.exp(b * t)
// return [r * Math.cos(angle + t), r * Math.sin(angle + t)]
Insert cell
drawing_window = [-width/2, width/2, -height/2, height/2]
Insert cell
tolerance = 1e-2
Insert cell
Insert cell
spirals_bezpts = angles.map(angle =>
bezeval(spiral(angle), [0, maxT], drawing_window, tolerance))
Insert cell
Insert cell
startpoints = {
const flat_bezpts = [].concat(...spirals_bezpts);
return Array.from(range(0, flat_bezpts.length, 8), i =>
flat_bezpts.slice(i, i+2)); }
Insert cell
Insert cell
spirals_paths = spirals_bezpts.map(bezpts => bezpts_to_svgpath(bezpts, 3));
Insert cell
Insert cell
svg_spirals = () =>
svg`<svg width="${width}" height="${height}">
<g transform="translate(${width/2}, ${height/2})" fill=none stroke=none>
<g stroke="#aaa" stroke-width=1.5>
${spirals_paths.map(path => svg`<path d="${path}" />`)} </g>
<g fill="#c7326b">
${startpoints.map(([x, y]) => svg`<circle cx="${x}" cy="${y}" r=2 />`)} `
Insert cell
Insert cell
{
const ctx = DOM.context2d(width, height);
ctx.translate(width / 2, height / 2);
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#aaa';
ctx.stroke(new Path2D(spirals_paths.join('')));

ctx.fillStyle = '#c7326b';
startpoints.forEach(([x, y]) => ctx.rect(x - 2, y - 2, 4, 4));
ctx.fill();

return ctx.canvas;
}
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