Skip to content

Commit 45ed2e4

Browse files
committed
fix for scheme + range for diverging scales
1 parent a6e4c43 commit 45ed2e4

File tree

3 files changed

+64
-27
lines changed

3 files changed

+64
-27
lines changed

src/scales/diverging.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
interpolateNumber,
23
interpolateRgb,
34
piecewise,
45
scaleDiverging,
@@ -9,34 +10,41 @@ import {
910
import {positive, negative} from "../defined.js";
1011
import {quantitativeScheme} from "./schemes.js";
1112
import {registry, color} from "./index.js";
12-
import {inferDomain, Interpolator, flip} from "./quantitative.js";
13+
import {inferDomain, Interpolator, flip, interpolatePiecewise} from "./quantitative.js";
1314

1415
function ScaleD(key, scale, transform, channels, {
16+
type,
1517
nice,
1618
clamp,
1719
domain = inferDomain(channels),
1820
unknown,
1921
pivot = 0,
22+
scheme,
2023
range,
21-
scheme = "rdbu",
2224
symmetric = true,
23-
interpolate = registry.get(key) === color ? (range !== undefined ? interpolateRgb : quantitativeScheme(scheme)) : undefined,
24-
reverse,
25-
type
25+
interpolate = registry.get(key) === color ? (scheme == null && range !== undefined ? interpolateRgb : quantitativeScheme(scheme !== undefined ? scheme : "rdbu")) : interpolateNumber,
26+
reverse
2627
}) {
2728
pivot = +pivot;
2829
let [min, max] = domain;
2930
min = Math.min(min, pivot);
3031
max = Math.max(max, pivot);
3132

3233
// Sometimes interpolate is a named interpolator, such as "lab" for Lab color
33-
// space; other times it is a function that takes t in [0, 1].
34-
if (interpolate !== undefined && typeof interpolate !== "function") {
34+
// space. Other times interpolate is a function that takes two arguments and
35+
// is used in conjunction with the range. And other times the interpolate
36+
// function is a “fixed” interpolator on the [0, 1] interval, as when a
37+
// color scheme such as interpolateRdBu is used.
38+
if (typeof interpolate !== "function") {
3539
interpolate = Interpolator(interpolate);
3640
}
3741

3842
// If an explicit range is specified, promote it to a piecewise interpolator.
39-
if (range !== undefined) interpolate = piecewise(interpolate, range);
43+
if (range !== undefined) {
44+
interpolate = interpolate.length === 1
45+
? interpolatePiecewise(interpolate)(...range)
46+
: piecewise(interpolate, range);
47+
}
4048

4149
// Normalize the interpolator for symmetric difference around the pivot.
4250
if (symmetric) {

src/scales/quantitative.js

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ export function Interpolator(interpolate) {
4848
}
4949

5050
export function ScaleQ(key, scale, channels, {
51+
type,
5152
nice,
5253
clamp,
5354
zero,
5455
domain = (registry.get(key) === radius || registry.get(key) === opacity ? inferZeroDomain : inferDomain)(channels),
5556
unknown,
5657
round,
57-
range = registry.get(key) === radius ? inferRadialRange(channels, domain) : registry.get(key) === opacity ? unit : undefined,
58-
type,
5958
scheme,
59+
range = registry.get(key) === radius ? inferRadialRange(channels, domain) : registry.get(key) === opacity ? unit : undefined,
6060
interpolate = registry.get(key) === color ? (scheme == null && range !== undefined ? interpolateRgb : quantitativeScheme(scheme !== undefined ? scheme : type === "cyclical" ? "rainbow" : "turbo")) : round ? interpolateRound : interpolateNumber,
6161
reverse,
6262
inset = 0
@@ -70,25 +70,21 @@ export function ScaleQ(key, scale, channels, {
7070
// is used in conjunction with the range. And other times the interpolate
7171
// function is a “fixed” interpolator on the [0, 1] interval, as when a
7272
// color scheme such as interpolateRdBu is used.
73-
if (interpolate !== undefined) {
74-
if (typeof interpolate !== "function") {
75-
interpolate = Interpolator(interpolate);
73+
if (typeof interpolate !== "function") {
74+
interpolate = Interpolator(interpolate);
75+
}
76+
if (interpolate.length === 1) {
77+
if (reverse) {
78+
interpolate = flip(interpolate);
79+
reverse = false;
7680
}
77-
if (interpolate.length === 1) {
78-
if (reverse) {
79-
interpolate = flip(interpolate);
80-
reverse = false;
81-
}
82-
if (range === undefined) {
83-
range = Float64Array.from(domain, (_, i) => i / (domain.length - 1));
84-
if (range.length === 2) range = unit; // optimize common case of [0, 1]
85-
}
86-
scale.interpolate((range === unit ? constant : interpolatePiecewise)(interpolate));
87-
} else {
88-
scale.interpolate(interpolate);
81+
if (range === undefined) {
82+
range = Float64Array.from(domain, (_, i) => i / (domain.length - 1));
83+
if (range.length === 2) range = unit; // optimize common case of [0, 1]
8984
}
85+
scale.interpolate((range === unit ? constant : interpolatePiecewise)(interpolate));
9086
} else {
91-
interpolate = scale.interpolate();
87+
scale.interpolate(interpolate);
9288
}
9389

9490
// If a zero option is specified, we assume that the domain is numeric, and we
@@ -205,6 +201,6 @@ function inferQuantileDomain(channels) {
205201
return domain;
206202
}
207203

208-
function interpolatePiecewise(interpolate) {
204+
export function interpolatePiecewise(interpolate) {
209205
return (i, j) => t => interpolate(i + t * (j - i));
210206
}

test/scales/scales-test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,39 @@ it("plot(…).scale('color') can return a symmetric diverging scale", async () =
407407
}
408408
});
409409

410+
it("plot(…).scale('color') can return a diverging scale with an explicit range", async () => {
411+
const gistemp = await d3.csv("data/gistemp.csv", d3.autoType);
412+
const plot = Plot.dot(gistemp, {x: "Date", stroke: "Anomaly"}).plot({color: {type: "diverging", symmetric: false, range: ["red", "white", "blue"]}});
413+
const {interpolate, ...color} = plot.scale("color");
414+
assert.deepStrictEqual(color, {
415+
type: "diverging",
416+
domain: [-0.78, 1.35],
417+
pivot: 0,
418+
clamp: false,
419+
label: "Anomaly"
420+
});
421+
const interpolateColors = d3.piecewise(d3.interpolateRgb, ["red", "white", "blue"]);
422+
for (const t of d3.ticks(0, 1, 100)) {
423+
assert.strictEqual(interpolate(t), interpolateColors(t));
424+
}
425+
});
426+
427+
it("plot(…).scale('color') can return a diverging scale with an explicit scheme and range", async () => {
428+
const gistemp = await d3.csv("data/gistemp.csv", d3.autoType);
429+
const plot = Plot.dot(gistemp, {x: "Date", stroke: "Anomaly"}).plot({color: {type: "diverging", symmetric: false, range: [0, 0.5], scheme: "rdbu"}});
430+
const {interpolate, ...color} = plot.scale("color");
431+
assert.deepStrictEqual(color, {
432+
type: "diverging",
433+
domain: [-0.78, 1.35],
434+
pivot: 0,
435+
clamp: false,
436+
label: "Anomaly"
437+
});
438+
for (const t of d3.ticks(0, 1, 100)) {
439+
assert.strictEqual(interpolate(t), d3.interpolateRdBu(t / 2));
440+
}
441+
});
442+
410443
it("plot(…).scale('color') can return a transformed diverging scale", async () => {
411444
const transform = d => d * 100;
412445
const gistemp = await d3.csv("data/gistemp.csv", d3.autoType);

0 commit comments

Comments
 (0)