From 9645e4a516c4933ba4fa8ee088de52e991c7b6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 13 Mar 2026 08:55:48 +0100 Subject: [PATCH 1/3] no strokeDasharray on markers closes #2131 --- src/marker.js | 11 +++++++++-- test/output/markerDasharray.svg | 28 ++++++++++++++++++++++++++++ test/plots/markers.ts | 25 +++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/output/markerDasharray.svg diff --git a/src/marker.js b/src/marker.js index 6d76ed9ff2..f70c596ba1 100644 --- a/src/marker.js +++ b/src/marker.js @@ -1,6 +1,6 @@ import {create} from "./context.js"; import {unset} from "./memoize.js"; -import {keyof} from "./options.js"; +import {isNoneish, keyof} from "./options.js"; export function markers(mark, {marker, markerStart = marker, markerMid = marker, markerEnd = marker} = {}) { mark.markerStart = maybeMarker(markerStart); @@ -144,7 +144,13 @@ function getGroupedOrientation(path, Z) { return ([i]) => O[i]; } -function applyMarkersColor(path, {markerStart, markerMid, markerEnd, stroke}, strokeof = () => stroke, Z, context) { +function applyMarkersColor( + path, + {markerStart, markerMid, markerEnd, stroke, strokeDasharray}, + strokeof = () => stroke, + Z, + context +) { if (!markerStart && !markerMid && !markerEnd) return; const iriByMarkerColor = new Map(); const orient = Z && getGroupedOrientation(path, Z); @@ -158,6 +164,7 @@ function applyMarkersColor(path, {markerStart, markerMid, markerEnd, stroke}, st let iri = iriByColor.get(color); if (!iri) { const node = this.parentNode.insertBefore(marker(color, context), this); + if (!isNoneish(strokeDasharray)) node.setAttribute("stroke-dasharray", "none"); const id = `plot-marker-${++nextMarkerId}`; node.setAttribute("id", id); iriByColor.set(color, (iri = `url(#${id})`)); diff --git a/test/output/markerDasharray.svg b/test/output/markerDasharray.svg new file mode 100644 index 0000000000..f19dfaa5fc --- /dev/null +++ b/test/output/markerDasharray.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/markers.ts b/test/plots/markers.ts index f6c19e61e5..8974e41bbf 100644 --- a/test/plots/markers.ts +++ b/test/plots/markers.ts @@ -1,5 +1,30 @@ import * as Plot from "@observablehq/plot"; +export async function markerDasharray() { + return Plot.plot({ + axis: null, + inset: 20, + marks: [ + Plot.lineY( + [ + [0, 5], + [5, 2], + [10, 0] + ], + { + x: (d) => d[0], + y: (d) => d[1], + strokeDasharray: "1,10", + strokeWidth: 3, + markerStart: "dot", + markerMid: "arrow", + markerEnd: "circle-stroke" + } + ) + ] + }); +} + export async function markerRuleX() { return Plot.ruleX([1, 2, 3], {marker: "arrow-reverse", inset: 3}).plot(); } From 5f65fce66f798aa0207f0def47307682f7a711e6 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Thu, 2 Apr 2026 17:56:11 -0700 Subject: [PATCH 2/3] always stroke-dasharray=none --- src/marker.js | 13 +++--- test/output/bigint2.svg | 2 +- test/output/crimeanWarArrow.svg | 6 +-- test/output/crimeanWarLine.svg | 6 +-- test/output/driving.svg | 2 +- test/output/errorBarX.svg | 6 +-- test/output/errorBarY.svg | 6 +-- test/output/flareTree.svg | 4 +- test/output/geoLink.svg | 2 +- test/output/groupMarkerEnd.svg | 10 ++--- test/output/groupMarkerStart.svg | 10 ++--- test/output/liborProjectionsFacet.html | 50 +++++++++++------------ test/output/markerDasharray.svg | 6 +-- test/output/markerRuleX.svg | 6 +-- test/output/markerRuleY.svg | 6 +-- test/output/markerTickX.svg | 6 +-- test/output/markerTickY.svg | 6 +-- test/output/pointerLinkedRectInterval.svg | 4 +- 18 files changed, 74 insertions(+), 77 deletions(-) diff --git a/src/marker.js b/src/marker.js index f70c596ba1..5d8868937a 100644 --- a/src/marker.js +++ b/src/marker.js @@ -45,6 +45,7 @@ function markerArrow(orient) { .attr("orient", orient) .attr("fill", "none") .attr("stroke", color) + .attr("stroke-dasharray", "none") .attr("stroke-width", 1.5) .attr("stroke-linecap", "round") .attr("stroke-linejoin", "round") @@ -70,6 +71,7 @@ function markerCircleFill(color, context) { .attr("markerHeight", 6.67) .attr("fill", color) .attr("stroke", "var(--plot-background)") + .attr("stroke-dasharray", "none") .attr("stroke-width", 1.5) .call((marker) => marker.append("circle").attr("r", 3)) .node(); @@ -82,6 +84,7 @@ function markerCircleStroke(color, context) { .attr("markerHeight", 6.67) .attr("fill", "var(--plot-background)") .attr("stroke", color) + .attr("stroke-dasharray", "none") .attr("stroke-width", 1.5) .call((marker) => marker.append("circle").attr("r", 3)) .node(); @@ -95,6 +98,7 @@ function markerTick(orient) { .attr("markerHeight", 6) .attr("orient", orient) .attr("stroke", color) + .attr("stroke-dasharray", "none") .call((marker) => marker.append("path").attr("d", "M0,-3v6")) .node(); } @@ -144,13 +148,7 @@ function getGroupedOrientation(path, Z) { return ([i]) => O[i]; } -function applyMarkersColor( - path, - {markerStart, markerMid, markerEnd, stroke, strokeDasharray}, - strokeof = () => stroke, - Z, - context -) { +function applyMarkersColor(path, {markerStart, markerMid, markerEnd, stroke}, strokeof = () => stroke, Z, context) { if (!markerStart && !markerMid && !markerEnd) return; const iriByMarkerColor = new Map(); const orient = Z && getGroupedOrientation(path, Z); @@ -164,7 +162,6 @@ function applyMarkersColor( let iri = iriByColor.get(color); if (!iri) { const node = this.parentNode.insertBefore(marker(color, context), this); - if (!isNoneish(strokeDasharray)) node.setAttribute("stroke-dasharray", "none"); const id = `plot-marker-${++nextMarkerId}`; node.setAttribute("id", id); iriByColor.set(color, (iri = `url(#${id})`)); diff --git a/test/output/bigint2.svg b/test/output/bigint2.svg index be2ca24f9b..2788d26ff7 100644 --- a/test/output/bigint2.svg +++ b/test/output/bigint2.svg @@ -60,7 +60,7 @@ big1 → - + diff --git a/test/output/crimeanWarArrow.svg b/test/output/crimeanWarArrow.svg index abeac3cb2c..29c050c0ca 100644 --- a/test/output/crimeanWarArrow.svg +++ b/test/output/crimeanWarArrow.svg @@ -72,15 +72,15 @@ - + - + - + diff --git a/test/output/crimeanWarLine.svg b/test/output/crimeanWarLine.svg index 657700a10d..952e12c1d2 100644 --- a/test/output/crimeanWarLine.svg +++ b/test/output/crimeanWarLine.svg @@ -72,15 +72,15 @@ - + - + - + diff --git a/test/output/driving.svg b/test/output/driving.svg index 625af3e75c..e6ffec1b34 100644 --- a/test/output/driving.svg +++ b/test/output/driving.svg @@ -83,7 +83,7 @@ Miles driven (per person-year) → - + diff --git a/test/output/errorBarX.svg b/test/output/errorBarX.svg index c9ca85fd69..ccb9e0b35e 100644 --- a/test/output/errorBarX.svg +++ b/test/output/errorBarX.svg @@ -122,13 +122,13 @@ - + - + - + diff --git a/test/output/errorBarY.svg b/test/output/errorBarY.svg index 8cc89efb32..1ce0b600cb 100644 --- a/test/output/errorBarY.svg +++ b/test/output/errorBarY.svg @@ -136,13 +136,13 @@ - + - + - + diff --git a/test/output/flareTree.svg b/test/output/flareTree.svg index 0e18f35465..f3b3109632 100644 --- a/test/output/flareTree.svg +++ b/test/output/flareTree.svg @@ -14,7 +14,7 @@ } - + @@ -30,7 +30,7 @@ - + diff --git a/test/output/geoLink.svg b/test/output/geoLink.svg index f8f4a5e745..dc0fe5ed2b 100644 --- a/test/output/geoLink.svg +++ b/test/output/geoLink.svg @@ -23,7 +23,7 @@ - + diff --git a/test/output/groupMarkerEnd.svg b/test/output/groupMarkerEnd.svg index 0af90376e4..69530e2ab5 100644 --- a/test/output/groupMarkerEnd.svg +++ b/test/output/groupMarkerEnd.svg @@ -112,7 +112,7 @@ - + @@ -215,7 +215,7 @@ - + @@ -318,7 +318,7 @@ - + @@ -421,7 +421,7 @@ - + @@ -524,7 +524,7 @@ - + diff --git a/test/output/groupMarkerStart.svg b/test/output/groupMarkerStart.svg index 3ad8b2dc12..51fda7fa87 100644 --- a/test/output/groupMarkerStart.svg +++ b/test/output/groupMarkerStart.svg @@ -14,7 +14,7 @@ } - + @@ -117,7 +117,7 @@ - + @@ -220,7 +220,7 @@ - + @@ -323,7 +323,7 @@ - + @@ -426,7 +426,7 @@ - + diff --git a/test/output/liborProjectionsFacet.html b/test/output/liborProjectionsFacet.html index 6d032ccf40..0675e7be46 100644 --- a/test/output/liborProjectionsFacet.html +++ b/test/output/liborProjectionsFacet.html @@ -265,115 +265,115 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/test/output/markerDasharray.svg b/test/output/markerDasharray.svg index f19dfaa5fc..fae3d5400a 100644 --- a/test/output/markerDasharray.svg +++ b/test/output/markerDasharray.svg @@ -14,13 +14,13 @@ } - + - + - + diff --git a/test/output/markerRuleX.svg b/test/output/markerRuleX.svg index a13c056142..ffda54d43f 100644 --- a/test/output/markerRuleX.svg +++ b/test/output/markerRuleX.svg @@ -40,13 +40,13 @@ 3.0 - + - + - + diff --git a/test/output/markerRuleY.svg b/test/output/markerRuleY.svg index ce11117a0e..b12e200c48 100644 --- a/test/output/markerRuleY.svg +++ b/test/output/markerRuleY.svg @@ -40,13 +40,13 @@ 3.0 - + - + - + diff --git a/test/output/markerTickX.svg b/test/output/markerTickX.svg index 78955f2e08..ba782718d5 100644 --- a/test/output/markerTickX.svg +++ b/test/output/markerTickX.svg @@ -40,13 +40,13 @@ 3.0 - + - + - + diff --git a/test/output/markerTickY.svg b/test/output/markerTickY.svg index 08f209a628..85bbd9c025 100644 --- a/test/output/markerTickY.svg +++ b/test/output/markerTickY.svg @@ -40,13 +40,13 @@ 3.0 - + - + - + diff --git a/test/output/pointerLinkedRectInterval.svg b/test/output/pointerLinkedRectInterval.svg index 87adcb7006..8b7ef9bb91 100644 --- a/test/output/pointerLinkedRectInterval.svg +++ b/test/output/pointerLinkedRectInterval.svg @@ -57,11 +57,11 @@ - + - + From 431199c1a047b2ab52b47d693ae0f940316ac25c Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Thu, 2 Apr 2026 17:56:35 -0700 Subject: [PATCH 3/3] remove unused import --- src/marker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/marker.js b/src/marker.js index 5d8868937a..cb1110adec 100644 --- a/src/marker.js +++ b/src/marker.js @@ -1,6 +1,6 @@ import {create} from "./context.js"; import {unset} from "./memoize.js"; -import {isNoneish, keyof} from "./options.js"; +import {keyof} from "./options.js"; export function markers(mark, {marker, markerStart = marker, markerMid = marker, markerEnd = marker} = {}) { mark.markerStart = maybeMarker(markerStart);