import Cartesian3 from "./Cartesian3.js";
import Check from "./Check.js";
import CesiumMath from "./Math.js";
import defaultValue from "./defaultValue.js";
import DeveloperError from "./DeveloperError.js";
import Quaternion from "./Quaternion.js";
/**
* Creates a curve parameterized and evaluated by time. This type describes an interface
* and is not intended to be instantiated directly.
*
* @alias Spline
* @constructor
*
* @see CatmullRomSpline
* @see LinearSpline
* @see HermiteSpline
* @see QuaternionSpline
* @see MorphWeightSpline
*/
function Spline() {
/**
* An array of times for the control points.
* @type {Number[]}
* @default undefined
*/
this.times = undefined;
/**
* An array of control points.
* @type {Cartesian3[]|Quaternion[]}
* @default undefined
*/
this.points = undefined;
DeveloperError.throwInstantiationError();
}
/**
* Gets the type of the point. This helps a spline determine how to interpolate
* and return its values.
*
* @param {Number|Cartesian3|Quaternion} point
* @returns {*} The type of the point.
*
* @exception {DeveloperError} value must be a Cartesian3, Quaternion, or Number.
*
* @private
*/
Spline.getPointType = function (point) {
if (typeof point === "number") {
return Number;
}
if (point instanceof Cartesian3) {
return Cartesian3;
}
if (point instanceof Quaternion) {
return Quaternion;
}
//>>includeStart('debug', pragmas.debug);
throw new DeveloperError(
"point must be a Cartesian3, Quaternion, or Number."
);
//>>includeEnd('debug');
};
/**
* Evaluates the curve at a given time.
* @function
*
* @param {Number} time The time at which to evaluate the curve.
* @param {Cartesian3|Quaternion|Number[]} [result] The object onto which to store the result.
* @returns {Cartesian3|Quaternion|Number[]} The modified result parameter or a new instance of the point on the curve at the given time.
*
* @exception {DeveloperError} time must be in the range [t0, tn]
, where t0
* is the first element in the array times
and tn
is the last element
* in the array times
.
*/
Spline.prototype.evaluate = DeveloperError.throwInstantiationError;
/**
* Finds an index i
in times
such that the parameter
* time
is in the interval [times[i], times[i + 1]]
.
*
* @param {Number} time The time.
* @param {Number} startIndex The index from which to start the search.
* @returns {Number} The index for the element at the start of the interval.
*
* @exception {DeveloperError} time must be in the range [t0, tn]
, where t0
* is the first element in the array times
and tn
is the last element
* in the array times
.
*/
Spline.prototype.findTimeInterval = function (time, startIndex) {
const times = this.times;
const length = times.length;
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number("time", time);
if (time < times[0] || time > times[length - 1]) {
throw new DeveloperError("time is out of range.");
}
//>>includeEnd('debug');
// Take advantage of temporal coherence by checking current, next and previous intervals
// for containment of time.
startIndex = defaultValue(startIndex, 0);
if (time >= times[startIndex]) {
if (startIndex + 1 < length && time < times[startIndex + 1]) {
return startIndex;
} else if (startIndex + 2 < length && time < times[startIndex + 2]) {
return startIndex + 1;
}
} else if (startIndex - 1 >= 0 && time >= times[startIndex - 1]) {
return startIndex - 1;
}
// The above failed so do a linear search. For the use cases so far, the
// length of the list is less than 10. In the future, if there is a bottle neck,
// it might be here.
let i;
if (time > times[startIndex]) {
for (i = startIndex; i < length - 1; ++i) {
if (time >= times[i] && time < times[i + 1]) {
break;
}
}
} else {
for (i = startIndex - 1; i >= 0; --i) {
if (time >= times[i] && time < times[i + 1]) {
break;
}
}
}
if (i === length - 1) {
i = length - 2;
}
return i;
};
/**
* Wraps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, wrapped around the animation period.
*/
Spline.prototype.wrapTime = function (time) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number("time", time);
//>>includeEnd('debug');
const times = this.times;
const timeEnd = times[times.length - 1];
const timeStart = times[0];
const timeStretch = timeEnd - timeStart;
let divs;
if (time < timeStart) {
divs = Math.floor((timeStart - time) / timeStretch) + 1;
time += divs * timeStretch;
}
if (time > timeEnd) {
divs = Math.floor((time - timeEnd) / timeStretch) + 1;
time -= divs * timeStretch;
}
return time;
};
/**
* Clamps the given time to the period covered by the spline.
* @function
*
* @param {Number} time The time.
* @return {Number} The time, clamped to the animation period.
*/
Spline.prototype.clampTime = function (time) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number("time", time);
//>>includeEnd('debug');
const times = this.times;
return CesiumMath.clamp(time, times[0], times[times.length - 1]);
};
export default Spline;