From e1451edf5b8c2f513e2fd49de3efb49f115e5cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 8 May 2023 10:31:40 -0700 Subject: [PATCH 1/3] imageFilter --- docs/features/marks.md | 1 + src/mark.d.ts | 8 ++++++++ src/style.js | 3 +++ 3 files changed, 12 insertions(+) diff --git a/docs/features/marks.md b/docs/features/marks.md index 65ce03d53a..df4a9dff0c 100644 --- a/docs/features/marks.md +++ b/docs/features/marks.md @@ -476,6 +476,7 @@ All marks support the following style options: * **strokeDashoffset** - the [stroke dash offset](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dashoffset) (typically in pixels) * **opacity** - object opacity (a number between 0 and 1) * **mixBlendMode** - the [blend mode](https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode) (*e.g.*, *multiply*) +* **imageFilter** - a CSS [filter](https://developer.mozilla.org/en-US/docs/Web/CSS/filter) (*e.g.*, *blur(5px)*) * **shapeRendering** - the [shape-rendering mode](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering) (*e.g.*, *crispEdges*) * **paintOrder** - the [paint order](https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order) (*e.g.*, *stroke*) * **dx** - horizontal offset (in pixels; defaults to 0) diff --git a/src/mark.d.ts b/src/mark.d.ts index 2989400944..64f4f496c9 100644 --- a/src/mark.d.ts +++ b/src/mark.d.ts @@ -399,6 +399,14 @@ export interface MarkOptions { */ mixBlendMode?: string; + /** + * A CSS [filter][1]; a constant string used to adjust the rendering of + * images, such as *blur(5px)*. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter + */ + imageFilter?: string; + /** * The [paint-order][1]; a constant string specifying the order in which the * **fill**, **stroke**, and any markers are drawn; defaults to *normal*, diff --git a/src/style.js b/src/style.js index 9c7fe40857..5c229e192a 100644 --- a/src/style.js +++ b/src/style.js @@ -44,6 +44,7 @@ export function styles( strokeDashoffset, opacity, mixBlendMode, + imageFilter, paintOrder, pointerEvents, shapeRendering @@ -135,6 +136,7 @@ export function styles( mark.ariaHidden = string(ariaHidden); mark.opacity = impliedNumber(copacity, 1); mark.mixBlendMode = impliedString(mixBlendMode, "normal"); + mark.imageFilter = impliedString(imageFilter, "none"); mark.paintOrder = impliedString(paintOrder, "normal"); mark.pointerEvents = impliedString(pointerEvents, "auto"); mark.shapeRendering = impliedString(shapeRendering, "auto"); @@ -368,6 +370,7 @@ export function applyIndirectStyles(selection, mark, dimensions, context) { applyAttr(selection, "stroke-dasharray", mark.strokeDasharray); applyAttr(selection, "stroke-dashoffset", mark.strokeDashoffset); applyAttr(selection, "shape-rendering", mark.shapeRendering); + applyAttr(selection, "filter", mark.imageFilter); applyAttr(selection, "paint-order", mark.paintOrder); applyAttr(selection, "pointer-events", mark.pointerEvents); } From fa7a14a956a0a378cf48f4a39574b54dc482924e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 8 May 2023 12:47:21 -0700 Subject: [PATCH 2/3] example --- test/output/penguinSpeciesCSSFilter.svg | 60 +++++++++++++++++++++++ test/plots/index.ts | 2 - test/plots/penguin-species-cheysson.ts | 30 ------------ test/plots/penguin-species-gradient.ts | 20 -------- test/plots/penguin-species-group.ts | 65 +++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 52 deletions(-) create mode 100644 test/output/penguinSpeciesCSSFilter.svg delete mode 100644 test/plots/penguin-species-cheysson.ts delete mode 100644 test/plots/penguin-species-gradient.ts diff --git a/test/output/penguinSpeciesCSSFilter.svg b/test/output/penguinSpeciesCSSFilter.svg new file mode 100644 index 0000000000..24f4f65058 --- /dev/null +++ b/test/output/penguinSpeciesCSSFilter.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + 0 + 20 + 40 + 60 + 80 + 100 + 120 + 140 + + + ↑ Frequency + + + + + + + + Adelie + Chinstrap + Gentoo + + + species + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/index.ts b/test/plots/index.ts index fed29255e1..42953072e1 100644 --- a/test/plots/index.ts +++ b/test/plots/index.ts @@ -195,8 +195,6 @@ export * from "./penguin-quantile-unknown.js"; export * from "./penguin-sex-mass-culmen-species.js"; export * from "./penguin-sex.js"; export * from "./penguin-size-symbols.js"; -export * from "./penguin-species-cheysson.js"; -export * from "./penguin-species-gradient.js"; export * from "./penguin-species-group.js"; export * from "./penguin-species-island-relative.js"; export * from "./penguin-species-island-sex.js"; diff --git a/test/plots/penguin-species-cheysson.ts b/test/plots/penguin-species-cheysson.ts deleted file mode 100644 index 43aa0040db..0000000000 --- a/test/plots/penguin-species-cheysson.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as Plot from "@observablehq/plot"; -import * as d3 from "d3"; -import {svg} from "htl"; - -export async function penguinSpeciesCheysson() { - const penguins = await d3.csv("data/penguins.csv", d3.autoType); - return Plot.plot({ - color: { - range: [ - "url(#grouped-12512014-1)", - "url(#grouped-12512014-2)", - "url(#grouped-12512014-3)", - "url(#grouped-12512014-4)" - ], - legend: true - }, - marks: [ - // Based on https://observablehq.com/@tomshanley/cheysson-color-palettes - () => svg` - - - - - `, - Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "species", inset: 3})), - Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", stroke: "currentColor"})), - Plot.ruleY([0]) - ] - }); -} diff --git a/test/plots/penguin-species-gradient.ts b/test/plots/penguin-species-gradient.ts deleted file mode 100644 index 8724060cdb..0000000000 --- a/test/plots/penguin-species-gradient.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as Plot from "@observablehq/plot"; -import * as d3 from "d3"; -import {svg} from "htl"; - -export async function penguinSpeciesGradient() { - const penguins = await d3.csv("data/penguins.csv", d3.autoType); - return Plot.plot({ - marks: [ - () => svg` - - - - - - `, - Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "url(#gradient)"})), - Plot.ruleY([0]) - ] - }); -} diff --git a/test/plots/penguin-species-group.ts b/test/plots/penguin-species-group.ts index 94e03b58b9..5374d2f5aa 100644 --- a/test/plots/penguin-species-group.ts +++ b/test/plots/penguin-species-group.ts @@ -1,5 +1,6 @@ import * as Plot from "@observablehq/plot"; import * as d3 from "d3"; +import {svg} from "htl"; export async function penguinSpeciesGroup() { const penguins = await d3.csv("data/penguins.csv", d3.autoType); @@ -11,3 +12,67 @@ export async function penguinSpeciesGroup() { ] }); } + +export async function penguinSpeciesCheysson() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + color: { + range: [ + "url(#grouped-12512014-1)", + "url(#grouped-12512014-2)", + "url(#grouped-12512014-3)", + "url(#grouped-12512014-4)" + ], + legend: true + }, + marks: [ + // Based on https://observablehq.com/@tomshanley/cheysson-color-palettes + () => svg` + + + + + `, + Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "species", inset: 3})), + Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", stroke: "currentColor"})), + Plot.ruleY([0]) + ] + }); +} + +export async function penguinSpeciesGradient() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + marks: [ + () => svg` + + + + + + `, + Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: "url(#gradient)"})), + Plot.ruleY([0]) + ] + }); +} + +export async function penguinSpeciesCSSFilter() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + marks: [ + Plot.barY( + penguins, + Plot.groupX( + {y: "count"}, + { + x: "species", + fill: "species", + imageFilter: "drop-shadow(3px 3px red) sepia(40%) drop-shadow(-3px -3px currentColor)" + } + ) + ), + Plot.ruleY([0]) + ] + }); +} From 9142af9f51ec9734546b863eeb6ea81cf021f001 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 8 May 2023 14:03:41 -0700 Subject: [PATCH 3/3] rename tests --- ...lter.svg => penguinSpeciesImageFilter.svg} | 0 test/plots/index.ts | 2 +- ...in-species-group.ts => penguin-species.ts} | 24 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) rename test/output/{penguinSpeciesCSSFilter.svg => penguinSpeciesImageFilter.svg} (100%) rename test/plots/{penguin-species-group.ts => penguin-species.ts} (99%) diff --git a/test/output/penguinSpeciesCSSFilter.svg b/test/output/penguinSpeciesImageFilter.svg similarity index 100% rename from test/output/penguinSpeciesCSSFilter.svg rename to test/output/penguinSpeciesImageFilter.svg diff --git a/test/plots/index.ts b/test/plots/index.ts index 42953072e1..ee9e6ab576 100644 --- a/test/plots/index.ts +++ b/test/plots/index.ts @@ -195,7 +195,7 @@ export * from "./penguin-quantile-unknown.js"; export * from "./penguin-sex-mass-culmen-species.js"; export * from "./penguin-sex.js"; export * from "./penguin-size-symbols.js"; -export * from "./penguin-species-group.js"; +export * from "./penguin-species.js"; export * from "./penguin-species-island-relative.js"; export * from "./penguin-species-island-sex.js"; export * from "./penguin-species-island.js"; diff --git a/test/plots/penguin-species-group.ts b/test/plots/penguin-species.ts similarity index 99% rename from test/plots/penguin-species-group.ts rename to test/plots/penguin-species.ts index 5374d2f5aa..c7eacdde31 100644 --- a/test/plots/penguin-species-group.ts +++ b/test/plots/penguin-species.ts @@ -2,17 +2,6 @@ import * as Plot from "@observablehq/plot"; import * as d3 from "d3"; import {svg} from "htl"; -export async function penguinSpeciesGroup() { - const penguins = await d3.csv("data/penguins.csv", d3.autoType); - return Plot.plot({ - marks: [ - Plot.barX(penguins, Plot.stackX(Plot.groupZ({x: "proportion"}, {fill: "species"}))), - Plot.text(penguins, Plot.stackX(Plot.groupZ({x: "proportion", text: "first"}, {z: "species", text: "species"}))), - Plot.ruleX([0, 1]) - ] - }); -} - export async function penguinSpeciesCheysson() { const penguins = await d3.csv("data/penguins.csv", d3.autoType); return Plot.plot({ @@ -57,7 +46,18 @@ export async function penguinSpeciesGradient() { }); } -export async function penguinSpeciesCSSFilter() { +export async function penguinSpeciesGroup() { + const penguins = await d3.csv("data/penguins.csv", d3.autoType); + return Plot.plot({ + marks: [ + Plot.barX(penguins, Plot.stackX(Plot.groupZ({x: "proportion"}, {fill: "species"}))), + Plot.text(penguins, Plot.stackX(Plot.groupZ({x: "proportion", text: "first"}, {z: "species", text: "species"}))), + Plot.ruleX([0, 1]) + ] + }); +} + +export async function penguinSpeciesImageFilter() { const penguins = await d3.csv("data/penguins.csv", d3.autoType); return Plot.plot({ marks: [