Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/features/scales.md
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ A scale’s domain (the extent of its inputs, abstract values) and range (the ex
* **reverse** - reverses the domain (or the range), say to flip the chart along *x* or *y*
* **interval** - an interval or time interval (for interval data; see below)

For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [**sort** option](#sort-mark-option) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*].
For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For linear scales, the default domain is extended to include zero if within 7% of the domain extent; use **zero**: false to disable this, or **zero**: true to always include zero. For the *radius*, *opacity*, and *length* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [**sort** option](#sort-mark-option) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*].

The default range depends on the scale: for position scales (*x*, *y*, *fx*, and *fy*), the default range depends on the [plot’s size and margins](./plots.md). For color scales, there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger.

Expand Down
25 changes: 18 additions & 7 deletions src/scales/quantitative.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function createScaleQ(
nice,
clamp,
zero,
domain = inferAutoDomain(key, channels),
domain = inferAutoDomain(key, channels, zero, type),
unknown,
round,
scheme,
Expand Down Expand Up @@ -282,13 +282,24 @@ export function inferDomain(channels, f = finite) {
: [0, 1];
}

function inferAutoDomain(key, channels) {
const type = registry.get(key);
return (type === radius || type === opacity || type === length ? inferZeroDomain : inferDomain)(channels);
}
function inferAutoDomain(key, channels, zero, type) {
const domain = inferDomain(channels, undefined);

if (zero === undefined && domain[0] * domain[1] > 0) {
// Default to zero for radius, opacity, and length scales
const scale = registry.get(key);
if (scale === radius || scale === opacity || scale === length) zero = true;
// If the zero option is not specified, then implicitly extend the domain to
// include zero if the minimum is less than 7% of the spread (and similarly
// for negative domains).
else if (type === "linear") {
const [min, max] = extent(domain);
zero = min > 0 ? min < 0.07 * (max - min) : max < 0 ? max > 0.07 * (min - max) : false;
}
if (zero) return [Math.min(0, domain[0]), Math.max(0, domain[1])];
}

function inferZeroDomain(channels) {
return [0, channels.length ? max(channels, ({value}) => (value === undefined ? value : max(value, finite))) : 1];
return domain;
}

// We don’t want the upper bound of the radial domain to be zero, as this would
Expand Down
54 changes: 27 additions & 27 deletions test/output/arcCollatz.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 33 additions & 33 deletions test/output/arcCollatzUp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading