playcanvas
Version:
PlayCanvas WebGL game engine
120 lines (117 loc) • 3.7 kB
JavaScript
import { CURVE_STEP, CURVE_LINEAR, CURVE_SMOOTHSTEP, CURVE_SPLINE } from './constants.js';
import { math } from './math.js';
class CurveEvaluator {
constructor(curve, time = 0){
this._left = -Infinity;
this._right = Infinity;
this._recip = 0;
this._p0 = 0;
this._p1 = 0;
this._m0 = 0;
this._m1 = 0;
this._curve = curve;
this._reset(time);
}
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;
}
_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.0 / (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);
}
}
}
}
_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]);
}
}
_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 };