Skip to content

explicit colors #660

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

Closed
wants to merge 3 commits into from
Closed
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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ The normal scale types — *linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordina
* *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

For categorical data, if all the values in the color channels are valid CSS colors, the color scale defaults to identity.

For a *threshold* scale, the *domain* represents *n* (typically numeric) thresholds which will produce a *range* of *n* + 1 output colors; the *i*th color of the *range* applies to values that are smaller than the *i*th element of the domain and larger or equal to the *i* - 1th element of the domain. For a *quantile* scale, the *domain* represents all input values to the scale, and the *quantiles* option specifies how many quantiles to compute from the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output range of *n* colors.

By default, all diverging color scales are symmetric around the pivot; set *symmetric* to false if you want to cover the whole extent on both sides.
Expand Down
2 changes: 1 addition & 1 deletion src/mark.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const second = d => d[1];
export const constant = x => () => x;

// A few extra color keywords not known to d3-color.
const colors = new Set(["currentColor", "none"]);
export const colors = new Set(["currentColor", "none"]);

// Some channels may allow a string constant to be specified; to differentiate
// string constants (e.g., "red") from named fields (e.g., "date"), this
Expand Down
22 changes: 14 additions & 8 deletions src/scales.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {descending} from "d3";
import {color as isColor, descending} from "d3";
import {parse as isoParse} from "isoformat";
import {registry, color, position, radius, opacity, symbol, length} from "./scales/index.js";
import {ScaleLinear, ScaleSqrt, ScalePow, ScaleLog, ScaleSymlog, ScaleQuantile, ScaleThreshold, ScaleIdentity} from "./scales/quantitative.js";
import {ScaleDiverging, ScaleDivergingSqrt, ScaleDivergingPow, ScaleDivergingLog, ScaleDivergingSymlog} from "./scales/diverging.js";
import {ScaleTime, ScaleUtc} from "./scales/temporal.js";
import {ScaleOrdinal, ScalePoint, ScaleBand} from "./scales/ordinal.js";
import {isOrdinal, isTemporal} from "./mark.js";
import {colors, isOrdinal, isTemporal} from "./mark.js";

export function Scales(channels, {
inset: globalInset = 0,
Expand Down Expand Up @@ -184,7 +184,7 @@ function Scale(key, channels = [], options = {}) {
}
}

function inferScaleType(key, channels, {type, domain, range}) {
function inferScaleType(key, channels, {type, domain, range, scheme}) {
if (key === "fx" || key === "fy") return "band";
if (type !== undefined) {
for (const {type: t} of channels) {
Expand All @@ -198,24 +198,30 @@ function inferScaleType(key, channels, {type, domain, range}) {
if (registry.get(key) === opacity || registry.get(key) === length) return "linear";
if (registry.get(key) === symbol) return "ordinal";
for (const {type} of channels) if (type !== undefined) return type;
if ((domain || range || []).length > 2) return asOrdinalType(key);
if ((domain || range || []).length > 2) return asOrdinalType(key, domain, range, scheme);
if (domain !== undefined) {
if (isOrdinal(domain)) return asOrdinalType(key);
if (isOrdinal(domain)) return asOrdinalType(key, domain, range, scheme);
if (isTemporal(domain)) return "utc";
return "linear";
}
// If any channel is ordinal or temporal, it takes priority.
const values = channels.map(({value}) => value).filter(value => value !== undefined);
if (values.some(isOrdinal)) return asOrdinalType(key);
if (values.some(isOrdinal)) return asOrdinalType(key, new Set(values.flat()), range, scheme);
if (values.some(isTemporal)) return "utc";
return "linear";
}

// Positional scales default to a point scale instead of an ordinal scale.
function asOrdinalType(key) {
// Color scales default to identity if every element of the domain is a color, and no range has been specified
function asOrdinalType(key, domain, range, scheme) {
switch (registry.get(key)) {
case position: return "point";
case color: return "categorical";
case color:
return range === undefined && scheme === undefined
&& (domain = Array.from(domain)).length > 0
&& domain.every(value => colors.has(value) || isColor(value))
? "identity"
: "categorical";
default: return "ordinal";
}
}
Expand Down
Loading