Public
Edited
Sep 8, 2023
1 star
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("stroke-width", 2);

const circles = d3.range(20).map(i => ({
x: Math.random() * (width - radius * 2) + radius,
y: Math.random() * (height - radius * 2) + radius,
}));

let voronoi = d3.Delaunay
.from(circles, d => d.x, d => d.y)
.voronoi([0, 0, width, height]);

const circle = svg.append("g")
.selectAll("circle")
.data(circles)
.join("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r",1)
.attr("fill", 'black');

const mesh = svg.append("path")
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("stroke-width", 1)
.attr("d", voronoi.render());

const cell = svg.append("g")
.attr("fill", "none")
.attr("pointer-events", "all")
.selectAll("path")
.data(circles)
.join("path")
.attr("d", (d, i) => voronoi.renderCell(i))
.call(d3.drag()
.on("start", (event, d) => circle.filter(p => p === d).raise().attr("stroke", "black"))
.on("drag", (event, d) => (d.x = event.x, d.y = event.y))
.on("end", (event, d) => circle.filter(p => p === d).attr("stroke", null))
.on("start.update drag.update end.update", update));

function update() {
voronoi = d3.Delaunay.from(circles, d => d.x, d => d.y).voronoi([0, 0, width, height]);
circle.attr("cx", d => d.x).attr("cy", d => d.y);
cell.attr("d", (d, i) => voronoi.renderCell(i));
mesh.attr("d", voronoi.render());
}

return svg.node();
}
Insert cell
prelim_data = FileAttachment("prelim_data.json").json()
Insert cell
height = 600
Insert cell
radius = 32
Insert cell
d3 = require("d3@6")
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