Skip to content

Commit

Permalink
support for CSS basic shapes in the mark’s **clip** option
Browse files Browse the repository at this point in the history
the **r** option of the image mark sets a default width = 2*r, and a default clip=circle()

closes #1413
  • Loading branch information
Fil committed Apr 3, 2023
1 parent 678df1d commit 6429dbe
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ All marks support the following style options:
* **pointerEvents** - the [pointer events](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) (*e.g.*, *none*)
* **clip** - whether and how to clip the mark

If the **clip** option is *frame* (or equivalently true), the mark is clipped to the frame’s dimensions; if the **clip** option is null (or equivalently false), the mark is not clipped. If the **clip** option is *sphere*, then a [geographic projection](#projection-options) is required and the mark will be clipped to the projected sphere (_e.g._, the front hemisphere when using the orthographic projection).
If the **clip** option is *frame* (or equivalently true), the mark is clipped to the frame’s dimensions; if the **clip** option is null (or equivalently false), the mark is not clipped. If the **clip** option is *sphere*, then a [geographic projection](#projection-options) is required and the mark will be clipped to the projected sphere (_e.g._, the front hemisphere when using the orthographic projection). The **clip** option can also be specified as a CSS [basic-shape](https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape).

For all marks except [text](#plottextdata-options), the **dx** and **dy** options are rendered as a transform property, possibly including a 0.5px offset on low-density screens.

Expand Down
5 changes: 4 additions & 1 deletion src/mark.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,14 @@ export interface MarkOptions {
*
* - *frame* or true - clip to the plot’s frame (inner area)
* - *sphere* - clip to the projected sphere (*e.g.*, front hemisphere)
* - a CSS [basic shape][1], such as *circle()*
* - null or false - do not clip
*
* The *sphere* clip option requires a geographic projection.
*
* [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape
*/
clip?: "frame" | "sphere" | boolean | null;
clip?: "frame" | "sphere" | "circle()" | (string & Record<never, never>) | boolean | null;

/**
* The horizontal offset in pixels; a constant option. On low-density screens,
Expand Down
2 changes: 1 addition & 1 deletion src/mark.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class Mark {
this.marginRight = +marginRight;
this.marginBottom = +marginBottom;
this.marginLeft = +marginLeft;
this.clip = maybeClip(clip);
[this.clip, this.clipShape] = maybeClip(clip);
// Super-faceting currently disallow position channels; in the future, we
// could allow position to be specified in fx and fy in addition to (or
// instead of) x and y.
Expand Down
12 changes: 10 additions & 2 deletions src/marks/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,15 @@ export class Image extends Mark {
}

export function image(data, options = {}) {
let {x, y, ...remainingOptions} = options;
let {
x,
y,
r,
width = typeof r === "number" ? 2 * r : undefined,
clip = typeof r === "number" ? "circle()" : undefined,
...remainingOptions
} = options;
if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y);
return new Image(data, {...remainingOptions, x, y});

return new Image(data, {...remainingOptions, width, clip, x, y});
}
5 changes: 4 additions & 1 deletion src/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,9 @@ export function* groupIndex(I, position, {z}, channels) {
export function maybeClip(clip) {
if (clip === true) clip = "frame";
else if (clip === false) clip = null;
return maybeKeyword(clip, "clip", ["frame", "sphere"]);
return typeof clip === "string" && /^(circle|ellipse|inset|polygon|rectangle)\([^)]*\)$/.test(clip)
? [, clip]
: [maybeKeyword(clip, "clip", ["frame", "sphere"])];
}

// Note: may mutate selection.node!
Expand Down Expand Up @@ -374,6 +376,7 @@ export function applyIndirectStyles(selection, mark, dimensions, context) {

export function applyDirectStyles(selection, mark) {
applyStyle(selection, "mix-blend-mode", mark.mixBlendMode);
applyStyle(selection, "clip-path", mark.clipShape);
applyAttr(selection, "opacity", mark.opacity);
}

Expand Down
87 changes: 87 additions & 0 deletions test/output/usPresidentGalleryRadius.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6429dbe

Please sign in to comment.