Public
Edited
Oct 24, 2023
29 stars
Insert cell
Insert cell
chart = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(null, context).pointRadius(2.5);

function render(i) {
context.clearRect(0, 0, width, height);

if (i >= 0) {
context.fillStyle = "#0f0";
context.beginPath();
voronoi.renderCell(i, context);
context.fill();

const V = [...voronoi.neighbors(i)];
const D = [...delaunay.neighbors(i)];
const U = D.filter(j => !V.includes(j));

context.fillStyle = "#ff0";
context.beginPath();
for (const j of U) voronoi.renderCell(j, context);
context.fill();

context.fillStyle = "#cfc";
context.beginPath();
for (const j of V) voronoi.renderCell(j, context);
context.fill();

context.beginPath();
for (const j of D) {
context.moveTo(...points[i]);
context.lineTo(...points[j]);
}
context.strokeStyle = "#000";
context.stroke();
}

context.strokeStyle = "#000";

context.beginPath();
voronoi.render(context);
voronoi.renderBounds(context);
context.stroke();

context.fillStyle = "#000";
context.beginPath();
path({type: "MultiPoint", coordinates: points});
context.fill();
}

context.canvas.ontouchmove =
context.canvas.onmousemove = event => {
render(delaunay.find(event.layerX, event.layerY));
};

// Find a case where the two neighbor sets are unequal (if any).
for (let i = 0, n = points.length; i < n; ++i) {
if ([...voronoi.neighbors(i)].length !== [...delaunay.neighbors(i)].length) {
render(i);
break;
}
}

return context.canvas;
}
Insert cell
points = Array.from({length: 200}, () => [Math.random() * width, Math.random() * height])
Insert cell
delaunay = d3.Delaunay.from(points)
Insert cell
voronoi = delaunay.voronoi([0.5, 0.5, width - 0.5, height - 0.5])
Insert cell
height = 600
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