|
1 |
| -import {geoPath, group, namespaces, select} from "d3"; |
| 1 | +import {group, namespaces, select} from "d3"; |
2 | 2 | import {create} from "./context.js";
|
3 | 3 | import {defined, nonempty} from "./defined.js";
|
4 | 4 | import {formatDefault} from "./format.js";
|
@@ -306,24 +306,20 @@ export function* groupIndex(I, position, mark, channels) {
|
306 | 306 | function applyClip(selection, mark, dimensions, context) {
|
307 | 307 | let clipUrl;
|
308 | 308 | const {clip = context.clip} = mark;
|
309 |
| - switch (clip) { |
310 |
| - case "frame": { |
311 |
| - // Wrap the G element with another (untransformed) G element, applying the |
312 |
| - // clip to the parent G element so that the clip path is not affected by |
313 |
| - // the mark’s transform. To simplify the adoption of this fix, mutate the |
314 |
| - // passed-in selection.node to return the parent G element. |
315 |
| - selection = create("svg:g", context).each(function () { |
316 |
| - this.appendChild(selection.node()); |
317 |
| - selection.node = () => this; // Note: mutation! |
318 |
| - }); |
319 |
| - clipUrl = getFrameClip(context, dimensions); |
320 |
| - break; |
321 |
| - } |
322 |
| - case "sphere": { |
323 |
| - clipUrl = getProjectionClip(context); |
324 |
| - break; |
325 |
| - } |
| 309 | + if (clip === "frame") { |
| 310 | + // Wrap the G element with another (untransformed) G element, applying the |
| 311 | + // clip to the parent G element so that the clip path is not affected by |
| 312 | + // the mark’s transform. To simplify the adoption of this fix, mutate the |
| 313 | + // passed-in selection.node to return the parent G element. |
| 314 | + selection = create("svg:g", context).each(function () { |
| 315 | + this.appendChild(selection.node()); |
| 316 | + selection.node = () => this; // Note: mutation! |
| 317 | + }); |
| 318 | + clipUrl = getFrameClip(context, dimensions); |
| 319 | + } else if (clip) { |
| 320 | + clipUrl = getGeoClip(clip, context); |
326 | 321 | }
|
| 322 | + |
327 | 323 | // Here we’re careful to apply the ARIA attributes to the outer G element when
|
328 | 324 | // clipping is applied, and to apply the ARIA attributes before any other
|
329 | 325 | // attributes (for readability).
|
@@ -356,11 +352,21 @@ const getFrameClip = memoizeClip((clipPath, context, dimensions) => {
|
356 | 352 | .attr("height", height - marginTop - marginBottom);
|
357 | 353 | });
|
358 | 354 |
|
359 |
| -const getProjectionClip = memoizeClip((clipPath, context) => { |
360 |
| - const {projection} = context; |
361 |
| - if (!projection) throw new Error(`the "sphere" clip option requires a projection`); |
362 |
| - clipPath.append("path").attr("d", geoPath(projection)({type: "Sphere"})); |
363 |
| -}); |
| 355 | +const getGeoClip = (function () { |
| 356 | + const cache = new WeakMap(); |
| 357 | + const sphere = {type: "Sphere"}; |
| 358 | + return (geo, context) => { |
| 359 | + let c, url; |
| 360 | + if (!(c = cache.get(context))) cache.set(context, (c = new WeakMap())); |
| 361 | + if (geo.type === "Sphere") geo = sphere; // coalesce all spheres. |
| 362 | + if (!(url = c.get(geo))) { |
| 363 | + const id = getClipId(); |
| 364 | + select(context.ownerSVGElement).append("clipPath").attr("id", id).append("path").attr("d", context.path()(geo)); |
| 365 | + c.set(geo, (url = `url(#${id})`)); |
| 366 | + } |
| 367 | + return url; |
| 368 | + }; |
| 369 | +})(); |
364 | 370 |
|
365 | 371 | // Note: may mutate selection.node!
|
366 | 372 | export function applyIndirectStyles(selection, mark, dimensions, context) {
|
|
0 commit comments