Skip to content

Commit

Permalink
waffle tips
Browse files Browse the repository at this point in the history
  • Loading branch information
Fil committed Aug 12, 2024
1 parent 1d01e25 commit 819e3f2
Show file tree
Hide file tree
Showing 4 changed files with 593 additions and 16 deletions.
80 changes: 64 additions & 16 deletions src/marks/waffle.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {extent, namespaces} from "d3";
import {extent, namespaces, polygonCentroid} from "d3";
import {create} from "../context.js";
import {composeRender} from "../mark.js";
import {hasXY, identity, indexOf} from "../options.js";
Expand All @@ -8,14 +8,36 @@ import {maybeIdentityX, maybeIdentityY} from "../transforms/identity.js";
import {maybeIntervalX, maybeIntervalY} from "../transforms/interval.js";
import {maybeStackX, maybeStackY} from "../transforms/stack.js";
import {BarX, BarY} from "./bar.js";
import {initializer} from "../transforms/basic.js";

const waffleDefaults = {
ariaLabel: "waffle"
};

export class WaffleX extends BarX {
constructor(data, {unit = 1, gap = 1, round, render, multiple, ...options} = {}) {
super(data, {...options, render: composeRender(render, waffleRender("x"))}, waffleDefaults);
super(
data,
initializer({...options, render: composeRender(render, waffleRender("x"))}, function (data, facets, channels) {
const n = channels.x1.value.length;
const X = new Float64Array(n);
const Y = new Float64Array(n);
return {
data,
facets,
channels: {
...channels,
x1: {value: X, scale: null, source: null},
x2: {value: X, scale: null, source: null},
y1: {value: Y, scale: null, source: null},
y2: {value: Y, scale: null, source: null},
s1: {...channels.x1, source: null},
s2: {...channels.x2, source: null}
}
};
}),
waffleDefaults
);
this.unit = Math.max(0, unit);
this.gap = +gap;
this.round = maybeRound(round);
Expand All @@ -25,7 +47,28 @@ export class WaffleX extends BarX {

export class WaffleY extends BarY {
constructor(data, {unit = 1, gap = 1, round, render, multiple, ...options} = {}) {
super(data, {...options, render: composeRender(render, waffleRender("y"))}, waffleDefaults);
super(
data,
initializer({...options, render: composeRender(render, waffleRender("y"))}, function (data, facets, channels) {
const n = channels.y1.value.length;
const X = new Float64Array(n);
const Y = new Float64Array(n);
return {
data,
facets,
channels: {
...channels,
x1: {value: X, scale: null, source: null},
x2: {value: X, scale: null, source: null},
y1: {value: Y, scale: null, source: null},
y2: {value: Y, scale: null, source: null},
s1: {...channels.y1, source: null},
s2: {...channels.y2, source: null}
}
};
}),
waffleDefaults
);
this.unit = Math.max(0, unit);
this.gap = +gap;
this.round = maybeRound(round);
Expand All @@ -37,8 +80,8 @@ function waffleRender(y) {
return function (index, scales, values, dimensions, context) {
const {unit, gap, rx, ry, round} = this;
const {document} = context;
const Y1 = values.channels[`${y}1`].value;
const Y2 = values.channels[`${y}2`].value;
const Y1 = values.channels["s1"].value;
const Y2 = values.channels["s2"].value;

// We might not use all the available bandwidth if the cells don’t fit evenly.
const barwidth = this[y === "y" ? "_width" : "_height"](scales, values, dimensions);
Expand Down Expand Up @@ -74,6 +117,9 @@ function waffleRender(y) {
if (rx != null) basePatternRect.setAttribute("rx", rx);
if (ry != null) basePatternRect.setAttribute("ry", ry);

const X = values.channels.x1.value;
const Y = values.channels.y1.value;

return create("svg:g", context)
.call(applyIndirectStyles, this, dimensions, context)
.call(this._transform, this, scales)
Expand All @@ -95,13 +141,13 @@ function waffleRender(y) {
.enter()
.append("path")
.attr("transform", y === "y" ? template`translate(${x0},${y0})` : template`translate(${y0},${x0})`)
.attr(
"d",
(i) =>
`M${wafflePoints(round(Y1[i] / unit), round(Y2[i] / unit), multiple)
.map(transform)
.join("L")}Z`
)
.attr("d", (i) => {
const pts = wafflePoints(round(Y1[i] / unit), round(Y2[i] / unit), multiple).map(transform);
const [xa, ya] = polygonCentroid(pts);
X[i] = xa + (typeof x0 === "function" ? x0(i) - barwidth / 2 : x0);
Y[i] = ya + (typeof y0 === "function" ? y0(i) : y0);
return `M${pts.join("L")}Z`;
})
.attr("fill", (i) => `url(#${patternId}-${i})`)
.attr("stroke", this.stroke == null ? null : (i) => `url(#${patternId}-${i})`)
)
Expand Down Expand Up @@ -198,12 +244,14 @@ function spread(domain) {
return max - min;
}

export function waffleX(data, options = {}) {
export function waffleX(data, {tip, ...options} = {}) {
if (tip === true) tip = "xy";
if (!hasXY(options)) options = {...options, y: indexOf, x2: identity};
return new WaffleX(data, maybeStackX(maybeIntervalX(maybeIdentityX(options))));
return new WaffleX(data, maybeStackX(maybeIntervalX(maybeIdentityX({...options, tip}))));
}

export function waffleY(data, options = {}) {
export function waffleY(data, {tip, ...options} = {}) {
if (tip === true) tip = "xy";
if (!hasXY(options)) options = {...options, x: indexOf, y2: identity};
return new WaffleY(data, maybeStackY(maybeIntervalY(maybeIdentityY(options))));
return new WaffleY(data, maybeStackY(maybeIntervalY(maybeIdentityY({...options, tip}))));
}
67 changes: 67 additions & 0 deletions test/output/waffleTip.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 819e3f2

Please sign in to comment.