Public
Edited
Mar 22, 2021
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function* lloydRelax(points, context, iterations) {
const diameter = circleRadius * 2;
const voronoi = new d3.Delaunay(points).voronoi(getBounds(points.length / 2).bounds);
for (let i = 0; i < iterations; i++) {
context.clearRect(0, 0, width, height);
context.beginPath();
lloydIteration(points, voronoi, context);
context.stroke();
if (showCounters) {
context.font = "50% monospace";
context.fillText(`Blobs: ${(points.length / 2).toString().padStart(4, " ")}`, 1, 10);
context.fillText(`Iterations: ${i.toString().padStart(4, "0")}`, 1, 20);
}
yield context.canvas;
voronoi.update();
}
}
Insert cell
Insert cell
function lloydIteration(points, voronoi, context) {
const line = d3.line()
.curve(d3.curveLinearClosed)
.x(d => d[0])
.y(d => d[1])
.context(context);
for (let i = 0; i < points.length; i += 2) {
const x = points[i];
const y = points[i + 1];
const cell = voronoi.cellPolygon(i >> 1);

const c = blob(x, y, circleRadius, cell);
if (c === null) continue;

line(c);

const [xc, yc] = d3.polygonCentroid(cell);
points[i] = xc;
points[i+1] = yc;
}
}
Insert cell
Insert cell
function circle(x, y, radius) {
const segments = 100;
return Array.from({length: segments}, (_, i) =>
[x + radius * Math.cos(2 * Math.PI * i / segments), y + radius * Math.sin(2 * Math.PI * i / segments)]);
}
Insert cell
Insert cell
function blob(x, y, radius, cell) {
const clip = cell.reverse();
return polygonClip(clip, circle(x, y, radius));
}
Insert cell
Insert cell
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