diff --git a/README.md b/README.md index 0dc694b..668baee 100644 --- a/README.md +++ b/README.md @@ -573,6 +573,10 @@ See [*continuous*.clamp](#continuous_clamp). If *interpolator* is specified, sets the scale’s interpolator to the specified function. If *interpolator* is not specified, returns the scale’s current interpolator. +# sequential.invert(fraction) · [Source](https://github.com/d3/d3-scale/blob/master/src/sequential.js) + +Inverts the given interpolation *fraction* and returns the corresponding domain value. This method does not invert the output of the interpolator function (which may be non-invertible), but rather inverts fractional values that serve as input to the interpolator. + # sequential.range([range]) · [Source](https://github.com/d3/d3-scale/blob/master/src/sequential.js), [Examples](https://observablehq.com/@d3/sequential-scales) See [*continuous*.range](#continuous_range). If *range* is specified, implicitly uses [d3.interpolate](https://github.com/d3/d3-interpolate/blob/master/README.md#interpolate) as the interpolator. @@ -637,6 +641,10 @@ See [*continuous*.clamp](#continuous_clamp). If *interpolator* is specified, sets the scale’s interpolator to the specified function. If *interpolator* is not specified, returns the scale’s current interpolator. +# diverging.invert(fraction) · [Source](https://github.com/d3/d3-scale/blob/master/src/diverging.js) + +Inverts the given interpolation *fraction* and returns the corresponding domain value. This method does not invert the output of the interpolator function (which may be non-invertible), but rather inverts fractional values that serve as input to the interpolator. + # diverging.range([range]) · [Source](https://github.com/d3/d3-scale/blob/master/src/diverging.js), [Examples](https://observablehq.com/@d3/diverging-scales) See [*continuous*.range](#continuous_range). If *range* is specified, implicitly uses [d3.interpolate](https://github.com/d3/d3-interpolate/blob/master/README.md#interpolate) as the interpolator. diff --git a/src/diverging.js b/src/diverging.js index f590748..5d646db 100644 --- a/src/diverging.js +++ b/src/diverging.js @@ -19,6 +19,7 @@ function transformer() { k21, interpolator = identity, transform, + untransform, clamp = false, unknown; @@ -26,6 +27,12 @@ function transformer() { return isNaN(x = +x) ? unknown : (x = 0.5 + ((x = +transform(x)) - t1) * (s * x < s * t1 ? k10 : k21), interpolator(clamp ? Math.max(0, Math.min(1, x)) : x)); } + scale.invert = function(_) { + _ = clamp ? Math.max(0, Math.min(1, _)) : _; + return _ === 0 ? x0 : _ === 1 ? x2 : _ === 0.5 ? x1 + : untransform((_ - 0.5) / (_ < 0.5 ? k10 : k21) + t1); + }; + scale.domain = function(_) { return arguments.length ? ([x0, x1, x2] = _, t0 = transform(x0 = +x0), t1 = transform(x1 = +x1), t2 = transform(x2 = +x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1, scale) : [x0, x1, x2]; }; @@ -53,14 +60,14 @@ function transformer() { return arguments.length ? (unknown = _, scale) : unknown; }; - return function(t) { - transform = t, t0 = t(x0), t1 = t(x1), t2 = t(x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1; + return function(t, u) { + transform = t, t0 = t(x0), t1 = t(x1), t2 = t(x2), k10 = t0 === t1 ? 0 : 0.5 / (t1 - t0), k21 = t1 === t2 ? 0 : 0.5 / (t2 - t1), s = t1 < t0 ? -1 : 1, untransform = u; return scale; }; } export default function diverging() { - var scale = linearish(transformer()(identity)); + var scale = linearish(transformer()(identity, identity)); scale.copy = function() { return copy(scale, diverging()); diff --git a/src/sequential.js b/src/sequential.js index bd38dba..5c707ac 100644 --- a/src/sequential.js +++ b/src/sequential.js @@ -13,6 +13,7 @@ function transformer() { t1, k10, transform, + untransform, interpolator = identity, clamp = false, unknown; @@ -21,6 +22,11 @@ function transformer() { return isNaN(x = +x) ? unknown : interpolator(k10 === 0 ? 0.5 : (x = (transform(x) - t0) * k10, clamp ? Math.max(0, Math.min(1, x)) : x)); } + scale.invert = function(_) { + _ = clamp ? Math.max(0, Math.min(1, _)) : _; + return _ === 0 ? x0 : _ === 1 ? x1 : untransform(_ / k10 + t0); + }; + scale.domain = function(_) { return arguments.length ? ([x0, x1] = _, t0 = transform(x0 = +x0), t1 = transform(x1 = +x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0), scale) : [x0, x1]; }; @@ -48,8 +54,8 @@ function transformer() { return arguments.length ? (unknown = _, scale) : unknown; }; - return function(t) { - transform = t, t0 = t(x0), t1 = t(x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0); + return function(t, u) { + transform = t, t0 = t(x0), t1 = t(x1), k10 = t0 === t1 ? 0 : 1 / (t1 - t0), untransform = u; return scale; }; } @@ -63,7 +69,7 @@ export function copy(source, target) { } export default function sequential() { - var scale = linearish(transformer()(identity)); + var scale = linearish(transformer()(identity, identity)); scale.copy = function() { return copy(scale, sequential()); diff --git a/test/diverging-test.js b/test/diverging-test.js index 5e7f5c0..116a901 100644 --- a/test/diverging-test.js +++ b/test/diverging-test.js @@ -162,3 +162,28 @@ tape("scaleDiverging(range) sets the interpolator", function(test) { test.deepEqual(s.range(), [1, 3, 10]); test.end(); }); + +tape("scaleDiverging.invert(value) inverts interpolation fractions", function(test) { + var s = scale.scaleDiverging().domain([1,2,4]); + test.equal(s.invert(0), 1); + test.equal(s.invert(0.25), 1.5); + test.equal(s.invert(0.50), 2); + test.equal(s.invert(0.75), 3); + test.equal(s.invert(1), 4); + test.equal(s.invert(-0.5), 0); + test.equal(s.invert(1.5), 6); + test.end(); +}); + +tape("scaleDivergingLog.invert(value) inverts interpolation fractions", function(test) { + var d = [1, 20, 100]; + var s = scale.scaleDivergingLog().domain(d); + test.equal(s.invert(0), d[0]); + test.equal(s.invert(0.25), Math.exp(Math.log(d[0]) + 0.5 * (Math.log(d[1]) - Math.log(d[0])))); + test.equal(s.invert(0.50), d[1]); + test.equal(s.invert(0.75), Math.exp(Math.log(d[1]) + 0.5 * (Math.log(d[2]) - Math.log(d[1])))); + test.equal(s.invert(1), d[2]); + test.inDelta(s.invert(-0.5), Math.exp(Math.log(d[0]) - 1 * (Math.log(d[1]) - Math.log(d[0])))); + test.inDelta(s.invert(1.5), Math.exp(Math.log(d[1]) + 2 * (Math.log(d[2]) - Math.log(d[1])))); + test.end(); +}); \ No newline at end of file diff --git a/test/sequential-test.js b/test/sequential-test.js index 9ba7837..4c5bd19 100644 --- a/test/sequential-test.js +++ b/test/sequential-test.js @@ -131,3 +131,28 @@ tape("scaleSequential(range) sets the interpolator", function(test) { test.deepEqual(s.range(), [1, 3]); test.end(); }); + +tape("scaleSequential.invert(value) inverts interpolation fractions", function(test) { + var s = scale.scaleSequential().domain([1,5]); + test.equal(s.invert(0), 1); + test.equal(s.invert(0.25), 2); + test.equal(s.invert(0.50), 3); + test.equal(s.invert(0.75), 4); + test.equal(s.invert(1), 5); + test.equal(s.invert(-1), -3); + test.equal(s.invert(2), 9); + test.end(); +}); + +tape("scaleSequentialLog.invert(value) inverts interpolation fractions", function(test) { + var d = [1, 100]; + var s = scale.scaleSequentialLog().domain(d); + test.equal(s.invert(0), d[0]); + test.equal(s.invert(0.25), Math.exp(Math.log(d[0]) + 0.25 * (Math.log(d[1]) - Math.log(d[0])))); + test.equal(s.invert(0.50), Math.exp(Math.log(d[0]) + 0.50 * (Math.log(d[1]) - Math.log(d[0])))); + test.equal(s.invert(0.75), Math.exp(Math.log(d[0]) + 0.75 * (Math.log(d[1]) - Math.log(d[0])))); + test.equal(s.invert(1), d[1]); + test.inDelta(s.invert(-1), d[0] / d[1]); + test.inDelta(s.invert(2), d[1] * d[1]); + test.end(); +});