Skip to content

Commit

Permalink
Merge pull request #77 from d3/interpolateNumberArray
Browse files Browse the repository at this point in the history
interpolateNumberArray
  • Loading branch information
Fil authored Nov 20, 2019
2 parents c3473cf + 92e0bc7 commit 903d6fe
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 6 deletions.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ Returns an interpolator between the two arbitrary values *a* and *b*. The interp
3. If *b* is a [color](https://github.com/d3/d3-color/blob/master/README.md#color) or a string coercible to a color, use [interpolateRgb](#interpolateRgb).
4. If *b* is a [date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date), use [interpolateDate](#interpolateDate).
5. If *b* is a string, use [interpolateString](#interpolateString).
6. If *b* is an [array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray), use [interpolateArray](#interpolateArray).
7. If *b* is coercible to a number, use [interpolateNumber](#interpolateNumber).
8. Use [interpolateObject](#interpolateObject).
6. If *b* is a [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) of numbers, use [interpolateNumberArray](#interpolateNumberArray).
7. If *b* is a generic [array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray), use [interpolateArray](#interpolateArray).
8. If *b* is coercible to a number, use [interpolateNumber](#interpolateNumber).
9. Use [interpolateObject](#interpolateObject).

Based on the chosen interpolator, *a* is coerced to the suitable corresponding type.

Expand Down Expand Up @@ -92,12 +93,20 @@ Note: **no defensive copy** of the returned date is created; the same Date insta

<a name="interpolateArray" href="#interpolateArray">#</a> d3.<b>interpolateArray</b>(<i>a</i>, <i>b</i>) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/array.js), [Examples](https://observablehq.com/@d3/d3-interpolateobject)

Returns an interpolator between the two arrays *a* and *b*. Internally, an array template is created that is the same length in *b*. For each element in *b*, if there exists a corresponding element in *a*, a generic interpolator is created for the two elements using [interpolate](#interpolate). If there is no such element, the static value from *b* is used in the template. Then, for the given parameter *t*, the template’s embedded interpolators are evaluated. The updated array template is then returned.
Returns an interpolator between the two arrays *a* and *b*. Internally, an array template is created that is the same length as *b*. For each element in *b*, if there exists a corresponding element in *a*, a generic interpolator is created for the two elements using [interpolate](#interpolate). If there is no such element, the static value from *b* is used in the template. Then, for the given parameter *t*, the template’s embedded interpolators are evaluated. The updated array template is then returned.

For example, if *a* is the array `[0, 1]` and *b* is the array `[1, 10, 100]`, then the result of the interpolator for *t* = 0.5 is the array `[0.5, 5.5, 100]`.

Note: **no defensive copy** of the template array is created; modifications of the returned array may adversely affect subsequent evaluation of the interpolator. No copy is made for performance reasons; interpolators are often part of the inner loop of [animated transitions](https://github.com/d3/d3-transition).

If the array is a typed array (Float64Array, etc), the interpolateNumberArray method is called instead.

<a name="interpolateNumberArray" href="#interpolateNumberArray">#</a> d3.<b>interpolateNumberArray</b>(<i>a</i>, <i>b</i>) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/numberArray.js), [Examples](https://observablehq.com/@d3/d3-interpolatenumberarray)

Returns an interpolator between the two number arrays *a* and *b*. Internally, an array template is created that is the same type and length as *b*. For each element in *b*, if there exists a corresponding element in *a*, the values are directly interpolated in the array template. If there is no such element, the static value from *b* is copied. The updated array template is then returned.

Note: For performance reasons, **no defensive copy** is made of the template array and the arguments *a* and *b*; modifications of these arrays may affect subsequent evaluation of the interpolator.

<a name="interpolateObject" href="#interpolateObject">#</a> d3.<b>interpolateObject</b>(<i>a</i>, <i>b</i>) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/object.js), [Examples](https://observablehq.com/@d3/d3-interpolateobject)

Returns an interpolator between the two objects *a* and *b*. Internally, an object template is created that has the same properties as *b*. For each property in *b*, if there exists a corresponding property in *a*, a generic interpolator is created for the two elements using [interpolate](#interpolate). If there is no such property, the static value from *b* is used in the template. Then, for the given parameter *t*, the template's embedded interpolators are evaluated and the updated object template is then returned.
Expand Down
5 changes: 5 additions & 0 deletions src/array.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import value from "./value.js";
import numberArray, {isNumberArray} from "./numberArray.js";

export default function(a, b) {
return (isNumberArray(b) ? numberArray : genericArray)(a, b);
}

export function genericArray(a, b) {
var nb = b ? b.length : 0,
na = a ? Math.min(nb, a.length) : 0,
x = new Array(na),
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export {default as interpolateDate} from "./date.js";
export {default as interpolateDiscrete} from "./discrete.js";
export {default as interpolateHue} from "./hue.js";
export {default as interpolateNumber} from "./number.js";
export {default as interpolateNumberArray} from "./numberArray.js";
export {default as interpolateObject} from "./object.js";
export {default as interpolateRound} from "./round.js";
export {default as interpolateString} from "./string.js";
Expand Down
14 changes: 14 additions & 0 deletions src/numberArray.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function(a, b) {
if (!b) b = [];
var n = a ? Math.min(b.length, a.length) : 0,
c = b.slice(),
i;
return function(t) {
for (i = 0; i < n; ++i) c[i] = a[i] * (1 - t) + b[i] * t;
return c;
};
}

export function isNumberArray(x) {
return ArrayBuffer.isView(x) && !(x instanceof DataView);
}
6 changes: 4 additions & 2 deletions src/value.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {color} from "d3-color";
import rgb from "./rgb.js";
import array from "./array.js";
import {genericArray} from "./array.js";
import date from "./date.js";
import number from "./number.js";
import object from "./object.js";
import string from "./string.js";
import constant from "./constant.js";
import numberArray, {isNumberArray} from "./numberArray.js";

export default function(a, b) {
var t = typeof b, c;
Expand All @@ -14,7 +15,8 @@ export default function(a, b) {
: t === "string" ? ((c = color(b)) ? (b = c, rgb) : string)
: b instanceof color ? rgb
: b instanceof Date ? date
: Array.isArray(b) ? array
: isNumberArray(b) ? numberArray
: Array.isArray(b) ? genericArray
: typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
: number)(a, b);
}
44 changes: 44 additions & 0 deletions test/numberArray-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var tape = require("tape"),
interpolate = require("../");

tape("interpolateNumberArray(a, b) interpolates defined elements in a and b", function(test) {
test.deepEqual(interpolate.interpolateNumberArray(Float64Array.of(2, 12), Float64Array.of(4, 24))(0.5), Float64Array.of(3, 18));
test.end();
});

tape("interpolateNumberArray(a, b) ignores elements in a that are not in b", function(test) {
test.deepEqual(interpolate.interpolateNumberArray(Float64Array.of(2, 12, 12), Float64Array.of(4, 24))(0.5), Float64Array.of(3, 18));
test.end();
});

tape("interpolateNumberArray(a, b) uses constant elements in b that are not in a", function(test) {
test.deepEqual(interpolate.interpolateNumberArray(Float64Array.of(2, 12), Float64Array.of(4, 24, 12))(0.5), Float64Array.of(3, 18, 12));
test.end();
});

tape("interpolateNumberArray(a, b) treats undefined as an empty array", function(test) {
test.deepEqual(interpolate.interpolateNumberArray(undefined, [2, 12])(0.5), [2, 12]);
test.deepEqual(interpolate.interpolateNumberArray([2, 12], undefined)(0.5), []);
test.deepEqual(interpolate.interpolateNumberArray(undefined, undefined)(0.5), []);
test.end();
});

tape("interpolateNumberArray(a, b) uses b’s array type", function(test) {
test.ok(interpolate.interpolateNumberArray(Float64Array.of(2, 12), Float64Array.of(4, 24, 12))(0.5) instanceof Float64Array);
test.ok(interpolate.interpolateNumberArray(Float64Array.of(2, 12), Float32Array.of(4, 24, 12))(0.5) instanceof Float32Array);
test.ok(interpolate.interpolateNumberArray(Float64Array.of(2, 12), Uint8Array.of(4, 24, 12))(0.5) instanceof Uint8Array);
test.ok(interpolate.interpolateNumberArray(Float64Array.of(2, 12), Uint16Array.of(4, 24, 12))(0.5) instanceof Uint16Array);
test.end();
});

tape("interpolateNumberArray(a, b) works with unsigned data", function(test) {
test.deepEqual(interpolate.interpolateNumberArray(Uint8Array.of(1, 12), Uint8Array.of(255, 0))(0.5), Uint8Array.of(128, 6));
test.end();
});

tape("interpolateNumberArray(a, b) gives exact ends", function(test) {
var i = interpolate.interpolateNumberArray(Float64Array.of(2e42), Float64Array.of(355));
test.deepEqual(i(0), Float64Array.of(2e42));
test.deepEqual(i(1), Float64Array.of(355));
test.end();
});
13 changes: 13 additions & 0 deletions test/value-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,19 @@ tape("interpolate(a, b) interpolates objects with toString as objects if toStrin
test.end();
});

tape("interpolate(a, b) interpolates number arrays if b is a typed array", function(test) {
test.deepEqual(interpolate.interpolate([0, 0], Float64Array.of(-1, 1))(0.5), Float64Array.of(-0.5, 0.5));
test.assert(interpolate.interpolate([0, 0], Float64Array.of(-1, 1))(0.5) instanceof Float64Array);
test.deepEqual(interpolate.interpolate([0, 0], Float32Array.of(-1, 1))(0.5), Float32Array.of(-0.5, 0.5));
test.assert(interpolate.interpolate([0, 0], Float32Array.of(-1, 1))(0.5) instanceof Float32Array);
test.deepEqual(interpolate.interpolate([0, 0], Uint32Array.of(-2, 2))(0.5), Uint32Array.of(Math.pow(2, 31) - 1, 1));
test.assert(interpolate.interpolate([0, 0], Uint32Array.of(-1, 1))(0.5) instanceof Uint32Array);
test.deepEqual(interpolate.interpolate([0, 0], Uint8Array.of(-2, 2))(0.5), Uint8Array.of(Math.pow(2, 7) - 1, 1));
test.assert(interpolate.interpolate([0, 0], Uint8Array.of(-1, 1))(0.5) instanceof Uint8Array);
test.end();
});


function noproto(properties, proto = null) {
return Object.assign(Object.create(proto), properties);
}
Expand Down

0 comments on commit 903d6fe

Please sign in to comment.