Published
Edited
Mar 4, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const A = size;
const N = Math.floor(width / A);
const W = N * A;
const scale = (rows, cols) =>
flags.includes("caleidoscope")
? `${1 - 2 * (rows % 2)},${1 - 2 * (cols % 2)}`
: '1';
return html`<svg width=${W} height=${W}>
${Array.from({ length: N * N }, (_, n) => {
const R = Math.floor(n / N);
const C = Math.floor(n % N);
return `<g transform="translate(${A * R + A / 2}, ${A * C +
A / 2}) scale(${scale(R, C)})">${drawRects(A)}</g>`;
}).join('')}
</svg>`;
}
Insert cell
drawRects = a => {
const angle = PI / 4 - degToRad(rotationOffset);
const scale = 1 / (Math.sqrt(2) * Math.cos(angle));
return Array.from({ length: 50 }, (_, i) => i).reduce(
(acc, i) =>
`<rect
x=${-a / 2}
y=${-a / 2}
width=${a}
height=${a}
fill="none" stroke="#000" stroke-width="1"/>
<g transform="rotate(${rotationOffset}) scale(${scale})">${acc}</g>`
);
}
Insert cell
Insert cell
Insert cell
{
const A = 600;
return html`<svg width=${A * 1.2} height=${A * 1.2}>
<g transform="translate(${A * 0.6}, ${A * 0.6})">
${drawPoly(A)}
</g>
</svg>`;
}
Insert cell
drawPoly = a => {
const angle = degToRad(rotationOffset2);
const points = [
{ x: a / 2, y: a / 2 },
{ x: -a / 2, y: a / 2 },
{ x: -a / 2, y: -a / 2 },
{ x: a / 2, y: -a / 2 },
{ x: a / 2, y: a / 2 }
];
for (let i = 0; i < 1000; ++i) {
const n = points.length;
const pRot = rotatePointOverPoint(points[n - 1], points[n - 4], angle);
const pN = intersectonOfTwoLines(
points[n - 4],
points[n - 3],
pRot,
points[n - 1]
);
if (!pN || !isPointBetweenPoints(pN, points[n - 4], points[n - 3])) break;
points.push(pN);
}
const d = points.map(p => "" + p.x + "," + p.y).join(" ");
return `<path fill="none" stroke="#000" strokeWidth="1" d="M ${d}" />`;
}
Insert cell
Insert cell
isPointBetweenPoints = (p, a, b) => {
const pa = abDist(p, a);
const pb = abDist(p, b);
const ab = abDist(a, b);
return pa + pb - ab < ab * 0.0001;
}
Insert cell
abDist = (a, b) => Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2)
Insert cell
intersectonOfTwoLines = (
{ x: x1, y: y1 },
{ x: x2, y: y2 },
{ x: x3, y: y3 },
{ x: x4, y: y4 }
) => {
const d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d < Number.EPSILON) return null;
const dx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
const dy = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
return {
x: dx / d,
y: dy / d
};
}
Insert cell
rotatePointOverPoint = (p0, p1, angle) => ({
x: (p1.x - p0.x) * Math.cos(angle) - (p1.y - p0.y) * Math.sin(angle) + p0.x,
y: (p1.x - p0.x) * Math.sin(angle) + (p1.y - p0.y) * Math.cos(angle) + p0.y
})
Insert cell
pointsDistance = ({x: x1, y: y1}, {x: x2, y: y2}) => Math.sqrt()
Insert cell
degToRad = d => d * (PI / 180)
Insert cell
radToDeg = r => r * (180 / PI)
Insert cell
PI = Math.PI
Insert cell
import { Checkbox, Range } from "@observablehq/inputs"
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