UNPKG

toosoon-utils

Version:
151 lines (150 loc) 4.66 kB
import { Curve, LineCurve, LineCurve3, PolylineCurve, PolylineCurve3, SplineCurve, SplineCurve3, EllipseCurve } from '../curves'; /** * Utility class for manipulating connected curves * * @exports * @class Path * @extends Curve */ export default class Path extends Curve { isPath = true; type = 'Path'; /** * Array of curves composing this path */ curves = []; /** * Array of points composing this path */ points = []; /** * Define if a last point should be automatically added to close this path */ autoClose; constructor({ autoClose = false } = {}) { super(); this.autoClose = autoClose; } /** * Add a curve to this path * * @param {Curve} curve Curve to add */ add(curve) { this.curves.push(curve); } /** * Interpolate a point on this path * * @param {number} t Normalized time value to interpolate * @returns {Vector} 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(`Path.getPoint()`, `No point found in curve.`, this); return this.curves[0].getPoint(0); } /** * Compute the curve shape into an array of points * * @param {number} [divisions=40] Number of divisions * @returns {Vector[]} */ getPoints(divisions = 40) { const points = []; let lastPoint = null; for (let i = 0; i < this.curves.length; i++) { const curve = this.curves[i]; let resolution = divisions; if (curve instanceof LineCurve || curve instanceof LineCurve3) { resolution = 1; } else if (curve instanceof PolylineCurve || curve instanceof PolylineCurve3) { resolution = curve.points.length; } else if (curve instanceof SplineCurve || curve instanceof SplineCurve3) { resolution *= curve.points.length; } else if (curve instanceof EllipseCurve) { resolution *= 2; } const pts = this.curves[i].getPoints(resolution); for (let j = 0; j < pts.length; j++) { const point = pts[j]; if (point?.equals(lastPoint)) continue; points.push(point); lastPoint = point; } } const isClosed = points.length > 1 && points[0].equals(points[points.length - 1]); if (this.autoClose && !isClosed) { 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 {Vector[]} */ getSpacedPoints(divisions = 40) { const points = []; for (let i = 0; i <= divisions; i++) { points.push(this.getPoint(i / divisions)); } const isClosed = points.length > 1 && points[0].equals(points[points.length - 1]); if (this.autoClose && !isClosed) { points.push(points[0]); } return points; } /** * Compute the total arc length of this path * * @returns {number} */ getLength() { const lengths = this.getCurveLengths(); return lengths[lengths.length - 1]; } /** * Compute the cumulative curve lengths of this path * * @returns {number[]} */ getCurveLengths() { if (this._cacheArcLengths.length === this.curves.length) { return this._cacheArcLengths; } const lengths = []; let sums = 0; for (let i = 0, j = this.curves.length; i < j; 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(); } }