|
| 1 | +import { Interpolant } from '../../../../../src/math/Interpolant.js'; |
| 2 | + |
| 3 | +export default QUnit.module( 'Maths', () => { |
| 4 | + |
| 5 | + QUnit.module( 'Interpolants', () => { |
| 6 | + |
| 7 | + QUnit.module( 'CustomInterpolant', () => { |
| 8 | + |
| 9 | + // A custom cubic spline interpolant mimicking `GLTFCubicSplineInterpolant` |
| 10 | + // from `GLTFLoader`. The keyframe layout for CUBICSPLINE animations is: |
| 11 | + // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] |
| 12 | + |
| 13 | + class CubicSplineInterpolant extends Interpolant { |
| 14 | + |
| 15 | + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { |
| 16 | + |
| 17 | + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); |
| 18 | + |
| 19 | + } |
| 20 | + |
| 21 | + copySampleValue_( index ) { |
| 22 | + |
| 23 | + const result = this.resultBuffer, |
| 24 | + values = this.sampleValues, |
| 25 | + valueSize = this.valueSize, |
| 26 | + offset = index * valueSize * 3 + valueSize; |
| 27 | + |
| 28 | + for ( let i = 0; i !== valueSize; i ++ ) { |
| 29 | + |
| 30 | + result[ i ] = values[ offset + i ]; |
| 31 | + |
| 32 | + } |
| 33 | + |
| 34 | + return result; |
| 35 | + |
| 36 | + } |
| 37 | + |
| 38 | + interpolate_( i1, t0, t, t1 ) { |
| 39 | + |
| 40 | + const result = this.resultBuffer; |
| 41 | + const values = this.sampleValues; |
| 42 | + const stride = this.valueSize; |
| 43 | + |
| 44 | + const stride2 = stride * 2; |
| 45 | + const stride3 = stride * 3; |
| 46 | + |
| 47 | + const td = t1 - t0; |
| 48 | + |
| 49 | + const p = ( t - t0 ) / td; |
| 50 | + const pp = p * p; |
| 51 | + const ppp = pp * p; |
| 52 | + |
| 53 | + const offset1 = i1 * stride3; |
| 54 | + const offset0 = offset1 - stride3; |
| 55 | + |
| 56 | + const s2 = - 2 * ppp + 3 * pp; |
| 57 | + const s3 = ppp - pp; |
| 58 | + const s0 = 1 - s2; |
| 59 | + const s1 = s3 - pp + p; |
| 60 | + |
| 61 | + for ( let i = 0; i !== stride; i ++ ) { |
| 62 | + |
| 63 | + const p0 = values[ offset0 + i + stride ]; |
| 64 | + const m0 = values[ offset0 + i + stride2 ] * td; |
| 65 | + const p1 = values[ offset1 + i + stride ]; |
| 66 | + const m1 = values[ offset1 + i ] * td; |
| 67 | + |
| 68 | + result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; |
| 69 | + |
| 70 | + } |
| 71 | + |
| 72 | + return result; |
| 73 | + |
| 74 | + } |
| 75 | + |
| 76 | + } |
| 77 | + |
| 78 | + // INHERITANCE |
| 79 | + QUnit.test( 'Extending', ( assert ) => { |
| 80 | + |
| 81 | + // parameterPositions, sampleValues, sampleSize, resultBuffer |
| 82 | + const object = new CubicSplineInterpolant( [ 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], 1, [] ); |
| 83 | + assert.strictEqual( |
| 84 | + object instanceof Interpolant, true, |
| 85 | + 'CubicSplineInterpolant extends from Interpolant' |
| 86 | + ); |
| 87 | + |
| 88 | + } ); |
| 89 | + |
| 90 | + // PUBLIC |
| 91 | + QUnit.test( 'evaluate', ( assert ) => { |
| 92 | + |
| 93 | + // Two keyframes at t = 0 and t = 1, valueSize = 1. |
| 94 | + // Layout: [ in_0, v_0, out_0, in_1, v_1, out_1 ] |
| 95 | + // Vertex values 0 -> 1 with non-zero tangents to exercise all spline terms. |
| 96 | + const positions = [ 0, 1 ]; |
| 97 | + const values = [ 0, 0, 1, - 1, 1, 0 ]; |
| 98 | + const interpolant = new CubicSplineInterpolant( positions, values, 1, [ 0 ] ); |
| 99 | + |
| 100 | + assert.deepEqual( interpolant.evaluate( 0 ), [ 0 ], 'evaluate at first keyframe' ); |
| 101 | + assert.deepEqual( interpolant.evaluate( 1 ), [ 1 ], 'evaluate at last keyframe' ); |
| 102 | + |
| 103 | + // At t = 0.5 with td = 1, p = 0.5 → s0 = 0.5, s1 = 0.125, s2 = 0.5, s3 = -0.125 |
| 104 | + // result = 0.5 * 0 + 0.125 * 1 + 0.5 * 1 + ( -0.125 ) * ( -1 ) = 0.75 |
| 105 | + assert.deepEqual( interpolant.evaluate( 0.5 ), [ 0.75 ], 'evaluate inside interval' ); |
| 106 | + |
| 107 | + // Out-of-range queries clamp to the boundary spline vertex. |
| 108 | + assert.deepEqual( interpolant.evaluate( - 1 ), [ 0 ], 'evaluate before first keyframe' ); |
| 109 | + assert.deepEqual( interpolant.evaluate( 2 ), [ 1 ], 'evaluate after last keyframe' ); |
| 110 | + |
| 111 | + } ); |
| 112 | + |
| 113 | + } ); |
| 114 | + |
| 115 | + } ); |
| 116 | + |
| 117 | +} ); |
0 commit comments