Skip to content

Commit 8c4db1d

Browse files
committed
waffle unit: "auto"
1 parent 1c6b239 commit 8c4db1d

4 files changed

Lines changed: 117 additions & 9 deletions

File tree

src/marks/waffle.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import type {BarXOptions, BarYOptions} from "./bar.js";
55
interface WaffleOptions {
66
/** The number of cells per row or column; defaults to undefined for automatic. */
77
multiple?: number;
8-
/** The quantity each cell represents; defaults to 1. */
9-
unit?: number;
8+
/**
9+
* The quantity each cell represents; defaults to *auto*, which defaults to 1
10+
* unless this makes the cell size unreasonable — in which case it adopts a
11+
* suitable power of 1,000.
12+
*/
13+
unit?: number | "auto";
1014
/** The gap in pixels between cells; defaults to 1. */
1115
gap?: number;
1216
/** If true, round to integers to avoid partial cells. */

src/marks/waffle.js

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {extent, namespaces} from "d3";
22
import {valueObject} from "../channel.js";
33
import {create} from "../context.js";
44
import {composeRender} from "../mark.js";
5-
import {hasXY, identity, indexOf, isObject} from "../options.js";
5+
import {hasXY, identity, indexOf, isObject, keyword} from "../options.js";
66
import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, getPatternId} from "../style.js";
77
import {template} from "../template.js";
88
import {initializer} from "../transforms/basic.js";
@@ -16,19 +16,19 @@ const waffleDefaults = {
1616
};
1717

1818
export class WaffleX extends BarX {
19-
constructor(data, {unit = 1, gap = 1, round, multiple, ...options} = {}) {
19+
constructor(data, {unit, gap = 1, round, multiple, ...options} = {}) {
2020
super(data, wafflePolygon("x", options), waffleDefaults);
21-
this.unit = Math.max(0, unit);
21+
this.unit = maybeUnit(unit);
2222
this.gap = +gap;
2323
this.round = maybeRound(round);
2424
this.multiple = maybeMultiple(multiple);
2525
}
2626
}
2727

2828
export class WaffleY extends BarY {
29-
constructor(data, {unit = 1, gap = 1, round, multiple, ...options} = {}) {
29+
constructor(data, {unit, gap = 1, round, multiple, ...options} = {}) {
3030
super(data, wafflePolygon("y", options), waffleDefaults);
31-
this.unit = Math.max(0, unit);
31+
this.unit = maybeUnit(unit);
3232
this.gap = +gap;
3333
this.round = maybeRound(round);
3434
this.multiple = maybeMultiple(multiple);
@@ -40,7 +40,7 @@ function wafflePolygon(y, options) {
4040
const y1 = `${y}1`;
4141
const y2 = `${y}2`;
4242
return initializer(waffleRender(options), function (data, facets, channels, scales, dimensions) {
43-
const {round, unit} = this;
43+
const {round} = this;
4444
const Y1 = channels[y1].value;
4545
const Y2 = channels[y2].value;
4646

@@ -49,8 +49,18 @@ function wafflePolygon(y, options) {
4949
const barwidth = this[y === "y" ? "_width" : "_height"](scales, xy, dimensions);
5050
const barx = this[y === "y" ? "_x" : "_y"](scales, xy, dimensions);
5151

52+
// Auto unit: if the scale of a unit makes it so small that it is invisible,
53+
// or conversely insanely large, adopt a different power of 10^3.
54+
const p = scaleof(scales.scales[y]); // pixel length per unit of 1
55+
let {unit} = this;
56+
if (unit === "auto") {
57+
const area = barwidth * p; // pixel area per unit of 1
58+
if (area < 5 || area > 5e4) unit = 1000 ** Math.ceil((1 - Math.log10(area)) / 3);
59+
else unit = 1;
60+
}
61+
5262
// The length of a unit along y in pixels.
53-
const scale = unit * scaleof(scales.scales[y]);
63+
const scale = unit * p;
5464

5565
// The number of cells on each row (or column) of the waffle.
5666
const {multiple = Math.max(1, Math.floor(Math.sqrt(barwidth / scale)))} = this;
@@ -301,3 +311,11 @@ function waffleTip(tip) {
301311
? {...tip, maxRadius: Infinity}
302312
: undefined;
303313
}
314+
315+
function maybeUnit(unit = "auto") {
316+
if (typeof unit === "number") {
317+
if (unit <= 0 || !isFinite(unit)) throw new Error(`invalid unit: ${unit}`);
318+
return unit;
319+
}
320+
return keyword(unit, "unit", ["auto"]);
321+
}

test/output/waffleAutoUnit.svg

Lines changed: 79 additions & 0 deletions
Loading

test/plots/waffle.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,10 @@ test(function waffleShapes() {
431431
]
432432
});
433433
});
434+
435+
test(function waffleAutoUnit() {
436+
return Plot.plot({
437+
marginLeft: 60,
438+
marks: [Plot.waffleY([1e6, 2e6, 3e6], {x: ["a", "b", "c"], fill: ["a", "b", "c"]}), Plot.ruleY([0])]
439+
});
440+
});

0 commit comments

Comments
 (0)