From 4110b4536637798cec9f680b003946f9cdde6152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Fri, 3 Sep 2021 16:51:51 +0200 Subject: [PATCH 01/12] polylinear (aka piecewise linear) scale should work without specifying the domain --- src/scales.js | 12 +++- test/output/polylinear.svg | 116 +++++++++++++++++++++++++++++++++++++ test/plots/index.js | 1 + test/plots/polylinear.js | 45 ++++++++++++++ 4 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 test/output/polylinear.svg create mode 100644 test/plots/polylinear.js diff --git a/src/scales.js b/src/scales.js index dec9731463..2980d70bca 100644 --- a/src/scales.js +++ b/src/scales.js @@ -35,7 +35,7 @@ function autoScaleRangeX(scale, dimensions) { if (scale.range === undefined) { const {inset = 0} = scale; const {width, marginLeft = 0, marginRight = 0} = dimensions; - scale.scale.range([marginLeft + inset, width - marginRight - inset]); + scale.scale.range(maybePiecewiseRange(marginLeft + inset, width - marginRight - inset, scale)); } autoScaleRound(scale); } @@ -44,7 +44,7 @@ function autoScaleRangeY(scale, dimensions) { if (scale.range === undefined) { const {inset = 0} = scale; const {height, marginTop = 0, marginBottom = 0} = dimensions; - const range = [height - marginBottom - inset, marginTop + inset]; + const range = maybePiecewiseRange(height - marginBottom - inset, marginTop + inset, scale); if (scale.type === "ordinal") range.reverse(); scale.scale.range(range); } @@ -57,6 +57,14 @@ function autoScaleRound(scale) { } } +function maybePiecewiseRange(start, end, {type, scale}) { + if (type === "quantitative") { + const l = scale.domain().length; + if (l > 2) return Array.from({length: l}, (_, i) => start + i / (l - 1) * (end - start)); + } + return [start, end]; +} + function Scale(key, channels = [], options = {}) { switch (inferScaleType(key, channels, options)) { case "diverging": return ScaleDiverging(key, channels, options); diff --git a/test/output/polylinear.svg b/test/output/polylinear.svg new file mode 100644 index 0000000000..099d2ccc60 --- /dev/null +++ b/test/output/polylinear.svg @@ -0,0 +1,116 @@ + + + + + 05 + + + + 06 + + + + 07 + + + + 08 + + + + 09 + + + + 10 + + + + 11 + + + + 12 + + + + 13 + + + + 14 + + + + 15 + + + + 16 + + + + 17 + + + + 18 + + + + 19 + + + + 20 + + + + 21 + + + + 22 + + + + 23 + + + + 24 + + + + 25 + + + + 26 + + + + 27 + date → + + + + + + + + + + + + + + + + + + + + + InitiateBeginEntryTestDriveDriveBrakeStopShutdown + \ No newline at end of file diff --git a/test/plots/index.js b/test/plots/index.js index a74e855f11..ccad05ecbb 100644 --- a/test/plots/index.js +++ b/test/plots/index.js @@ -81,6 +81,7 @@ export {default as penguinSpeciesGroup} from "./penguin-species-group.js"; export {default as penguinSpeciesIsland} from "./penguin-species-island.js"; export {default as penguinSpeciesIslandRelative} from "./penguin-species-island-relative.js"; export {default as penguinSpeciesIslandSex} from "./penguin-species-island-sex.js"; +export {default as polylinear} from "./polylinear.js"; export {default as policeDeaths} from "./police-deaths.js"; export {default as policeDeathsBar} from "./police-deaths-bar.js"; export {default as randomBins} from "./random-bins.js"; diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js new file mode 100644 index 0000000000..bd7f3eb439 --- /dev/null +++ b/test/plots/polylinear.js @@ -0,0 +1,45 @@ +import * as Plot from "@observablehq/plot"; +import * as d3 from "d3"; + +const times = [ + new Date(2013, 3, 5), + new Date(2013, 3, 11), + new Date(2013, 3, 14), + new Date(2013, 3, 16), + new Date(2013, 3, 18), + new Date(2013, 3, 21), + new Date(2013, 3, 27) +]; + +const events = [ + { date: new Date(2013, 3, 5, 13, 0), text: "Initiate" }, + { date: new Date(2013, 3, 11, 13, 0), text: "Begin" }, + { date: new Date(2013, 3, 13, 20, 0), text: "Entry" }, + { date: new Date(2013, 3, 15, 0, 0), text: "Test" }, + { date: new Date(2013, 3, 16, 0, 0), text: "Drive" }, + { date: new Date(2013, 3, 17, 8, 0), text: "Drive" }, + { date: new Date(2013, 3, 18, 15, 0), text: "Brake" }, + { date: new Date(2013, 3, 20, 10, 0), text: "Stop" }, + { date: new Date(2013, 3, 23, 14, 0), text: "Shutdown" } +]; + +export default async function() { + return Plot.plot({ + grid: true, + x: { + domain: times, + type: "linear", + ticks: [...d3.timeDays(...d3.extent(times)), times[times.length-1]], + tickFormat: d3.timeFormat("%d"), + inset: 20, + label: "date →" + }, + color: { scheme: "cool" }, + marks: [ + Plot.barX(d3.pairs(times), {x1: "0", x2: "1", fill: (_,i) => i}), + Plot.dotX(events, {x: "date", fill: "white"}), + Plot.textX(events, {x: "date", text: "text", dx: -5, dy: -10, fill: "white", textAnchor: "start"}) + ], + height: 90 + }); +} From cc28a5dc512d77de6dd37e86d013a2450f700b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Sep 2021 21:14:06 +0200 Subject: [PATCH 02/12] utc --- test/plots/polylinear.js | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index bd7f3eb439..a401f9f525 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -2,26 +2,28 @@ import * as Plot from "@observablehq/plot"; import * as d3 from "d3"; const times = [ - new Date(2013, 3, 5), - new Date(2013, 3, 11), - new Date(2013, 3, 14), - new Date(2013, 3, 16), - new Date(2013, 3, 18), - new Date(2013, 3, 21), - new Date(2013, 3, 27) -]; + "2013-04-05", + "2013-04-11", + "2013-04-14", + "2013-04-16", + "2013-04-18", + "2013-04-21", + "2013-04-27" +].map(d3.isoParse); const events = [ - { date: new Date(2013, 3, 5, 13, 0), text: "Initiate" }, - { date: new Date(2013, 3, 11, 13, 0), text: "Begin" }, - { date: new Date(2013, 3, 13, 20, 0), text: "Entry" }, - { date: new Date(2013, 3, 15, 0, 0), text: "Test" }, - { date: new Date(2013, 3, 16, 0, 0), text: "Drive" }, - { date: new Date(2013, 3, 17, 8, 0), text: "Drive" }, - { date: new Date(2013, 3, 18, 15, 0), text: "Brake" }, - { date: new Date(2013, 3, 20, 10, 0), text: "Stop" }, - { date: new Date(2013, 3, 23, 14, 0), text: "Shutdown" } -]; + {date: "2013-04-05T13:00Z", text: "Initiate"}, + {date: "2013-04-11T13:00Z", text: "Begin"}, + {date: "2013-03-13T20:00Z", text: "Entry"}, + {date: "2013-03-15T00:00Z", text: "Test"}, + {date: "2013-03-16T00:00Z", text: "Drive"}, + {date: "2013-03-17T08:00Z", text: "Drive"}, + {date: "2013-03-18T15:00Z", text: "Brake"}, + {date: "2013-03-20T10:00Z", text: "Stop"}, + {date: "2013-03-23T14:00Z", text: "Shutdown"} +].map(d => ({text: d.text, date: d3.isoParse(d.date)})); + +const days = [...d3.utcDays(...d3.extent(times)), times[times.length-1]]; export default async function() { return Plot.plot({ @@ -29,7 +31,7 @@ export default async function() { x: { domain: times, type: "linear", - ticks: [...d3.timeDays(...d3.extent(times)), times[times.length-1]], + ticks: days, tickFormat: d3.timeFormat("%d"), inset: 20, label: "date →" From 9d1851c7fc00a870c1b7da222dcef0a7904812af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Sep 2021 21:15:13 +0200 Subject: [PATCH 03/12] braces --- test/plots/polylinear.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index a401f9f525..886cdd4940 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -36,7 +36,7 @@ export default async function() { inset: 20, label: "date →" }, - color: { scheme: "cool" }, + color: {scheme: "cool"}, marks: [ Plot.barX(d3.pairs(times), {x1: "0", x2: "1", fill: (_,i) => i}), Plot.dotX(events, {x: "date", fill: "white"}), From 42679eb51072b49e798233a1bd8a528511d3b197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Sep 2021 21:17:21 +0200 Subject: [PATCH 04/12] fix test --- test/plots/polylinear.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index 886cdd4940..430845de0e 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -14,13 +14,13 @@ const times = [ const events = [ {date: "2013-04-05T13:00Z", text: "Initiate"}, {date: "2013-04-11T13:00Z", text: "Begin"}, - {date: "2013-03-13T20:00Z", text: "Entry"}, - {date: "2013-03-15T00:00Z", text: "Test"}, - {date: "2013-03-16T00:00Z", text: "Drive"}, - {date: "2013-03-17T08:00Z", text: "Drive"}, - {date: "2013-03-18T15:00Z", text: "Brake"}, - {date: "2013-03-20T10:00Z", text: "Stop"}, - {date: "2013-03-23T14:00Z", text: "Shutdown"} + {date: "2013-04-13T20:00Z", text: "Entry"}, + {date: "2013-04-15T00:00Z", text: "Test"}, + {date: "2013-04-16T00:00Z", text: "Drive"}, + {date: "2013-04-17T08:00Z", text: "Drive"}, + {date: "2013-04-18T15:00Z", text: "Brake"}, + {date: "2013-04-20T10:00Z", text: "Stop"}, + {date: "2013-04-23T14:00Z", text: "Shutdown"} ].map(d => ({text: d.text, date: d3.isoParse(d.date)})); const days = [...d3.utcDays(...d3.extent(times)), times[times.length-1]]; From 9438f70b11b8a7e153e9f521bf80e27562e829db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Sep 2021 21:27:35 +0200 Subject: [PATCH 05/12] =?UTF-8?q?timezones=E2=80=A6=20is=20this=20going=20?= =?UTF-8?q?to=20work=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/plots/polylinear.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index 430845de0e..716a5a120f 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -2,13 +2,13 @@ import * as Plot from "@observablehq/plot"; import * as d3 from "d3"; const times = [ - "2013-04-05", - "2013-04-11", - "2013-04-14", - "2013-04-16", - "2013-04-18", - "2013-04-21", - "2013-04-27" + "2013-04-05T00:00Z", + "2013-04-11T00:00Z", + "2013-04-14T00:00Z", + "2013-04-16T00:00Z", + "2013-04-18T00:00Z", + "2013-04-21T00:00Z", + "2013-04-27T00:00Z" ].map(d3.isoParse); const events = [ From bffffbfb414184a136cfa40cc80e3295f47afd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Sep 2021 21:29:11 +0200 Subject: [PATCH 06/12] more utc --- test/plots/polylinear.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index 716a5a120f..d2b0071653 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -32,7 +32,7 @@ export default async function() { domain: times, type: "linear", ticks: days, - tickFormat: d3.timeFormat("%d"), + tickFormat: d3.utcFormat("%d"), inset: 20, label: "date →" }, From 1584ae1b7d4d01df3a932964dc3ad4d1be714661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Sep 2021 22:57:37 +0200 Subject: [PATCH 07/12] polylinear color schemes --- src/scales/quantitative.js | 12 ++++++++++++ test/output/polylinear.svg | 28 ++++++++++++++++++++++------ test/plots/polylinear.js | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index 4bf6ec0a7b..aba5ebb0b0 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -73,6 +73,7 @@ export function ScaleQ(key, scale, channels, { interpolate = Interpolator(interpolate); } else if (interpolate.length === 1) { if (reverse) interpolate = flip(interpolate); + if (domain.length > 2) interpolate = polyLinear(interpolate, scale, domain); interpolate = constant(interpolate); } scale.interpolate(interpolate); @@ -170,3 +171,14 @@ function inferQuantileDomain(channels) { } return domain; } + +// stopgap solution until scaleLinear deals with polylinear domains +// and an interpolate function +function polyLinear(interpolate, scale, domain) { + const [a, b] = [domain[0], domain[domain.length-1]]; + const tr = scaleLinear() + .domain(domain.map(d => (d - a) / (b - a))) + .range(domain.map((_, i) => i / (domain.length - 1))); + scale.domain([a, b]); + return t => interpolate(tr(t)); +} diff --git a/test/output/polylinear.svg b/test/output/polylinear.svg index 099d2ccc60..947e915a60 100644 --- a/test/output/polylinear.svg +++ b/test/output/polylinear.svg @@ -94,12 +94,28 @@ date → - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index d2b0071653..a6ed5a1d90 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -38,7 +38,7 @@ export default async function() { }, color: {scheme: "cool"}, marks: [ - Plot.barX(d3.pairs(times), {x1: "0", x2: "1", fill: (_,i) => i}), + Plot.barX(d3.pairs(days), {x1: "0", x2: "1", fill: "0", stroke: "1"}), Plot.dotX(events, {x: "date", fill: "white"}), Plot.textX(events, {x: "date", text: "text", dx: -5, dy: -10, fill: "white", textAnchor: "start"}) ], From 85ed81230611b61acbdf9b4eb2c14375f79f06df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 8 Sep 2021 18:27:55 +0200 Subject: [PATCH 08/12] polylinear scale with a color interpolate function (color scheme) (seems like the most straightforward approach) --- src/scales/quantitative.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index aba5ebb0b0..4836ad64a5 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -23,7 +23,7 @@ import { import {ordinalRange, quantitativeScheme} from "./schemes.js"; import {registry, radius, opacity, color} from "./index.js"; import {positive, negative} from "../defined.js"; -import {constant} from "../mark.js"; +import {constant, range} from "../mark.js"; const flip = i => t => i(1 - t); @@ -73,7 +73,7 @@ export function ScaleQ(key, scale, channels, { interpolate = Interpolator(interpolate); } else if (interpolate.length === 1) { if (reverse) interpolate = flip(interpolate); - if (domain.length > 2) interpolate = polyLinear(interpolate, scale, domain); + if (domain.length > 2 && range === undefined) ({range, interpolate} = polyLinear(interpolate, scale, domain)); interpolate = constant(interpolate); } scale.interpolate(interpolate); @@ -172,13 +172,11 @@ function inferQuantileDomain(channels) { return domain; } -// stopgap solution until scaleLinear deals with polylinear domains -// and an interpolate function +// polylinear domain with an interpolate function (e.g. color scheme) function polyLinear(interpolate, scale, domain) { - const [a, b] = [domain[0], domain[domain.length-1]]; - const tr = scaleLinear() - .domain(domain.map(d => (d - a) / (b - a))) - .range(domain.map((_, i) => i / (domain.length - 1))); - scale.domain([a, b]); - return t => interpolate(tr(t)); + const n = domain.length - 1; + return { + range: range(domain), + interpolate: i => t => interpolate((i + t) / n) + }; } From 24cf670deb052bed6ac255b3f86edfa730ebf814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 8 Sep 2021 18:52:24 +0200 Subject: [PATCH 09/12] fix polylinear scheme --- src/scales/quantitative.js | 6 +++--- test/output/polylinear.svg | 44 +++++++++++++++++++------------------- test/plots/polylinear.js | 6 +++++- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index 4836ad64a5..6861c0ceb9 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -73,8 +73,8 @@ export function ScaleQ(key, scale, channels, { interpolate = Interpolator(interpolate); } else if (interpolate.length === 1) { if (reverse) interpolate = flip(interpolate); - if (domain.length > 2 && range === undefined) ({range, interpolate} = polyLinear(interpolate, scale, domain)); - interpolate = constant(interpolate); + if (domain.length > 2 && range === undefined) ({range, interpolate} = piecewiseInterpolate(interpolate, domain)); + else interpolate = constant(interpolate); } scale.interpolate(interpolate); } @@ -173,7 +173,7 @@ function inferQuantileDomain(channels) { } // polylinear domain with an interpolate function (e.g. color scheme) -function polyLinear(interpolate, scale, domain) { +function piecewiseInterpolate(interpolate, domain) { const n = domain.length - 1; return { range: range(domain), diff --git a/test/output/polylinear.svg b/test/output/polylinear.svg index 947e915a60..7e4ed335df 100644 --- a/test/output/polylinear.svg +++ b/test/output/polylinear.svg @@ -94,28 +94,28 @@ date → - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index a6ed5a1d90..afc0941406 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -36,7 +36,11 @@ export default async function() { inset: 20, label: "date →" }, - color: {scheme: "cool"}, + color: { + domain: times, + type: "linear", + scheme: "cool" + }, marks: [ Plot.barX(d3.pairs(days), {x1: "0", x2: "1", fill: "0", stroke: "1"}), Plot.dotX(events, {x: "date", fill: "white"}), From 7690ea1da818a20839b0a969771d19efa89e96ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 8 Sep 2021 21:17:44 +0200 Subject: [PATCH 10/12] honor reverse: true, and accept a reversed (monotone-decreasing) domain. --- src/scales/quantitative.js | 14 ++++++++------ test/plots/polylinear.js | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index 6861c0ceb9..2e9b0c6e18 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -58,10 +58,7 @@ export function ScaleQ(key, scale, channels, { reverse, inset }) { - if (zero) domain = domain[1] < 0 ? [domain[0], 0] : domain[0] > 0 ? [0, domain[1]] : domain; - if (reverse = !!reverse) domain = reverseof(domain); - scale.domain(domain); - if (nice) scale.nice(nice === true ? undefined : nice); + reverse = !!reverse; // Sometimes interpolator is named interpolator, such as "lab" for Lab color // space. Other times interpolate is a function that takes two arguments and @@ -72,13 +69,18 @@ export function ScaleQ(key, scale, channels, { if (typeof interpolate !== "function") { interpolate = Interpolator(interpolate); } else if (interpolate.length === 1) { - if (reverse) interpolate = flip(interpolate); + if (reverse) interpolate = flip(interpolate), reverse = false; if (domain.length > 2 && range === undefined) ({range, interpolate} = piecewiseInterpolate(interpolate, domain)); else interpolate = constant(interpolate); } scale.interpolate(interpolate); } + if (zero) domain = domain[1] < 0 ? [domain[0], 0] : domain[0] > 0 ? [0, domain[1]] : domain; + if (reverse) domain = reverseof(domain); + scale.domain(domain); + if (nice) scale.nice(nice === true ? undefined : nice); + if (range !== undefined) scale.range(range); if (clamp) scale.clamp(clamp); return {type: "quantitative", reverse, domain, range, scale, inset, percent}; @@ -177,6 +179,6 @@ function piecewiseInterpolate(interpolate, domain) { const n = domain.length - 1; return { range: range(domain), - interpolate: i => t => interpolate((i + t) / n) + interpolate: (i, j) => t => interpolate((i + t * (j - i)) / n) }; } diff --git a/test/plots/polylinear.js b/test/plots/polylinear.js index afc0941406..995d4cce7d 100644 --- a/test/plots/polylinear.js +++ b/test/plots/polylinear.js @@ -37,7 +37,8 @@ export default async function() { label: "date →" }, color: { - domain: times, + reverse: true, // unit tests both reverse… + domain: d3.reverse(times), // …and a decreasing domain type: "linear", scheme: "cool" }, From 0124bcfcff8528e6c610fce07514f1e556c82f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 8 Sep 2021 21:57:51 +0200 Subject: [PATCH 11/12] `zero: true` was not compatible with a polylinear domain. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (In practice, one probably wouldn’t try to use both options together. Still, looks safer.) --- src/scales/quantitative.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index 2e9b0c6e18..0b1adea47e 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -76,7 +76,13 @@ export function ScaleQ(key, scale, channels, { scale.interpolate(interpolate); } - if (zero) domain = domain[1] < 0 ? [domain[0], 0] : domain[0] > 0 ? [0, domain[1]] : domain; + if (zero) { + if (domain[0] > 0) { + domain[0] = 0; + } else if (domain[domain.length - 1] < 0) { + domain[domain.length - 1] = 0; + } + } if (reverse) domain = reverseof(domain); scale.domain(domain); if (nice) scale.nice(nice === true ? undefined : nice); From 41f426850bc1c0eb38716f2d96ab22bfffe4842b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Thu, 9 Sep 2021 12:42:47 +0200 Subject: [PATCH 12/12] simplify --- src/scales/quantitative.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/scales/quantitative.js b/src/scales/quantitative.js index 0b1adea47e..86d479f46a 100644 --- a/src/scales/quantitative.js +++ b/src/scales/quantitative.js @@ -23,7 +23,6 @@ import { import {ordinalRange, quantitativeScheme} from "./schemes.js"; import {registry, radius, opacity, color} from "./index.js"; import {positive, negative} from "../defined.js"; -import {constant, range} from "../mark.js"; const flip = i => t => i(1 - t); @@ -63,15 +62,15 @@ export function ScaleQ(key, scale, channels, { // Sometimes interpolator is named interpolator, such as "lab" for Lab color // space. Other times interpolate is a function that takes two arguments and // is used in conjunction with the range. And other times the interpolate - // function is a “fixed” interpolator independent of the range, as when a + // function is a “fixed” interpolator on the [0, 1] interval, as when a // color scheme such as interpolateRdBu is used. if (interpolate !== undefined) { if (typeof interpolate !== "function") { interpolate = Interpolator(interpolate); } else if (interpolate.length === 1) { if (reverse) interpolate = flip(interpolate), reverse = false; - if (domain.length > 2 && range === undefined) ({range, interpolate} = piecewiseInterpolate(interpolate, domain)); - else interpolate = constant(interpolate); + if (range === undefined && domain.length > 2) range = Float64Array.from(domain, (d, i) => i / (domain.length - 1)); + interpolate = maybePiecewiseInterpolate(interpolate); } scale.interpolate(interpolate); } @@ -180,11 +179,6 @@ function inferQuantileDomain(channels) { return domain; } -// polylinear domain with an interpolate function (e.g. color scheme) -function piecewiseInterpolate(interpolate, domain) { - const n = domain.length - 1; - return { - range: range(domain), - interpolate: (i, j) => t => interpolate((i + t * (j - i)) / n) - }; +function maybePiecewiseInterpolate(interpolate) { + return (i, j) => (i === 0 && j === 1) ? interpolate : t => interpolate(i + t * (j - i)); }