Skip to content

Commit 9ea2e27

Browse files
authored
waffle multiple option (#2121)
1 parent 67736b1 commit 9ea2e27

File tree

5 files changed

+138
-9
lines changed

5 files changed

+138
-9
lines changed

docs/marks/waffle.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,22 @@ Plot.waffleX([apples], {y: ["apples"]}).plot({height: 240})
124124
The number of rows in the waffle above is guaranteed to be an integer, but it might not be a multiple or factor of the *x*-axis tick interval. For example, the waffle might have 15 rows while the *x*-axis shows ticks every 100 units.
125125
:::
126126
:::tip
127-
While you can’t control the number of rows (or columns) directly, you can affect it via the **padding** option on the corresponding band scale. Padding defaults to 0.1; a higher value may produce more rows, while a lower (or zero) value may produce fewer rows.
127+
To set the number of rows (or columns) directly, use the **multiple** option, though note that manually setting the multiple may result in non-square cells if there isn’t enough room. Alternatively, you can bias the automatic multiple while preserving square cells by setting the **padding** option on the corresponding band scale: padding defaults to 0.1; a higher value may produce more rows, while a lower (or zero) value may produce fewer rows.
128128
:::
129129

130130
## Waffle options
131131

132132
For required channels, see the [bar mark](./bar.md). The waffle mark supports the [standard mark options](../features/marks.md), including [insets](../features/marks.md#insets) and [rounded corners](../features/marks.md#rounded-corners). The **stroke** defaults to *none*. The **fill** defaults to *currentColor* if the stroke is *none*, and to *none* otherwise.
133133

134+
The waffle mark supports a few additional options to control the rendering of cells:
135+
136+
* **unit** - the quantity each cell represents; defaults to 1
137+
* **multiple** - the number of cells per row (or column); defaults to undefined
138+
* **gap** - the separation between adjacent cells, in pixels; defaults to 1
139+
* **round** - whether to round values to avoid partial cells; defaults to false
140+
141+
If **multiple** is undefined (the default), the waffle mark will use as many cells per row (or column) that fits within the available bandwidth while ensuring that the cells are square, or one cell per row if square cells are not possible. You can change the rounding behavior by specifying **round** as a function, such as Math.floor or Math.ceil; true is equivalent to Math.round.
142+
134143
## waffleX(*data*, *options*) {#waffleX}
135144

136145
```js

src/marks/waffle.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type {BarXOptions, BarYOptions} from "./bar.js";
33

44
/** Options for the waffleX and waffleY mark. */
55
interface WaffleOptions {
6+
/** The number of cells per row or column; defaults to undefined for automatic. */
7+
multiple?: number;
68
/** The quantity each cell represents; defaults to 1. */
79
unit?: number;
810
/** The gap in pixels between cells; defaults to 1. */

src/marks/waffle.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@ const waffleDefaults = {
1414
};
1515

1616
export class WaffleX extends BarX {
17-
constructor(data, {unit = 1, gap = 1, round, render, ...options} = {}) {
17+
constructor(data, {unit = 1, gap = 1, round, render, multiple, ...options} = {}) {
1818
super(data, {...options, render: composeRender(render, waffleRender("x"))}, waffleDefaults);
1919
this.unit = Math.max(0, unit);
2020
this.gap = +gap;
2121
this.round = maybeRound(round);
22+
this.multiple = maybeMultiple(multiple);
2223
}
2324
}
2425

2526
export class WaffleY extends BarY {
26-
constructor(data, {unit = 1, gap = 1, round, render, ...options} = {}) {
27+
constructor(data, {unit = 1, gap = 1, round, render, multiple, ...options} = {}) {
2728
super(data, {...options, render: composeRender(render, waffleRender("y"))}, waffleDefaults);
2829
this.unit = Math.max(0, unit);
2930
this.gap = +gap;
3031
this.round = maybeRound(round);
32+
this.multiple = maybeMultiple(multiple);
3133
}
3234
}
3335

@@ -45,16 +47,16 @@ function waffleRender(y) {
4547
// The length of a unit along y in pixels.
4648
const scale = unit * scaleof(scales.scales[y]);
4749

48-
// The number of cells on each row of the waffle.
49-
const columns = Math.max(1, Math.floor(Math.sqrt(barwidth / scale)));
50+
// The number of cells on each row (or column) of the waffle.
51+
const {multiple = Math.max(1, Math.floor(Math.sqrt(barwidth / scale)))} = this;
5052

5153
// The outer size of each square cell, in pixels, including the gap.
52-
const cx = Math.min(barwidth, scale * columns);
53-
const cy = scale * columns;
54+
const cx = Math.min(barwidth / multiple, scale * multiple);
55+
const cy = scale * multiple;
5456

5557
// TODO insets?
5658
const transform = y === "y" ? ([x, y]) => [x * cx, -y * cy] : ([x, y]) => [y * cy, x * cx];
57-
const tx = (barwidth - columns * cx) / 2;
59+
const tx = (barwidth - multiple * cx) / 2;
5860
const x0 = typeof barx === "function" ? (i) => barx(i) + tx : barx + tx;
5961
const y0 = scales[y](0);
6062

@@ -96,7 +98,7 @@ function waffleRender(y) {
9698
.attr(
9799
"d",
98100
(i) =>
99-
`M${wafflePoints(round(Y1[i] / unit), round(Y2[i] / unit), columns)
101+
`M${wafflePoints(round(Y1[i] / unit), round(Y2[i] / unit), multiple)
100102
.map(transform)
101103
.join("L")}Z`
102104
)
@@ -183,6 +185,10 @@ function maybeRound(round) {
183185
return round;
184186
}
185187

188+
function maybeMultiple(multiple) {
189+
return multiple === undefined ? undefined : Math.max(1, Math.floor(multiple));
190+
}
191+
186192
function scaleof({domain, range}) {
187193
return spread(range) / spread(domain);
188194
}

test/output/waffleMultiple.svg

+102
Loading

test/plots/waffle.ts

+10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ export function waffleSquished() {
1515
return Plot.waffleX([10]).plot();
1616
}
1717

18+
export function waffleMultiple() {
19+
return Plot.plot({
20+
y: {inset: 12},
21+
marks: [
22+
Plot.waffleY([4, 9, 24, 46, 66, 7], {multiple: 10, fill: "currentColor"}),
23+
Plot.waffleY([-4, -9, -24, -46, -66, -7], {multiple: 10, fill: "red"})
24+
]
25+
});
26+
}
27+
1828
export function waffleShorthand() {
1929
return Plot.plot({
2030
y: {inset: 12},

0 commit comments

Comments
 (0)