UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

170 lines (169 loc) 5.6 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { CURVE_LINEAR, CURVE_SMOOTHSTEP, CURVE_SPLINE, CURVE_STEP } from "./constants.js"; import { math } from "./math.js"; class CurveEvaluator { /** * Create a new CurveEvaluator instance. * * @param {Curve} curve - The curve to evaluate. * @param {number} time - The initial time to evaluate the curve at. Defaults to 0. */ constructor(curve, time = 0) { /** @private */ __publicField(this, "_curve"); /** @private */ __publicField(this, "_left", -Infinity); /** @private */ __publicField(this, "_right", Infinity); /** @private */ __publicField(this, "_recip", 0); /** @private */ __publicField(this, "_p0", 0); /** @private */ __publicField(this, "_p1", 0); /** @private */ __publicField(this, "_m0", 0); /** @private */ __publicField(this, "_m1", 0); this._curve = curve; this._reset(time); } /** * Evaluate the curve at the given time. Specify forceReset if the underlying curve keys have * changed since the last evaluation. * * @param {number} time - Time to evaluate the curve at. * @param {boolean} [forceReset] - Force reset of the curve. * @returns {number} The evaluated value. */ evaluate(time, forceReset = false) { if (forceReset || time < this._left || time >= this._right) { this._reset(time); } let result; const type = this._curve.type; if (type === CURVE_STEP) { result = this._p0; } else { const t = this._recip === 0 ? 0 : (time - this._left) * this._recip; if (type === CURVE_LINEAR) { result = math.lerp(this._p0, this._p1, t); } else if (type === CURVE_SMOOTHSTEP) { result = math.lerp(this._p0, this._p1, t * t * (3 - 2 * t)); } else { result = this._evaluateHermite(this._p0, this._p1, this._m0, this._m1, t); } } return result; } /** * Calculate weights for the curve interval at the given time. * * @param {number} time - Time to evaluate the curve at. * @private */ _reset(time) { const keys = this._curve.keys; const len = keys.length; if (!len) { this._left = -Infinity; this._right = Infinity; this._recip = 0; this._p0 = this._p1 = this._m0 = this._m1 = 0; } else { if (time < keys[0][0]) { this._left = -Infinity; this._right = keys[0][0]; this._recip = 0; this._p0 = this._p1 = keys[0][1]; this._m0 = this._m1 = 0; } else if (time >= keys[len - 1][0]) { this._left = keys[len - 1][0]; this._right = Infinity; this._recip = 0; this._p0 = this._p1 = keys[len - 1][1]; this._m0 = this._m1 = 0; } else { let index = 0; while (time >= keys[index + 1][0]) { index++; } this._left = keys[index][0]; this._right = keys[index + 1][0]; const diff = 1 / (this._right - this._left); this._recip = isFinite(diff) ? diff : 0; this._p0 = keys[index][1]; this._p1 = keys[index + 1][1]; if (this._curve.type === CURVE_SPLINE) { this._calcTangents(keys, index); } } } } /** * Calculate tangents for the hermite curve. * * @param {number[][]} keys - The keys of the curve. * @param {number} index - The key index of the key to calculate the tangents for. * @private */ _calcTangents(keys, index) { let a; const b = keys[index]; const c = keys[index + 1]; let d; if (index === 0) { a = [ keys[0][0] + (keys[0][0] - keys[1][0]), keys[0][1] + (keys[0][1] - keys[1][1]) ]; } else { a = keys[index - 1]; } if (index === keys.length - 2) { d = [ keys[index + 1][0] + (keys[index + 1][0] - keys[index][0]), keys[index + 1][1] + (keys[index + 1][1] - keys[index][1]) ]; } else { d = keys[index + 2]; } if (this._curve.type === CURVE_SPLINE) { const s1_ = 2 * (c[0] - b[0]) / (c[0] - a[0]); const s2_ = 2 * (c[0] - b[0]) / (d[0] - b[0]); this._m0 = this._curve.tension * (isFinite(s1_) ? s1_ : 0) * (c[1] - a[1]); this._m1 = this._curve.tension * (isFinite(s2_) ? s2_ : 0) * (d[1] - b[1]); } else { const s1 = (c[0] - b[0]) / (b[0] - a[0]); const s2 = (c[0] - b[0]) / (d[0] - c[0]); const a_ = b[1] + (a[1] - b[1]) * (isFinite(s1) ? s1 : 0); const d_ = c[1] + (d[1] - c[1]) * (isFinite(s2) ? s2 : 0); const tension = this._curve.tension; this._m0 = tension * (c[1] - a_); this._m1 = tension * (d_ - b[1]); } } /** * Evaluate the hermite curve at the given time. * * @param {number} p0 - The first key. * @param {number} p1 - The second key. * @param {number} m0 - The first tangent. * @param {number} m1 - The second tangent. * @param {number} t - Time to evaluate the curve at. * @returns {number} The value of the hermite curve at the given time. * @private */ _evaluateHermite(p0, p1, m0, m1, t) { const t2 = t * t; const twot = t + t; const omt = 1 - t; const omt2 = omt * omt; return p0 * ((1 + twot) * omt2) + m0 * (t * omt2) + p1 * (t2 * (3 - twot)) + m1 * (t2 * (t - 1)); } } export { CurveEvaluator };