Skip to content
Merged
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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ A scale’s domain (the extent of its inputs, abstract values) and range (the ex
* *scale*.**range** - typically [*min*, *max*], or an array of ordinal or categorical values
* *scale*.**reverse** - reverses the domain, say to flip the chart along *x* or *y*

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; set the domain explicitly for a different order. 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 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; set the domain explicitly for a different order. 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](#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](#layout-options). For [color scales](#color-options), 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 Expand Up @@ -252,12 +252,16 @@ The normal scale types — *linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordina
* *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* scheme
* *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* scheme
* *diverging-symlog* - like *symlog*, but with a pivot; defaults to the *rdbu* scheme
* *threshold* - given a *domain* of *n* = 1 or more values and a *range* of *n* + 1 colors, returns the *i*th color of the *range* for values that are smaller than the *i*th element of the domain. Returns the *n*th color for values above the highest threshold
* *quantile* - given a number of *quantiles*, bins the channels into ordered *quantiles* having roughly the same number of values, then returns a threshold scale based on the bins’ limits

Color scales support two additional options:

* *scale*.**scheme** - a named color scheme in lieu of a range, such as *reds*
* *scale*.**interpolate** - in conjunction with a range, how to interpolate colors

For quantile color scales, the *scale*.scheme option is used in conjunction with *scale*.**quantiles**, which determines how many quantiles to compute, and thus the number of elements in the scale’s range; it defaults to 5 for quintiles.

The following sequential scale schemes are supported for both quantitative and ordinal data:

* <sub><img src="./img/blues.png" width="32" height="16" alt="blues"></sub> *blues*
Expand Down
4 changes: 3 additions & 1 deletion src/scales.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {registry, position, radius, opacity} from "./scales/index.js";
import {ScaleLinear, ScaleSqrt, ScalePow, ScaleLog, ScaleSymlog, ScaleIdentity} from "./scales/quantitative.js";
import {ScaleLinear, ScaleSqrt, ScalePow, ScaleLog, ScaleSymlog, ScaleQuantile, ScaleThreshold, ScaleIdentity} from "./scales/quantitative.js";
import {ScaleDiverging, ScaleDivergingSqrt, ScaleDivergingPow, ScaleDivergingLog, ScaleDivergingSymlog} from "./scales/quantitative.js";
import {ScaleTime, ScaleUtc} from "./scales/temporal.js";
import {ScaleOrdinal, ScalePoint, ScaleBand} from "./scales/ordinal.js";
Expand Down Expand Up @@ -67,6 +67,8 @@ function Scale(key, channels = [], options = {}) {
case "categorical": case "ordinal": return ScaleOrdinal(key, channels, options);
case "cyclical": case "sequential": case "linear": return ScaleLinear(key, channels, options);
case "sqrt": return ScaleSqrt(key, channels, options);
case "threshold": return ScaleThreshold(key, channels, options);
case "quantile": return ScaleQuantile(key, channels, options);
case "pow": return ScalePow(key, channels, options);
case "log": return ScaleLog(key, channels, options);
case "symlog": return ScaleSymlog(key, channels, options);
Expand Down
183 changes: 3 additions & 180 deletions src/scales/ordinal.js
Original file line number Diff line number Diff line change
@@ -1,184 +1,9 @@
import {InternSet, reverse as reverseof, sort} from "d3";
import {quantize} from "d3";
import {scaleBand, scaleOrdinal, scalePoint} from "d3";
import {
interpolateBlues,
interpolateBrBG,
interpolateBuGn,
interpolateBuPu,
interpolateGnBu,
interpolateGreens,
interpolateGreys,
interpolateOranges,
interpolateOrRd,
interpolatePiYG,
interpolatePRGn,
interpolatePuBu,
interpolatePuBuGn,
interpolatePuOr,
interpolatePuRd,
interpolatePurples,
interpolateRdBu,
interpolateRdGy,
interpolateRdPu,
interpolateRdYlBu,
interpolateRdYlGn,
interpolateReds,
interpolateSpectral,
interpolateYlGn,
interpolateYlGnBu,
interpolateYlOrBr,
interpolateYlOrRd,
interpolateTurbo,
interpolateViridis,
interpolateMagma,
interpolateInferno,
interpolatePlasma,
interpolateCividis,
interpolateCubehelixDefault,
interpolateWarm,
interpolateCool,
interpolateRainbow,
interpolateSinebow,
schemeAccent,
schemeBlues,
schemeBrBG,
schemeBuGn,
schemeBuPu,
schemeCategory10,
schemeDark2,
schemeGnBu,
schemeGreens,
schemeGreys,
schemeOranges,
schemeOrRd,
schemePaired,
schemePastel1,
schemePastel2,
schemePiYG,
schemePRGn,
schemePuBu,
schemePuBuGn,
schemePuOr,
schemePuRd,
schemePurples,
schemeRdBu,
schemeRdGy,
schemeRdPu,
schemeRdYlBu,
schemeRdYlGn,
schemeReds,
schemeSet1,
schemeSet2,
schemeSet3,
schemeSpectral,
schemeTableau10,
schemeYlGn,
schemeYlGnBu,
schemeYlOrBr,
schemeYlOrRd
} from "d3";
import {ordinalScheme} from "./schemes.js";
import {ascendingDefined} from "../defined.js";
import {registry, color} from "./index.js";

// TODO Allow this to be extended.
const schemes = new Map([
// categorical
["accent", schemeAccent],
["category10", schemeCategory10],
["dark2", schemeDark2],
["paired", schemePaired],
["pastel1", schemePastel1],
["pastel2", schemePastel2],
["set1", schemeSet1],
["set2", schemeSet2],
["set3", schemeSet3],
["tableau10", schemeTableau10],

// diverging
["brbg", scheme11(schemeBrBG, interpolateBrBG)],
["prgn", scheme11(schemePRGn, interpolatePRGn)],
["piyg", scheme11(schemePiYG, interpolatePiYG)],
["puor", scheme11(schemePuOr, interpolatePuOr)],
["rdbu", scheme11(schemeRdBu, interpolateRdBu)],
["rdgy", scheme11(schemeRdGy, interpolateRdGy)],
["rdylbu", scheme11(schemeRdYlBu, interpolateRdYlBu)],
["rdylgn", scheme11(schemeRdYlGn, interpolateRdYlGn)],
["spectral", scheme11(schemeSpectral, interpolateSpectral)],

// reversed diverging (for temperature data)
["burd", scheme11r(schemeRdBu, interpolateRdBu)],
["buylrd", scheme11r(schemeRdGy, interpolateRdGy)],

// sequential (single-hue)
["blues", scheme9(schemeBlues, interpolateBlues)],
["greens", scheme9(schemeGreens, interpolateGreens)],
["greys", scheme9(schemeGreys, interpolateGreys)],
["oranges", scheme9(schemeOranges, interpolateOranges)],
["purples", scheme9(schemePurples, interpolatePurples)],
["reds", scheme9(schemeReds, interpolateReds)],

// sequential (multi-hue)
["turbo", schemei(interpolateTurbo)],
["viridis", schemei(interpolateViridis)],
["magma", schemei(interpolateMagma)],
["inferno", schemei(interpolateInferno)],
["plasma", schemei(interpolatePlasma)],
["cividis", schemei(interpolateCividis)],
["cubehelix", schemei(interpolateCubehelixDefault)],
["warm", schemei(interpolateWarm)],
["cool", schemei(interpolateCool)],
["bugn", scheme9(schemeBuGn, interpolateBuGn)],
["bupu", scheme9(schemeBuPu, interpolateBuPu)],
["gnbu", scheme9(schemeGnBu, interpolateGnBu)],
["orrd", scheme9(schemeOrRd, interpolateOrRd)],
["pubu", scheme9(schemePuBu, interpolatePuBu)],
["pubugn", scheme9(schemePuBuGn, interpolatePuBuGn)],
["purd", scheme9(schemePuRd, interpolatePuRd)],
["rdpu", scheme9(schemeRdPu, interpolateRdPu)],
["ylgn", scheme9(schemeYlGn, interpolateYlGn)],
["ylgnbu", scheme9(schemeYlGnBu, interpolateYlGnBu)],
["ylorbr", scheme9(schemeYlOrBr, interpolateYlOrBr)],
["ylorrd", scheme9(schemeYlOrRd, interpolateYlOrRd)],

// cyclical
["rainbow", schemei(interpolateRainbow)],
["sinebow", schemei(interpolateSinebow)]
]);

function scheme9(scheme, interpolate) {
return ({length: n}) => {
n = n > 3 ? Math.floor(n) : 3;
return n > 9 ? quantize(interpolate, n) : scheme[n];
};
}

function scheme11(scheme, interpolate) {
return ({length: n}) => {
n = n > 3 ? Math.floor(n) : 3;
return n > 11 ? quantize(interpolate, n) : scheme[n];
};
}

function scheme11r(scheme, interpolate) {
return ({length: n}) => {
n = n > 3 ? Math.floor(n) : 3;
return n > 11 ? quantize(t => interpolate(1 - t), n) : scheme[n].slice().reverse();
};
}

function schemei(interpolate) {
return ({length: n}) => {
return quantize(interpolate, n > 0 ? Math.floor(n) : 0);
};
}

function Scheme(scheme) {
const s = (scheme + "").toLowerCase();
if (!schemes.has(s)) throw new Error(`unknown scheme: ${s}`);
return schemes.get(s);
}

export function ScaleO(scale, channels, {
domain = inferDomain(channels),
range,
Expand All @@ -196,11 +21,9 @@ export function ScaleO(scale, channels, {
}

export function ScaleOrdinal(key, channels, {
scheme,
type,
range = registry.get(key) === color ? (scheme !== undefined ? Scheme(scheme)
: type === "ordinal" ? schemes.get("turbo")
: schemeTableau10) : undefined,
scheme = type === "ordinal" ? "turbo" : "tableau10", // ignored if not color
range = registry.get(key) === color ? ordinalScheme(scheme) : undefined,
...options
}) {
return ScaleO(scaleOrdinal().unknown(undefined), channels, {range, ...options});
Expand Down
Loading