Spaces:
Running
Running
| import { ZeroCurvatureEnding } from '../../constants.js'; | |
| import { Interpolant } from '../Interpolant.js'; | |
| import { WrapAroundEnding, ZeroSlopeEnding } from '../../constants.js'; | |
| /** | |
| * Fast and simple cubic spline interpolant. | |
| * | |
| * It was derived from a Hermitian construction setting the first derivative | |
| * at each sample position to the linear slope between neighboring positions | |
| * over their parameter interval. | |
| */ | |
| class CubicInterpolant extends Interpolant { | |
| constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { | |
| super(parameterPositions, sampleValues, sampleSize, resultBuffer); | |
| this._weightPrev = -0; | |
| this._offsetPrev = -0; | |
| this._weightNext = -0; | |
| this._offsetNext = -0; | |
| this.DefaultSettings_ = { | |
| endingStart: ZeroCurvatureEnding, | |
| endingEnd: ZeroCurvatureEnding, | |
| }; | |
| } | |
| intervalChanged_(i1, t0, t1) { | |
| const pp = this.parameterPositions; | |
| let iPrev = i1 - 2, | |
| iNext = i1 + 1, | |
| tPrev = pp[iPrev], | |
| tNext = pp[iNext]; | |
| if (tPrev === undefined) { | |
| switch (this.getSettings_().endingStart) { | |
| case ZeroSlopeEnding: | |
| // f'(t0) = 0 | |
| iPrev = i1; | |
| tPrev = 2 * t0 - t1; | |
| break; | |
| case WrapAroundEnding: | |
| // use the other end of the curve | |
| iPrev = pp.length - 2; | |
| tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; | |
| break; | |
| default: | |
| // ZeroCurvatureEnding | |
| // f''(t0) = 0 a.k.a. Natural Spline | |
| iPrev = i1; | |
| tPrev = t1; | |
| } | |
| } | |
| if (tNext === undefined) { | |
| switch (this.getSettings_().endingEnd) { | |
| case ZeroSlopeEnding: | |
| // f'(tN) = 0 | |
| iNext = i1; | |
| tNext = 2 * t1 - t0; | |
| break; | |
| case WrapAroundEnding: | |
| // use the other end of the curve | |
| iNext = 1; | |
| tNext = t1 + pp[1] - pp[0]; | |
| break; | |
| default: | |
| // ZeroCurvatureEnding | |
| // f''(tN) = 0, a.k.a. Natural Spline | |
| iNext = i1 - 1; | |
| tNext = t0; | |
| } | |
| } | |
| const halfDt = (t1 - t0) * 0.5, | |
| stride = this.valueSize; | |
| this._weightPrev = halfDt / (t0 - tPrev); | |
| this._weightNext = halfDt / (tNext - t1); | |
| this._offsetPrev = iPrev * stride; | |
| this._offsetNext = iNext * stride; | |
| } | |
| interpolate_(i1, t0, t, t1) { | |
| const result = this.resultBuffer, | |
| values = this.sampleValues, | |
| stride = this.valueSize, | |
| o1 = i1 * stride, | |
| o0 = o1 - stride, | |
| oP = this._offsetPrev, | |
| oN = this._offsetNext, | |
| wP = this._weightPrev, | |
| wN = this._weightNext, | |
| p = (t - t0) / (t1 - t0), | |
| pp = p * p, | |
| ppp = pp * p; | |
| // evaluate polynomials | |
| const sP = -wP * ppp + 2 * wP * pp - wP * p; | |
| const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; | |
| const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; | |
| const sN = wN * ppp - wN * pp; | |
| // combine data linearly | |
| for (let i = 0; i !== stride; ++i) { | |
| result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; | |
| } | |
| return result; | |
| } | |
| } | |
| export { CubicInterpolant }; | |