-
Notifications
You must be signed in to change notification settings - Fork 187
stable clip paths #1624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stable clip paths #1624
Changes from 3 commits
227a43e
0b25ebf
c205953
c28da54
6509206
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
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"; | ||
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 | ||
|
@@ -302,43 +302,62 @@ export function* groupIndex(I, position, mark, channels) { | |
} | ||
} | ||
|
||
// TODO avoid creating a new clip-path each time? | ||
// 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; | ||
} | ||
|
||
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! | ||
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 Map()); | ||
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 Map()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These probably shouldn’t be exposed publicly on the context. Maybe use a WeakMap keyed by the context instead of assigning to a new property on the context? (Also note that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there only at most two keys in the clip cache ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently yes, these are the only keys; related: #1109 |
||
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; | ||
} | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.