import { ZeroCurvatureEnding, WrapAroundEnding, ZeroSlopeEnding } from '../../constants.js'; import { Interpolant } from '../Interpolant.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 };