Spaces:
Running
Running
| /** | |
| * Abstract base class of interpolants over parametric samples. | |
| * | |
| * The parameter domain is one dimensional, typically the time or a path | |
| * along a curve defined by the data. | |
| * | |
| * The sample values can have any dimensionality and derived classes may | |
| * apply special interpretations to the data. | |
| * | |
| * This class provides the interval seek in a Template Method, deferring | |
| * the actual interpolation to derived classes. | |
| * | |
| * Time complexity is O(1) for linear access crossing at most two points | |
| * and O(log N) for random access, where N is the number of positions. | |
| * | |
| * References: | |
| * | |
| * http://www.oodesign.com/template-method-pattern.html | |
| * | |
| * @author tschw | |
| */ | |
| function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
| this.parameterPositions = parameterPositions; | |
| this._cachedIndex = 0; | |
| this.resultBuffer = resultBuffer !== undefined ? | |
| resultBuffer : new sampleValues.constructor( sampleSize ); | |
| this.sampleValues = sampleValues; | |
| this.valueSize = sampleSize; | |
| } | |
| Object.assign( Interpolant.prototype, { | |
| evaluate: function ( t ) { | |
| var pp = this.parameterPositions, | |
| i1 = this._cachedIndex, | |
| t1 = pp[ i1 ], | |
| t0 = pp[ i1 - 1 ]; | |
| validate_interval: { | |
| seek: { | |
| var right; | |
| linear_scan: { | |
| //- See http://jsperf.com/comparison-to-undefined/3 | |
| //- slower code: | |
| //- | |
| //- if ( t >= t1 || t1 === undefined ) { | |
| forward_scan: if ( ! ( t < t1 ) ) { | |
| for ( var giveUpAt = i1 + 2; ; ) { | |
| if ( t1 === undefined ) { | |
| if ( t < t0 ) break forward_scan; | |
| // after end | |
| i1 = pp.length; | |
| this._cachedIndex = i1; | |
| return this.afterEnd_( i1 - 1, t, t0 ); | |
| } | |
| if ( i1 === giveUpAt ) break; // this loop | |
| t0 = t1; | |
| t1 = pp[ ++ i1 ]; | |
| if ( t < t1 ) { | |
| // we have arrived at the sought interval | |
| break seek; | |
| } | |
| } | |
| // prepare binary search on the right side of the index | |
| right = pp.length; | |
| break linear_scan; | |
| } | |
| //- slower code: | |
| //- if ( t < t0 || t0 === undefined ) { | |
| if ( ! ( t >= t0 ) ) { | |
| // looping? | |
| var t1global = pp[ 1 ]; | |
| if ( t < t1global ) { | |
| i1 = 2; // + 1, using the scan for the details | |
| t0 = t1global; | |
| } | |
| // linear reverse scan | |
| for ( var giveUpAt = i1 - 2; ; ) { | |
| if ( t0 === undefined ) { | |
| // before start | |
| this._cachedIndex = 0; | |
| return this.beforeStart_( 0, t, t1 ); | |
| } | |
| if ( i1 === giveUpAt ) break; // this loop | |
| t1 = t0; | |
| t0 = pp[ -- i1 - 1 ]; | |
| if ( t >= t0 ) { | |
| // we have arrived at the sought interval | |
| break seek; | |
| } | |
| } | |
| // prepare binary search on the left side of the index | |
| right = i1; | |
| i1 = 0; | |
| break linear_scan; | |
| } | |
| // the interval is valid | |
| break validate_interval; | |
| } // linear scan | |
| // binary search | |
| while ( i1 < right ) { | |
| var mid = ( i1 + right ) >>> 1; | |
| if ( t < pp[ mid ] ) { | |
| right = mid; | |
| } else { | |
| i1 = mid + 1; | |
| } | |
| } | |
| t1 = pp[ i1 ]; | |
| t0 = pp[ i1 - 1 ]; | |
| // check boundary cases, again | |
| if ( t0 === undefined ) { | |
| this._cachedIndex = 0; | |
| return this.beforeStart_( 0, t, t1 ); | |
| } | |
| if ( t1 === undefined ) { | |
| i1 = pp.length; | |
| this._cachedIndex = i1; | |
| return this.afterEnd_( i1 - 1, t0, t ); | |
| } | |
| } // seek | |
| this._cachedIndex = i1; | |
| this.intervalChanged_( i1, t0, t1 ); | |
| } // validate_interval | |
| return this.interpolate_( i1, t0, t, t1 ); | |
| }, | |
| settings: null, // optional, subclass-specific settings structure | |
| // Note: The indirection allows central control of many interpolants. | |
| // --- Protected interface | |
| DefaultSettings_: {}, | |
| getSettings_: function () { | |
| return this.settings || this.DefaultSettings_; | |
| }, | |
| copySampleValue_: function ( index ) { | |
| // copies a sample value to the result buffer | |
| var result = this.resultBuffer, | |
| values = this.sampleValues, | |
| stride = this.valueSize, | |
| offset = index * stride; | |
| for ( var i = 0; i !== stride; ++ i ) { | |
| result[ i ] = values[ offset + i ]; | |
| } | |
| return result; | |
| }, | |
| // Template methods for derived classes: | |
| interpolate_: function ( /* i1, t0, t, t1 */ ) { | |
| throw new Error( 'call to abstract method' ); | |
| // implementations shall return this.resultBuffer | |
| }, | |
| intervalChanged_: function ( /* i1, t0, t1 */ ) { | |
| // empty | |
| } | |
| } ); | |
| //!\ DECLARE ALIAS AFTER assign prototype ! | |
| Object.assign( Interpolant.prototype, { | |
| //( 0, t, t0 ), returns this.resultBuffer | |
| beforeStart_: Interpolant.prototype.copySampleValue_, | |
| //( N-1, tN-1, t ), returns this.resultBuffer | |
| afterEnd_: Interpolant.prototype.copySampleValue_, | |
| } ); | |
| export { Interpolant }; | |