diff --git a/src/marks/waffle.js b/src/marks/waffle.js index c9d8771d21..c6eb4acc30 100644 --- a/src/marks/waffle.js +++ b/src/marks/waffle.js @@ -1,9 +1,11 @@ import {extent, namespaces} from "d3"; +import {valueObject} from "../channel.js"; import {create} from "../context.js"; import {composeRender} from "../mark.js"; import {hasXY, identity, indexOf} from "../options.js"; import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, getPatternId} from "../style.js"; import {template} from "../template.js"; +import {initializer} from "../transforms/basic.js"; import {maybeIdentityX, maybeIdentityY} from "../transforms/identity.js"; import {maybeIntervalX, maybeIntervalY} from "../transforms/interval.js"; import {maybeStackX, maybeStackY} from "../transforms/stack.js"; @@ -15,7 +17,8 @@ const waffleDefaults = { export class WaffleX extends BarX { constructor(data, {unit = 1, gap = 1, round, render, multiple, ...options} = {}) { - super(data, {...options, render: composeRender(render, waffleRender("x"))}, waffleDefaults); + options = initializer({...options, render: composeRender(render, waffleRender("x"))}, waffleInitializer("x")); + super(data, options, waffleDefaults); this.unit = Math.max(0, unit); this.gap = +gap; this.round = maybeRound(round); @@ -25,7 +28,8 @@ 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); + options = initializer({...options, render: composeRender(render, waffleRender("y"))}, waffleInitializer("y")); + super(data, options, waffleDefaults); this.unit = Math.max(0, unit); this.gap = +gap; this.round = maybeRound(round); @@ -33,10 +37,11 @@ export class WaffleY extends BarY { } } -function waffleRender(y) { - return function (index, scales, values, dimensions, context) { - const {unit, gap, rx, ry, round} = this; - const {document} = context; +function waffleInitializer(y) { + return function (data, facets, channels, scales, dimensions) { + const {round, unit} = this; + + const values = valueObject(channels, scales); const Y1 = values.channels[`${y}1`].value; const Y2 = values.channels[`${y}2`].value; @@ -54,12 +59,50 @@ function waffleRender(y) { const cx = Math.min(barwidth / multiple, scale * multiple); const cy = scale * multiple; - // TODO insets? - const transform = y === "y" ? ([x, y]) => [x * cx, -y * cy] : ([x, y]) => [y * cy, x * cx]; + // The reference position. const tx = (barwidth - multiple * cx) / 2; const x0 = typeof barx === "function" ? (i) => barx(i) + tx : barx + tx; const y0 = scales[y](0); + // TODO insets? + const transform = y === "y" ? ([x, y]) => [x * cx, -y * cy] : ([x, y]) => [y * cy, x * cx]; + const mx = typeof x0 === "function" ? (i) => x0(i) - barwidth / 2 : () => x0; + const [ix, iy] = y === "y" ? [0, 1] : [1, 0]; + + const n = Y2.length; + const P = new Array(n); + const X = new Float64Array(n); + const Y = new Float64Array(n); + + for (let i = 0; i < n; ++i) { + P[i] = wafflePoints(round(Y1[i] / unit), round(Y2[i] / unit), multiple).map(transform); + const c = P[i].pop(); + X[i] = c[ix] + mx(i); + Y[i] = c[iy] + y0; + } + + this.cx = cx; + this.cy = cy; + this.x0 = x0; + this.y0 = y0; + + return { + channels: { + polygon: {value: P, source: null}, + [y === "y" ? "x" : "y"]: {value: X, scale: null, source: null}, + [`${y}1`]: {value: Y, scale: null, source: channels[`${y}1`]}, + [`${y}2`]: {value: Y, scale: null, source: channels[`${y}2`]} + } + }; + }; +} + +function waffleRender(y) { + return function (index, scales, values, dimensions, context) { + const {gap, cx, cy, rx, ry, x0, y0} = this; + const {document} = context; + const polygon = values.channels.polygon.value; + // Create a base pattern with shared attributes for cloning. const patternId = getPatternId(); const basePattern = document.createElementNS(namespaces.svg, "pattern"); @@ -95,13 +138,7 @@ 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) => `M${polygon[i].join("L")}Z`) .attr("fill", (i) => `url(#${patternId}-${i})`) .attr("stroke", this.stroke == null ? null : (i) => `url(#${patternId}-${i})`) ) @@ -146,6 +183,8 @@ function waffleRender(y) { // Waffles can also represent fractional intervals (e.g., 2.4–10.1). These // require additional corner cuts, so the implementation below generates a few // more points. +// +// The last point describes the centroid (used for pointing) function wafflePoints(i1, i2, columns) { if (i1 < 0 || i2 < 0) { const k = Math.ceil(-Math.min(i1, i2) / columns); // shift negative to positive @@ -174,10 +213,43 @@ function wafflePoints(i1, i2, columns) { : [ [Math.floor(i2 % columns), Math.ceil(i2 / columns)], [0, Math.ceil(i2 / columns)] - ]) + ]), + centroid(i1, i2, columns) ]; } +function centroid(i1, i2, columns) { + const r = Math.floor(i2 / columns) - Math.floor(i1 / columns); + return r === 0 // Single row + ? singleRowCentroid(i1, i2, columns) + : // Two incomplete rows, use the midpoint of their overlap if they do, otherwise use the largest + r === 1 + ? Math.floor(i2 % columns) > Math.ceil(i1 % columns) + ? [(Math.floor(i2 % columns) + Math.ceil(i1 % columns)) / 2, Math.floor(i2 / columns)] + : i2 % columns > columns - (i1 % columns) + ? singleRowCentroid(i2 - (i2 % columns), i2, columns) + : singleRowCentroid(i1, columns * Math.ceil(i1 / columns), columns) + : // At least one full row, take the midpoint of all the rows that include the middle + [columns / 2, (Math.round(i1 / columns) + Math.round(i2 / columns)) / 2]; +} + +function singleRowCentroid(i1, i2, columns) { + const c = Math.floor(i2) - Math.floor(i1); + return c === 0 // Single cell + ? [Math.floor(i1 % columns) + 0.5, Math.floor(i1 / columns) + (((i1 + i2) / 2) % 1)] + : c === 1 // Two incomplete cells, use the overlap if it is large enough, otherwise use the largest + ? (i2 % 1) - (i1 % 1) > 0.5 + ? [Math.ceil(i1 % columns), Math.floor(i2 / columns) + ((i1 % 1) + (i2 % 1)) / 2] + : i2 % 1 > 1 - (i1 % 1) + ? [Math.floor(i2 % columns) + 0.5, Math.floor(i2 / columns) + (i2 % 1) / 2] + : [Math.floor(i1 % columns) + 0.5, Math.floor(i1 / columns) + (1 + (i1 % 1)) / 2] + : // At least one full cell, take their midpoint + [ + Math.ceil(i1 % columns) + Math.ceil(Math.floor(i2) - Math.ceil(i1)) / 2, + Math.floor(i1 / columns) + (i2 >= 1 + i1 ? 0.5 : ((i1 + i2) / 2) % 1) + ]; +} + function maybeRound(round) { if (round === undefined || round === false) return Number; if (round === true) return Math.round; @@ -198,12 +270,12 @@ function spread(domain) { return max - min; } -export function waffleX(data, options = {}) { +export function waffleX(data, {tip, ...options} = {}) { if (!hasXY(options)) options = {...options, y: indexOf, x2: identity}; - return new WaffleX(data, maybeStackX(maybeIntervalX(maybeIdentityX(options)))); + return new WaffleX(data, {tip, ...maybeStackX(maybeIntervalX(maybeIdentityX(options)))}); } -export function waffleY(data, options = {}) { +export function waffleY(data, {tip, ...options} = {}) { if (!hasXY(options)) options = {...options, x: indexOf, y2: identity}; - return new WaffleY(data, maybeStackY(maybeIntervalY(maybeIdentityY(options)))); + return new WaffleY(data, {tip, ...maybeStackY(maybeIntervalY(maybeIdentityY(options)))}); } diff --git a/test/output/wafflePointer.svg b/test/output/wafflePointer.svg new file mode 100644 index 0000000000..52bc316d01 --- /dev/null +++ b/test/output/wafflePointer.svg @@ -0,0 +1,450 @@ + + + + + 0 + 5 + 10 + 15 + 20 + 25 + 30 + + + + 0 + 1 + 2 + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/wafflePointerFractional.svg b/test/output/wafflePointerFractional.svg new file mode 100644 index 0000000000..fd12792d0c --- /dev/null +++ b/test/output/wafflePointerFractional.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.51 + 0.99 + 0.5 + 6 + 0.3 + 1.6 + 9.1 + 2 + 18 + 6 + 0.5 + 2.5 + 46 + 34 + 20 + 7 + 0.5 + 0.1 + 0 + 2.5 + 1 + 0.1 + 0.8 + + + \ No newline at end of file diff --git a/test/output/waffleTip.svg b/test/output/waffleTip.svg new file mode 100644 index 0000000000..c9925a71f1 --- /dev/null +++ b/test/output/waffleTip.svg @@ -0,0 +1,67 @@ + + + + + 0 + 20 + 40 + 60 + 80 + 100 + 120 + 140 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/waffleTipFacet.svg b/test/output/waffleTipFacet.svg new file mode 100644 index 0000000000..83e2abf8c8 --- /dev/null +++ b/test/output/waffleTipFacet.svg @@ -0,0 +1,2084 @@ + + + + + 0 + + + 1 + + + + + + 0 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + + + + + + 0 + 1 + 2 + + + 0 + 1 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/waffleTipFacetX.svg b/test/output/waffleTipFacetX.svg new file mode 100644 index 0000000000..2fb0a28a3d --- /dev/null +++ b/test/output/waffleTipFacetX.svg @@ -0,0 +1,2080 @@ + + + + + 0 + + + 1 + + + + + + 0 + 1 + 2 + + + + + + 0 + 20 + 40 + 60 + 80 + + + 0 + 20 + 40 + 60 + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/waffleTipFacetXY.svg b/test/output/waffleTipFacetXY.svg new file mode 100644 index 0000000000..a7dda41ded --- /dev/null +++ b/test/output/waffleTipFacetXY.svg @@ -0,0 +1,2085 @@ + + + + + 0 + + + 1 + + + + + 0 + + + 1 + + + 2 + + + + + + 0 + 50 + + + 0 + 50 + + + 0 + 50 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/waffleTipUnit.svg b/test/output/waffleTipUnit.svg new file mode 100644 index 0000000000..2b8dfcc906 --- /dev/null +++ b/test/output/waffleTipUnit.svg @@ -0,0 +1,447 @@ + + + + + 0 + 5 + 10 + 15 + 20 + 25 + 30 + + + + 0 + 1 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/waffleTipUnitX.svg b/test/output/waffleTipUnitX.svg new file mode 100644 index 0000000000..e5e4a33dcc --- /dev/null +++ b/test/output/waffleTipUnitX.svg @@ -0,0 +1,447 @@ + + + + + 0 + 1 + 2 + + + + 0 + 5 + 10 + 15 + 20 + 25 + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/output/waffleTipX.svg b/test/output/waffleTipX.svg new file mode 100644 index 0000000000..4940dfa48b --- /dev/null +++ b/test/output/waffleTipX.svg @@ -0,0 +1,70 @@ + + + + + 0 + 20 + 40 + 60 + 80 + 100 + 120 + 140 + + + quantity → + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plots/waffle.ts b/test/plots/waffle.ts index 455efd3a4a..c30f69cc27 100644 --- a/test/plots/waffle.ts +++ b/test/plots/waffle.ts @@ -1,5 +1,6 @@ import * as Plot from "@observablehq/plot"; import * as d3 from "d3"; +import {svg} from "htl"; const demographics = d3.csvParse( `group,label,freq @@ -246,3 +247,115 @@ export async function waffleYGrouped() { marks: [Plot.waffleY(athletes, Plot.groupX({y: "count"}, {x: "sport", unit: 10})), Plot.ruleY([0])] }); } + +export function wafflePointer() { + const random = d3.randomLcg(42); + const data = Array.from({length: 100}, (_, i) => ({x: i % 3, fill: random()})); + return Plot.plot({ + y: {inset: 12}, + marks: [ + Plot.waffleY(data, {x: "x", y: 1, fill: "#888"}), + Plot.waffleY(data, Plot.pointer({x: "x", y: 1, fill: "fill"})) + ] + }); +} + +export function wafflePointerFractional() { + const values = [0.51, 0.99, 0.5, 6, 0.3, 1.6, 9.1, 2, 18, 6, 0.5, 2.5, 46, 34, 20, 7, 0.5, 0.1, 0, 2.5, 1, 0.1, 0.8]; + const multiple = 16; + return Plot.plot({ + axis: null, + y: {insetTop: 12}, + color: {scheme: "Dark2"}, + marks: [ + Plot.waffleY(values, { + x: null, + multiple, + fill: (d, i) => i % 7, + tip: true + }), + Plot.waffleY(values, { + x: null, + multiple, + // eslint-disable-next-line + render: (index, scales, values, dimensions, context, next) => { + const format = (d: number) => +d.toFixed(2); + const y1 = (values.channels.y1 as any).source.value; + const y2 = (values.channels.y2 as any).source.value; + return svg`${Array.from( + index, + (i) => + svg`${format(y2[i] - y1[i])}` + )}`; + } + }) + ] + }); +} +export function waffleTip() { + return Plot.plot({ + color: {type: "sqrt", scheme: "spectral"}, + y: {inset: 12}, + marks: [Plot.waffleY([1, 4, 9, 24, 46, 66, 7], {x: null, fill: Plot.identity, tip: true})] + }); +} + +export function waffleTipUnit() { + return Plot.plot({ + y: {inset: 12}, + marks: [Plot.waffleY({length: 100}, {x: (d, i) => i % 3, y: 1, fill: d3.randomLcg(42), tip: true})] + }); +} + +export function waffleTipFacet() { + return Plot.plot({ + marks: [ + Plot.waffleY({length: 500}, {x: (d, i) => i % 3, fx: (d, i) => i % 2, y: 1, fill: d3.randomLcg(42), tip: true}) + ] + }); +} + +export function waffleTipX() { + return Plot.plot({ + style: {overflow: "visible"}, + color: {type: "sqrt", scheme: "spectral"}, + x: {label: "quantity"}, + y: {inset: 12}, + marks: [Plot.waffleX([1, 4, 9, 24, 46, 66, 7], {y: null, fill: Plot.identity, tip: true})] + }); +} + +export function waffleTipUnitX() { + return Plot.plot({ + height: 300, + y: {inset: 12}, + marks: [ + Plot.waffleX( + {length: 100}, + {multiple: 5, y: (d, i) => i % 3, x: 1, fill: d3.randomLcg(42), tip: {format: {x: false}}} + ) + ] + }); +} + +export function waffleTipFacetX() { + return Plot.plot({ + height: 500, + marks: [ + Plot.waffleX({length: 500}, {y: (d, i) => i % 3, fx: (d, i) => i % 2, x: 1, fill: d3.randomLcg(42), tip: true}) + ] + }); +} + +export function waffleTipFacetXY() { + return Plot.plot({ + height: 600, + marks: [ + Plot.waffleX({length: 500}, {fx: (d, i) => i % 3, fy: (d, i) => i % 2, x: 1, fill: d3.randomLcg(42), tip: true}) + ] + }); +}