浏览代码

Updated builds.

Mr.doob 7 年之前
父节点
当前提交
d4d514a140
共有 3 个文件被更改,包括 1421 次插入1415 次删除
  1. 616 613
      build/three.js
  2. 189 189
      build/three.min.js
  3. 616 613
      build/three.module.js

+ 616 - 613
build/three.js

@@ -31480,168 +31480,69 @@
 	} );
 	} );
 
 
 	/**
 	/**
-	 * @author tschw
+	 *
+	 * A Track that interpolates Strings
+	 *
+	 *
 	 * @author Ben Houston / http://clara.io/
 	 * @author Ben Houston / http://clara.io/
 	 * @author David Sarno / http://lighthaus.us/
 	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
 	 */
 	 */
 
 
-	var AnimationUtils = {
-
-		// same as Array.prototype.slice, but also works on typed arrays
-		arraySlice: function ( array, from, to ) {
-
-			if ( AnimationUtils.isTypedArray( array ) ) {
-
-				// in ios9 array.subarray(from, undefined) will return empty array
-				// but array.subarray(from) or array.subarray(from, len) is correct
-				return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
-
-			}
-
-			return array.slice( from, to );
-
-		},
-
-		// converts an array to a specific type
-		convertArray: function ( array, type, forceClone ) {
-
-			if ( ! array || // let 'undefined' and 'null' pass
-					! forceClone && array.constructor === type ) return array;
-
-			if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
-
-				return new type( array ); // create typed array
-
-			}
-
-			return Array.prototype.slice.call( array ); // create Array
-
-		},
-
-		isTypedArray: function ( object ) {
-
-			return ArrayBuffer.isView( object ) &&
-					! ( object instanceof DataView );
-
-		},
-
-		// returns an array by which times and values can be sorted
-		getKeyframeOrder: function ( times ) {
-
-			function compareTime( i, j ) {
-
-				return times[ i ] - times[ j ];
-
-			}
-
-			var n = times.length;
-			var result = new Array( n );
-			for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
-
-			result.sort( compareTime );
-
-			return result;
-
-		},
-
-		// uses the array previously returned by 'getKeyframeOrder' to sort data
-		sortedArray: function ( values, stride, order ) {
-
-			var nValues = values.length;
-			var result = new values.constructor( nValues );
-
-			for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
-
-				var srcOffset = order[ i ] * stride;
-
-				for ( var j = 0; j !== stride; ++ j ) {
-
-					result[ dstOffset ++ ] = values[ srcOffset + j ];
-
-				}
-
-			}
-
-			return result;
-
-		},
-
-		// function for parsing AOS keyframe formats
-		flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
-
-			var i = 1, key = jsonKeys[ 0 ];
-
-			while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
-
-				key = jsonKeys[ i ++ ];
-
-			}
-
-			if ( key === undefined ) return; // no data
-
-			var value = key[ valuePropertyName ];
-			if ( value === undefined ) return; // no data
-
-			if ( Array.isArray( value ) ) {
-
-				do {
-
-					value = key[ valuePropertyName ];
-
-					if ( value !== undefined ) {
-
-						times.push( key.time );
-						values.push.apply( values, value ); // push all elements
-
-					}
-
-					key = jsonKeys[ i ++ ];
-
-				} while ( key !== undefined );
-
-			} else if ( value.toArray !== undefined ) {
+	function StringKeyframeTrack( name, times, values, interpolation ) {
 
 
-				// ...assume THREE.Math-ish
+		KeyframeTrack.call( this, name, times, values, interpolation );
 
 
-				do {
+	}
 
 
-					value = key[ valuePropertyName ];
+	StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-					if ( value !== undefined ) {
+		constructor: StringKeyframeTrack,
 
 
-						times.push( key.time );
-						value.toArray( values, values.length );
+		ValueTypeName: 'string',
+		ValueBufferType: Array,
 
 
-					}
+		DefaultInterpolation: InterpolateDiscrete,
 
 
-					key = jsonKeys[ i ++ ];
+		InterpolantFactoryMethodLinear: undefined,
 
 
-				} while ( key !== undefined );
+		InterpolantFactoryMethodSmooth: undefined
 
 
-			} else {
+	} );
 
 
-				// otherwise push as-is
+	/**
+	 *
+	 * A Track of Boolean keyframe values.
+	 *
+	 *
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
+	 */
 
 
-				do {
+	function BooleanKeyframeTrack( name, times, values ) {
 
 
-					value = key[ valuePropertyName ];
+		KeyframeTrack.call( this, name, times, values );
 
 
-					if ( value !== undefined ) {
+	}
 
 
-						times.push( key.time );
-						values.push( value );
+	BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-					}
+		constructor: BooleanKeyframeTrack,
 
 
-					key = jsonKeys[ i ++ ];
+		ValueTypeName: 'bool',
+		ValueBufferType: Array,
 
 
-				} while ( key !== undefined );
+		DefaultInterpolation: InterpolateDiscrete,
 
 
-			}
+		InterpolantFactoryMethodLinear: undefined,
+		InterpolantFactoryMethodSmooth: undefined
 
 
-		}
+		// Note: Actually this track could have a optimized / compressed
+		// representation of a single value and a custom interpolant that
+		// computes "firstValue ^ isOdd( index )".
 
 
-	};
+	} );
 
 
 	/**
 	/**
 	 * Abstract base class of interpolants over parametric samples.
 	 * Abstract base class of interpolants over parametric samples.
@@ -31901,89 +31802,220 @@
 	} );
 	} );
 
 
 	/**
 	/**
-	 * 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.
+	 * Spherical linear unit quaternion interpolant.
 	 *
 	 *
 	 * @author tschw
 	 * @author tschw
 	 */
 	 */
 
 
-	function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+	function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
 
 
 		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
 		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
 
 
-		this._weightPrev = - 0;
-		this._offsetPrev = - 0;
-		this._weightNext = - 0;
-		this._offsetNext = - 0;
-
 	}
 	}
 
 
-	CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+	QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
 
 
-		constructor: CubicInterpolant,
+		constructor: QuaternionLinearInterpolant,
 
 
-		DefaultSettings_: {
+		interpolate_: function ( i1, t0, t, t1 ) {
 
 
-			endingStart: ZeroCurvatureEnding,
-			endingEnd: ZeroCurvatureEnding
+			var result = this.resultBuffer,
+				values = this.sampleValues,
+				stride = this.valueSize,
 
 
-		},
+				offset = i1 * stride,
 
 
-		intervalChanged_: function ( i1, t0, t1 ) {
+				alpha = ( t - t0 ) / ( t1 - t0 );
 
 
-			var pp = this.parameterPositions,
-				iPrev = i1 - 2,
-				iNext = i1 + 1,
+			for ( var end = offset + stride; offset !== end; offset += 4 ) {
 
 
-				tPrev = pp[ iPrev ],
-				tNext = pp[ iNext ];
+				Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
 
 
-			if ( tPrev === undefined ) {
+			}
 
 
-				switch ( this.getSettings_().endingStart ) {
+			return result;
 
 
-					case ZeroSlopeEnding:
+		}
 
 
-						// f'(t0) = 0
-						iPrev = i1;
-						tPrev = 2 * t0 - t1;
+	} );
 
 
-						break;
+	/**
+	 *
+	 * A Track of quaternion keyframe values.
+	 *
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
+	 */
 
 
-					case WrapAroundEnding:
+	function QuaternionKeyframeTrack( name, times, values, interpolation ) {
 
 
-						// use the other end of the curve
-						iPrev = pp.length - 2;
-						tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
+		KeyframeTrack.call( this, name, times, values, interpolation );
 
 
-						break;
+	}
 
 
-					default: // ZeroCurvatureEnding
+	QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-						// f''(t0) = 0 a.k.a. Natural Spline
-						iPrev = i1;
-						tPrev = t1;
+		constructor: QuaternionKeyframeTrack,
 
 
-				}
+		ValueTypeName: 'quaternion',
 
 
-			}
+		// ValueBufferType is inherited
 
 
-			if ( tNext === undefined ) {
+		DefaultInterpolation: InterpolateLinear,
 
 
-				switch ( this.getSettings_().endingEnd ) {
+		InterpolantFactoryMethodLinear: function ( result ) {
 
 
-					case ZeroSlopeEnding:
+			return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-						// f'(tN) = 0
-						iNext = i1;
-						tNext = 2 * t1 - t0;
+		},
 
 
-						break;
+		InterpolantFactoryMethodSmooth: undefined // not yet implemented
 
 
-					case WrapAroundEnding:
+	} );
+
+	/**
+	 *
+	 * A Track of keyframe values that represent color.
+	 *
+	 *
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
+	 */
+
+	function ColorKeyframeTrack( name, times, values, interpolation ) {
+
+		KeyframeTrack.call( this, name, times, values, interpolation );
+
+	}
+
+	ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
+
+		constructor: ColorKeyframeTrack,
+
+		ValueTypeName: 'color'
+
+		// ValueBufferType is inherited
+
+		// DefaultInterpolation is inherited
+
+		// Note: Very basic implementation and nothing special yet.
+		// However, this is the place for color space parameterization.
+
+	} );
+
+	/**
+	 *
+	 * A Track of numeric keyframe values.
+	 *
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
+	 */
+
+	function NumberKeyframeTrack( name, times, values, interpolation ) {
+
+		KeyframeTrack.call( this, name, times, values, interpolation );
+
+	}
+
+	NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
+
+		constructor: NumberKeyframeTrack,
+
+		ValueTypeName: 'number'
+
+		// ValueBufferType is inherited
+
+		// DefaultInterpolation is inherited
+
+	} );
+
+	/**
+	 * 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.
+	 *
+	 * @author tschw
+	 */
+
+	function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+
+		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+
+		this._weightPrev = - 0;
+		this._offsetPrev = - 0;
+		this._weightNext = - 0;
+		this._offsetNext = - 0;
+
+	}
+
+	CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+
+		constructor: CubicInterpolant,
+
+		DefaultSettings_: {
+
+			endingStart: ZeroCurvatureEnding,
+			endingEnd: ZeroCurvatureEnding
+
+		},
+
+		intervalChanged_: function ( i1, t0, t1 ) {
+
+			var pp = this.parameterPositions,
+				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
 						// use the other end of the curve
 						iNext = 1;
 						iNext = 1;
@@ -32116,745 +32148,716 @@
 
 
 	} );
 	} );
 
 
-	var KeyframeTrackPrototype;
-
-	KeyframeTrackPrototype = {
-
-		TimeBufferType: Float32Array,
-		ValueBufferType: Float32Array,
+	/**
+	 * @author tschw
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 */
 
 
-		DefaultInterpolation: InterpolateLinear,
+	var AnimationUtils = {
 
 
-		InterpolantFactoryMethodDiscrete: function ( result ) {
+		// same as Array.prototype.slice, but also works on typed arrays
+		arraySlice: function ( array, from, to ) {
 
 
-			return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
+			if ( AnimationUtils.isTypedArray( array ) ) {
 
 
-		},
+				// in ios9 array.subarray(from, undefined) will return empty array
+				// but array.subarray(from) or array.subarray(from, len) is correct
+				return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
 
 
-		InterpolantFactoryMethodLinear: function ( result ) {
+			}
 
 
-			return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
+			return array.slice( from, to );
 
 
 		},
 		},
 
 
-		InterpolantFactoryMethodSmooth: function ( result ) {
+		// converts an array to a specific type
+		convertArray: function ( array, type, forceClone ) {
 
 
-			return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
+			if ( ! array || // let 'undefined' and 'null' pass
+					! forceClone && array.constructor === type ) return array;
 
 
-		},
+			if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
 
 
-		setInterpolation: function ( interpolation ) {
+				return new type( array ); // create typed array
 
 
-			var factoryMethod;
+			}
 
 
-			switch ( interpolation ) {
+			return Array.prototype.slice.call( array ); // create Array
 
 
-				case InterpolateDiscrete:
+		},
 
 
-					factoryMethod = this.InterpolantFactoryMethodDiscrete;
+		isTypedArray: function ( object ) {
 
 
-					break;
+			return ArrayBuffer.isView( object ) &&
+					! ( object instanceof DataView );
 
 
-				case InterpolateLinear:
+		},
 
 
-					factoryMethod = this.InterpolantFactoryMethodLinear;
+		// returns an array by which times and values can be sorted
+		getKeyframeOrder: function ( times ) {
 
 
-					break;
+			function compareTime( i, j ) {
 
 
-				case InterpolateSmooth:
+				return times[ i ] - times[ j ];
 
 
-					factoryMethod = this.InterpolantFactoryMethodSmooth;
+			}
 
 
-					break;
+			var n = times.length;
+			var result = new Array( n );
+			for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
 
 
-			}
+			result.sort( compareTime );
 
 
-			if ( factoryMethod === undefined ) {
+			return result;
 
 
-				var message = "unsupported interpolation for " +
-						this.ValueTypeName + " keyframe track named " + this.name;
+		},
 
 
-				if ( this.createInterpolant === undefined ) {
+		// uses the array previously returned by 'getKeyframeOrder' to sort data
+		sortedArray: function ( values, stride, order ) {
 
 
-					// fall back to default, unless the default itself is messed up
-					if ( interpolation !== this.DefaultInterpolation ) {
+			var nValues = values.length;
+			var result = new values.constructor( nValues );
 
 
-						this.setInterpolation( this.DefaultInterpolation );
+			for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
 
 
-					} else {
+				var srcOffset = order[ i ] * stride;
 
 
-						throw new Error( message ); // fatal, in this case
+				for ( var j = 0; j !== stride; ++ j ) {
 
 
-					}
+					result[ dstOffset ++ ] = values[ srcOffset + j ];
 
 
 				}
 				}
 
 
-				console.warn( 'THREE.KeyframeTrackPrototype:', message );
-				return;
-
 			}
 			}
 
 
-			this.createInterpolant = factoryMethod;
+			return result;
 
 
 		},
 		},
 
 
-		getInterpolation: function () {
+		// function for parsing AOS keyframe formats
+		flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
 
 
-			switch ( this.createInterpolant ) {
+			var i = 1, key = jsonKeys[ 0 ];
 
 
-				case this.InterpolantFactoryMethodDiscrete:
+			while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
 
 
-					return InterpolateDiscrete;
+				key = jsonKeys[ i ++ ];
 
 
-				case this.InterpolantFactoryMethodLinear:
+			}
 
 
-					return InterpolateLinear;
+			if ( key === undefined ) return; // no data
 
 
-				case this.InterpolantFactoryMethodSmooth:
+			var value = key[ valuePropertyName ];
+			if ( value === undefined ) return; // no data
 
 
-					return InterpolateSmooth;
+			if ( Array.isArray( value ) ) {
 
 
-			}
+				do {
 
 
-		},
+					value = key[ valuePropertyName ];
 
 
-		getValueSize: function () {
+					if ( value !== undefined ) {
 
 
-			return this.values.length / this.times.length;
+						times.push( key.time );
+						values.push.apply( values, value ); // push all elements
 
 
-		},
+					}
 
 
-		// move all keyframes either forwards or backwards in time
-		shift: function ( timeOffset ) {
+					key = jsonKeys[ i ++ ];
 
 
-			if ( timeOffset !== 0.0 ) {
+				} while ( key !== undefined );
 
 
-				var times = this.times;
+			} else if ( value.toArray !== undefined ) {
 
 
-				for ( var i = 0, n = times.length; i !== n; ++ i ) {
+				// ...assume THREE.Math-ish
 
 
-					times[ i ] += timeOffset;
+				do {
 
 
-				}
+					value = key[ valuePropertyName ];
 
 
-			}
+					if ( value !== undefined ) {
 
 
-			return this;
+						times.push( key.time );
+						value.toArray( values, values.length );
 
 
-		},
+					}
 
 
-		// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
-		scale: function ( timeScale ) {
+					key = jsonKeys[ i ++ ];
 
 
-			if ( timeScale !== 1.0 ) {
+				} while ( key !== undefined );
 
 
-				var times = this.times;
+			} else {
 
 
-				for ( var i = 0, n = times.length; i !== n; ++ i ) {
+				// otherwise push as-is
 
 
-					times[ i ] *= timeScale;
+				do {
 
 
-				}
+					value = key[ valuePropertyName ];
 
 
-			}
+					if ( value !== undefined ) {
 
 
-			return this;
+						times.push( key.time );
+						values.push( value );
 
 
-		},
+					}
 
 
-		// removes keyframes before and after animation without changing any values within the range [startTime, endTime].
-		// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
-		trim: function ( startTime, endTime ) {
+					key = jsonKeys[ i ++ ];
 
 
-			var times = this.times,
-				nKeys = times.length,
-				from = 0,
-				to = nKeys - 1;
+				} while ( key !== undefined );
 
 
-			while ( from !== nKeys && times[ from ] < startTime ) ++ from;
-			while ( to !== - 1 && times[ to ] > endTime ) -- to;
+			}
 
 
-			++ to; // inclusive -> exclusive bound
+		}
 
 
-			if ( from !== 0 || to !== nKeys ) {
+	};
 
 
-				// empty tracks are forbidden, so keep at least one keyframe
-				if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
+	/**
+	 *
+	 * A timed sequence of keyframes for a specific property.
+	 *
+	 *
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
+	 */
 
 
-				var stride = this.getValueSize();
-				this.times = AnimationUtils.arraySlice( times, from, to );
-				this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
+	function KeyframeTrack( name, times, values, interpolation ) {
 
 
-			}
+		if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );
+		if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );
 
 
-			return this;
+		this.name = name;
 
 
-		},
+		this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
+		this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
 
 
-		// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
-		validate: function () {
+		this.setInterpolation( interpolation || this.DefaultInterpolation );
 
 
-			var valid = true;
+		this.validate();
+		this.optimize();
 
 
-			var valueSize = this.getValueSize();
-			if ( valueSize - Math.floor( valueSize ) !== 0 ) {
+	}
 
 
-				console.error( 'THREE.KeyframeTrackPrototype: Invalid value size in track.', this );
-				valid = false;
+	// Static methods:
 
 
-			}
+	Object.assign( KeyframeTrack, {
 
 
-			var times = this.times,
-				values = this.values,
+		// Serialization (in static context, because of constructor invocation
+		// and automatic invocation of .toJSON):
 
 
-				nKeys = times.length;
+		parse: function ( json ) {
 
 
-			if ( nKeys === 0 ) {
+			if ( json.type === undefined ) {
 
 
-				console.error( 'THREE.KeyframeTrackPrototype: Track is empty.', this );
-				valid = false;
+				throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );
 
 
 			}
 			}
 
 
-			var prevTime = null;
+			var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
 
 
-			for ( var i = 0; i !== nKeys; i ++ ) {
+			if ( json.times === undefined ) {
 
 
-				var currTime = times[ i ];
+				var times = [], values = [];
 
 
-				if ( typeof currTime === 'number' && isNaN( currTime ) ) {
+				AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
 
 
-					console.error( 'THREE.KeyframeTrackPrototype: Time is not a valid number.', this, i, currTime );
-					valid = false;
-					break;
+				json.times = times;
+				json.values = values;
 
 
-				}
+			}
 
 
-				if ( prevTime !== null && prevTime > currTime ) {
+			// derived classes can define a static parse method
+			if ( trackType.parse !== undefined ) {
 
 
-					console.error( 'THREE.KeyframeTrackPrototype: Out of order keys.', this, i, currTime, prevTime );
-					valid = false;
-					break;
+				return trackType.parse( json );
 
 
-				}
+			} else {
 
 
-				prevTime = currTime;
+				// by default, we assume a constructor compatible with the base
+				return new trackType( json.name, json.times, json.values, json.interpolation );
 
 
 			}
 			}
 
 
-			if ( values !== undefined ) {
+		},
 
 
-				if ( AnimationUtils.isTypedArray( values ) ) {
+		toJSON: function ( track ) {
 
 
-					for ( var i = 0, n = values.length; i !== n; ++ i ) {
+			var trackType = track.constructor;
 
 
-						var value = values[ i ];
+			var json;
 
 
-						if ( isNaN( value ) ) {
+			// derived classes can define a static toJSON method
+			if ( trackType.toJSON !== undefined ) {
 
 
-							console.error( 'THREE.KeyframeTrackPrototype: Value is not a valid number.', this, i, value );
-							valid = false;
-							break;
+				json = trackType.toJSON( track );
 
 
-						}
+			} else {
 
 
-					}
+				// by default, we assume the data can be serialized as-is
+				json = {
 
 
-				}
+					'name': track.name,
+					'times': AnimationUtils.convertArray( track.times, Array ),
+					'values': AnimationUtils.convertArray( track.values, Array )
 
 
-			}
+				};
 
 
-			return valid;
+				var interpolation = track.getInterpolation();
 
 
-		},
+				if ( interpolation !== track.DefaultInterpolation ) {
 
 
-		// removes equivalent sequential keys as common in morph target sequences
-		// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
-		optimize: function () {
+					json.interpolation = interpolation;
 
 
-			var times = this.times,
-				values = this.values,
-				stride = this.getValueSize(),
+				}
 
 
-				smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
+			}
 
 
-				writeIndex = 1,
-				lastIndex = times.length - 1;
+			json.type = track.ValueTypeName; // mandatory
 
 
-			for ( var i = 1; i < lastIndex; ++ i ) {
+			return json;
 
 
-				var keep = false;
+		},
 
 
-				var time = times[ i ];
-				var timeNext = times[ i + 1 ];
+		_getTrackTypeForValueTypeName: function ( typeName ) {
 
 
-				// remove adjacent keyframes scheduled at the same time
+			switch ( typeName.toLowerCase() ) {
 
 
-				if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
+				case 'scalar':
+				case 'double':
+				case 'float':
+				case 'number':
+				case 'integer':
 
 
-					if ( ! smoothInterpolation ) {
+					return NumberKeyframeTrack;
 
 
-						// remove unnecessary keyframes same as their neighbors
+				case 'vector':
+				case 'vector2':
+				case 'vector3':
+				case 'vector4':
 
 
-						var offset = i * stride,
-							offsetP = offset - stride,
-							offsetN = offset + stride;
+					return VectorKeyframeTrack;
 
 
-						for ( var j = 0; j !== stride; ++ j ) {
+				case 'color':
 
 
-							var value = values[ offset + j ];
+					return ColorKeyframeTrack;
 
 
-							if ( value !== values[ offsetP + j ] ||
-									value !== values[ offsetN + j ] ) {
+				case 'quaternion':
 
 
-								keep = true;
-								break;
+					return QuaternionKeyframeTrack;
 
 
-							}
+				case 'bool':
+				case 'boolean':
 
 
-						}
+					return BooleanKeyframeTrack;
 
 
-					} else keep = true;
+				case 'string':
 
 
-				}
+					return StringKeyframeTrack;
 
 
-				// in-place compaction
+			}
 
 
-				if ( keep ) {
+			throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );
 
 
-					if ( i !== writeIndex ) {
+		}
 
 
-						times[ writeIndex ] = times[ i ];
+	} );
 
 
-						var readOffset = i * stride,
-							writeOffset = writeIndex * stride;
+	Object.assign( KeyframeTrack.prototype, {
 
 
-						for ( var j = 0; j !== stride; ++ j )
+		constructor: KeyframeTrack,
 
 
-							values[ writeOffset + j ] = values[ readOffset + j ];
+		TimeBufferType: Float32Array,
 
 
-					}
+		ValueBufferType: Float32Array,
 
 
-					++ writeIndex;
+		DefaultInterpolation: InterpolateLinear,
 
 
-				}
+		InterpolantFactoryMethodDiscrete: function ( result ) {
 
 
-			}
+			return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-			// flush last keyframe (compaction looks ahead)
+		},
 
 
-			if ( lastIndex > 0 ) {
+		InterpolantFactoryMethodLinear: function ( result ) {
 
 
-				times[ writeIndex ] = times[ lastIndex ];
+			return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-				for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j )
+		},
 
 
-					values[ writeOffset + j ] = values[ readOffset + j ];
+		InterpolantFactoryMethodSmooth: function ( result ) {
 
 
-				++ writeIndex;
+			return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-			}
+		},
 
 
-			if ( writeIndex !== times.length ) {
+		setInterpolation: function ( interpolation ) {
 
 
-				this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
-				this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
+			var factoryMethod;
 
 
-			}
+			switch ( interpolation ) {
 
 
-			return this;
+				case InterpolateDiscrete:
 
 
-		}
+					factoryMethod = this.InterpolantFactoryMethodDiscrete;
 
 
-	};
+					break;
 
 
-	function KeyframeTrackConstructor( name, times, values, interpolation ) {
+				case InterpolateLinear:
 
 
-		if ( name === undefined ) throw new Error( 'track name is undefined' );
+					factoryMethod = this.InterpolantFactoryMethodLinear;
 
 
-		if ( times === undefined || times.length === 0 ) {
+					break;
 
 
-			throw new Error( 'no keyframes in track named ' + name );
+				case InterpolateSmooth:
 
 
-		}
+					factoryMethod = this.InterpolantFactoryMethodSmooth;
 
 
-		this.name = name;
+					break;
 
 
-		this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
-		this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
+			}
 
 
-		this.setInterpolation( interpolation || this.DefaultInterpolation );
+			if ( factoryMethod === undefined ) {
 
 
-		this.validate();
-		this.optimize();
+				var message = "unsupported interpolation for " +
+					this.ValueTypeName + " keyframe track named " + this.name;
 
 
-	}
+				if ( this.createInterpolant === undefined ) {
 
 
-	/**
-	 *
-	 * A Track of vectored keyframe values.
-	 *
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+					// fall back to default, unless the default itself is messed up
+					if ( interpolation !== this.DefaultInterpolation ) {
 
 
-	function VectorKeyframeTrack( name, times, values, interpolation ) {
+						this.setInterpolation( this.DefaultInterpolation );
 
 
-		KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+					} else {
 
 
-	}
+						throw new Error( message ); // fatal, in this case
 
 
-	VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+					}
 
 
-		constructor: VectorKeyframeTrack,
+				}
 
 
-		ValueTypeName: 'vector'
+				console.warn( 'THREE.KeyframeTrack:', message );
+				return;
 
 
-		// ValueBufferType is inherited
+			}
 
 
-		// DefaultInterpolation is inherited
+			this.createInterpolant = factoryMethod;
 
 
-	} );
+		},
 
 
-	/**
-	 * Spherical linear unit quaternion interpolant.
-	 *
-	 * @author tschw
-	 */
+		getInterpolation: function () {
 
 
-	function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+			switch ( this.createInterpolant ) {
 
 
-		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+				case this.InterpolantFactoryMethodDiscrete:
 
 
-	}
+					return InterpolateDiscrete;
 
 
-	QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+				case this.InterpolantFactoryMethodLinear:
 
 
-		constructor: QuaternionLinearInterpolant,
+					return InterpolateLinear;
 
 
-		interpolate_: function ( i1, t0, t, t1 ) {
+				case this.InterpolantFactoryMethodSmooth:
 
 
-			var result = this.resultBuffer,
-				values = this.sampleValues,
-				stride = this.valueSize,
+					return InterpolateSmooth;
 
 
-				offset = i1 * stride,
+			}
 
 
-				alpha = ( t - t0 ) / ( t1 - t0 );
+		},
 
 
-			for ( var end = offset + stride; offset !== end; offset += 4 ) {
+		getValueSize: function () {
 
 
-				Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
+			return this.values.length / this.times.length;
 
 
-			}
+		},
 
 
-			return result;
+		// move all keyframes either forwards or backwards in time
+		shift: function ( timeOffset ) {
 
 
-		}
+			if ( timeOffset !== 0.0 ) {
 
 
-	} );
+				var times = this.times;
 
 
-	/**
-	 *
-	 * A Track of quaternion keyframe values.
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+				for ( var i = 0, n = times.length; i !== n; ++ i ) {
 
 
-	function QuaternionKeyframeTrack( name, times, values, interpolation ) {
+					times[ i ] += timeOffset;
 
 
-		KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+				}
 
 
-	}
+			}
 
 
-	QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+			return this;
 
 
-		constructor: QuaternionKeyframeTrack,
+		},
 
 
-		ValueTypeName: 'quaternion',
+		// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
+		scale: function ( timeScale ) {
 
 
-		// ValueBufferType is inherited
+			if ( timeScale !== 1.0 ) {
 
 
-		DefaultInterpolation: InterpolateLinear,
+				var times = this.times;
 
 
-		InterpolantFactoryMethodLinear: function ( result ) {
+				for ( var i = 0, n = times.length; i !== n; ++ i ) {
 
 
-			return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
+					times[ i ] *= timeScale;
 
 
-		},
+				}
 
 
-		InterpolantFactoryMethodSmooth: undefined // not yet implemented
+			}
 
 
-	} );
+			return this;
 
 
-	/**
-	 *
-	 * A Track of numeric keyframe values.
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+		},
 
 
-	function NumberKeyframeTrack( name, times, values, interpolation ) {
+		// removes keyframes before and after animation without changing any values within the range [startTime, endTime].
+		// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
+		trim: function ( startTime, endTime ) {
 
 
-		KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+			var times = this.times,
+				nKeys = times.length,
+				from = 0,
+				to = nKeys - 1;
 
 
-	}
+			while ( from !== nKeys && times[ from ] < startTime ) {
 
 
-	NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+				++ from;
 
 
-		constructor: NumberKeyframeTrack,
+			}
 
 
-		ValueTypeName: 'number'
+			while ( to !== - 1 && times[ to ] > endTime ) {
 
 
-		// ValueBufferType is inherited
+				-- to;
 
 
-		// DefaultInterpolation is inherited
+			}
 
 
-	} );
+			++ to; // inclusive -> exclusive bound
 
 
-	/**
-	 *
-	 * A Track that interpolates Strings
-	 *
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+			if ( from !== 0 || to !== nKeys ) {
 
 
-	function StringKeyframeTrack( name, times, values, interpolation ) {
+				// empty tracks are forbidden, so keep at least one keyframe
+				if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
 
 
-		KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+				var stride = this.getValueSize();
+				this.times = AnimationUtils.arraySlice( times, from, to );
+				this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
 
 
-	}
+			}
 
 
-	StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+			return this;
 
 
-		constructor: StringKeyframeTrack,
+		},
 
 
-		ValueTypeName: 'string',
-		ValueBufferType: Array,
+		// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
+		validate: function () {
 
 
-		DefaultInterpolation: InterpolateDiscrete,
+			var valid = true;
 
 
-		InterpolantFactoryMethodLinear: undefined,
+			var valueSize = this.getValueSize();
+			if ( valueSize - Math.floor( valueSize ) !== 0 ) {
 
 
-		InterpolantFactoryMethodSmooth: undefined
+				console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );
+				valid = false;
 
 
-	} );
+			}
 
 
-	/**
-	 *
-	 * A Track of Boolean keyframe values.
-	 *
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+			var times = this.times,
+				values = this.values,
 
 
-	function BooleanKeyframeTrack( name, times, values ) {
+				nKeys = times.length;
 
 
-		KeyframeTrackConstructor.call( this, name, times, values );
+			if ( nKeys === 0 ) {
 
 
-	}
+				console.error( 'THREE.KeyframeTrack: Track is empty.', this );
+				valid = false;
 
 
-	BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+			}
 
 
-		constructor: BooleanKeyframeTrack,
+			var prevTime = null;
 
 
-		ValueTypeName: 'bool',
-		ValueBufferType: Array,
+			for ( var i = 0; i !== nKeys; i ++ ) {
 
 
-		DefaultInterpolation: InterpolateDiscrete,
+				var currTime = times[ i ];
 
 
-		InterpolantFactoryMethodLinear: undefined,
-		InterpolantFactoryMethodSmooth: undefined
+				if ( typeof currTime === 'number' && isNaN( currTime ) ) {
 
 
-		// Note: Actually this track could have a optimized / compressed
-		// representation of a single value and a custom interpolant that
-		// computes "firstValue ^ isOdd( index )".
+					console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );
+					valid = false;
+					break;
 
 
-	} );
+				}
 
 
-	/**
-	 *
-	 * A Track of keyframe values that represent color.
-	 *
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+				if ( prevTime !== null && prevTime > currTime ) {
 
 
-	function ColorKeyframeTrack( name, times, values, interpolation ) {
+					console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
+					valid = false;
+					break;
 
 
-		KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+				}
 
 
-	}
+				prevTime = currTime;
 
 
-	ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+			}
 
 
-		constructor: ColorKeyframeTrack,
+			if ( values !== undefined ) {
 
 
-		ValueTypeName: 'color'
+				if ( AnimationUtils.isTypedArray( values ) ) {
 
 
-		// ValueBufferType is inherited
+					for ( var i = 0, n = values.length; i !== n; ++ i ) {
 
 
-		// DefaultInterpolation is inherited
+						var value = values[ i ];
 
 
+						if ( isNaN( value ) ) {
 
 
-		// Note: Very basic implementation and nothing special yet.
-		// However, this is the place for color space parameterization.
+							console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );
+							valid = false;
+							break;
 
 
-	} );
+						}
 
 
-	/**
-	 *
-	 * A timed sequence of keyframes for a specific property.
-	 *
-	 *
-	 * @author Ben Houston / http://clara.io/
-	 * @author David Sarno / http://lighthaus.us/
-	 * @author tschw
-	 */
+					}
 
 
-	function KeyframeTrack( name, times, values, interpolation ) {
+				}
 
 
-		KeyframeTrackConstructor.apply( this, name, times, values, interpolation );
+			}
 
 
-	}
+			return valid;
 
 
-	KeyframeTrack.prototype = KeyframeTrackPrototype;
-	KeyframeTrackPrototype.constructor = KeyframeTrack;
+		},
 
 
-	// Static methods:
+		// removes equivalent sequential keys as common in morph target sequences
+		// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
+		optimize: function () {
 
 
-	Object.assign( KeyframeTrack, {
+			var times = this.times,
+				values = this.values,
+				stride = this.getValueSize(),
 
 
-		// Serialization (in static context, because of constructor invocation
-		// and automatic invocation of .toJSON):
+				smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
 
 
-		parse: function ( json ) {
+				writeIndex = 1,
+				lastIndex = times.length - 1;
 
 
-			if ( json.type === undefined ) {
+			for ( var i = 1; i < lastIndex; ++ i ) {
 
 
-				throw new Error( 'track type undefined, can not parse' );
+				var keep = false;
 
 
-			}
+				var time = times[ i ];
+				var timeNext = times[ i + 1 ];
 
 
-			var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
+				// remove adjacent keyframes scheduled at the same time
 
 
-			if ( json.times === undefined ) {
+				if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
 
 
-				var times = [], values = [];
+					if ( ! smoothInterpolation ) {
 
 
-				AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
+						// remove unnecessary keyframes same as their neighbors
 
 
-				json.times = times;
-				json.values = values;
+						var offset = i * stride,
+							offsetP = offset - stride,
+							offsetN = offset + stride;
 
 
-			}
+						for ( var j = 0; j !== stride; ++ j ) {
 
 
-			// derived classes can define a static parse method
-			if ( trackType.parse !== undefined ) {
+							var value = values[ offset + j ];
 
 
-				return trackType.parse( json );
+							if ( value !== values[ offsetP + j ] ||
+								value !== values[ offsetN + j ] ) {
 
 
-			} else {
+								keep = true;
+								break;
 
 
-				// by default, we assume a constructor compatible with the base
-				return new trackType( json.name, json.times, json.values, json.interpolation );
+							}
 
 
-			}
+						}
 
 
-		},
+					} else {
 
 
-		toJSON: function ( track ) {
+						keep = true;
 
 
-			var trackType = track.constructor;
+					}
 
 
-			var json;
+				}
 
 
-			// derived classes can define a static toJSON method
-			if ( trackType.toJSON !== undefined ) {
+				// in-place compaction
 
 
-				json = trackType.toJSON( track );
+				if ( keep ) {
 
 
-			} else {
+					if ( i !== writeIndex ) {
 
 
-				// by default, we assume the data can be serialized as-is
-				json = {
+						times[ writeIndex ] = times[ i ];
 
 
-					'name': track.name,
-					'times': AnimationUtils.convertArray( track.times, Array ),
-					'values': AnimationUtils.convertArray( track.values, Array )
+						var readOffset = i * stride,
+							writeOffset = writeIndex * stride;
 
 
-				};
+						for ( var j = 0; j !== stride; ++ j ) {
 
 
-				var interpolation = track.getInterpolation();
+							values[ writeOffset + j ] = values[ readOffset + j ];
 
 
-				if ( interpolation !== track.DefaultInterpolation ) {
+						}
 
 
-					json.interpolation = interpolation;
+					}
+
+					++ writeIndex;
 
 
 				}
 				}
 
 
 			}
 			}
 
 
-			json.type = track.ValueTypeName; // mandatory
+			// flush last keyframe (compaction looks ahead)
 
 
-			return json;
+			if ( lastIndex > 0 ) {
 
 
-		},
+				times[ writeIndex ] = times[ lastIndex ];
 
 
-		_getTrackTypeForValueTypeName: function ( typeName ) {
+				for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
 
 
-			switch ( typeName.toLowerCase() ) {
+					values[ writeOffset + j ] = values[ readOffset + j ];
 
 
-				case 'scalar':
-				case 'double':
-				case 'float':
-				case 'number':
-				case 'integer':
+				}
 
 
-					return NumberKeyframeTrack;
+				++ writeIndex;
 
 
-				case 'vector':
-				case 'vector2':
-				case 'vector3':
-				case 'vector4':
+			}
 
 
-					return VectorKeyframeTrack;
+			if ( writeIndex !== times.length ) {
 
 
-				case 'color':
+				this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
+				this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
 
 
-					return ColorKeyframeTrack;
+			}
 
 
-				case 'quaternion':
+			return this;
 
 
-					return QuaternionKeyframeTrack;
+		}
 
 
-				case 'bool':
-				case 'boolean':
+	} );
 
 
-					return BooleanKeyframeTrack;
+	/**
+	 *
+	 * A Track of vectored keyframe values.
+	 *
+	 *
+	 * @author Ben Houston / http://clara.io/
+	 * @author David Sarno / http://lighthaus.us/
+	 * @author tschw
+	 */
 
 
-				case 'string':
+	function VectorKeyframeTrack( name, times, values, interpolation ) {
 
 
-					return StringKeyframeTrack;
+		KeyframeTrack.call( this, name, times, values, interpolation );
 
 
-			}
+	}
 
 
-			throw new Error( 'Unsupported typeName: ' + typeName );
+	VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-		}
+		constructor: VectorKeyframeTrack,
+
+		ValueTypeName: 'vector'
+
+		// ValueBufferType is inherited
+
+		// DefaultInterpolation is inherited
 
 
 	} );
 	} );
 
 

文件差异内容过多而无法显示
+ 189 - 189
build/three.min.js


+ 616 - 613
build/three.module.js

@@ -31474,168 +31474,69 @@ RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), {
 } );
 } );
 
 
 /**
 /**
- * @author tschw
+ *
+ * A Track that interpolates Strings
+ *
+ *
  * @author Ben Houston / http://clara.io/
  * @author Ben Houston / http://clara.io/
  * @author David Sarno / http://lighthaus.us/
  * @author David Sarno / http://lighthaus.us/
+ * @author tschw
  */
  */
 
 
-var AnimationUtils = {
-
-	// same as Array.prototype.slice, but also works on typed arrays
-	arraySlice: function ( array, from, to ) {
-
-		if ( AnimationUtils.isTypedArray( array ) ) {
-
-			// in ios9 array.subarray(from, undefined) will return empty array
-			// but array.subarray(from) or array.subarray(from, len) is correct
-			return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
-
-		}
-
-		return array.slice( from, to );
-
-	},
-
-	// converts an array to a specific type
-	convertArray: function ( array, type, forceClone ) {
-
-		if ( ! array || // let 'undefined' and 'null' pass
-				! forceClone && array.constructor === type ) return array;
-
-		if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
-
-			return new type( array ); // create typed array
-
-		}
-
-		return Array.prototype.slice.call( array ); // create Array
-
-	},
-
-	isTypedArray: function ( object ) {
-
-		return ArrayBuffer.isView( object ) &&
-				! ( object instanceof DataView );
-
-	},
-
-	// returns an array by which times and values can be sorted
-	getKeyframeOrder: function ( times ) {
-
-		function compareTime( i, j ) {
-
-			return times[ i ] - times[ j ];
-
-		}
-
-		var n = times.length;
-		var result = new Array( n );
-		for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
-
-		result.sort( compareTime );
-
-		return result;
-
-	},
-
-	// uses the array previously returned by 'getKeyframeOrder' to sort data
-	sortedArray: function ( values, stride, order ) {
-
-		var nValues = values.length;
-		var result = new values.constructor( nValues );
-
-		for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
-
-			var srcOffset = order[ i ] * stride;
-
-			for ( var j = 0; j !== stride; ++ j ) {
-
-				result[ dstOffset ++ ] = values[ srcOffset + j ];
-
-			}
-
-		}
-
-		return result;
-
-	},
-
-	// function for parsing AOS keyframe formats
-	flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
-
-		var i = 1, key = jsonKeys[ 0 ];
-
-		while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
-
-			key = jsonKeys[ i ++ ];
-
-		}
-
-		if ( key === undefined ) return; // no data
-
-		var value = key[ valuePropertyName ];
-		if ( value === undefined ) return; // no data
-
-		if ( Array.isArray( value ) ) {
-
-			do {
-
-				value = key[ valuePropertyName ];
-
-				if ( value !== undefined ) {
-
-					times.push( key.time );
-					values.push.apply( values, value ); // push all elements
-
-				}
-
-				key = jsonKeys[ i ++ ];
-
-			} while ( key !== undefined );
-
-		} else if ( value.toArray !== undefined ) {
+function StringKeyframeTrack( name, times, values, interpolation ) {
 
 
-			// ...assume THREE.Math-ish
+	KeyframeTrack.call( this, name, times, values, interpolation );
 
 
-			do {
+}
 
 
-				value = key[ valuePropertyName ];
+StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-				if ( value !== undefined ) {
+	constructor: StringKeyframeTrack,
 
 
-					times.push( key.time );
-					value.toArray( values, values.length );
+	ValueTypeName: 'string',
+	ValueBufferType: Array,
 
 
-				}
+	DefaultInterpolation: InterpolateDiscrete,
 
 
-				key = jsonKeys[ i ++ ];
+	InterpolantFactoryMethodLinear: undefined,
 
 
-			} while ( key !== undefined );
+	InterpolantFactoryMethodSmooth: undefined
 
 
-		} else {
+} );
 
 
-			// otherwise push as-is
+/**
+ *
+ * A Track of Boolean keyframe values.
+ *
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ * @author tschw
+ */
 
 
-			do {
+function BooleanKeyframeTrack( name, times, values ) {
 
 
-				value = key[ valuePropertyName ];
+	KeyframeTrack.call( this, name, times, values );
 
 
-				if ( value !== undefined ) {
+}
 
 
-					times.push( key.time );
-					values.push( value );
+BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-				}
+	constructor: BooleanKeyframeTrack,
 
 
-				key = jsonKeys[ i ++ ];
+	ValueTypeName: 'bool',
+	ValueBufferType: Array,
 
 
-			} while ( key !== undefined );
+	DefaultInterpolation: InterpolateDiscrete,
 
 
-		}
+	InterpolantFactoryMethodLinear: undefined,
+	InterpolantFactoryMethodSmooth: undefined
 
 
-	}
+	// Note: Actually this track could have a optimized / compressed
+	// representation of a single value and a custom interpolant that
+	// computes "firstValue ^ isOdd( index )".
 
 
-};
+} );
 
 
 /**
 /**
  * Abstract base class of interpolants over parametric samples.
  * Abstract base class of interpolants over parametric samples.
@@ -31895,89 +31796,220 @@ Object.assign( Interpolant.prototype, {
 } );
 } );
 
 
 /**
 /**
- * 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.
+ * Spherical linear unit quaternion interpolant.
  *
  *
  * @author tschw
  * @author tschw
  */
  */
 
 
-function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
 
 
 	Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
 	Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
 
 
-	this._weightPrev = - 0;
-	this._offsetPrev = - 0;
-	this._weightNext = - 0;
-	this._offsetNext = - 0;
-
 }
 }
 
 
-CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
 
 
-	constructor: CubicInterpolant,
+	constructor: QuaternionLinearInterpolant,
 
 
-	DefaultSettings_: {
+	interpolate_: function ( i1, t0, t, t1 ) {
 
 
-		endingStart: ZeroCurvatureEnding,
-		endingEnd: ZeroCurvatureEnding
+		var result = this.resultBuffer,
+			values = this.sampleValues,
+			stride = this.valueSize,
 
 
-	},
+			offset = i1 * stride,
 
 
-	intervalChanged_: function ( i1, t0, t1 ) {
+			alpha = ( t - t0 ) / ( t1 - t0 );
 
 
-		var pp = this.parameterPositions,
-			iPrev = i1 - 2,
-			iNext = i1 + 1,
+		for ( var end = offset + stride; offset !== end; offset += 4 ) {
 
 
-			tPrev = pp[ iPrev ],
-			tNext = pp[ iNext ];
+			Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
 
 
-		if ( tPrev === undefined ) {
+		}
 
 
-			switch ( this.getSettings_().endingStart ) {
+		return result;
 
 
-				case ZeroSlopeEnding:
+	}
 
 
-					// f'(t0) = 0
-					iPrev = i1;
-					tPrev = 2 * t0 - t1;
+} );
 
 
-					break;
+/**
+ *
+ * A Track of quaternion keyframe values.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ * @author tschw
+ */
 
 
-				case WrapAroundEnding:
+function QuaternionKeyframeTrack( name, times, values, interpolation ) {
 
 
-					// use the other end of the curve
-					iPrev = pp.length - 2;
-					tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
+	KeyframeTrack.call( this, name, times, values, interpolation );
 
 
-					break;
+}
 
 
-				default: // ZeroCurvatureEnding
+QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-					// f''(t0) = 0 a.k.a. Natural Spline
-					iPrev = i1;
-					tPrev = t1;
+	constructor: QuaternionKeyframeTrack,
 
 
-			}
+	ValueTypeName: 'quaternion',
 
 
-		}
+	// ValueBufferType is inherited
 
 
-		if ( tNext === undefined ) {
+	DefaultInterpolation: InterpolateLinear,
 
 
-			switch ( this.getSettings_().endingEnd ) {
+	InterpolantFactoryMethodLinear: function ( result ) {
 
 
-				case ZeroSlopeEnding:
+		return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-					// f'(tN) = 0
-					iNext = i1;
-					tNext = 2 * t1 - t0;
+	},
 
 
-					break;
+	InterpolantFactoryMethodSmooth: undefined // not yet implemented
 
 
-				case WrapAroundEnding:
+} );
+
+/**
+ *
+ * A Track of keyframe values that represent color.
+ *
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ * @author tschw
+ */
+
+function ColorKeyframeTrack( name, times, values, interpolation ) {
+
+	KeyframeTrack.call( this, name, times, values, interpolation );
+
+}
+
+ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
+
+	constructor: ColorKeyframeTrack,
+
+	ValueTypeName: 'color'
+
+	// ValueBufferType is inherited
+
+	// DefaultInterpolation is inherited
+
+	// Note: Very basic implementation and nothing special yet.
+	// However, this is the place for color space parameterization.
+
+} );
+
+/**
+ *
+ * A Track of numeric keyframe values.
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ * @author tschw
+ */
+
+function NumberKeyframeTrack( name, times, values, interpolation ) {
+
+	KeyframeTrack.call( this, name, times, values, interpolation );
+
+}
+
+NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
+
+	constructor: NumberKeyframeTrack,
+
+	ValueTypeName: 'number'
+
+	// ValueBufferType is inherited
+
+	// DefaultInterpolation is inherited
+
+} );
+
+/**
+ * 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.
+ *
+ * @author tschw
+ */
+
+function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+
+	Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+
+	this._weightPrev = - 0;
+	this._offsetPrev = - 0;
+	this._weightNext = - 0;
+	this._offsetNext = - 0;
+
+}
+
+CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+
+	constructor: CubicInterpolant,
+
+	DefaultSettings_: {
+
+		endingStart: ZeroCurvatureEnding,
+		endingEnd: ZeroCurvatureEnding
+
+	},
+
+	intervalChanged_: function ( i1, t0, t1 ) {
+
+		var pp = this.parameterPositions,
+			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
 					// use the other end of the curve
 					iNext = 1;
 					iNext = 1;
@@ -32110,745 +32142,716 @@ DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.protot
 
 
 } );
 } );
 
 
-var KeyframeTrackPrototype;
-
-KeyframeTrackPrototype = {
-
-	TimeBufferType: Float32Array,
-	ValueBufferType: Float32Array,
+/**
+ * @author tschw
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
 
 
-	DefaultInterpolation: InterpolateLinear,
+var AnimationUtils = {
 
 
-	InterpolantFactoryMethodDiscrete: function ( result ) {
+	// same as Array.prototype.slice, but also works on typed arrays
+	arraySlice: function ( array, from, to ) {
 
 
-		return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
+		if ( AnimationUtils.isTypedArray( array ) ) {
 
 
-	},
+			// in ios9 array.subarray(from, undefined) will return empty array
+			// but array.subarray(from) or array.subarray(from, len) is correct
+			return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
 
 
-	InterpolantFactoryMethodLinear: function ( result ) {
+		}
 
 
-		return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
+		return array.slice( from, to );
 
 
 	},
 	},
 
 
-	InterpolantFactoryMethodSmooth: function ( result ) {
+	// converts an array to a specific type
+	convertArray: function ( array, type, forceClone ) {
 
 
-		return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
+		if ( ! array || // let 'undefined' and 'null' pass
+				! forceClone && array.constructor === type ) return array;
 
 
-	},
+		if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
 
 
-	setInterpolation: function ( interpolation ) {
+			return new type( array ); // create typed array
 
 
-		var factoryMethod;
+		}
 
 
-		switch ( interpolation ) {
+		return Array.prototype.slice.call( array ); // create Array
 
 
-			case InterpolateDiscrete:
+	},
 
 
-				factoryMethod = this.InterpolantFactoryMethodDiscrete;
+	isTypedArray: function ( object ) {
 
 
-				break;
+		return ArrayBuffer.isView( object ) &&
+				! ( object instanceof DataView );
 
 
-			case InterpolateLinear:
+	},
 
 
-				factoryMethod = this.InterpolantFactoryMethodLinear;
+	// returns an array by which times and values can be sorted
+	getKeyframeOrder: function ( times ) {
 
 
-				break;
+		function compareTime( i, j ) {
 
 
-			case InterpolateSmooth:
+			return times[ i ] - times[ j ];
 
 
-				factoryMethod = this.InterpolantFactoryMethodSmooth;
+		}
 
 
-				break;
+		var n = times.length;
+		var result = new Array( n );
+		for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
 
 
-		}
+		result.sort( compareTime );
 
 
-		if ( factoryMethod === undefined ) {
+		return result;
 
 
-			var message = "unsupported interpolation for " +
-					this.ValueTypeName + " keyframe track named " + this.name;
+	},
 
 
-			if ( this.createInterpolant === undefined ) {
+	// uses the array previously returned by 'getKeyframeOrder' to sort data
+	sortedArray: function ( values, stride, order ) {
 
 
-				// fall back to default, unless the default itself is messed up
-				if ( interpolation !== this.DefaultInterpolation ) {
+		var nValues = values.length;
+		var result = new values.constructor( nValues );
 
 
-					this.setInterpolation( this.DefaultInterpolation );
+		for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
 
 
-				} else {
+			var srcOffset = order[ i ] * stride;
 
 
-					throw new Error( message ); // fatal, in this case
+			for ( var j = 0; j !== stride; ++ j ) {
 
 
-				}
+				result[ dstOffset ++ ] = values[ srcOffset + j ];
 
 
 			}
 			}
 
 
-			console.warn( 'THREE.KeyframeTrackPrototype:', message );
-			return;
-
 		}
 		}
 
 
-		this.createInterpolant = factoryMethod;
+		return result;
 
 
 	},
 	},
 
 
-	getInterpolation: function () {
+	// function for parsing AOS keyframe formats
+	flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
 
 
-		switch ( this.createInterpolant ) {
+		var i = 1, key = jsonKeys[ 0 ];
 
 
-			case this.InterpolantFactoryMethodDiscrete:
+		while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
 
 
-				return InterpolateDiscrete;
+			key = jsonKeys[ i ++ ];
 
 
-			case this.InterpolantFactoryMethodLinear:
+		}
 
 
-				return InterpolateLinear;
+		if ( key === undefined ) return; // no data
 
 
-			case this.InterpolantFactoryMethodSmooth:
+		var value = key[ valuePropertyName ];
+		if ( value === undefined ) return; // no data
 
 
-				return InterpolateSmooth;
+		if ( Array.isArray( value ) ) {
 
 
-		}
+			do {
 
 
-	},
+				value = key[ valuePropertyName ];
 
 
-	getValueSize: function () {
+				if ( value !== undefined ) {
 
 
-		return this.values.length / this.times.length;
+					times.push( key.time );
+					values.push.apply( values, value ); // push all elements
 
 
-	},
+				}
 
 
-	// move all keyframes either forwards or backwards in time
-	shift: function ( timeOffset ) {
+				key = jsonKeys[ i ++ ];
 
 
-		if ( timeOffset !== 0.0 ) {
+			} while ( key !== undefined );
 
 
-			var times = this.times;
+		} else if ( value.toArray !== undefined ) {
 
 
-			for ( var i = 0, n = times.length; i !== n; ++ i ) {
+			// ...assume THREE.Math-ish
 
 
-				times[ i ] += timeOffset;
+			do {
 
 
-			}
+				value = key[ valuePropertyName ];
 
 
-		}
+				if ( value !== undefined ) {
 
 
-		return this;
+					times.push( key.time );
+					value.toArray( values, values.length );
 
 
-	},
+				}
 
 
-	// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
-	scale: function ( timeScale ) {
+				key = jsonKeys[ i ++ ];
 
 
-		if ( timeScale !== 1.0 ) {
+			} while ( key !== undefined );
 
 
-			var times = this.times;
+		} else {
 
 
-			for ( var i = 0, n = times.length; i !== n; ++ i ) {
+			// otherwise push as-is
 
 
-				times[ i ] *= timeScale;
+			do {
 
 
-			}
+				value = key[ valuePropertyName ];
 
 
-		}
+				if ( value !== undefined ) {
 
 
-		return this;
+					times.push( key.time );
+					values.push( value );
 
 
-	},
+				}
 
 
-	// removes keyframes before and after animation without changing any values within the range [startTime, endTime].
-	// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
-	trim: function ( startTime, endTime ) {
+				key = jsonKeys[ i ++ ];
 
 
-		var times = this.times,
-			nKeys = times.length,
-			from = 0,
-			to = nKeys - 1;
+			} while ( key !== undefined );
 
 
-		while ( from !== nKeys && times[ from ] < startTime ) ++ from;
-		while ( to !== - 1 && times[ to ] > endTime ) -- to;
+		}
 
 
-		++ to; // inclusive -> exclusive bound
+	}
 
 
-		if ( from !== 0 || to !== nKeys ) {
+};
 
 
-			// empty tracks are forbidden, so keep at least one keyframe
-			if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
+/**
+ *
+ * A timed sequence of keyframes for a specific property.
+ *
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ * @author tschw
+ */
 
 
-			var stride = this.getValueSize();
-			this.times = AnimationUtils.arraySlice( times, from, to );
-			this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
+function KeyframeTrack( name, times, values, interpolation ) {
 
 
-		}
+	if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );
+	if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );
 
 
-		return this;
+	this.name = name;
 
 
-	},
+	this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
+	this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
 
 
-	// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
-	validate: function () {
+	this.setInterpolation( interpolation || this.DefaultInterpolation );
 
 
-		var valid = true;
+	this.validate();
+	this.optimize();
 
 
-		var valueSize = this.getValueSize();
-		if ( valueSize - Math.floor( valueSize ) !== 0 ) {
+}
 
 
-			console.error( 'THREE.KeyframeTrackPrototype: Invalid value size in track.', this );
-			valid = false;
+// Static methods:
 
 
-		}
+Object.assign( KeyframeTrack, {
 
 
-		var times = this.times,
-			values = this.values,
+	// Serialization (in static context, because of constructor invocation
+	// and automatic invocation of .toJSON):
 
 
-			nKeys = times.length;
+	parse: function ( json ) {
 
 
-		if ( nKeys === 0 ) {
+		if ( json.type === undefined ) {
 
 
-			console.error( 'THREE.KeyframeTrackPrototype: Track is empty.', this );
-			valid = false;
+			throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );
 
 
 		}
 		}
 
 
-		var prevTime = null;
+		var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
 
 
-		for ( var i = 0; i !== nKeys; i ++ ) {
+		if ( json.times === undefined ) {
 
 
-			var currTime = times[ i ];
+			var times = [], values = [];
 
 
-			if ( typeof currTime === 'number' && isNaN( currTime ) ) {
+			AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
 
 
-				console.error( 'THREE.KeyframeTrackPrototype: Time is not a valid number.', this, i, currTime );
-				valid = false;
-				break;
+			json.times = times;
+			json.values = values;
 
 
-			}
+		}
 
 
-			if ( prevTime !== null && prevTime > currTime ) {
+		// derived classes can define a static parse method
+		if ( trackType.parse !== undefined ) {
 
 
-				console.error( 'THREE.KeyframeTrackPrototype: Out of order keys.', this, i, currTime, prevTime );
-				valid = false;
-				break;
+			return trackType.parse( json );
 
 
-			}
+		} else {
 
 
-			prevTime = currTime;
+			// by default, we assume a constructor compatible with the base
+			return new trackType( json.name, json.times, json.values, json.interpolation );
 
 
 		}
 		}
 
 
-		if ( values !== undefined ) {
+	},
 
 
-			if ( AnimationUtils.isTypedArray( values ) ) {
+	toJSON: function ( track ) {
 
 
-				for ( var i = 0, n = values.length; i !== n; ++ i ) {
+		var trackType = track.constructor;
 
 
-					var value = values[ i ];
+		var json;
 
 
-					if ( isNaN( value ) ) {
+		// derived classes can define a static toJSON method
+		if ( trackType.toJSON !== undefined ) {
 
 
-						console.error( 'THREE.KeyframeTrackPrototype: Value is not a valid number.', this, i, value );
-						valid = false;
-						break;
+			json = trackType.toJSON( track );
 
 
-					}
+		} else {
 
 
-				}
+			// by default, we assume the data can be serialized as-is
+			json = {
 
 
-			}
+				'name': track.name,
+				'times': AnimationUtils.convertArray( track.times, Array ),
+				'values': AnimationUtils.convertArray( track.values, Array )
 
 
-		}
+			};
 
 
-		return valid;
+			var interpolation = track.getInterpolation();
 
 
-	},
+			if ( interpolation !== track.DefaultInterpolation ) {
 
 
-	// removes equivalent sequential keys as common in morph target sequences
-	// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
-	optimize: function () {
+				json.interpolation = interpolation;
 
 
-		var times = this.times,
-			values = this.values,
-			stride = this.getValueSize(),
+			}
 
 
-			smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
+		}
 
 
-			writeIndex = 1,
-			lastIndex = times.length - 1;
+		json.type = track.ValueTypeName; // mandatory
 
 
-		for ( var i = 1; i < lastIndex; ++ i ) {
+		return json;
 
 
-			var keep = false;
+	},
 
 
-			var time = times[ i ];
-			var timeNext = times[ i + 1 ];
+	_getTrackTypeForValueTypeName: function ( typeName ) {
 
 
-			// remove adjacent keyframes scheduled at the same time
+		switch ( typeName.toLowerCase() ) {
 
 
-			if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
+			case 'scalar':
+			case 'double':
+			case 'float':
+			case 'number':
+			case 'integer':
 
 
-				if ( ! smoothInterpolation ) {
+				return NumberKeyframeTrack;
 
 
-					// remove unnecessary keyframes same as their neighbors
+			case 'vector':
+			case 'vector2':
+			case 'vector3':
+			case 'vector4':
 
 
-					var offset = i * stride,
-						offsetP = offset - stride,
-						offsetN = offset + stride;
+				return VectorKeyframeTrack;
 
 
-					for ( var j = 0; j !== stride; ++ j ) {
+			case 'color':
 
 
-						var value = values[ offset + j ];
+				return ColorKeyframeTrack;
 
 
-						if ( value !== values[ offsetP + j ] ||
-								value !== values[ offsetN + j ] ) {
+			case 'quaternion':
 
 
-							keep = true;
-							break;
+				return QuaternionKeyframeTrack;
 
 
-						}
+			case 'bool':
+			case 'boolean':
 
 
-					}
+				return BooleanKeyframeTrack;
 
 
-				} else keep = true;
+			case 'string':
 
 
-			}
+				return StringKeyframeTrack;
 
 
-			// in-place compaction
+		}
 
 
-			if ( keep ) {
+		throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );
 
 
-				if ( i !== writeIndex ) {
+	}
 
 
-					times[ writeIndex ] = times[ i ];
+} );
 
 
-					var readOffset = i * stride,
-						writeOffset = writeIndex * stride;
+Object.assign( KeyframeTrack.prototype, {
 
 
-					for ( var j = 0; j !== stride; ++ j )
+	constructor: KeyframeTrack,
 
 
-						values[ writeOffset + j ] = values[ readOffset + j ];
+	TimeBufferType: Float32Array,
 
 
-				}
+	ValueBufferType: Float32Array,
 
 
-				++ writeIndex;
+	DefaultInterpolation: InterpolateLinear,
 
 
-			}
+	InterpolantFactoryMethodDiscrete: function ( result ) {
 
 
-		}
+		return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-		// flush last keyframe (compaction looks ahead)
+	},
 
 
-		if ( lastIndex > 0 ) {
+	InterpolantFactoryMethodLinear: function ( result ) {
 
 
-			times[ writeIndex ] = times[ lastIndex ];
+		return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-			for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j )
+	},
 
 
-				values[ writeOffset + j ] = values[ readOffset + j ];
+	InterpolantFactoryMethodSmooth: function ( result ) {
 
 
-			++ writeIndex;
+		return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
 
 
-		}
+	},
 
 
-		if ( writeIndex !== times.length ) {
+	setInterpolation: function ( interpolation ) {
 
 
-			this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
-			this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
+		var factoryMethod;
 
 
-		}
+		switch ( interpolation ) {
 
 
-		return this;
+			case InterpolateDiscrete:
 
 
-	}
+				factoryMethod = this.InterpolantFactoryMethodDiscrete;
 
 
-};
+				break;
 
 
-function KeyframeTrackConstructor( name, times, values, interpolation ) {
+			case InterpolateLinear:
 
 
-	if ( name === undefined ) throw new Error( 'track name is undefined' );
+				factoryMethod = this.InterpolantFactoryMethodLinear;
 
 
-	if ( times === undefined || times.length === 0 ) {
+				break;
 
 
-		throw new Error( 'no keyframes in track named ' + name );
+			case InterpolateSmooth:
 
 
-	}
+				factoryMethod = this.InterpolantFactoryMethodSmooth;
 
 
-	this.name = name;
+				break;
 
 
-	this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
-	this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
+		}
 
 
-	this.setInterpolation( interpolation || this.DefaultInterpolation );
+		if ( factoryMethod === undefined ) {
 
 
-	this.validate();
-	this.optimize();
+			var message = "unsupported interpolation for " +
+				this.ValueTypeName + " keyframe track named " + this.name;
 
 
-}
+			if ( this.createInterpolant === undefined ) {
 
 
-/**
- *
- * A Track of vectored keyframe values.
- *
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+				// fall back to default, unless the default itself is messed up
+				if ( interpolation !== this.DefaultInterpolation ) {
 
 
-function VectorKeyframeTrack( name, times, values, interpolation ) {
+					this.setInterpolation( this.DefaultInterpolation );
 
 
-	KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+				} else {
 
 
-}
+					throw new Error( message ); // fatal, in this case
 
 
-VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+				}
 
 
-	constructor: VectorKeyframeTrack,
+			}
 
 
-	ValueTypeName: 'vector'
+			console.warn( 'THREE.KeyframeTrack:', message );
+			return;
 
 
-	// ValueBufferType is inherited
+		}
 
 
-	// DefaultInterpolation is inherited
+		this.createInterpolant = factoryMethod;
 
 
-} );
+	},
 
 
-/**
- * Spherical linear unit quaternion interpolant.
- *
- * @author tschw
- */
+	getInterpolation: function () {
 
 
-function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+		switch ( this.createInterpolant ) {
 
 
-	Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+			case this.InterpolantFactoryMethodDiscrete:
 
 
-}
+				return InterpolateDiscrete;
 
 
-QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+			case this.InterpolantFactoryMethodLinear:
 
 
-	constructor: QuaternionLinearInterpolant,
+				return InterpolateLinear;
 
 
-	interpolate_: function ( i1, t0, t, t1 ) {
+			case this.InterpolantFactoryMethodSmooth:
 
 
-		var result = this.resultBuffer,
-			values = this.sampleValues,
-			stride = this.valueSize,
+				return InterpolateSmooth;
 
 
-			offset = i1 * stride,
+		}
 
 
-			alpha = ( t - t0 ) / ( t1 - t0 );
+	},
 
 
-		for ( var end = offset + stride; offset !== end; offset += 4 ) {
+	getValueSize: function () {
 
 
-			Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
+		return this.values.length / this.times.length;
 
 
-		}
+	},
 
 
-		return result;
+	// move all keyframes either forwards or backwards in time
+	shift: function ( timeOffset ) {
 
 
-	}
+		if ( timeOffset !== 0.0 ) {
 
 
-} );
+			var times = this.times;
 
 
-/**
- *
- * A Track of quaternion keyframe values.
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+			for ( var i = 0, n = times.length; i !== n; ++ i ) {
 
 
-function QuaternionKeyframeTrack( name, times, values, interpolation ) {
+				times[ i ] += timeOffset;
 
 
-	KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+			}
 
 
-}
+		}
 
 
-QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+		return this;
 
 
-	constructor: QuaternionKeyframeTrack,
+	},
 
 
-	ValueTypeName: 'quaternion',
+	// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
+	scale: function ( timeScale ) {
 
 
-	// ValueBufferType is inherited
+		if ( timeScale !== 1.0 ) {
 
 
-	DefaultInterpolation: InterpolateLinear,
+			var times = this.times;
 
 
-	InterpolantFactoryMethodLinear: function ( result ) {
+			for ( var i = 0, n = times.length; i !== n; ++ i ) {
 
 
-		return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
+				times[ i ] *= timeScale;
 
 
-	},
+			}
 
 
-	InterpolantFactoryMethodSmooth: undefined // not yet implemented
+		}
 
 
-} );
+		return this;
 
 
-/**
- *
- * A Track of numeric keyframe values.
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+	},
 
 
-function NumberKeyframeTrack( name, times, values, interpolation ) {
+	// removes keyframes before and after animation without changing any values within the range [startTime, endTime].
+	// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
+	trim: function ( startTime, endTime ) {
 
 
-	KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+		var times = this.times,
+			nKeys = times.length,
+			from = 0,
+			to = nKeys - 1;
 
 
-}
+		while ( from !== nKeys && times[ from ] < startTime ) {
 
 
-NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+			++ from;
 
 
-	constructor: NumberKeyframeTrack,
+		}
 
 
-	ValueTypeName: 'number'
+		while ( to !== - 1 && times[ to ] > endTime ) {
 
 
-	// ValueBufferType is inherited
+			-- to;
 
 
-	// DefaultInterpolation is inherited
+		}
 
 
-} );
+		++ to; // inclusive -> exclusive bound
 
 
-/**
- *
- * A Track that interpolates Strings
- *
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+		if ( from !== 0 || to !== nKeys ) {
 
 
-function StringKeyframeTrack( name, times, values, interpolation ) {
+			// empty tracks are forbidden, so keep at least one keyframe
+			if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
 
 
-	KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+			var stride = this.getValueSize();
+			this.times = AnimationUtils.arraySlice( times, from, to );
+			this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
 
 
-}
+		}
 
 
-StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+		return this;
 
 
-	constructor: StringKeyframeTrack,
+	},
 
 
-	ValueTypeName: 'string',
-	ValueBufferType: Array,
+	// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
+	validate: function () {
 
 
-	DefaultInterpolation: InterpolateDiscrete,
+		var valid = true;
 
 
-	InterpolantFactoryMethodLinear: undefined,
+		var valueSize = this.getValueSize();
+		if ( valueSize - Math.floor( valueSize ) !== 0 ) {
 
 
-	InterpolantFactoryMethodSmooth: undefined
+			console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );
+			valid = false;
 
 
-} );
+		}
 
 
-/**
- *
- * A Track of Boolean keyframe values.
- *
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+		var times = this.times,
+			values = this.values,
 
 
-function BooleanKeyframeTrack( name, times, values ) {
+			nKeys = times.length;
 
 
-	KeyframeTrackConstructor.call( this, name, times, values );
+		if ( nKeys === 0 ) {
 
 
-}
+			console.error( 'THREE.KeyframeTrack: Track is empty.', this );
+			valid = false;
 
 
-BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+		}
 
 
-	constructor: BooleanKeyframeTrack,
+		var prevTime = null;
 
 
-	ValueTypeName: 'bool',
-	ValueBufferType: Array,
+		for ( var i = 0; i !== nKeys; i ++ ) {
 
 
-	DefaultInterpolation: InterpolateDiscrete,
+			var currTime = times[ i ];
 
 
-	InterpolantFactoryMethodLinear: undefined,
-	InterpolantFactoryMethodSmooth: undefined
+			if ( typeof currTime === 'number' && isNaN( currTime ) ) {
 
 
-	// Note: Actually this track could have a optimized / compressed
-	// representation of a single value and a custom interpolant that
-	// computes "firstValue ^ isOdd( index )".
+				console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );
+				valid = false;
+				break;
 
 
-} );
+			}
 
 
-/**
- *
- * A Track of keyframe values that represent color.
- *
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+			if ( prevTime !== null && prevTime > currTime ) {
 
 
-function ColorKeyframeTrack( name, times, values, interpolation ) {
+				console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
+				valid = false;
+				break;
 
 
-	KeyframeTrackConstructor.call( this, name, times, values, interpolation );
+			}
 
 
-}
+			prevTime = currTime;
 
 
-ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
+		}
 
 
-	constructor: ColorKeyframeTrack,
+		if ( values !== undefined ) {
 
 
-	ValueTypeName: 'color'
+			if ( AnimationUtils.isTypedArray( values ) ) {
 
 
-	// ValueBufferType is inherited
+				for ( var i = 0, n = values.length; i !== n; ++ i ) {
 
 
-	// DefaultInterpolation is inherited
+					var value = values[ i ];
 
 
+					if ( isNaN( value ) ) {
 
 
-	// Note: Very basic implementation and nothing special yet.
-	// However, this is the place for color space parameterization.
+						console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );
+						valid = false;
+						break;
 
 
-} );
+					}
 
 
-/**
- *
- * A timed sequence of keyframes for a specific property.
- *
- *
- * @author Ben Houston / http://clara.io/
- * @author David Sarno / http://lighthaus.us/
- * @author tschw
- */
+				}
 
 
-function KeyframeTrack( name, times, values, interpolation ) {
+			}
 
 
-	KeyframeTrackConstructor.apply( this, name, times, values, interpolation );
+		}
 
 
-}
+		return valid;
 
 
-KeyframeTrack.prototype = KeyframeTrackPrototype;
-KeyframeTrackPrototype.constructor = KeyframeTrack;
+	},
 
 
-// Static methods:
+	// removes equivalent sequential keys as common in morph target sequences
+	// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
+	optimize: function () {
 
 
-Object.assign( KeyframeTrack, {
+		var times = this.times,
+			values = this.values,
+			stride = this.getValueSize(),
 
 
-	// Serialization (in static context, because of constructor invocation
-	// and automatic invocation of .toJSON):
+			smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
 
 
-	parse: function ( json ) {
+			writeIndex = 1,
+			lastIndex = times.length - 1;
 
 
-		if ( json.type === undefined ) {
+		for ( var i = 1; i < lastIndex; ++ i ) {
 
 
-			throw new Error( 'track type undefined, can not parse' );
+			var keep = false;
 
 
-		}
+			var time = times[ i ];
+			var timeNext = times[ i + 1 ];
 
 
-		var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );
+			// remove adjacent keyframes scheduled at the same time
 
 
-		if ( json.times === undefined ) {
+			if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
 
 
-			var times = [], values = [];
+				if ( ! smoothInterpolation ) {
 
 
-			AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
+					// remove unnecessary keyframes same as their neighbors
 
 
-			json.times = times;
-			json.values = values;
+					var offset = i * stride,
+						offsetP = offset - stride,
+						offsetN = offset + stride;
 
 
-		}
+					for ( var j = 0; j !== stride; ++ j ) {
 
 
-		// derived classes can define a static parse method
-		if ( trackType.parse !== undefined ) {
+						var value = values[ offset + j ];
 
 
-			return trackType.parse( json );
+						if ( value !== values[ offsetP + j ] ||
+							value !== values[ offsetN + j ] ) {
 
 
-		} else {
+							keep = true;
+							break;
 
 
-			// by default, we assume a constructor compatible with the base
-			return new trackType( json.name, json.times, json.values, json.interpolation );
+						}
 
 
-		}
+					}
 
 
-	},
+				} else {
 
 
-	toJSON: function ( track ) {
+					keep = true;
 
 
-		var trackType = track.constructor;
+				}
 
 
-		var json;
+			}
 
 
-		// derived classes can define a static toJSON method
-		if ( trackType.toJSON !== undefined ) {
+			// in-place compaction
 
 
-			json = trackType.toJSON( track );
+			if ( keep ) {
 
 
-		} else {
+				if ( i !== writeIndex ) {
 
 
-			// by default, we assume the data can be serialized as-is
-			json = {
+					times[ writeIndex ] = times[ i ];
 
 
-				'name': track.name,
-				'times': AnimationUtils.convertArray( track.times, Array ),
-				'values': AnimationUtils.convertArray( track.values, Array )
+					var readOffset = i * stride,
+						writeOffset = writeIndex * stride;
 
 
-			};
+					for ( var j = 0; j !== stride; ++ j ) {
 
 
-			var interpolation = track.getInterpolation();
+						values[ writeOffset + j ] = values[ readOffset + j ];
 
 
-			if ( interpolation !== track.DefaultInterpolation ) {
+					}
 
 
-				json.interpolation = interpolation;
+				}
+
+				++ writeIndex;
 
 
 			}
 			}
 
 
 		}
 		}
 
 
-		json.type = track.ValueTypeName; // mandatory
+		// flush last keyframe (compaction looks ahead)
 
 
-		return json;
+		if ( lastIndex > 0 ) {
 
 
-	},
+			times[ writeIndex ] = times[ lastIndex ];
 
 
-	_getTrackTypeForValueTypeName: function ( typeName ) {
+			for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
 
 
-		switch ( typeName.toLowerCase() ) {
+				values[ writeOffset + j ] = values[ readOffset + j ];
 
 
-			case 'scalar':
-			case 'double':
-			case 'float':
-			case 'number':
-			case 'integer':
+			}
 
 
-				return NumberKeyframeTrack;
+			++ writeIndex;
 
 
-			case 'vector':
-			case 'vector2':
-			case 'vector3':
-			case 'vector4':
+		}
 
 
-				return VectorKeyframeTrack;
+		if ( writeIndex !== times.length ) {
 
 
-			case 'color':
+			this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
+			this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
 
 
-				return ColorKeyframeTrack;
+		}
 
 
-			case 'quaternion':
+		return this;
 
 
-				return QuaternionKeyframeTrack;
+	}
 
 
-			case 'bool':
-			case 'boolean':
+} );
 
 
-				return BooleanKeyframeTrack;
+/**
+ *
+ * A Track of vectored keyframe values.
+ *
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ * @author tschw
+ */
 
 
-			case 'string':
+function VectorKeyframeTrack( name, times, values, interpolation ) {
 
 
-				return StringKeyframeTrack;
+	KeyframeTrack.call( this, name, times, values, interpolation );
 
 
-		}
+}
 
 
-		throw new Error( 'Unsupported typeName: ' + typeName );
+VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
 
 
-	}
+	constructor: VectorKeyframeTrack,
+
+	ValueTypeName: 'vector'
+
+	// ValueBufferType is inherited
+
+	// DefaultInterpolation is inherited
 
 
 } );
 } );
 
 

部分文件因为文件数量过多而无法显示