UNPKG

toosoon-utils

Version:
149 lines (148 loc) 4.49 kB
import { isCoincident } from '../../geometry'; import Curve from './Curve'; import LineCurve from './LineCurve'; /** * Utility class for manipulating connected curves * * @exports * @class PathCurve * @extends Curve */ export default class PathCurve extends Curve { type = 'PathCurve'; /** * Array of curves composing the path */ curves = []; /** * Array of points composing the path */ points = []; autoClose = false; /** * Add a curve to this curve path * * @param {Curve} curve Curve to add */ add(curve) { this.curves.push(curve); } /** * Interpolate a point on the curve path * * @param {number} t Normalized time value to interpolate * @returns {Point|null} Interpolated coordinates on the curve */ getPoint(t) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; while (i < curveLengths.length) { if (curveLengths[i] >= d) { const delta = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - delta / segmentLength; return curve.getPointAt(u); } i++; } console.warn(`PathCurve.getPoint()`, `No point found in curve.`, this); return [0, 0]; } /** * Compute the curve shape into an array of points * * @param {number} [divisions=40] Number of divisions * @returns {Point[]} */ getPoints(divisions = 40) { const curves = this.curves; const points = []; let lastPoint = null; for (let i = 0; i < curves.length; i++) { const curve = curves[i]; const resolution = curve.type === 'EllipseCurve' ? divisions * 2 : curve.type === 'LineCurve' ? 1 : curve.type === 'SplineCurve' ? divisions * curve.points.length : divisions; const points = curve.getPoints(resolution); for (let j = 0; j < points.length; j++) { const point = points[j]; if (lastPoint && isCoincident(...lastPoint, ...point)) continue; points.push(point); lastPoint = point; } } if (this.autoClose && !Curve.isClosed(points)) { points.push(points[0]); } return points; } /** * Compute the curve shape into an array of equi-spaced points across the entire curve path * * @param {number} [divisions=40] Number of divisions * @returns {Point[]} */ getSpacedPoints(divisions = 40) { const points = []; for (let i = 0; i <= divisions; i++) { points.push(this.getPoint(i / divisions)); } if (this.autoClose && !Curve.isClosed(points)) { points.push(points[0]); } return points; } /** * Compute the total arc length of the curve path * * @returns {number} */ getLength() { const lengths = this.getCurveLengths(); return lengths[lengths.length - 1]; } /** * Compute the cumulative curve lengths of the curve path * * @returns {number[]} */ getCurveLengths() { if (this._cacheArcLengths.length === this.curves.length) { return this._cacheArcLengths; } const lengths = []; let sums = 0; for (let i = 0, l = this.curves.length; i < l; i++) { sums += this.curves[i].getLength(); lengths.push(sums); } this._cacheArcLengths = lengths; return lengths; } /** * Update the cached cumulative segment lengths */ updateArcLengths() { this.needsUpdate = true; this._cacheArcLengths = []; this.getCurveLengths(); } /** * Add a line curve to close the curve path * Add an instance of {@link LineCurve} to the path */ closePath() { const startPoint = this.curves[0]?.getPoint(0); const endPoint = this.curves[this.curves.length - 1]?.getPoint(1); if (!isCoincident(...startPoint, ...endPoint)) { this.add(new LineCurve(...endPoint, ...startPoint)); } } }