diff --git a/.gitignore b/.gitignore
index 8abcf6a..7d1118c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
dist/
node_modules
npm-debug.log
+.idea
diff --git a/README.md b/README.md
index c38a90c..c594b2d 100644
--- a/README.md
+++ b/README.md
@@ -230,6 +230,26 @@ Returns a uniform nonrational B-spline interpolator through the specified array
Returns a uniform nonrational B-spline interpolator through the specified array of *values*, which must be numbers. The control points are implicitly repeated such that the resulting one-dimensional spline has cyclical C² continuity when repeated around *t* in [0,1]. See also [d3.curveBasisClosed](https://github.com/d3/d3-shape/blob/master/README.md#curveBasisClosed).
+# d3.interpolateCardinal(values) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/cardinal.js)
+
+Returns interpolator based on cubic Cardinal spline.
+
+# d3.interpolateCatmullRom(values) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/catmullRom.js)
+
+Returns interpolator based on a cubic Catmull–Rom spline.
+
+# d3.interpolateMonotoneX(values) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/monotoneX.js)
+
+Returns interpolator based on MonotoneX spline.
+
+# d3.interpolateFromCurve(values, curve, epsilon, samples) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/fromCurve.js)
+
+Returns interpolator based on d3.curve function.
+
+```js
+var interpolator = d3.interpolateFromCurve([1,2,7,2], d3.curveMonotoneX, 0.00001, 100);
+```
+
### Piecewise
# d3.piecewise(interpolate, values) · [Source](https://github.com/d3/d3-interpolate/blob/master/src/piecewise.js), [Examples](https://observablehq.com/@d3/d3-piecewise)
diff --git a/package.json b/package.json
index ccc3a0a..a9e0a6d 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,9 @@
"postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../${npm_package_name}/dist/${npm_package_name}.js ${npm_package_name}.v${npm_package_version%%.*}.js && cp ../${npm_package_name}/dist/${npm_package_name}.min.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git add ${npm_package_name}.v${npm_package_version%%.*}.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git commit -m \"${npm_package_name} ${npm_package_version}\" && git push && cd - && zip -j dist/${npm_package_name}.zip -- LICENSE README.md dist/${npm_package_name}.js dist/${npm_package_name}.min.js"
},
"dependencies": {
- "d3-color": "1"
+ "d3-color": "1",
+ "d3-shape": "1",
+ "d3-array": "2"
},
"devDependencies": {
"eslint": "5",
diff --git a/src/cardinal.js b/src/cardinal.js
new file mode 100644
index 0000000..fd4fa31
--- /dev/null
+++ b/src/cardinal.js
@@ -0,0 +1,6 @@
+import {default as fromCurve} from "./fromCurve";
+import {curveCardinal} from "d3-shape";
+
+export default function(values) {
+ return fromCurve(values, curveCardinal)
+}
diff --git a/src/catmullRom.js b/src/catmullRom.js
new file mode 100644
index 0000000..580ae0c
--- /dev/null
+++ b/src/catmullRom.js
@@ -0,0 +1,6 @@
+import {default as fromCurve} from "./fromCurve";
+import {curveCatmullRom} from "d3-shape";
+
+export default function(values) {
+ return fromCurve(values, curveCatmullRom)
+}
diff --git a/src/fromCurve.js b/src/fromCurve.js
new file mode 100644
index 0000000..927530b
--- /dev/null
+++ b/src/fromCurve.js
@@ -0,0 +1,69 @@
+import {line as shapeLine} from "d3-shape";
+import {range as arrayRange} from "d3-array";
+
+function curvePolator(points, curve, epsilon, samples) {
+ const path = shapeLine().curve(curve)(points);
+
+ return svgPathInterpolator(path, epsilon, samples);
+}
+
+function svgPathInterpolator(path, epsilon, samples) {
+ // Create detached SVG path
+ path = path || "M0,0L1,1";
+
+ const area = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ area.innerHTML = ``;
+ const svgpath = area.querySelector('path');
+ svgpath.setAttribute('d', path);
+
+ // Calculate lengths and max points
+ const totalLength = svgpath.getTotalLength();
+ const minPoint = svgpath.getPointAtLength(0);
+ const maxPoint = svgpath.getPointAtLength(totalLength);
+ let reverse = maxPoint.x < minPoint.x;
+ const range = reverse ? [maxPoint, minPoint] : [minPoint, maxPoint];
+ reverse = reverse ? -1 : 1;
+
+ // Return function
+ return function(x) {
+ const targetX = x === 0 ? 0 : x || minPoint.x; // Check for 0 and null/undefined
+ if (targetX < range[0].x) return range[0]; // Clamp
+ if (targetX > range[1].x) return range[1];
+
+ function estimateLength(l, mn, mx) {
+ let delta = svgpath.getPointAtLength(l).x - targetX;
+ let nextDelta = 0;
+ let iter = 0;
+
+ while (Math.abs(delta) > epsilon && iter < samples) {
+ if (iter > samples) return false;
+ iter++;
+
+ if (reverse * delta < 0) {
+ mn = l;
+ l = (l + mx) / 2;
+ } else {
+ mx = l;
+ l = (mn + l) / 2;
+ }
+ nextDelta = svgpath.getPointAtLength(l).x - targetX;
+
+ delta = nextDelta;
+ }
+
+ return l;
+ }
+
+ const estimatedLength = estimateLength(totalLength / 2, 0, totalLength);
+
+ return svgpath.getPointAtLength(estimatedLength).y;
+ }
+}
+
+export default function(values, curve, epsilon = 0.00001, samples = 100) {
+ const length = values.length;
+ const xrange = arrayRange(length).map(function(d, i) { return i * (1 / (length - 1)); });
+ const points = values.map((v, i) => [xrange[i], v]);
+
+ return curvePolator(points, curve, epsilon, samples);
+}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 8f545f6..3393fc7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,6 +8,10 @@ export {default as interpolateHue} from "./hue";
export {default as interpolateNumber} from "./number";
export {default as interpolateObject} from "./object";
export {default as interpolateRound} from "./round";
+export {default as interpolateFromCurve} from "./fromCurve";
+export {default as interpolateCardinal} from "./cardinal";
+export {default as interpolateCatmullRom} from "./catmullRom";
+export {default as interpolateMonotoneX} from "./monotoneX";
export {default as interpolateString} from "./string";
export {interpolateTransformCss, interpolateTransformSvg} from "./transform/index";
export {default as interpolateZoom} from "./zoom";
diff --git a/src/monotoneX.js b/src/monotoneX.js
new file mode 100644
index 0000000..9969a2b
--- /dev/null
+++ b/src/monotoneX.js
@@ -0,0 +1,6 @@
+import {default as fromCurve} from "./fromCurve";
+import {curveMonotoneX} from "d3-shape";
+
+export default function(values) {
+ return fromCurve(values, curveMonotoneX)
+}