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 cell = svg.append("defs")
.selectAll("clipPath")
.data(circles)
.join("clipPath")
.attr("id", (d, i) => (d.id = DOM.uid("cell")).id)
.append("path")
.attr("d", (d, i) => voronoi.renderCell(i));
const circle = svg.append("g")
.selectAll("g")
.data(circles)
.join("g")
.attr("clip-path", d => d.id)
.append("g")
.attr("transform", d => `translate(${d.x},${d.y})`)
.call(g => g.append("circle")
.attr("r", radius)
.attr("fill", (d, i) => d3.schemeCategory10[i % 10])
.attr("fill-opacity", 0.2))
.call(g => g.append("circle")
.attr("r", 2.5))
.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("transform", d => `translate(${d.x},${d.y})`);
cell.attr("d", (d, i) => voronoi.renderCell(i));
}
return svg.node();
}