diff --git a/README.md b/README.md
index 9024eaf..ed9aef3 100644
--- a/README.md
+++ b/README.md
@@ -165,6 +165,14 @@ x.invert(-160); // 10, clamped to domain
If *clamp* is not specified, returns whether or not the scale currently clamps values to within the range.
+# continuous.low([value]) · [Source](https://github.com/d3/d3-scale/blob/master/src/continuous.js), [Examples](https://observablehq.com/@d3/continuous-scales)
+
+If *value* is specified, sets the output value for values lower than the lower bound of the domain and returns this scale. If *value* is undefined, interpolation or clamping may happen. If *value* is not specified, returns the current low value, which defaults to undefined.
+
+# continuous.high([value]) · [Source](https://github.com/d3/d3-scale/blob/master/src/continuous.js), [Examples](https://observablehq.com/@d3/continuous-scales)
+
+If *value* is specified, sets the output value for values higher than the upper bound of the domain and returns this scale. If *value* is undefined, interpolation or clamping may happen. If *value* is not specified, returns the current high value, which defaults to undefined.
+
# continuous.unknown([value]) · [Source](https://github.com/d3/d3-scale/blob/master/src/continuous.js), [Examples](https://observablehq.com/@d3/continuous-scales)
If *value* is specified, sets the output value of the scale for undefined (or NaN) input values and returns this scale. If *value* is not specified, returns the current unknown value, which defaults to undefined.
diff --git a/src/continuous.js b/src/continuous.js
index d932095..0149d2e 100644
--- a/src/continuous.js
+++ b/src/continuous.js
@@ -15,12 +15,6 @@ function normalize(a, b) {
: constant(isNaN(b) ? NaN : 0.5);
}
-function clamper(a, b) {
- var t;
- if (a > b) t = a, a = b, b = t;
- return function(x) { return Math.max(a, Math.min(b, x)); };
-}
-
// normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
// interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
function bimap(domain, range, interpolate) {
@@ -69,25 +63,37 @@ export function transformer() {
transform,
untransform,
unknown,
- clamp = identity,
+ low,
+ high,
+ min,
+ max,
+ clamp,
piecewise,
output,
input;
function rescale() {
var n = Math.min(domain.length, range.length);
- if (clamp !== identity) clamp = clamper(domain[0], domain[n - 1]);
+ min = domain[0];
+ max = domain[n - 1];
+ if (max < min) ([min, max] = [max, min]);
+ if (clamp) clamp = x => x < min ? min : x > max ? max : x;
piecewise = n > 2 ? polymap : bimap;
output = input = null;
return scale;
}
function scale(x) {
- return x == null || isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate)))(transform(clamp(x)));
+ const tr = clamp ? x => transform(clamp(x)) : transform;
+ return x == null || isNaN(x = +x) ? unknown
+ : low !== undefined && x < min ? low
+ : high !== undefined && x > max ? high
+ : (output || (output = piecewise(domain.map(transform), range, interpolate)))(tr(x));
}
scale.invert = function(y) {
- return clamp(untransform((input || (input = piecewise(range, domain.map(transform), interpolateNumber)))(y)));
+ const x = untransform((input || (input = piecewise(range, domain.map(transform), interpolateNumber)))(y));
+ return clamp ? clamp(x) : x;
};
scale.domain = function(_) {
@@ -103,7 +109,7 @@ export function transformer() {
};
scale.clamp = function(_) {
- return arguments.length ? (clamp = _ ? true : identity, rescale()) : clamp !== identity;
+ return arguments.length ? (clamp = !!_, rescale()) : !!clamp;
};
scale.interpolate = function(_) {
@@ -114,6 +120,14 @@ export function transformer() {
return arguments.length ? (unknown = _, scale) : unknown;
};
+ scale.low = function(_) {
+ return arguments.length ? (low = _, scale) : low;
+ };
+
+ scale.high = function(_) {
+ return arguments.length ? (high = _, scale) : high;
+ };
+
return function(t, u) {
transform = t, untransform = u;
return rescale();