src/viewer/scene/paths/Curve.js
import {Component} from "../Component.js"
import {math} from "../math/math.js";
/**
* @desc Abstract base class for curve classes.
*/
class Curve extends Component {
/**
* @constructor
* @param {Component} [owner] Owner component. When destroyed, the owner will destroy this Curve as well.
* @param {*} [cfg] Configs
* @param {String} [cfg.id] Optional ID, unique among all components in the parent {@link Curve}, generated automatically when omitted.
* @param {Object} [cfg] Configs for this Curve.
* @param {Number} [cfg.t=0] Current position on this Curve, in range between ````0..1````.
*/
constructor(owner, cfg = {}) {
super(owner, cfg);
this.t = cfg.t;
}
/**
* Sets the progress along this Curve.
*
* Automatically clamps to range ````[0..1]````.
*
* Default value is ````0````.
*
* @param {Number} value The progress value.
*/
set t(value) {
value = value || 0;
this._t = value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value);
}
/**
* Gets the progress along this Curve.
*
* @returns {Number} The progress value.
*/
get t() {
return this._t;
}
/**
* Gets the tangent on this Curve at position {@link Curve#t}.
*
* @returns {Number[]} The tangent.
*/
get tangent() {
return this.getTangent(this._t);
}
/**
* Gets the length of this Curve.
*
* @returns {Number} The Curve length.
*/
get length() {
var lengths = this._getLengths();
return lengths[lengths.length - 1];
}
/**
* Returns a normalized tangent vector on this Curve at the given position.
*
* @param {Number} t Position to get tangent at.
* @returns {Number[]} Normalized tangent vector
*/
getTangent(t) {
var delta = 0.0001;
if (t === undefined) {
t = this._t;
}
var t1 = t - delta;
var t2 = t + delta;
if (t1 < 0) {
t1 = 0;
}
if (t2 > 1) {
t2 = 1;
}
var pt1 = this.getPoint(t1);
var pt2 = this.getPoint(t2);
var vec = math.subVec3(pt2, pt1, []);
return math.normalizeVec3(vec, []);
}
getPointAt(u) {
var t = this.getUToTMapping(u);
return this.getPoint(t);
}
/**
* Samples points on this Curve, at the given number of equally-spaced divisions.
*
* @param {Number} divisions The number of divisions.
* @returns {{Array of Array}} Array of sampled 3D points.
*/
getPoints(divisions) {
if (!divisions) {
divisions = 5;
}
var d, pts = [];
for (d = 0; d <= divisions; d++) {
pts.push(this.getPoint(d / divisions));
}
return pts;
}
_getLengths(divisions) {
if (!divisions) {
divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions) : 200;
}
if (this.cacheArcLengths && (this.cacheArcLengths.length === divisions + 1) && !this.needsUpdate) {
return this.cacheArcLengths;
}
this.needsUpdate = false;
var cache = [];
var current;
var last = this.getPoint(0);
var p;
var sum = 0;
cache.push(0);
for (p = 1; p <= divisions; p++) {
current = this.getPoint(p / divisions);
sum += math.lenVec3(math.subVec3(current, last, []));
cache.push(sum);
last = current;
}
this.cacheArcLengths = cache;
return cache; // { sums: cache, sum:sum }, Sum is in the last element.
}
_updateArcLengths() {
this.needsUpdate = true;
this._getLengths();
}
// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
getUToTMapping(u, distance) {
var arcLengths = this._getLengths();
var i = 0;
var il = arcLengths.length;
var t;
var targetArcLength; // The targeted u distance value to get
if (distance) {
targetArcLength = distance;
} else {
targetArcLength = u * arcLengths[il - 1];
}
//var time = Date.now();
var low = 0, high = il - 1, comparison;
while (low <= high) {
i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
comparison = arcLengths[i] - targetArcLength;
if (comparison < 0) {
low = i + 1;
} else if (comparison > 0) {
high = i - 1;
} else {
high = i;
break;
// DONE
}
}
i = high;
if (arcLengths[i] === targetArcLength) {
t = i / (il - 1);
return t;
}
var lengthBefore = arcLengths[i];
var lengthAfter = arcLengths[i + 1];
var segmentLength = lengthAfter - lengthBefore;
var segmentFraction = (targetArcLength - lengthBefore) / segmentLength;
t = (i + segmentFraction) / (il - 1);
return t;
}
}
export {Curve}