Public
Edited
Nov 8, 2023
4 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
squares = 144
Insert cell
deformityRange = math.linspace(16, true)
Insert cell
md`[Link to this variation](${getNotebookUrlWithParams({
seed: randomize
})})<br> You can get back to this particular generated graphic using this link.`
Insert cell
strokeWidth = 1.5
Insert cell
function drawTrapezoids({
dimensions,
midPoints,
trapezoids,
gapUV,
stroke,
strokeWidth
}) {
const { marginX, marginY, size, width, height } = dimensions;
const cellSize = size / rows;
const gap = computeGap(cellSize, gapUV);
const s = cellSize - gap;

const line = d3.line().curve(d3.curveLinearClosed);

return midPoints.map(([u, v], i) => {
const cx = math.lerp(
marginX + gap / 2 + s / 2,
width - marginX - gap / 2 - s / 2,
u
);
const cy = math.lerp(
marginY + gap / 2 + s / 2,
height - marginY - gap / 2 - s / 2,
v
);

const trapezoid = trapezoids[i];
const polygon = trapezoid.map(([u, v]) => [
math.lerp(-s / 2, s / 2, u),
math.lerp(-s / 2, s / 2, v)
]);

return htl.svg`<g
transform=${`translate(${cx},${cy})`}
stroke-linejoin="round"
style="filter: url(#foreground-filter)"
>
<path d=${line(
polygon
)} fill="none" stroke=${stroke} stroke-width=${strokeWidth}></path>
</g>`;
});
}
Insert cell
function computeGap(cellSize, gapUV) {
return math.lerp(0, cellSize / 1.5, gapUV);
}
Insert cell
function drawDebug({ dimensions, midPoints, rows, gapUV, debug } = {}) {
if (!debug) return "";
const { marginX, marginY, size, width, height } = dimensions;
const cellSize = size / rows;
const gap = computeGap(cellSize, gapUV);
const s = cellSize - gap;

return midPoints.map(([u, v]) => {
const cx = math.lerp(
marginX + gap / 2 + s / 2,
width - marginX - gap / 2 - s / 2,
u
);
const cy = math.lerp(
marginY + gap / 2 + s / 2,
height - marginY - gap / 2 - s / 2,
v
);
const x = cx - s / 2;
const y = cy - s / 2;
return svg`<rect x=${x} y=${y} width=${s} height=${s} fill="none" stroke="#f0f"></rect>`;
});
}
Insert cell
midPoints = math
.linspace(rows, true)
.flatMap((u) => math.linspace(rows, true).map((v) => [u, v]))
Insert cell
unitSquare = [
[0, 0],
[1, 0],
[1, 1],
[0, 1]
]
Insert cell
trapezoids = {
randomize;
const uAmplitude = math.lerp(0, 3, deformity);
const uFrequency = math.lerp(1 / 100, 0.5, randomness);

const vDeformity = math.clamp01(math.inverseLerp(0.5, 1, deformity));
const vAmplitude = math.lerp(0, 0.2, vDeformity);
const vFrequency = math.lerp(1 / 100, 0.5, randomness);

return math.linspace(rows, true).flatMap((_, j) =>
math.linspace(rows, true).map((_, i) => {
return unitSquare.map(([u, v], k) => {
const nu = rnd.noise3D(i, j, k * 1000, uFrequency, uAmplitude);
const nv = rnd.noise3D(i, j, k * 20000, vFrequency, vAmplitude);
return [u + nu, v + nv];
});
})
);
}
Insert cell
rows = Math.sqrt(squares)
Insert cell
aspectRatio = 1
Insert cell
dimensions = {
const w = Math.max(480, width);
const h = Math.floor(width / aspectRatio);
const maxGridSize = Math.min(w, h);
const margin = math.lerp(0, maxGridSize / 4, marginUV);
const gridSize = maxGridSize - 2 * margin;
const marginX = (w - gridSize) / 2;
const marginY = (h - gridSize) / 2;

return {
aspectRatio,
width: w,
height: h,
marginX,
marginY,
size: gridSize
};
}
Insert cell
function roundToDecimal(num, decimals = 3) {
const multiplier = Math.pow(10, decimals);
return Math.round(num * multiplier) / multiplier;
}
Insert cell
palette = ({
bg: "hsl(72, 14%, 93%)",
fg: "hsl(7, 9%, 19%)"
})
Insert cell
rnd = {
CSRandom.setSeed(randomize);
return CSRandom;
}
Insert cell
svg = htl.svg
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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