chrt-interpolations
Version:
Interpolation functions for Chrt
242 lines (232 loc) • 9.72 kB
JavaScript
// chrt-interpolations v0.0.15 Copyright 2020-2024 chrt chrt.io
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _arrayWithoutHoles(r) {
if (Array.isArray(r)) return _arrayLikeToArray(r);
}
function _iterableToArray(r) {
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toConsumableArray(r) {
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function isNull(value) {
return value === null || value == null || typeof value === 'undefined';
}
function isInfinity(value) {
return typeof value === 'number' && !isFinite(value);
}
function getCoords(data) {
var _this = this;
if (!(this !== null && this !== void 0 && this.fields)) {
return [];
}
return data.map(function (d) {
var x = isNull(d[_this.fields.x]) || isInfinity(d[_this.fields.x]) ? null : _this.parentNode.scales.x[_this.scales.x](d[_this.fields.x]);
var y = isNull(d[_this.fields.y]) || isInfinity(d[_this.fields.y]) ? null : _this.parentNode.scales.y[_this.scales.y](d[_this.fields.y]);
return [isInfinity(x) ? 0 : x, isInfinity(y) ? 0 : y];
}); //.filter(d => !isNull(d[0]) && !isNull(d[1]))
}
// Render the svg <path> element
// I: - points (array): points coordinates
// - command (function)
// I: - point (array) [x,y]: current point coordinates
// - i (integer): index of 'point' in the array 'a'
// - a (array): complete array of points coordinates
// O: - (string) a svg path command
// O: - (string): a Svg <path> element
var svgPath = function svgPath(points, command) {
var _ref;
// build the d attributes by looping over the points
var splitByNullPoints = points.reduce(function (acc, point) {
if (isNull(point[1])) {
acc.push([]);
} else {
acc[acc.length - 1].push(point);
}
return acc;
}, [[]]);
var paths = splitByNullPoints.map(function (points) {
return points.reduce(function (acc, point, i, a) {
acc.push(i === 0 ? // if first point
"M".concat(isNaN(point[0]) ? 0 : point[0], ",").concat(isNaN(point[1]) ? 0 : point[1]) : // else
"".concat(command(point, i, a)));
return acc;
}, []);
});
return (_ref = []).concat.apply(_ref, _toConsumableArray(paths));
};
function linearInterpolation(data) {
return svgPath(getCoords.call(this, data), lineCommand);
}
var lineCommand = function lineCommand(point) {
return "L".concat(isNaN(point[0]) ? 0 : point[0], ",").concat(isNaN(point[1]) ? 0 : point[1]);
};
function splineInterpolation(data) {
return svgPath(getCoords.call(this, data), bezierCommand);
}
// Properties of a line
// I: - pointA (array) [x,y]: coordinates
// - pointB (array) [x,y]: coordinates
// O: - (object) { length: l, angle: a }: properties of the line
var line = function line(pointA, pointB) {
var lengthX = pointB[0] - pointA[0];
var lengthY = pointB[1] - pointA[1];
return {
length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
angle: Math.atan2(lengthY, lengthX)
};
};
// Position of a control point
// I: - current (array) [x, y]: current point coordinates
// - previous (array) [x, y]: previous point coordinates
// - next (array) [x, y]: next point coordinates
// - reverse (boolean, optional): sets the direction
// O: - (array) [x,y]: a tuple of coordinates
var controlPoint = function controlPoint(current, previous, next, reverse) {
// When 'current' is the first or last point of the array
// 'previous' or 'next' don't exist.
// Replace with 'current'
var p = previous || current;
var n = next || current;
// The smoothing ratio
var smoothing = 0.1;
// Properties of the opposed-line
var o = line(p, n);
// If is end-control-point, add PI to the angle to go backward
var angle = o.angle + (reverse ? Math.PI : 0);
var length = o.length * smoothing;
// The control point position is relative to the current point
var x = current[0] + Math.cos(angle) * length;
var y = current[1] + Math.sin(angle) * length;
return [x, y];
};
// Create the bezier curve command
// I: - point (array) [x,y]: current point coordinates
// - i (integer): index of 'point' in the array 'a'
// - a (array): complete array of points coordinates
// O: - (string) 'C x2,y2 x1,y1 x,y': SVG cubic bezier C command
var bezierCommand = function bezierCommand(point, i, a) {
if (isNaN(point[0]) || isNaN(point[1])) {
return '';
}
// start control point
var _controlPoint = controlPoint(a[i - 1], a[i - 2], point),
_controlPoint2 = _slicedToArray(_controlPoint, 2),
cpsX = _controlPoint2[0],
cpsY = _controlPoint2[1];
// end control point
var _controlPoint3 = controlPoint(point, a[i - 1], a[i + 1], true),
_controlPoint4 = _slicedToArray(_controlPoint3, 2),
cpeX = _controlPoint4[0],
cpeY = _controlPoint4[1];
return "C ".concat(cpsX, ",").concat(cpsY, " ").concat(cpeX, ",").concat(cpeY, " ").concat(point[0], ",").concat(point[1]);
};
//
// function splineCurve(firstPoint, middlePoint, afterPoint, t = 1) {
// // Props to Rob Spencer at scaled innovation for his post on splining between points
// // http://scaledinnovation.com/analytics/splines/aboutSplines.html
//
// // This function must also respect "skipped" points
//
// // console.log('splineCurve', firstPoint, middlePoint, afterPoint);
//
// const previous = firstPoint.skip ? middlePoint : firstPoint;
// const current = middlePoint;
// const next = afterPoint.skip ? middlePoint : afterPoint;
//
// const d01 = Math.sqrt(
// Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)
// );
// const d12 = Math.sqrt(
// Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)
// );
//
// let s01 = d01 / (d01 + d12);
// let s12 = d12 / (d01 + d12);
//
// // If all points are the same, s01 & s02 will be inf
// s01 = isNaN(s01) ? 0 : s01;
// s12 = isNaN(s12) ? 0 : s12;
//
// const fa = t * s01; // scaling factor for triangle Ta
// const fb = t * s12;
//
// return {
// previous: {
// x: current.x - fa * (next.x - previous.x),
// y: current.y - fa * (next.y - previous.y)
// },
// next: {
// x: current.x + fb * (next.x - previous.x),
// y: current.y + fb * (next.y - previous.y)
// }
// };
// }
function stepInterpolation(data) {
var command = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : stepCommand;
return svgPath(getCoords.call(this, data), command);
}
function stepAfterInterpolation(data) {
return stepInterpolation.call(this, data, stepCommandAfter);
}
function stepBeforeInterpolation(data) {
return stepInterpolation.call(this, data, stepCommandBefore);
}
var stepCommand = function stepCommand(point, i, a) {
var xMiddle = ((isNaN(point[0]) ? 0 : point[0]) + (isNaN(a[i - 1][0]) ? 0 : a[i - 1][0])) / 2;
return "L".concat(isNaN(xMiddle) ? 0 : xMiddle, ",").concat(isNaN(a[i - 1][1]) ? 0 : a[i - 1][1], "\n L").concat(isNaN(xMiddle) ? 0 : xMiddle, ",").concat(isNaN(point[1]) ? 0 : point[1], "\n L").concat(isNaN(point[0]) ? 0 : point[0], ",").concat(isNaN(point[1]) ? 0 : point[1]);
};
var stepCommandAfter = function stepCommandAfter(point, i, a) {
return "L".concat(isNaN(point[0]) ? 0 : point[0], ",").concat(isNaN(a[i - 1][1]) ? 0 : a[i - 1][1], "\n L").concat(isNaN(point[0]) ? 0 : point[0], ",").concat(isNaN(point[1]) ? 0 : point[1]);
};
var stepCommandBefore = function stepCommandBefore(point, i, a) {
return "L".concat(isNaN(a[i - 1][0]) ? 0 : a[i - 1][0], ",").concat(isNaN(point[1]) ? 0 : point[1], "\n L").concat(isNaN(point[0]) ? 0 : point[0], ",").concat(isNaN(point[1]) ? 0 : point[1]);
};
export { linearInterpolation as linear, splineInterpolation as spline, stepInterpolation as step, stepAfterInterpolation as stepAfter, stepBeforeInterpolation as stepBefore };