Public
Edited
Mar 29
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
regenerate;

const width = 640,
height = 640,
cols = 8,
rows = 8,
margin = 5,
cellWidth = width / cols,
cellHeight = height / rows,
squares = fillWithSquares(cols, rows),
colors = await patterns(palettes[selectPalette]),
shapes = [rect, circle, diamond, x],
n = colors.length,
m = shapes.length;

function draw(app) {
app.append(cm.clear, { fill: background });

app
.data(squares)
.append(cm.group, {
x: (d) => d.col * cellWidth + margin,
y: (d) => d.row * cellHeight + margin
})
.append(cm.path, {
d: (d) => {
const size = d.size * cellWidth - margin * 2;
const path = shapes[cm.randomInt(0, m)];
const ctx = cm.pathContext();
path(ctx, 0, 0, size, size);
return ctx.toArray();
},
fill: (d) => colors[cm.randomInt(0, n)],
stroke: "black",
strokeWidth: 2
});
}

function dispose(app) {
invalidation.then(() => app.dispose());
}

return cm.app({ width, height }).call(draw).call(dispose).start().node();
}
Insert cell
function rect(ctx, x, y, size) {
ctx.moveTo(x, y);
ctx.lineTo(x + size, y);
ctx.lineTo(x + size, y + size);
ctx.lineTo(x, y + size);
ctx.closePath();
}
Insert cell
function circle(ctx, x, y, size) {
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2);
}
Insert cell
function diamond(ctx, x, y, size) {
ctx.moveTo(x + size / 2, y);
ctx.lineTo(x, y + size / 2);
ctx.lineTo(x + size / 2, y + size);
ctx.lineTo(x + size, y + size / 2);
ctx.closePath();
}
Insert cell
function x(ctx, x, y, size) {
const b = size / 2;
const a = b / 2;
ctx.moveTo(x + a, y);
ctx.lineTo(x, y + a);
ctx.lineTo(x + a, y + b);
ctx.lineTo(x, y + b + a);
ctx.lineTo(x + a, y + b + b);
ctx.lineTo(x + b, y + b + a);
ctx.lineTo(x + a + b, y + b + b);
ctx.lineTo(x + b + b, y + a + b);
ctx.lineTo(x + a + b, y + b);
ctx.lineTo(x + b + b, y + a);
ctx.lineTo(x + a + b, y);
ctx.lineTo(x + b, y + a);
ctx.closePath();
}
Insert cell
function patterns(palette) {
const patternElements = palette.patterns
.replace(/\r?\n(?!<pattern)/g, "")
.split(/\n/g);
return Promise.all(patternElements.map(pattern));
}
Insert cell
function pattern(patternElement) {
const id = patternElement.match(/id="([^"]+)"/)[1];
const width = patternElement.match(/width="([^"]+)"/)[1] ?? 100;
const height = patternElement.match(/height="([^"]+)"/)[1] ?? 100;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const image = new Image();
const svgContent = `
<svg width="${width * 2}" height="${
height * 2
}" xmlns="http://www.w3.org/2000/svg" transform="scale(2, 2)">
${patternElement}
<rect x="0" fill="url(#${id})" width="100%" height="100%"/>
</svg>`;
image.src = "data:image/svg+xml," + encodeURIComponent(svgContent);
return new Promise((resolve) => {
image.onload = () => {
const pattern = context.createPattern(image, "repeat");
const matrix = createMatrix(0.5);
pattern.setTransform(matrix);
resolve(pattern);
};
});
}
Insert cell
function createMatrix(scale, rotation = 0) {
const radian = (rotation * Math.PI) / 180;
const matrix = {
a: Math.cos(radian) * scale,
b: Math.sin(radian) * scale,
c: -Math.sin(radian) * scale,
d: Math.cos(radian) * scale,
e: 0,
f: 0
};
return matrix;
}
Insert cell
cm = require(await FileAttachment("cm@2.umd.min.js").url())
Insert cell
import { fillWithSquares } from "@esperanc/random-shapes-in-a-grid"
Insert cell
import { palettes } from "@tomshanley/cheysson-color-palettes"
Insert cell
import { quote } from "@pearmini/charming-shared"
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