Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

waffle unit: auto #2215

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
8 changes: 6 additions & 2 deletions src/marks/waffle.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import type {BarXOptions, BarYOptions} from "./bar.js";
interface WaffleOptions {
/** The number of cells per row or column; defaults to undefined for automatic. */
multiple?: number;
/** The quantity each cell represents; defaults to 1. */
unit?: number;
/**
* The quantity each cell represents; defaults to "auto", which defaults to 1
* unless this makes the cell size unreasonable — in which case it adopts a
* suitable power of 1,000.
*/
unit?: number | "auto";
/** The gap in pixels between cells; defaults to 1. */
gap?: number;
/** If true, round to integers to avoid partial cells. */
Expand Down
28 changes: 21 additions & 7 deletions src/marks/waffle.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {extent, namespaces} from "d3";
import {valueObject} from "../channel.js";
import {create} from "../context.js";
import {composeRender} from "../mark.js";
import {hasXY, identity, indexOf, isObject} from "../options.js";
import {hasXY, identity, indexOf, isObject, keyword} from "../options.js";
import {applyChannelStyles, applyDirectStyles, applyIndirectStyles, getPatternId} from "../style.js";
import {template} from "../template.js";
import {initializer} from "../transforms/basic.js";
Expand All @@ -16,19 +16,19 @@ const waffleDefaults = {
};

export class WaffleX extends BarX {
constructor(data, {unit = 1, gap = 1, round, multiple, ...options} = {}) {
constructor(data, {unit, gap = 1, round, multiple, ...options} = {}) {
super(data, wafflePolygon("x", options), waffleDefaults);
this.unit = Math.max(0, unit);
this.unit = maybeUnit(unit);
this.gap = +gap;
this.round = maybeRound(round);
this.multiple = maybeMultiple(multiple);
}
}

export class WaffleY extends BarY {
constructor(data, {unit = 1, gap = 1, round, multiple, ...options} = {}) {
constructor(data, {unit, gap = 1, round, multiple, ...options} = {}) {
super(data, wafflePolygon("y", options), waffleDefaults);
this.unit = Math.max(0, unit);
this.unit = maybeUnit(unit);
this.gap = +gap;
this.round = maybeRound(round);
this.multiple = maybeMultiple(multiple);
Expand All @@ -40,7 +40,7 @@ function wafflePolygon(y, options) {
const y1 = `${y}1`;
const y2 = `${y}2`;
return initializer(waffleRender(options), function (data, facets, channels, scales, dimensions) {
const {round, unit} = this;
const {round} = this;
const Y1 = channels[y1].value;
const Y2 = channels[y2].value;

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

// Auto unit: if the scale of a unit makes it so small that it is invisible,
// or conversely insanely large, adopt a different power of 10**3.
const p = scaleof(scales.scales[y]); // pixel length per unit of 1
let {unit} = this;
if (unit === "auto") {
const area = barwidth * p; // pixel area per unit of 1
if (area < 5 || area > 5e4) unit = 1000 ** Math.ceil((1 - Math.log10(area)) / 3);
else unit = 1;
}

// The length of a unit along y in pixels.
const scale = unit * scaleof(scales.scales[y]);
const scale = unit * p;

// The number of cells on each row (or column) of the waffle.
const {multiple = Math.max(1, Math.floor(Math.sqrt(barwidth / scale)))} = this;
Expand Down Expand Up @@ -263,6 +273,10 @@ function maybeRound(round) {
return round;
}

function maybeUnit(unit = "auto") {
return typeof unit === "number" ? Math.max(0, unit) : keyword(unit, "unit", ["auto"]);
}

function maybeMultiple(multiple) {
return multiple === undefined ? undefined : Math.max(1, Math.floor(multiple));
}
Expand Down
Loading