From 5658113060be17fe3cffddb08b1beb0bdf922b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 24 May 2023 06:56:20 +0200 Subject: [PATCH] unique clip paths - smaller files - better interaction performance --- src/style.js | 65 +- test/output/armadillo.svg | 13 +- test/output/bandClip.svg | 8 +- test/output/bandClip2.svg | 8 +- test/output/contourVapor.svg | 8 +- test/output/hexbin.svg | 8 +- test/output/hexbinIdentityReduce.svg | 8 +- test/output/hexbinR.html | 18 +- test/output/hexbinText.svg | 18 +- test/output/hexbinZ.html | 8 +- test/output/hexbinZNull.svg | 8 +- test/output/penguinDensityFill.html | 18 +- test/output/penguinDensityZ.html | 18 +- test/output/projectionClipBerghaus.svg | 13 +- test/output/rasterVaporEqualEarth.svg | 8 +- .../rasterVaporEqualEarthBarycentric.svg | 8 +- test/output/trafficHorizon.html | 953 ++++-------------- test/output/usStateCapitalsVoronoi.svg | 14 +- 18 files changed, 323 insertions(+), 879 deletions(-) diff --git a/src/style.js b/src/style.js index 8480173152..f57304f36c 100644 --- a/src/style.js +++ b/src/style.js @@ -1,4 +1,4 @@ -import {geoPath, group, namespaces} from "d3"; +import {geoPath, group, namespaces, select} from "d3"; import {create} from "./context.js"; import {defined, nonempty} from "./defined.js"; import {formatDefault} from "./format.js"; @@ -306,43 +306,54 @@ export function maybeClip(clip) { return clip; } -// TODO avoid creating a new clip-path each time? +function clipDefs({ownerSVGElement}) { + const svg = select(ownerSVGElement); + const defs = svg.select("defs.clip"); + return defs.size() ? defs : svg.insert("defs", ":first-child").attr("class", "clip"); +} + // Note: may mutate selection.node! +const frame = Symbol("frame"); function applyClip(selection, mark, dimensions, context) { let clipUrl; const {clip = context.clip} = mark; switch (clip) { case "frame": { - const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions; - const id = getClipId(); - clipUrl = `url(#${id})`; - selection = create("svg:g", context) - .call((g) => - g - .append("svg:clipPath") - .attr("id", id) - .append("rect") - .attr("x", marginLeft) - .attr("y", marginTop) - .attr("width", width - marginRight - marginLeft) - .attr("height", height - marginTop - marginBottom) - ) - .each(function () { - this.appendChild(selection.node()); - selection.node = () => this; // Note: mutation! - }); + const clips = context.clips ?? (context.clips = new WeakMap()); + if (!clips.has(frame)) { + const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions; + const id = getClipId(); + clips.set(frame, id); + clipDefs(context) + .append("clipPath") + .attr("id", id) + .append("rect") + .attr("x", marginLeft) + .attr("y", marginTop) + .attr("width", width - marginRight - marginLeft) + .attr("height", height - marginTop - marginBottom); + } + selection = create("svg:g", context).each(function () { + this.appendChild(selection.node()); + selection.node = () => this; // Note: mutation! + }); + clipUrl = `url(#${clips.get(frame)})`; break; } case "sphere": { + const clips = context.clips ?? (context.clips = new WeakMap()); const {projection} = context; if (!projection) throw new Error(`the "sphere" clip option requires a projection`); - const id = getClipId(); - clipUrl = `url(#${id})`; - selection - .append("clipPath") - .attr("id", id) - .append("path") - .attr("d", geoPath(projection)({type: "Sphere"})); + if (!clips.has(projection)) { + const id = getClipId(); + clips.set(projection, id); + clipDefs(context) + .append("clipPath") + .attr("id", id) + .append("path") + .attr("d", geoPath(projection)({type: "Sphere"})); + } + clipUrl = `url(#${clips.get(projection)})`; break; } } diff --git a/test/output/armadillo.svg b/test/output/armadillo.svg index 47b71cbb27..40eef06344 100644 --- a/test/output/armadillo.svg +++ b/test/output/armadillo.svg @@ -1,4 +1,9 @@ + + + + + - - - - - - - + diff --git a/test/output/bandClip.svg b/test/output/bandClip.svg index 9212b5a80f..a01cfbb744 100644 --- a/test/output/bandClip.svg +++ b/test/output/bandClip.svg @@ -1,4 +1,9 @@ + + + + + - - - A B diff --git a/test/output/bandClip2.svg b/test/output/bandClip2.svg index 08d095600a..37fb0e4305 100644 --- a/test/output/bandClip2.svg +++ b/test/output/bandClip2.svg @@ -1,4 +1,9 @@ + + + + + - - - diff --git a/test/output/contourVapor.svg b/test/output/contourVapor.svg index fa1e59a104..f5a01e19d5 100644 --- a/test/output/contourVapor.svg +++ b/test/output/contourVapor.svg @@ -1,4 +1,9 @@ + + + + + - - - diff --git a/test/output/hexbin.svg b/test/output/hexbin.svg index 9a68c8e133..b407be7c0c 100644 --- a/test/output/hexbin.svg +++ b/test/output/hexbin.svg @@ -1,4 +1,9 @@ + + + + + - - - diff --git a/test/output/hexbinIdentityReduce.svg b/test/output/hexbinIdentityReduce.svg index 46d5b11a90..ff3729793e 100644 --- a/test/output/hexbinIdentityReduce.svg +++ b/test/output/hexbinIdentityReduce.svg @@ -1,4 +1,9 @@ + + + + + - - - diff --git a/test/output/hexbinR.html b/test/output/hexbinR.html index 0cfb6a423b..9b380ae8f9 100644 --- a/test/output/hexbinR.html +++ b/test/output/hexbinR.html @@ -34,6 +34,11 @@ Proportion of each sex (%) + + + + + - - - - - - - + - - - - + diff --git a/test/output/hexbinText.svg b/test/output/hexbinText.svg index d0a0bb0721..0bcad10b4b 100644 --- a/test/output/hexbinText.svg +++ b/test/output/hexbinText.svg @@ -1,4 +1,9 @@ + + + + + - - - - - - - + - - - - + diff --git a/test/output/hexbinZ.html b/test/output/hexbinZ.html index f4f35918ad..2081db0a27 100644 --- a/test/output/hexbinZ.html +++ b/test/output/hexbinZ.html @@ -32,6 +32,11 @@ Gentoo + + + + + + + + + + Gentoo + + + + +