Skip to content

Commit 227a43e

Browse files
committed
Use (and re-use) the same clip-path when two renders are clipped with the same shape
1 parent 7fdbbab commit 227a43e

File tree

1 file changed

+47
-28
lines changed

1 file changed

+47
-28
lines changed

src/style.js

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {geoPath, group, namespaces} from "d3";
1+
import {geoPath, group, namespaces, select} from "d3";
22
import {create} from "./context.js";
33
import {defined, nonempty} from "./defined.js";
44
import {formatDefault} from "./format.js";
55
import {isNone, isNoneish, isRound, maybeColorChannel, maybeNumberChannel} from "./options.js";
6-
import {keyof, number, string} from "./options.js";
6+
import {keyof, keyword, number, string} from "./options.js";
77
import {warn} from "./warnings.js";
88

99
export const offset = (typeof window !== "undefined" ? window.devicePixelRatio > 1 : typeof it === "undefined") ? 0 : 0.5; // prettier-ignore
@@ -297,43 +297,62 @@ export function* groupIndex(I, position, mark, channels) {
297297
}
298298
}
299299

300-
// TODO avoid creating a new clip-path each time?
300+
// TODO Accept other types of clips (paths, urls, x, y, other marks…)?
301+
// https://github.com/observablehq/plot/issues/181
302+
export function maybeClip(clip) {
303+
if (clip === true) clip = "frame";
304+
else if (clip === false) clip = null;
305+
else if (clip != null) clip = keyword(clip, "clip", ["frame", "sphere"]);
306+
return clip;
307+
}
308+
309+
function clipDefs({ownerSVGElement}) {
310+
const svg = select(ownerSVGElement);
311+
const defs = svg.select("defs.clip");
312+
return defs.size() ? defs : svg.insert("defs", ":first-child").attr("class", "clip");
313+
}
314+
301315
// Note: may mutate selection.node!
302316
function applyClip(selection, mark, dimensions, context) {
303317
let clipUrl;
304318
const {clip = context.clip} = mark;
305319
switch (clip) {
306320
case "frame": {
307-
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
308-
const id = getClipId();
309-
clipUrl = `url(#${id})`;
310-
selection = create("svg:g", context)
311-
.call((g) =>
312-
g
313-
.append("svg:clipPath")
314-
.attr("id", id)
315-
.append("rect")
316-
.attr("x", marginLeft)
317-
.attr("y", marginTop)
318-
.attr("width", width - marginRight - marginLeft)
319-
.attr("height", height - marginTop - marginBottom)
320-
)
321-
.each(function () {
322-
this.appendChild(selection.node());
323-
selection.node = () => this; // Note: mutation!
324-
});
321+
const clips = context.clips ?? (context.clips = new Map());
322+
if (!clips.has("frame")) {
323+
const {width, height, marginLeft, marginRight, marginTop, marginBottom} = dimensions;
324+
const id = getClipId();
325+
clips.set("frame", id);
326+
clipDefs(context)
327+
.append("clipPath")
328+
.attr("id", id)
329+
.append("rect")
330+
.attr("x", marginLeft)
331+
.attr("y", marginTop)
332+
.attr("width", width - marginRight - marginLeft)
333+
.attr("height", height - marginTop - marginBottom);
334+
}
335+
selection = create("svg:g", context).each(function () {
336+
this.appendChild(selection.node());
337+
selection.node = () => this; // Note: mutation!
338+
});
339+
clipUrl = `url(#${clips.get("frame")})`;
325340
break;
326341
}
327342
case "sphere": {
343+
const clips = context.clips ?? (context.clips = new Map());
328344
const {projection} = context;
329345
if (!projection) throw new Error(`the "sphere" clip option requires a projection`);
330-
const id = getClipId();
331-
clipUrl = `url(#${id})`;
332-
selection
333-
.append("clipPath")
334-
.attr("id", id)
335-
.append("path")
336-
.attr("d", geoPath(projection)({type: "Sphere"}));
346+
if (!clips.has("projection")) {
347+
const id = getClipId();
348+
clips.set("projection", id);
349+
clipDefs(context)
350+
.append("clipPath")
351+
.attr("id", id)
352+
.append("path")
353+
.attr("d", geoPath(projection)({type: "Sphere"}));
354+
}
355+
clipUrl = `url(#${clips.get("projection")})`;
337356
break;
338357
}
339358
}

0 commit comments

Comments
 (0)