class Voronoi extends Plot.Mark {
constructor(
data,
{
x = first,
y = second,
z,
r,
transform,
...style
} = {}
) {
super(
data,
[
{name: "x", value: x, scale: "x"},
{name: "y", value: y, scale: "y"},
{name: "z", value: z, optional: true},
],
transform
);
Plot.Style && Plot.Style(this)
}
initialize (data) {
if (data !== undefined) data = this.transform(data);
this.data = data;
return {
index: data === undefined ? undefined : Float32Array.from(data, indexOf),
channels: this.channels.map(channel => {
const {name} = channel;
return [name == null ? undefined : name + "", Channel(data, channel)];
})
};
}
render (I,
{ x, y },
{ x: X, y: Y },
{ width, height, marginLeft, marginTop, marginRight, marginBottom }
) {
mutable debugR = arguments;
const g = create("svg:g");
const v = d3.Delaunay.from(I, i => x(X[i]), i => y(Y[i]))
.voronoi([marginLeft, marginTop, width - marginRight, height - marginBottom]);
const paths = g.selectAll("path")
.data(I)
.join("path")
.attr("d", i => v.renderCell(i))
.style("fill", "none")
.style("stroke", "red");
g.append("rect")
.attr("x", marginLeft)
.attr("y", marginTop)
.attr("width", width - marginRight - marginLeft)
.attr("height", height - marginBottom-marginTop)
.style("fill", "none")
.style("pointer-events", "all")
.on("pointermove", (event) => {
const p = d3.pointers(event)[0],
i = v.delaunay.find(...p),
cx = x(X[i]),
cy = y(Y[i]),
dist = Math.hypot(cx - p[0], cy - p[1]);
if (dist < 20) {
mutable found = this.data[i];
finder.attr("r", 8)
.attr("cx", cx)
.attr("cy", cy);
} else {
mutable found = undefined;
finder.attr("r", 0);
}
});
const finder = g.append("circle").attr("fill", "none").attr("stroke", "black").attr("stroke-width", 2);
return g.node();
}
}