voronoi = ({ x, y, stroke: tstroke, fill: tfill, z, ...options } = {}) => {
const S = (tstroke && isNaN(d3.rgb(tstroke).r)) || z ? [] : null;
const stroke = S ? undefined : tstroke;
const F = (tfill && isNaN(d3.rgb(tfill).r)) || z ? [] : null;
const fill = F ? undefined : tfill;
return Plot.initializer(
{ x, y, stroke, fill, z, ...options },
(
data,
facets,
{ x: X, y: Y, stroke: ZS, fill: ZF },
scales,
dimensions
) => {
if (X.scale == null) X = X.value;
else X = X.value.map(scales[X.scale]);
if (Y.scale == null) Y = Y.value;
else Y = Y.value.map(scales[Y.scale]);
const Z = z ? Plot.valueof(data, z) : ZS ? ZS : ZF ? ZF : {};
const P = [];
const X0 = [];
const Y0 = [];
let k = -1;
const newFacets = [];
const newData = [];
for (const facet of facets) {
const f = [];
for (const [z, I] of d3.group(facet, (i) => Z[i])) {
const voronoi = d3.Delaunay.from(
I,
(i) => X[i],
(i) => Y[i]
).voronoi([
dimensions.marginLeft,
dimensions.marginTop,
dimensions.width - dimensions.marginRight,
dimensions.height - dimensions.marginBottom
]);
for (const c of voronoi.cellPolygons()) {
for (const p of c.slice(1)) {
f.push(++k);
X0.push(p[0]);
Y0.push(p[1]);
P.push(c.index);
newData.push(data[c.index]);
}
}
newFacets.push(f);
}
}
return {
data: newData,
facets: newFacets,
channels: {
x: { value: X0 },
y: { value: Y0 },
z: { value: P },
...(S && {
stroke: { value: Plot.valueof(newData, tstroke), scale: "color" }
}),
...(F && {
fill: { value: Plot.valueof(newData, tfill), scale: "color" }
})
}
};
}
);
}