Skip to content

Commit

Permalink
paint
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Jun 28, 2023
1 parent 7b92d54 commit d5359e8
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ export {pointer, pointerX, pointerY} from "./interactions/pointer.js";
export {formatIsoDate, formatWeekday, formatMonth} from "./format.js";
export {scale} from "./scales.js";
export {legend} from "./legends.js";
export {linearGradient} from "./paint.js";
5 changes: 5 additions & 0 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,12 +442,17 @@ export function isEvery(values, is) {
return every;
}

export function isPaint(value) {
return typeof value?.paint === "function";
}

// Mostly relies on d3-color, with a few extra color keywords. Currently this
// strictly requires that the value be a string; we might want to apply string
// coercion here, though note that d3-color instances would need to support
// valueOf to work correctly with InternMap.
// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
export function isColor(value) {
if (isPaint(value)) return true;
if (typeof value !== "string") return false;
value = value.toLowerCase().trim();
return (
Expand Down
13 changes: 13 additions & 0 deletions src/paint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {create} from "./context.js";

export function linearGradient() {
return {
paint(context) {
const gradient = create("svg:linearGradient", context).attr("gradientTransform", "rotate(90)");
gradient.append("stop").attr("offset", "5%").attr("stop-color", "purple");
gradient.append("stop").attr("offset", "75%").attr("stop-color", "red");
gradient.append("stop").attr("offset", "100%").attr("stop-color", "gold");
return gradient.node();
}
};
}
24 changes: 11 additions & 13 deletions src/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,8 @@ import {geoPath, group, namespaces} from "d3";
import {create} from "./context.js";
import {defined, nonempty} from "./defined.js";
import {formatDefault} from "./format.js";
import {
string,
number,
maybeColorChannel,
maybeNumberChannel,
maybeKeyword,
isNoneish,
isNone,
isRound,
keyof
} from "./options.js";
import {isNone, isNoneish, isPaint, isRound} from "./options.js";
import {keyof, maybeColorChannel, maybeKeyword, maybeNumberChannel, 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
Expand Down Expand Up @@ -115,7 +106,7 @@ export function styles(

// Some marks don’t support fill (e.g., tick and rule).
if (defaultFill !== null) {
mark.fill = impliedString(cfill, "currentColor");
mark.fill = isPaint(cfill) ? cfill : impliedString(cfill, "currentColor");
mark.fillOpacity = impliedNumber(cfillOpacity, 1);
}

Expand Down Expand Up @@ -364,7 +355,14 @@ function applyClip(selection, mark, dimensions, context) {
// Note: may mutate selection.node!
export function applyIndirectStyles(selection, mark, dimensions, context) {
applyClip(selection, mark, dimensions, context);
applyAttr(selection, "fill", mark.fill);
if (isPaint(mark.fill)) {
const paint = mark.fill.paint(context);
paint.setAttribute("id", "test-paint");
context.ownerSVGElement.append(paint);
selection.attr("fill", "url(#test-paint)");
} else {
applyAttr(selection, "fill", mark.fill);
}
applyAttr(selection, "fill-opacity", mark.fillOpacity);
applyAttr(selection, "stroke", mark.stroke);
applyAttr(selection, "stroke-width", mark.strokeWidth);
Expand Down
10 changes: 10 additions & 0 deletions test/plots/penguin-species.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ export async function penguinSpeciesCheysson() {
});
}

export async function penguinSpeciesPaint() {
const penguins = await d3.csv<any>("data/penguins.csv", d3.autoType);
return Plot.plot({
marks: [
Plot.barY(penguins, Plot.groupX({y: "count"}, {x: "species", fill: Plot.linearGradient()})),
Plot.ruleY([0])
]
});
}

export async function penguinSpeciesGradient() {
const penguins = await d3.csv<any>("data/penguins.csv", d3.autoType);
return Plot.plot({
Expand Down

0 comments on commit d5359e8

Please sign in to comment.