isomorphic-svg-charts
Version:
A lightweight, environment-agnostic SVG charting library that works both in the browser and server-side, with no dependencies and no DOM reliance.
82 lines (81 loc) • 3.46 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.toCatmullRom = void 0;
// The "Catmull-Rom" Interpolating Splines in ES6
// - Authors: Nicholas Kyriakides (2017) & Unknown
// - From "A class of local interpolating splines"
// - by Catmull, Edwin; Rom,Raphael
// - University of Utah, 1974
//
//
// Barnhill, Robert E.; Riesenfeld, Richard F. (eds.).
// Computer Aided Geometric Design.
//
// Interpolates a Catmull-Rom spline through a series of x/y points
//
// - If `alpha` is `0` then the 'Uniform' variant is used.
// - If `alpha` is `0.5` then the 'Centripetal' variant is used.
// Read: https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
// - If `alpha` is `1` then the 'Chordal' variant is used.
//
// @param {Array} `points` - `Point` is an object like: `{ x: 10, y: 20 }`
// @param {Number} `alpha` - knot parameterization, can be `0`, `0.5` or `1`
//
// @return {String} `d` - An SVG path of cubic beziers
//
const toCatmullRom = (points, alpha = 0.5) => {
if (![0.5, 1].includes(alpha))
throw RangeError(`'alpha' should be: 1 or 0.5. Got: ${alpha}`);
if (points.length < 2) {
throw RangeError('Catmull-Rom needs at least two points');
}
const firstPoint = points[0];
let p0, p1, p2, p3, bp1, bp2, d1, d2, d3, A, B, N, M;
let d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA;
let d = 'M' + Math.round(firstPoint.x) + ',' + Math.round(firstPoint.y) + ' ';
for (let i = 0; i < points.length - 1; i++) {
p0 = i == 0 ? points[0] : points[i - 1];
p1 = points[i];
p2 = points[i + 1];
p3 = i + 2 < points.length ? points[i + 2] : p2;
d1 = Math.sqrt(Math.pow(p0.x - p1.x, 2) + Math.pow(p0.y - p1.y, 2));
d2 = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
d3 = Math.sqrt(Math.pow(p2.x - p3.x, 2) + Math.pow(p2.y - p3.y, 2));
// Catmull-Rom to Cubic Bezier conversion matrix
// A = 2d1^2a + 3d1^a * d2^a + d3^2a
// B = 2d3^2a + 3d3^a * d2^a + d2^2a
// [ 0 1 0 0 ]
// [ -d2^2a /N A/N d1^2a /N 0 ]
// [ 0 d3^2a /M B/M -d2^2a /M ]
// [ 0 0 1 0 ]
d3powA = Math.pow(d3, alpha);
d3pow2A = Math.pow(d3, 2 * alpha);
d2powA = Math.pow(d2, alpha);
d2pow2A = Math.pow(d2, 2 * alpha);
d1powA = Math.pow(d1, alpha);
d1pow2A = Math.pow(d1, 2 * alpha);
A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
N = 3 * d1powA * (d1powA + d2powA);
if (N > 0)
N = 1 / N;
M = 3 * d3powA * (d3powA + d2powA);
if (M > 0)
M = 1 / M;
bp1 = {
x: (-d2pow2A * p0.x + A * p1.x + d1pow2A * p2.x) * N,
y: (-d2pow2A * p0.y + A * p1.y + d1pow2A * p2.y) * N
};
bp2 = {
x: (d3pow2A * p1.x + B * p2.x - d2pow2A * p3.x) * M,
y: (d3pow2A * p1.y + B * p2.y - d2pow2A * p3.y) * M
};
if (bp1.x == 0 && bp1.y == 0)
bp1 = p1;
if (bp2.x == 0 && bp2.y == 0)
bp2 = p2;
d += 'C' + bp1.x + ',' + bp1.y + ' ' + bp2.x + ',' + bp2.y + ' ' + p2.x + ',' + p2.y + ' ';
}
return d;
};
exports.toCatmullRom = toCatmullRom;