From 85cd8f1c125157df9562970408ce31a6e9f49e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 23 May 2023 22:42:46 +0200 Subject: [PATCH 01/17] selectively generate one voronoi cell with the pointer --- src/marks/delaunay.js | 34 ++++++++++++++++++------- test/plots/us-state-capitals-voronoi.ts | 13 +++++++++- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index cf8c696c01..8e18f1e19f 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -212,7 +212,7 @@ class Hull extends AbstractDelaunayMark { } class Voronoi extends Mark { - constructor(data, options = {}) { + constructor(data, {initializer, ...options} = {}) { const {x, y, z} = options; super( data, @@ -221,20 +221,36 @@ class Voronoi extends Mark { y: {value: y, scale: "y", optional: true}, z: {value: z, optional: true} }, - options, + { + ...options, + // TODO compose with existing initializer + initializer: function (data, facets) { + const D = new Array(data.length); + for (const f of facets) { + for (const i of f) D[i] = f; + } + return {data, facets, channels: {diagrams: {value: D}}}; + } + }, voronoiDefaults ); } render(index, scales, channels, dimensions, context) { const {x, y} = scales; - const {x: X, y: Y, z: Z} = channels; + const {x: X, y: Y, z: Z, diagrams: D} = channels; const [cx, cy] = applyFrameAnchor(this, dimensions); const xi = X ? (i) => X[i] : constant(cx); const yi = Y ? (i) => Y[i] : constant(cy); const mark = this; - function cells(index) { - const delaunay = Delaunay.from(index, xi, yi); + const i0 = index[0]; + if (i0 === undefined) return create("svg:g", context).node(); + const diagram = D[index[0]]; + function cells(subset) { + subset = subset.filter((i) => isFinite(xi(i)) && isFinite(yi(i))); + const rank = new Array(subset.length); + for (let c = 0; c < subset.length; ++c) rank[subset[c]] = c; + const delaunay = Delaunay.from(subset, xi, yi); // TODO memoize? const voronoi = voronoiof(delaunay, dimensions); select(this) .selectAll() @@ -242,23 +258,23 @@ class Voronoi extends Mark { .enter() .append("path") .call(applyDirectStyles, mark) - .attr("d", (_, i) => voronoi.renderCell(i)) + .attr("d", (i) => voronoi.renderCell(rank[i])) .call(applyChannelStyles, mark, channels); } return create("svg:g", context) - .call(applyIndirectStyles, this, dimensions, context) + .call(applyIndirectStyles, this, dimensions, context) // TODO avoid creating a new clip-path each time .call(applyTransform, this, {x: X && x, y: Y && y}) .call( Z ? (g) => g .selectAll() - .data(group(index, (i) => Z[i]).values()) + .data(group(diagram, (i) => Z[i]).values()) .enter() .append("g") .each(cells) - : (g) => g.datum(index).each(cells) + : (g) => g.datum(diagram).each(cells) ) .node(); } diff --git a/test/plots/us-state-capitals-voronoi.ts b/test/plots/us-state-capitals-voronoi.ts index c89d09e5e5..820f26b14e 100644 --- a/test/plots/us-state-capitals-voronoi.ts +++ b/test/plots/us-state-capitals-voronoi.ts @@ -16,7 +16,18 @@ export async function usStateCapitalsVoronoi() { marks: [ Plot.geo(nation, {fill: "currentColor", fillOpacity: 0.2}), Plot.dot(capitals, {x: "longitude", y: "latitude", r: 2.5, fill: "currentColor"}), - Plot.voronoi(capitals, {x: "longitude", y: "latitude", clip: "sphere", title: "state", pointerEvents: "all"}), + Plot.voronoi( + capitals, + Plot.pointer({ + x: "longitude", + y: "latitude", + clip: "sphere", + title: "state", + fill: "red", + fillOpacity: 0.4, + pointerEvents: "all" + }) + ), Plot.sphere({strokeWidth: 2}) ] }); From 1f5776eef7ed1444f29f3594b9a3891db21015bf Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 23 May 2023 18:59:49 -0700 Subject: [PATCH 02/17] voronoi initializer --- src/marks/delaunay.js | 96 +++---- src/style.js | 10 + test/output/penguinCulmenVoronoi.svg | 341 ++++++++++++++++++++++++ test/output/penguinVoronoi1D.svg | 231 ++++++++++++++++ test/output/usStateCapitalsVoronoi.svg | 56 +--- test/plots/us-state-capitals-voronoi.ts | 5 +- 6 files changed, 633 insertions(+), 106 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 8e18f1e19f..67a2f3158a 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -4,13 +4,11 @@ import {maybeCurve} from "../curve.js"; import {Mark} from "../mark.js"; import {markers, applyMarkers} from "../marker.js"; import {constant, maybeTuple, maybeZ} from "../options.js"; -import { - applyChannelStyles, - applyDirectStyles, - applyFrameAnchor, - applyIndirectStyles, - applyTransform -} from "../style.js"; +import {applyPosition} from "../projection.js"; +import {applyFrameAnchor, applyTransform} from "../style.js"; +import {applyChannelStyles, applyDirectStyles, applyIndirectStyles} from "../style.js"; +import {initializer} from "../transforms/basic.js"; +import {maybeGroup} from "../transforms/group.js"; const delaunayLinkDefaults = { ariaLabel: "delaunay link", @@ -94,14 +92,15 @@ class DelaunayLink extends Mark { for (const k in channels) newChannels[k].push(channels[k][tj]); } + // TODO move to initializer? const {halfedges, hull, triangles} = Delaunay.from(index, xi, yi); + // inner edges for (let i = 0; i < halfedges.length; ++i) { - // inner edges const j = halfedges[i]; if (j > i) link(triangles[i], triangles[j]); } + // convex hull for (let i = 0; i < hull.length; ++i) { - // convex hull link(hull[i], hull[(i + 1) % hull.length]); } @@ -164,6 +163,7 @@ class AbstractDelaunayMark extends Mark { const yi = Y ? (i) => Y[i] : constant(cy); const mark = this; + // TODO move to initializer? function mesh(index) { const delaunay = Delaunay.from(index, xi, yi); select(this) @@ -212,7 +212,7 @@ class Hull extends AbstractDelaunayMark { } class Voronoi extends Mark { - constructor(data, {initializer, ...options} = {}) { + constructor(data, options = {}) { const {x, y, z} = options; super( data, @@ -221,61 +221,45 @@ class Voronoi extends Mark { y: {value: y, scale: "y", optional: true}, z: {value: z, optional: true} }, - { - ...options, - // TODO compose with existing initializer - initializer: function (data, facets) { - const D = new Array(data.length); - for (const f of facets) { - for (const i of f) D[i] = f; + initializer(options, function (data, facets, channels, scales, dimensions, context) { + let {x: X, y: Y, z: Z} = channels; + ({x: X, y: Y} = applyPosition(channels, scales, context)); + Z = Z?.value; + const C = new Array(data.length); + const [cx, cy] = applyFrameAnchor(this, dimensions); + const xi = X ? (i) => X[i] : constant(cx); + const yi = Y ? (i) => Y[i] : constant(cy); + for (let facet of facets) { + if (X) facet = facet.filter((i) => isFinite(X[i])); + if (Y) facet = facet.filter((i) => isFinite(Y[i])); + for (const [, index] of maybeGroup(facet, Z)) { + const delaunay = Delaunay.from(index, xi, yi); + const voronoi = voronoiof(delaunay, dimensions); + for (let i = 0, n = index.length; i < n; ++i) { + C[index[i]] = voronoi.renderCell(i); + } } - return {data, facets, channels: {diagrams: {value: D}}}; } - }, + return {data, facets, channels: {cells: {value: C}}}; + }), voronoiDefaults ); } render(index, scales, channels, dimensions, context) { const {x, y} = scales; - const {x: X, y: Y, z: Z, diagrams: D} = channels; - const [cx, cy] = applyFrameAnchor(this, dimensions); - const xi = X ? (i) => X[i] : constant(cx); - const yi = Y ? (i) => Y[i] : constant(cy); - const mark = this; - - const i0 = index[0]; - if (i0 === undefined) return create("svg:g", context).node(); - const diagram = D[index[0]]; - function cells(subset) { - subset = subset.filter((i) => isFinite(xi(i)) && isFinite(yi(i))); - const rank = new Array(subset.length); - for (let c = 0; c < subset.length; ++c) rank[subset[c]] = c; - const delaunay = Delaunay.from(subset, xi, yi); // TODO memoize? - const voronoi = voronoiof(delaunay, dimensions); - select(this) - .selectAll() - .data(index) - .enter() - .append("path") - .call(applyDirectStyles, mark) - .attr("d", (i) => voronoi.renderCell(rank[i])) - .call(applyChannelStyles, mark, channels); - } - + const {x: X, y: Y, cells: C} = channels; return create("svg:g", context) - .call(applyIndirectStyles, this, dimensions, context) // TODO avoid creating a new clip-path each time + .call(applyIndirectStyles, this, dimensions, context) .call(applyTransform, this, {x: X && x, y: Y && y}) - .call( - Z - ? (g) => - g - .selectAll() - .data(group(diagram, (i) => Z[i]).values()) - .enter() - .append("g") - .each(cells) - : (g) => g.datum(diagram).each(cells) - ) + .call((g) => { + g.selectAll() + .data(index) + .enter() + .append("path") + .call(applyDirectStyles, this) + .attr("d", (i) => C[i]) + .call(applyChannelStyles, this, channels); + }) .node(); } } diff --git a/src/style.js b/src/style.js index 53601f3f62..39960fabe3 100644 --- a/src/style.js +++ b/src/style.js @@ -297,6 +297,16 @@ export function* groupIndex(I, position, mark, channels) { } } +// TODO Accept other types of clips (paths, urls, x, y, other marks…)? +// https://github.com/observablehq/plot/issues/181 +export function maybeClip(clip) { + if (clip === true) clip = "frame"; + else if (clip === false) clip = null; + else if (clip != null) clip = keyword(clip, "clip", ["frame", "sphere"]); + return clip; +} + +// TODO avoid creating a new clip-path each time? // Note: may mutate selection.node! function applyClip(selection, mark, dimensions, context) { let clipUrl; diff --git a/test/output/penguinCulmenVoronoi.svg b/test/output/penguinCulmenVoronoi.svg index d5be80710c..f157d59791 100644 --- a/test/output/penguinCulmenVoronoi.svg +++ b/test/output/penguinCulmenVoronoi.svg @@ -414,6 +414,7 @@ +<<<<<<< HEAD @@ -756,5 +757,345 @@ +======= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>>>>>>> c08ce379 (voronoi initializer) \ No newline at end of file diff --git a/test/output/penguinVoronoi1D.svg b/test/output/penguinVoronoi1D.svg index 23bf873942..780e407933 100644 --- a/test/output/penguinVoronoi1D.svg +++ b/test/output/penguinVoronoi1D.svg @@ -38,6 +38,7 @@ Adelie (MALE) Torgersen +<<<<<<< HEAD Adelie (FEMALE) Torgersen @@ -51,11 +52,15 @@ Torgersen Adelie (FEMALE) +======= + <path d="M181.04164687455392,0L181.04168171928973,30L177.01385173207882,30L177.0139278753263,0Z" fill="#4e79a7"><title>Adelie (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Torgersen Adelie (MALE) Torgersen +<<<<<<< HEAD Adelie (null) Torgersen @@ -84,6 +89,9 @@ Torgersen Adelie (MALE) +======= + <path d="M317.98614958190575,30L317.9860727532543,0L324.0277940203273,0L324.02776345100403,30Z" fill="#4e79a7"><title>Adelie (MALE) +>>>>>>> c08ce379 (voronoi initializer) Torgersen Adelie (FEMALE) @@ -92,6 +100,7 @@ <path d="M275.6944417596831,0L275.69443982826346,30L267.6388730979733,30L267.638905444604,0Z" fill="#4269d0"><title>Adelie (MALE) Torgersen +<<<<<<< HEAD Adelie (FEMALE) Biscoe @@ -171,11 +180,30 @@ Dream Adelie (MALE) +======= + <path d="M114.58332232007535,0L114.58334949525134,30L108.5416434603081,30L108.5416941096706,0Z" fill="#4e79a7"><title>Adelie (FEMALE) + Biscoe + + Adelie (FEMALE) + Dream + + Adelie (FEMALE) + Dream + + Adelie (FEMALE) + Dream + + Adelie (FEMALE) + Dream + + Adelie (MALE) +>>>>>>> c08ce379 (voronoi initializer) Dream Adelie (null) Dream +<<<<<<< HEAD Adelie (FEMALE) Dream @@ -285,11 +313,45 @@ Torgersen Adelie (FEMALE) +======= + <path d="M267.638905444604,0L267.6388730979733,30L259.58334427255613,30L259.58332938881216,0Z" fill="#4e79a7"><title>Adelie (MALE) + Dream + + Adelie (FEMALE) + Biscoe + + Adelie (MALE) + Biscoe + + Adelie (FEMALE) + Biscoe + + Adelie (FEMALE) + Biscoe + + Adelie (FEMALE) + Biscoe + + Adelie (MALE) + Biscoe + + Adelie (MALE) + Torgersen + + Adelie (MALE) + Torgersen + + Adelie (MALE) + Torgersen + + Adelie (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Dream Adelie (MALE) Dream +<<<<<<< HEAD Adelie (MALE) Dream @@ -336,11 +398,18 @@ Biscoe Adelie (MALE) +======= + <path d="M297.84722057901774,0L297.8472286731956,30L291.80555310853725,30L291.805564439134,0Z" fill="#4e79a7"><title>Adelie (MALE) + Dream + + Adelie (MALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Adelie (FEMALE) Biscoe +<<<<<<< HEAD Adelie (MALE) Biscoe @@ -357,6 +426,12 @@ Biscoe Adelie (FEMALE) +======= + <path d="M64.23607925975584,30L64.23614615225246,0L70.27776340301583,0L70.2777953747487,30Z" fill="#4e79a7"><title>Adelie (FEMALE) + Biscoe + + Adelie (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Adelie (MALE) @@ -365,6 +440,7 @@ <path d="M213.26389985076756,0L213.26387355874758,30L209.23609325926105,30L209.23613356705138,0Z" fill="#4269d0"><title>Adelie (FEMALE) Biscoe +<<<<<<< HEAD Adelie (MALE) Biscoe @@ -402,20 +478,34 @@ Torgersen Adelie (MALE) +======= + <path d="M281.73612015650525,30L281.7360971441389,0L285.76386910845633,0L285.76391233596587,30Z" fill="#4e79a7"><title>Adelie (MALE) + Biscoe + + Adelie (MALE) + Biscoe + + Adelie (MALE) +>>>>>>> c08ce379 (voronoi initializer) Torgersen Adelie (FEMALE) Torgersen +<<<<<<< HEAD Adelie (MALE) Torgersen Adelie (FEMALE) +======= + <path d="M120.62496505075026,30L120.62503749133616,0L124.65276367421538,0L124.65278696244403,30Z" fill="#4e79a7"><title>Adelie (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Torgersen Adelie (MALE) Torgersen +<<<<<<< HEAD Adelie (FEMALE) Torgersen @@ -490,10 +580,30 @@ Chinstrap (FEMALE) Dream +======= + Adelie (MALE) + Torgersen + + Adelie (MALE) + Dream + + Adelie (MALE) + Dream + + Adelie (MALE) + Dream + + Adelie (MALE) + Dream + + Adelie (MALE) + Dream +>>>>>>> c08ce379 (voronoi initializer) Chinstrap (MALE) Dream +<<<<<<< HEAD Chinstrap (MALE) Dream @@ -552,11 +662,21 @@ Dream Chinstrap (MALE) +======= + <path d="M164.93058716646527,0L164.93052771290883,30L160.90281623199576,30L160.9027394548966,0Z" fill="#f28e2c"><title>Chinstrap (FEMALE) + Dream + + Chinstrap (FEMALE) + Dream + + Chinstrap (MALE) +>>>>>>> c08ce379 (voronoi initializer) Dream Chinstrap (FEMALE) Dream +<<<<<<< HEAD Chinstrap (MALE) Dream @@ -700,10 +820,36 @@ Gentoo (FEMALE) Biscoe +======= + Chinstrap (MALE) + Dream + + Chinstrap (FEMALE) + Dream + + Chinstrap (FEMALE) + Dream + + Chinstrap (MALE) + Dream + + Chinstrap (FEMALE) + Dream + + Chinstrap (MALE) + Dream + + Chinstrap (FEMALE) + Dream + + Chinstrap (MALE) + Dream +>>>>>>> c08ce379 (voronoi initializer) Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (MALE) Biscoe @@ -729,11 +875,18 @@ Biscoe Gentoo (FEMALE) +======= + <path d="M324.02776345100403,30L324.0277940203273,0L330.0694148320279,0L330.06947646625434,30Z" fill="#e15759"><title>Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (FEMALE) Biscoe @@ -744,6 +897,9 @@ Biscoe Gentoo (MALE) +======= + <path d="M620,0L620,30L589.8611129221783,30L589.8611125044947,0Z" fill="#e15759"><title>Gentoo (MALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (FEMALE) @@ -752,6 +908,7 @@ <path d="M460.97222365477444,0L460.97222812357745,30L452.9166818075012,30L452.91665020794636,0Z" fill="#ff725c"><title>Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (MALE) Biscoe @@ -789,6 +946,12 @@ Biscoe Gentoo (FEMALE) +======= + <path d="M307.9166530086645,0L307.9166757845127,30L301.87496634263863,30L301.8750316028728,0Z" fill="#e15759"><title>Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (MALE) @@ -800,6 +963,7 @@ <path d="M469.02779366290554,0L469.02776027386705,30L460.97222812357745,30L460.97222365477444,0Z" fill="#ff725c"><title>Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (FEMALE) Biscoe @@ -948,11 +1112,45 @@ Biscoe Gentoo (MALE) +======= + <path d="M493.194457348966,0L493.19443650544974,30L485.1389059492565,30L485.1388761244209,0Z" fill="#e15759"><title>Gentoo (MALE) + Biscoe + + Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) + Biscoe + + Gentoo (MALE) + Biscoe + + Gentoo (MALE) + Biscoe + + Gentoo (MALE) + Biscoe + + Gentoo (FEMALE) + Biscoe + + Gentoo (MALE) + Biscoe + + Gentoo (MALE) + Biscoe + + Gentoo (MALE) + Biscoe + + Gentoo (MALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (FEMALE) Biscoe +<<<<<<< HEAD Gentoo (MALE) Biscoe @@ -987,11 +1185,21 @@ Biscoe Gentoo (FEMALE) +======= + <path d="M378.40274609028046,30L378.4028126832142,0L382.43054778617346,0L382.4305583525107,30Z" fill="#e15759"><title>Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (FEMALE) Biscoe @@ -999,11 +1207,15 @@ Biscoe Gentoo (FEMALE) +======= + <path d="M394.51390930437447,30L394.5138640257929,0L398.5416586241414,0L398.54167924574955,30Z" fill="#e15759"><title>Gentoo (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (null) Biscoe @@ -1023,11 +1235,18 @@ Biscoe Gentoo (FEMALE) +======= + <path d="M330.06947646625434,30L330.0694148320279,0L334.0972611851675,0L334.09718510600567,30Z" fill="#e15759"><title>Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (MALE) Biscoe +<<<<<<< HEAD Gentoo (FEMALE) Biscoe @@ -1053,13 +1272,25 @@ Biscoe Gentoo (MALE) +======= + <path d="M301.8750316028728,0L301.87496634263863,30L297.8472286731956,30L297.84722057901774,0Z" fill="#e15759"><title>Gentoo (FEMALE) + Biscoe + + Gentoo (FEMALE) + Biscoe + + Gentoo (MALE) +>>>>>>> c08ce379 (voronoi initializer) Biscoe Gentoo (FEMALE) Biscoe +<<<<<<< HEAD Gentoo (MALE) Biscoe +======= +>>>>>>> c08ce379 (voronoi initializer) diff --git a/test/output/usStateCapitalsVoronoi.svg b/test/output/usStateCapitalsVoronoi.svg index 25ecb0ebcf..14bf8f5a13 100644 --- a/test/output/usStateCapitalsVoronoi.svg +++ b/test/output/usStateCapitalsVoronoi.svg @@ -66,58 +66,16 @@ - + - Alabama - Arizona - Arkansas - California - Colorado - Connecticut - Delaware - Florida - Georgia - Idaho - Illinois - Indiana - Iowa - Kansas - Kentucky - Louisiana - Maine - Maryland - Massachusetts - Michigan - Minnesota - Mississippi - Missouri - Montana - Nebraska - Nevada - New Hampshire - New Jersey - New Mexico - North Carolina - North Dakota - New York - Ohio - Oklahoma - Oregon - Pennsylvania - Rhode Island - South Carolina - South Dakota - Tennessee - Texas - Utah - Vermont - Virginia - Washington - West Virginia - Wisconsin - Wyoming + + + + + + diff --git a/test/plots/us-state-capitals-voronoi.ts b/test/plots/us-state-capitals-voronoi.ts index 820f26b14e..0f72096b96 100644 --- a/test/plots/us-state-capitals-voronoi.ts +++ b/test/plots/us-state-capitals-voronoi.ts @@ -16,6 +16,7 @@ export async function usStateCapitalsVoronoi() { marks: [ Plot.geo(nation, {fill: "currentColor", fillOpacity: 0.2}), Plot.dot(capitals, {x: "longitude", y: "latitude", r: 2.5, fill: "currentColor"}), + Plot.voronoiMesh(capitals, {x: "longitude", y: "latitude", clip: "sphere"}), Plot.voronoi( capitals, Plot.pointer({ @@ -23,9 +24,11 @@ export async function usStateCapitalsVoronoi() { y: "latitude", clip: "sphere", title: "state", + stroke: "red", fill: "red", fillOpacity: 0.4, - pointerEvents: "all" + pointerEvents: "all", + maxRadius: Infinity }) ), Plot.sphere({strokeWidth: 2}) From 597d8a992a50d3c50321a8811185f79284b2eb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 24 May 2023 04:48:43 +0200 Subject: [PATCH 03/17] defined --- src/marks/delaunay.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 67a2f3158a..ee3a653bc6 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -1,6 +1,7 @@ import {group, pathRound as path, select, Delaunay} from "d3"; import {create} from "../context.js"; import {maybeCurve} from "../curve.js"; +import {defined} from "../defined.js"; import {Mark} from "../mark.js"; import {markers, applyMarkers} from "../marker.js"; import {constant, maybeTuple, maybeZ} from "../options.js"; @@ -230,8 +231,8 @@ class Voronoi extends Mark { const xi = X ? (i) => X[i] : constant(cx); const yi = Y ? (i) => Y[i] : constant(cy); for (let facet of facets) { - if (X) facet = facet.filter((i) => isFinite(X[i])); - if (Y) facet = facet.filter((i) => isFinite(Y[i])); + if (X) facet = facet.filter((i) => defined(X[i])); + if (Y) facet = facet.filter((i) => defined(Y[i])); for (const [, index] of maybeGroup(facet, Z)) { const delaunay = Delaunay.from(index, xi, yi); const voronoi = voronoiof(delaunay, dimensions); From 3cdfe67dca311f54246bf2eeaede62a2d93004be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 24 May 2023 04:59:08 +0200 Subject: [PATCH 04/17] the voronoi cell depends on i and fi (e.g. facet: exclude) --- src/marks/delaunay.js | 8 +- test/output/penguinCulmenVoronoi.svg | 1 + test/output/penguinCulmenVoronoiExclude.svg | 1161 +++++++++++++++++++ test/plots/penguin-culmen-voronoi.ts | 27 +- 4 files changed, 1193 insertions(+), 4 deletions(-) create mode 100644 test/output/penguinCulmenVoronoiExclude.svg diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index ee3a653bc6..5ea9ad6877 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -230,14 +230,15 @@ class Voronoi extends Mark { const [cx, cy] = applyFrameAnchor(this, dimensions); const xi = X ? (i) => X[i] : constant(cx); const yi = Y ? (i) => Y[i] : constant(cy); - for (let facet of facets) { + for (let [fi, facet] of facets.entries()) { if (X) facet = facet.filter((i) => defined(X[i])); if (Y) facet = facet.filter((i) => defined(Y[i])); for (const [, index] of maybeGroup(facet, Z)) { const delaunay = Delaunay.from(index, xi, yi); const voronoi = voronoiof(delaunay, dimensions); for (let i = 0, n = index.length; i < n; ++i) { - C[index[i]] = voronoi.renderCell(i); + const c = voronoi.renderCell(i); + if (c) (C[index[i]] ||= [])[fi] = c; } } } @@ -249,6 +250,7 @@ class Voronoi extends Mark { render(index, scales, channels, dimensions, context) { const {x, y} = scales; const {x: X, y: Y, cells: C} = channels; + const fi = index.fi ?? 0; return create("svg:g", context) .call(applyIndirectStyles, this, dimensions, context) .call(applyTransform, this, {x: X && x, y: Y && y}) @@ -258,7 +260,7 @@ class Voronoi extends Mark { .enter() .append("path") .call(applyDirectStyles, this) - .attr("d", (i) => C[i]) + .attr("d", (i) => C[i][fi]) .call(applyChannelStyles, this, channels); }) .node(); diff --git a/test/output/penguinCulmenVoronoi.svg b/test/output/penguinCulmenVoronoi.svg index f157d59791..55c7a3f41d 100644 --- a/test/output/penguinCulmenVoronoi.svg +++ b/test/output/penguinCulmenVoronoi.svg @@ -1098,4 +1098,5 @@ >>>>>>> c08ce379 (voronoi initializer) + \ No newline at end of file diff --git a/test/output/penguinCulmenVoronoiExclude.svg b/test/output/penguinCulmenVoronoiExclude.svg new file mode 100644 index 0000000000..b99195e84e --- /dev/null +++ b/test/output/penguinCulmenVoronoiExclude.svg @@ -0,0 +1,1161 @@ + + + + + Adelie + + + Chinstrap + + + Gentoo + + + + species + + + + + + + + + + + + + + + + + + + + + 34 + 36 + 38 + 40 + 42 + 44 + 46 + 48 + 50 + 52 + 54 + 56 + 58 + + + + ↑ culmen_length_mm + + + + + + + + + + + + + + + + + + 15 + 20 + + + 15 + 20 + + + 15 + 20 + + + + culmen_depth_mm → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/penguin-culmen-voronoi.ts b/test/plots/penguin-culmen-voronoi.ts index 07d2d7f96c..4f3bd165a5 100644 --- a/test/plots/penguin-culmen-voronoi.ts +++ b/test/plots/penguin-culmen-voronoi.ts @@ -6,7 +6,32 @@ export async function penguinCulmenVoronoi() { return Plot.plot({ marks: [ Plot.dot(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", fill: "currentColor", r: 1.5}), - Plot.voronoi(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", stroke: "species"}) + Plot.voronoi(penguins, {x: "culmen_depth_mm", y: "culmen_length_mm", stroke: "species", tip: true}) + ] + }); +} + +export async function penguinCulmenVoronoiExclude() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + const xy = {fx: "species", x: "culmen_depth_mm", y: "culmen_length_mm"}; + return Plot.plot({ + inset: 10, + marks: [ + Plot.frame(), + Plot.dot(penguins, {...xy, facet: "exclude", fill: "currentColor", r: 1.5}), + Plot.dot(penguins, {...xy, facet: "include", fillOpacity: 0.25, fill: "currentColor", r: 1.5}), + Plot.voronoiMesh(penguins, {...xy, facet: "exclude"}), + Plot.voronoi( + penguins, + Plot.pointer({ + ...xy, + facet: "exclude", + stroke: "species", + fill: "species", + fillOpacity: 0.2, + maxRadius: Infinity + }) + ) ] }); } From c4bc353347804768bf7812f09254c67729e125e6 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 23 May 2023 21:10:25 -0700 Subject: [PATCH 05/17] transpose cells channel --- src/marks/delaunay.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 5ea9ad6877..513d37a3f4 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -226,41 +226,40 @@ class Voronoi extends Mark { let {x: X, y: Y, z: Z} = channels; ({x: X, y: Y} = applyPosition(channels, scales, context)); Z = Z?.value; - const C = new Array(data.length); + const C = new Array(facets.length); const [cx, cy] = applyFrameAnchor(this, dimensions); const xi = X ? (i) => X[i] : constant(cx); const yi = Y ? (i) => Y[i] : constant(cy); for (let [fi, facet] of facets.entries()) { if (X) facet = facet.filter((i) => defined(X[i])); if (Y) facet = facet.filter((i) => defined(Y[i])); + const Cf = C[fi] = []; for (const [, index] of maybeGroup(facet, Z)) { const delaunay = Delaunay.from(index, xi, yi); const voronoi = voronoiof(delaunay, dimensions); for (let i = 0, n = index.length; i < n; ++i) { - const c = voronoi.renderCell(i); - if (c) (C[index[i]] ||= [])[fi] = c; + Cf[index[i]] = voronoi.renderCell(i); } } } - return {data, facets, channels: {cells: {value: C}}}; + return {data, facets, channels: {cells: {value: C, filter: null}}}; }), voronoiDefaults ); } render(index, scales, channels, dimensions, context) { const {x, y} = scales; - const {x: X, y: Y, cells: C} = channels; - const fi = index.fi ?? 0; + const {x: X, y: Y, cells: {[index.fi ?? 0]: C}} = channels; return create("svg:g", context) .call(applyIndirectStyles, this, dimensions, context) .call(applyTransform, this, {x: X && x, y: Y && y}) .call((g) => { g.selectAll() - .data(index) + .data(index.filter((i) => C[i] != null)) .enter() .append("path") .call(applyDirectStyles, this) - .attr("d", (i) => C[i][fi]) + .attr("d", (i) => C[i]) .call(applyChannelStyles, this, channels); }) .node(); From 57ca06d486fbdba953d1550ebdc9692ff7c2cb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 24 May 2023 06:32:54 +0200 Subject: [PATCH 06/17] prettier --- src/marks/delaunay.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 513d37a3f4..b98361dd37 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -233,7 +233,7 @@ class Voronoi extends Mark { for (let [fi, facet] of facets.entries()) { if (X) facet = facet.filter((i) => defined(X[i])); if (Y) facet = facet.filter((i) => defined(Y[i])); - const Cf = C[fi] = []; + const Cf = (C[fi] = []); for (const [, index] of maybeGroup(facet, Z)) { const delaunay = Delaunay.from(index, xi, yi); const voronoi = voronoiof(delaunay, dimensions); @@ -249,7 +249,11 @@ class Voronoi extends Mark { } render(index, scales, channels, dimensions, context) { const {x, y} = scales; - const {x: X, y: Y, cells: {[index.fi ?? 0]: C}} = channels; + const { + x: X, + y: Y, + cells: {[index.fi ?? 0]: C} + } = channels; return create("svg:g", context) .call(applyIndirectStyles, this, dimensions, context) .call(applyTransform, this, {x: X && x, y: Y && y}) From e740e3e58b729ac771c8c0eaa099ea97166780ce Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Sun, 28 May 2023 09:12:00 -0700 Subject: [PATCH 07/17] undo comment edits --- src/marks/delaunay.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index b98361dd37..b0392deee7 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -93,15 +93,14 @@ class DelaunayLink extends Mark { for (const k in channels) newChannels[k].push(channels[k][tj]); } - // TODO move to initializer? const {halfedges, hull, triangles} = Delaunay.from(index, xi, yi); - // inner edges for (let i = 0; i < halfedges.length; ++i) { + // inner edges const j = halfedges[i]; if (j > i) link(triangles[i], triangles[j]); } - // convex hull for (let i = 0; i < hull.length; ++i) { + // convex hull link(hull[i], hull[(i + 1) % hull.length]); } @@ -164,7 +163,6 @@ class AbstractDelaunayMark extends Mark { const yi = Y ? (i) => Y[i] : constant(cy); const mark = this; - // TODO move to initializer? function mesh(index) { const delaunay = Delaunay.from(index, xi, yi); select(this) From d445819d2c2b794843ac4bc390d7f68e03cffb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 29 May 2023 14:35:07 +0200 Subject: [PATCH 08/17] facet reindexation to be handled separately (#1648) --- src/marks/delaunay.js | 27 +- test/output/penguinCulmenVoronoiExclude.svg | 1161 ------------------- test/plots/penguin-culmen-voronoi.ts | 25 - 3 files changed, 11 insertions(+), 1202 deletions(-) delete mode 100644 test/output/penguinCulmenVoronoiExclude.svg diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index b0392deee7..3e07113861 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -224,40 +224,35 @@ class Voronoi extends Mark { let {x: X, y: Y, z: Z} = channels; ({x: X, y: Y} = applyPosition(channels, scales, context)); Z = Z?.value; - const C = new Array(facets.length); + const C = new Array((X ?? Y).length).fill(null); const [cx, cy] = applyFrameAnchor(this, dimensions); const xi = X ? (i) => X[i] : constant(cx); const yi = Y ? (i) => Y[i] : constant(cy); - for (let [fi, facet] of facets.entries()) { - if (X) facet = facet.filter((i) => defined(X[i])); - if (Y) facet = facet.filter((i) => defined(Y[i])); - const Cf = (C[fi] = []); - for (const [, index] of maybeGroup(facet, Z)) { - const delaunay = Delaunay.from(index, xi, yi); + for (let I of facets) { + if (X) I = I.filter((i) => defined(xi(i))); + if (Y) I = I.filter((i) => defined(yi(i))); + for (const [, J] of maybeGroup(I, Z)) { + const delaunay = Delaunay.from(J, xi, yi); const voronoi = voronoiof(delaunay, dimensions); - for (let i = 0, n = index.length; i < n; ++i) { - Cf[index[i]] = voronoi.renderCell(i); + for (let i = 0, n = J.length; i < n; ++i) { + C[J[i]] = voronoi.renderCell(i); } } } - return {data, facets, channels: {cells: {value: C, filter: null}}}; + return {data, facets, channels: {cells: {value: C}}}; }), voronoiDefaults ); } render(index, scales, channels, dimensions, context) { const {x, y} = scales; - const { - x: X, - y: Y, - cells: {[index.fi ?? 0]: C} - } = channels; + const {x: X, y: Y, cells: C} = channels; return create("svg:g", context) .call(applyIndirectStyles, this, dimensions, context) .call(applyTransform, this, {x: X && x, y: Y && y}) .call((g) => { g.selectAll() - .data(index.filter((i) => C[i] != null)) + .data(index) .enter() .append("path") .call(applyDirectStyles, this) diff --git a/test/output/penguinCulmenVoronoiExclude.svg b/test/output/penguinCulmenVoronoiExclude.svg deleted file mode 100644 index b99195e84e..0000000000 --- a/test/output/penguinCulmenVoronoiExclude.svg +++ /dev/null @@ -1,1161 +0,0 @@ - - - - - Adelie - - - Chinstrap - - - Gentoo - - - - species - - - - - - - - - - - - - - - - - - - - - 34 - 36 - 38 - 40 - 42 - 44 - 46 - 48 - 50 - 52 - 54 - 56 - 58 - - - - ↑ culmen_length_mm - - - - - - - - - - - - - - - - - - 15 - 20 - - - 15 - 20 - - - 15 - 20 - - - - culmen_depth_mm → - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/plots/penguin-culmen-voronoi.ts b/test/plots/penguin-culmen-voronoi.ts index 4f3bd165a5..6c3d46e0b8 100644 --- a/test/plots/penguin-culmen-voronoi.ts +++ b/test/plots/penguin-culmen-voronoi.ts @@ -10,28 +10,3 @@ export async function penguinCulmenVoronoi() { ] }); } - -export async function penguinCulmenVoronoiExclude() { - const penguins = await d3.csv("data/penguins.csv", d3.autoType); - const xy = {fx: "species", x: "culmen_depth_mm", y: "culmen_length_mm"}; - return Plot.plot({ - inset: 10, - marks: [ - Plot.frame(), - Plot.dot(penguins, {...xy, facet: "exclude", fill: "currentColor", r: 1.5}), - Plot.dot(penguins, {...xy, facet: "include", fillOpacity: 0.25, fill: "currentColor", r: 1.5}), - Plot.voronoiMesh(penguins, {...xy, facet: "exclude"}), - Plot.voronoi( - penguins, - Plot.pointer({ - ...xy, - facet: "exclude", - stroke: "species", - fill: "species", - fillOpacity: 0.2, - maxRadius: Infinity - }) - ) - ] - }); -} From c93aa9ddc9ccf81b65a499e7594f3731e2176b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 21 Aug 2023 10:04:41 +0200 Subject: [PATCH 09/17] update test --- test/output/penguinCulmenVoronoi.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/output/penguinCulmenVoronoi.svg b/test/output/penguinCulmenVoronoi.svg index 55c7a3f41d..00f2db08ca 100644 --- a/test/output/penguinCulmenVoronoi.svg +++ b/test/output/penguinCulmenVoronoi.svg @@ -1098,5 +1098,5 @@ >>>>>>> c08ce379 (voronoi initializer) - + \ No newline at end of file From adc9cfdd714f336a884b479d1b32b133ceeb304c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 21 Aug 2023 10:22:53 +0200 Subject: [PATCH 10/17] voronoi mark + reindexed facets --- src/marks/delaunay.js | 4 +- test/output/penguinCulmenVoronoiExclude.svg | 1161 +++++++++++++++++++ test/plots/penguin-culmen-voronoi.ts | 25 + 3 files changed, 1189 insertions(+), 1 deletion(-) create mode 100644 test/output/penguinCulmenVoronoiExclude.svg diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 3e07113861..f0427cfa2d 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -8,7 +8,8 @@ import {constant, maybeTuple, maybeZ} from "../options.js"; import {applyPosition} from "../projection.js"; import {applyFrameAnchor, applyTransform} from "../style.js"; import {applyChannelStyles, applyDirectStyles, applyIndirectStyles} from "../style.js"; -import {initializer} from "../transforms/basic.js"; +import {basic, initializer} from "../transforms/basic.js"; +import {exclusiveFacets} from "../transforms/exclusiveFacets.js"; import {maybeGroup} from "../transforms/group.js"; const delaunayLinkDefaults = { @@ -296,6 +297,7 @@ export function hull(data, options) { } export function voronoi(data, options) { + options = basic(options, (data, facets) => exclusiveFacets(data, facets)); return delaunayMark(Voronoi, data, options); } diff --git a/test/output/penguinCulmenVoronoiExclude.svg b/test/output/penguinCulmenVoronoiExclude.svg new file mode 100644 index 0000000000..ef999af5d9 --- /dev/null +++ b/test/output/penguinCulmenVoronoiExclude.svg @@ -0,0 +1,1161 @@ + + + + + Adelie + + + Chinstrap + + + Gentoo + + + + species + + + + + + + + + + + + + + + + + + + + + 34 + 36 + 38 + 40 + 42 + 44 + 46 + 48 + 50 + 52 + 54 + 56 + 58 + + + + ↑ culmen_length_mm + + + + + + + + + + + + + + + + + + 15 + 20 + + + 15 + 20 + + + 15 + 20 + + + + culmen_depth_mm → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/penguin-culmen-voronoi.ts b/test/plots/penguin-culmen-voronoi.ts index 6c3d46e0b8..4f3bd165a5 100644 --- a/test/plots/penguin-culmen-voronoi.ts +++ b/test/plots/penguin-culmen-voronoi.ts @@ -10,3 +10,28 @@ export async function penguinCulmenVoronoi() { ] }); } + +export async function penguinCulmenVoronoiExclude() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + const xy = {fx: "species", x: "culmen_depth_mm", y: "culmen_length_mm"}; + return Plot.plot({ + inset: 10, + marks: [ + Plot.frame(), + Plot.dot(penguins, {...xy, facet: "exclude", fill: "currentColor", r: 1.5}), + Plot.dot(penguins, {...xy, facet: "include", fillOpacity: 0.25, fill: "currentColor", r: 1.5}), + Plot.voronoiMesh(penguins, {...xy, facet: "exclude"}), + Plot.voronoi( + penguins, + Plot.pointer({ + ...xy, + facet: "exclude", + stroke: "species", + fill: "species", + fillOpacity: 0.2, + maxRadius: Infinity + }) + ) + ] + }); +} From ff5c44a4e305fb185578f9324d4e1802561a0234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 21 Aug 2023 11:32:29 +0200 Subject: [PATCH 11/17] Apply the transform option before any initializer --- src/marks/delaunay.js | 9 +- test/output/usCountyVoronoi.svg | 6234 +++++++++++++++++++++++++++ test/output/usCountyVoronoiMesh.svg | 22 + test/plots/index.ts | 1 + test/plots/us-county-voronoi.ts | 31 + 5 files changed, 6294 insertions(+), 3 deletions(-) create mode 100644 test/output/usCountyVoronoi.svg create mode 100644 test/output/usCountyVoronoiMesh.svg create mode 100644 test/plots/us-county-voronoi.ts diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index f0427cfa2d..49a91d2200 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -279,9 +279,13 @@ function voronoiof(delaunay, dimensions) { return delaunay.voronoi([marginLeft, marginTop, width - marginRight, height - marginBottom]); } -function delaunayMark(DelaunayMark, data, {x, y, ...options} = {}) { +function delaunayMark(DelaunayMark, data, {x, y, transform, initializer, ...options} = {}) { [x, y] = maybeTuple(x, y); - return new DelaunayMark(data, {...options, x, y}); + // Apply the exclusiveFacets transform before any other transform or initializer. + options = basic(options, exclusiveFacets); + if (transform != null) options = basic(options, transform); + options = {...basic(options, exclusiveFacets), initializer, x, y}; + return new DelaunayMark(data, options); } export function delaunayLink(data, options) { @@ -297,7 +301,6 @@ export function hull(data, options) { } export function voronoi(data, options) { - options = basic(options, (data, facets) => exclusiveFacets(data, facets)); return delaunayMark(Voronoi, data, options); } diff --git a/test/output/usCountyVoronoi.svg b/test/output/usCountyVoronoi.svg new file mode 100644 index 0000000000..1f62684052 --- /dev/null +++ b/test/output/usCountyVoronoi.svg @@ -0,0 +1,6234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/usCountyVoronoiMesh.svg b/test/output/usCountyVoronoiMesh.svg new file mode 100644 index 0000000000..b458f3f19e --- /dev/null +++ b/test/output/usCountyVoronoiMesh.svg @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/plots/index.ts b/test/plots/index.ts index 7b99e1ada1..5c34720eb8 100644 --- a/test/plots/index.ts +++ b/test/plots/index.ts @@ -318,6 +318,7 @@ export * from "./us-congress-age-symbol-explicit.js"; export * from "./us-congress-age.js"; export * from "./us-county-choropleth.js"; export * from "./us-county-spikes.js"; +export * from "./us-county-voronoi.js"; export * from "./us-population-state-age-dots.js"; export * from "./us-population-state-age.js"; export * from "./us-president-favorability-dots.js"; diff --git a/test/plots/us-county-voronoi.ts b/test/plots/us-county-voronoi.ts new file mode 100644 index 0000000000..d721f0777c --- /dev/null +++ b/test/plots/us-county-voronoi.ts @@ -0,0 +1,31 @@ +import * as Plot from "@observablehq/plot"; +import * as d3 from "d3"; +import {feature} from "topojson-client"; + +const transform = (data, facets) => ({data, facets: facets.map((I) => I.slice(1))}); + +export async function usCountyVoronoi() { + const counties = await d3 + .json("data/us-counties-10m.json") + .then((us) => feature(us, us.objects.counties).features); + return Plot.plot({ + projection: "albers", + marks: [ + Plot.voronoi(counties, Plot.geoCentroid({transform, stroke: "red"})), + Plot.voronoi(counties, Plot.centroid({transform, stroke: "blue", mixBlendMode: "multiply"})) + ] + }); +} + +export async function usCountyVoronoiMesh() { + const counties = await d3 + .json("data/us-counties-10m.json") + .then((us) => feature(us, us.objects.counties).features); + return Plot.plot({ + projection: "albers", + marks: [ + Plot.voronoiMesh(counties, Plot.geoCentroid({transform, stroke: "red", strokeOpacity: 1})), + Plot.voronoiMesh(counties, Plot.centroid({transform, stroke: "blue", strokeOpacity: 1, mixBlendMode: "multiply"})) + ] + }); +} From bd41e0c79c3eac6ab97fe69d191bc6732286f754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 14 Sep 2023 11:12:27 +0200 Subject: [PATCH 12/17] simpler! --- src/marks/delaunay.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 49a91d2200..a14fe764e1 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -279,13 +279,9 @@ function voronoiof(delaunay, dimensions) { return delaunay.voronoi([marginLeft, marginTop, width - marginRight, height - marginBottom]); } -function delaunayMark(DelaunayMark, data, {x, y, transform, initializer, ...options} = {}) { +function delaunayMark(DelaunayMark, data, {x, y, initializer, ...options} = {}) { [x, y] = maybeTuple(x, y); - // Apply the exclusiveFacets transform before any other transform or initializer. - options = basic(options, exclusiveFacets); - if (transform != null) options = basic(options, transform); - options = {...basic(options, exclusiveFacets), initializer, x, y}; - return new DelaunayMark(data, options); + return new DelaunayMark(data, {...basic({...options, x, y}, exclusiveFacets), initializer}); } export function delaunayLink(data, options) { From 160959f41f96672a38fa2b625743b79828a6454a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 14 Sep 2023 11:12:42 +0200 Subject: [PATCH 13/17] more tests --- .../output/penguinCulmenVoronoiExcludeHex.svg | 315 ++++++++++++++++++ test/output/usStateCentroidVoronoi.svg | 84 +++++ test/output/usStateGeoCentroidVoronoi.svg | 84 +++++ test/plots/penguin-culmen-voronoi.ts | 17 + test/plots/us-state-capitals-voronoi.ts | 43 +++ 5 files changed, 543 insertions(+) create mode 100644 test/output/penguinCulmenVoronoiExcludeHex.svg create mode 100644 test/output/usStateCentroidVoronoi.svg create mode 100644 test/output/usStateGeoCentroidVoronoi.svg diff --git a/test/output/penguinCulmenVoronoiExcludeHex.svg b/test/output/penguinCulmenVoronoiExcludeHex.svg new file mode 100644 index 0000000000..01ba8833fe --- /dev/null +++ b/test/output/penguinCulmenVoronoiExcludeHex.svg @@ -0,0 +1,315 @@ + + + + + Adelie + + + Chinstrap + + + Gentoo + + + + species + + + + + + + + + + + + + + + + + + + + + 34 + 36 + 38 + 40 + 42 + 44 + 46 + 48 + 50 + 52 + 54 + 56 + 58 + + + + ↑ culmen_length_mm + + + + + + + + + + + + + + + + + + 15 + 20 + + + 15 + 20 + + + 15 + 20 + + + + culmen_depth_mm → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/usStateCentroidVoronoi.svg b/test/output/usStateCentroidVoronoi.svg new file mode 100644 index 0000000000..b5849f8c54 --- /dev/null +++ b/test/output/usStateCentroidVoronoi.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/usStateGeoCentroidVoronoi.svg b/test/output/usStateGeoCentroidVoronoi.svg new file mode 100644 index 0000000000..7c85efe509 --- /dev/null +++ b/test/output/usStateGeoCentroidVoronoi.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/penguin-culmen-voronoi.ts b/test/plots/penguin-culmen-voronoi.ts index 4f3bd165a5..ecb99cf817 100644 --- a/test/plots/penguin-culmen-voronoi.ts +++ b/test/plots/penguin-culmen-voronoi.ts @@ -35,3 +35,20 @@ export async function penguinCulmenVoronoiExclude() { ] }); } + +export async function penguinCulmenVoronoiExcludeHex() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + const xy = {fx: "species", x: "culmen_depth_mm", y: "culmen_length_mm"}; + return Plot.plot({ + inset: 20, + marks: [ + Plot.frame(), + Plot.dot(penguins, Plot.hexbin({}, {...xy, facet: "exclude", stroke: "species", fill: "species"})), + Plot.voronoiMesh(penguins, Plot.hexbin({}, {...xy, facet: "exclude", strokeOpacity: 1})), + Plot.voronoi( + penguins, + Plot.pointer(Plot.hexbin({}, {...xy, facet: "exclude", strokeWidth: 2, maxRadius: Infinity})) + ) + ] + }); +} diff --git a/test/plots/us-state-capitals-voronoi.ts b/test/plots/us-state-capitals-voronoi.ts index 0f72096b96..a2481cfa07 100644 --- a/test/plots/us-state-capitals-voronoi.ts +++ b/test/plots/us-state-capitals-voronoi.ts @@ -35,3 +35,46 @@ export async function usStateCapitalsVoronoi() { ] }); } + +async function voronoiMap(centroid) { + const [nation, states] = await d3 + .json("data/us-counties-10m.json") + .then((us) => [feature(us, us.objects.nation), feature(us, us.objects.states)]); + return Plot.plot({ + width: 640, + height: 640, + margin: 1, + projection: ({width, height}) => + d3.geoAzimuthalEqualArea().rotate([96, -40]).clipAngle(24).fitSize([width, height], {type: "Sphere"}), + marks: [ + Plot.geo(nation, {fill: "currentColor", fillOpacity: 0.2}), + Plot.dot(states.features, centroid({r: 2.5, fill: "currentColor"})), + Plot.voronoiMesh(states.features, centroid({clip: "sphere"})), + Plot.voronoi( + states.features, + Plot.pointer( + centroid({ + x: "longitude", + y: "latitude", + clip: "sphere", + title: "state", + stroke: "red", + fill: "red", + fillOpacity: 0.4, + pointerEvents: "all", + maxRadius: Infinity + }) + ) + ), + Plot.sphere({strokeWidth: 2}) + ] + }); +} + +export async function usStateCentroidVoronoi() { + return voronoiMap(Plot.centroid); +} + +export async function usStateGeoCentroidVoronoi() { + return voronoiMap(Plot.geoCentroid); +} From 95f9f1c8bcfb81c0c4ebbb2c10f90474f174346f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 12 Jul 2024 13:44:05 +0200 Subject: [PATCH 14/17] update tests --- test/output/penguinCulmenVoronoi.svg | 347 +----- test/output/penguinCulmenVoronoiExclude.svg | 12 +- .../output/penguinCulmenVoronoiExcludeHex.svg | 388 +++---- test/output/penguinVoronoi1D.svg | 1019 +---------------- test/output/usCountyVoronoi.svg | 8 +- test/output/usCountyVoronoiMesh.svg | 8 +- test/output/usStateCapitalsVoronoi.svg | 2 +- test/output/usStateCentroidVoronoi.svg | 14 +- test/output/usStateGeoCentroidVoronoi.svg | 14 +- 9 files changed, 246 insertions(+), 1566 deletions(-) diff --git a/test/output/penguinCulmenVoronoi.svg b/test/output/penguinCulmenVoronoi.svg index 00f2db08ca..633d3c10dd 100644 --- a/test/output/penguinCulmenVoronoi.svg +++ b/test/output/penguinCulmenVoronoi.svg @@ -414,7 +414,6 @@ -<<<<<<< HEAD @@ -466,7 +465,6 @@ - @@ -518,7 +516,6 @@ - @@ -655,7 +652,6 @@ - @@ -678,7 +674,6 @@ - @@ -757,346 +752,6 @@ -======= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->>>>>>> c08ce379 (voronoi initializer) - + \ No newline at end of file diff --git a/test/output/penguinCulmenVoronoiExclude.svg b/test/output/penguinCulmenVoronoiExclude.svg index ef999af5d9..5db2c7a863 100644 --- a/test/output/penguinCulmenVoronoiExclude.svg +++ b/test/output/penguinCulmenVoronoiExclude.svg @@ -1,15 +1,15 @@ @@ -27,7 +27,7 @@ species - + \ No newline at end of file From 66fd6a6d713a8fe782f9808083a926038bf1c4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 12 Jul 2024 13:47:58 +0200 Subject: [PATCH 15/17] missing import --- src/style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/style.js b/src/style.js index 39960fabe3..8395e0b5c5 100644 --- a/src/style.js +++ b/src/style.js @@ -3,7 +3,7 @@ import {create} from "./context.js"; import {defined, nonempty} from "./defined.js"; import {formatDefault} from "./format.js"; import {isNone, isNoneish, isRound, maybeColorChannel, maybeNumberChannel} from "./options.js"; -import {keyof, number, string} from "./options.js"; +import {keyof, keyword, number, string} from "./options.js"; import {warn} from "./warnings.js"; export const offset = (typeof window !== "undefined" ? window.devicePixelRatio > 1 : typeof it === "undefined") ? 0 : 0.5; // prettier-ignore From 67be45fab4ea26bdbcdc5aac9a2afb9376f8b9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 15 Jul 2024 13:36:25 +0200 Subject: [PATCH 16/17] remove maybeClip (bad merge operation) --- src/style.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/style.js b/src/style.js index 8395e0b5c5..deeb66346f 100644 --- a/src/style.js +++ b/src/style.js @@ -3,7 +3,7 @@ import {create} from "./context.js"; import {defined, nonempty} from "./defined.js"; import {formatDefault} from "./format.js"; import {isNone, isNoneish, isRound, maybeColorChannel, maybeNumberChannel} from "./options.js"; -import {keyof, keyword, number, string} from "./options.js"; +import {keyof, number, string} from "./options.js"; import {warn} from "./warnings.js"; export const offset = (typeof window !== "undefined" ? window.devicePixelRatio > 1 : typeof it === "undefined") ? 0 : 0.5; // prettier-ignore @@ -297,15 +297,6 @@ export function* groupIndex(I, position, mark, channels) { } } -// TODO Accept other types of clips (paths, urls, x, y, other marks…)? -// https://github.com/observablehq/plot/issues/181 -export function maybeClip(clip) { - if (clip === true) clip = "frame"; - else if (clip === false) clip = null; - else if (clip != null) clip = keyword(clip, "clip", ["frame", "sphere"]); - return clip; -} - // TODO avoid creating a new clip-path each time? // Note: may mutate selection.node! function applyClip(selection, mark, dimensions, context) { From 3cffb4c7b11a0c44dbf7dd24ee4e3c0b1cacbfb5 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 31 Jul 2024 21:28:42 -0400 Subject: [PATCH 17/17] only voronoi needs exclusiveFacets --- src/marks/delaunay.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index a14fe764e1..57a927d22e 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -279,9 +279,9 @@ function voronoiof(delaunay, dimensions) { return delaunay.voronoi([marginLeft, marginTop, width - marginRight, height - marginBottom]); } -function delaunayMark(DelaunayMark, data, {x, y, initializer, ...options} = {}) { +function delaunayMark(DelaunayMark, data, {x, y, ...options} = {}) { [x, y] = maybeTuple(x, y); - return new DelaunayMark(data, {...basic({...options, x, y}, exclusiveFacets), initializer}); + return new DelaunayMark(data, {...options, x, y}); } export function delaunayLink(data, options) { @@ -296,8 +296,8 @@ export function hull(data, options) { return delaunayMark(Hull, data, options); } -export function voronoi(data, options) { - return delaunayMark(Voronoi, data, options); +export function voronoi(data, {x, y, initializer, ...options} = {}) { + return delaunayMark(Voronoi, data, {...basic({...options, x, y}, exclusiveFacets), initializer}); } export function voronoiMesh(data, options) {