未验证 提交 ce31424c 编写于 作者: M Mr.doob 提交者: GitHub

Merge pull request #12584 from Itee/KeyFrameTrack

KeyframeTrack: Remove KeyframeTrackPrototype and KeyframeTrackConstructor
import { KeyframeTrackPrototype } from './KeyframeTrackPrototype.js';
import { StringKeyframeTrack } from './tracks/StringKeyframeTrack.js';
import { BooleanKeyframeTrack } from './tracks/BooleanKeyframeTrack.js';
import { QuaternionKeyframeTrack } from './tracks/QuaternionKeyframeTrack.js';
import { ColorKeyframeTrack } from './tracks/ColorKeyframeTrack.js';
import { VectorKeyframeTrack } from './tracks/VectorKeyframeTrack.js';
import { NumberKeyframeTrack } from './tracks/NumberKeyframeTrack.js';
import {
InterpolateLinear,
InterpolateSmooth,
InterpolateDiscrete
} from '../constants.js';
import { CubicInterpolant } from '../math/interpolants/CubicInterpolant.js';
import { LinearInterpolant } from '../math/interpolants/LinearInterpolant.js';
import { DiscreteInterpolant } from '../math/interpolants/DiscreteInterpolant.js';
import { AnimationUtils } from './AnimationUtils.js';
import { KeyframeTrackConstructor } from './KeyframeTrackConstructor.js';
/**
*
......@@ -20,12 +26,20 @@ import { KeyframeTrackConstructor } from './KeyframeTrackConstructor.js';
function KeyframeTrack( name, times, values, interpolation ) {
KeyframeTrackConstructor.apply( this, 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 );
}
this.name = name;
this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
KeyframeTrack.prototype = KeyframeTrackPrototype;
KeyframeTrackPrototype.constructor = KeyframeTrack;
this.setInterpolation( interpolation || this.DefaultInterpolation );
this.validate();
this.optimize();
}
// Static methods:
......@@ -38,7 +52,7 @@ Object.assign( KeyframeTrack, {
if ( json.type === undefined ) {
throw new Error( 'track type undefined, can not parse' );
throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );
}
......@@ -145,11 +159,380 @@ Object.assign( KeyframeTrack, {
}
throw new Error( 'Unsupported typeName: ' + typeName );
throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );
}
} );
Object.assign( KeyframeTrack.prototype, {
constructor: KeyframeTrack,
TimeBufferType: Float32Array,
ValueBufferType: Float32Array,
DefaultInterpolation: InterpolateLinear,
InterpolantFactoryMethodDiscrete: function ( result ) {
return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
},
InterpolantFactoryMethodLinear: function ( result ) {
return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
},
InterpolantFactoryMethodSmooth: function ( result ) {
return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
},
setInterpolation: function ( interpolation ) {
var factoryMethod;
switch ( interpolation ) {
case InterpolateDiscrete:
factoryMethod = this.InterpolantFactoryMethodDiscrete;
break;
case InterpolateLinear:
factoryMethod = this.InterpolantFactoryMethodLinear;
break;
case InterpolateSmooth:
factoryMethod = this.InterpolantFactoryMethodSmooth;
break;
}
if ( factoryMethod === undefined ) {
var message = "unsupported interpolation for " +
this.ValueTypeName + " keyframe track named " + this.name;
if ( this.createInterpolant === undefined ) {
// fall back to default, unless the default itself is messed up
if ( interpolation !== this.DefaultInterpolation ) {
this.setInterpolation( this.DefaultInterpolation );
} else {
throw new Error( message ); // fatal, in this case
}
}
console.warn( 'THREE.KeyframeTrack:', message );
return;
}
this.createInterpolant = factoryMethod;
},
getInterpolation: function () {
switch ( this.createInterpolant ) {
case this.InterpolantFactoryMethodDiscrete:
return InterpolateDiscrete;
case this.InterpolantFactoryMethodLinear:
return InterpolateLinear;
case this.InterpolantFactoryMethodSmooth:
return InterpolateSmooth;
}
},
getValueSize: function () {
return this.values.length / this.times.length;
},
// move all keyframes either forwards or backwards in time
shift: function ( timeOffset ) {
if ( timeOffset !== 0.0 ) {
var times = this.times;
for ( var i = 0, n = times.length; i !== n; ++ i ) {
times[ i ] += timeOffset;
}
}
return this;
},
// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
scale: function ( timeScale ) {
if ( timeScale !== 1.0 ) {
var times = this.times;
for ( var i = 0, n = times.length; i !== n; ++ i ) {
times[ i ] *= timeScale;
}
}
return this;
},
// 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 ) {
var times = this.times,
nKeys = times.length,
from = 0,
to = nKeys - 1;
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;
var stride = this.getValueSize();
this.times = AnimationUtils.arraySlice( times, from, to );
this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
}
return this;
},
// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
validate: function () {
var valid = true;
var valueSize = this.getValueSize();
if ( valueSize - Math.floor( valueSize ) !== 0 ) {
console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );
valid = false;
}
var times = this.times,
values = this.values,
nKeys = times.length;
if ( nKeys === 0 ) {
console.error( 'THREE.KeyframeTrack: Track is empty.', this );
valid = false;
}
var prevTime = null;
for ( var i = 0; i !== nKeys; i ++ ) {
var currTime = times[ i ];
if ( typeof currTime === 'number' && isNaN( currTime ) ) {
console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );
valid = false;
break;
}
if ( prevTime !== null && prevTime > currTime ) {
console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
valid = false;
break;
}
prevTime = currTime;
}
if ( values !== undefined ) {
if ( AnimationUtils.isTypedArray( values ) ) {
for ( var i = 0, n = values.length; i !== n; ++ i ) {
var value = values[ i ];
if ( isNaN( value ) ) {
console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );
valid = false;
break;
}
}
}
}
return valid;
},
// 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 () {
var times = this.times,
values = this.values,
stride = this.getValueSize(),
smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
writeIndex = 1,
lastIndex = times.length - 1;
for ( var i = 1; i < lastIndex; ++ i ) {
var keep = false;
var time = times[ i ];
var timeNext = times[ i + 1 ];
// remove adjacent keyframes scheduled at the same time
if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
if ( ! smoothInterpolation ) {
// remove unnecessary keyframes same as their neighbors
var offset = i * stride,
offsetP = offset - stride,
offsetN = offset + stride;
for ( var j = 0; j !== stride; ++ j ) {
var value = values[ offset + j ];
if ( value !== values[ offsetP + j ] ||
value !== values[ offsetN + j ] ) {
keep = true;
break;
}
}
} else {
keep = true;
}
}
// in-place compaction
if ( keep ) {
if ( i !== writeIndex ) {
times[ writeIndex ] = times[ i ];
var readOffset = i * stride,
writeOffset = writeIndex * stride;
for ( var j = 0; j !== stride; ++ j ) {
values[ writeOffset + j ] = values[ readOffset + j ];
}
}
++ writeIndex;
}
}
// flush last keyframe (compaction looks ahead)
if ( lastIndex > 0 ) {
times[ writeIndex ] = times[ lastIndex ];
for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
values[ writeOffset + j ] = values[ readOffset + j ];
}
++ writeIndex;
}
if ( writeIndex !== times.length ) {
this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
}
return this;
}
} );
export { KeyframeTrack };
import { AnimationUtils } from './AnimationUtils.js';
function KeyframeTrackConstructor( name, times, values, interpolation ) {
if ( name === undefined ) throw new Error( 'track name is undefined' );
if ( times === undefined || times.length === 0 ) {
throw new Error( 'no keyframes in track named ' + name );
}
this.name = name;
this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
this.setInterpolation( interpolation || this.DefaultInterpolation );
this.validate();
this.optimize();
}
export { KeyframeTrackConstructor };
import { InterpolateLinear } from '../constants.js';
import { AnimationUtils } from './AnimationUtils.js';
import { InterpolateSmooth, InterpolateDiscrete } from '../constants.js';
import { CubicInterpolant } from '../math/interpolants/CubicInterpolant.js';
import { LinearInterpolant } from '../math/interpolants/LinearInterpolant.js';
import { DiscreteInterpolant } from '../math/interpolants/DiscreteInterpolant.js';
var KeyframeTrackPrototype;
KeyframeTrackPrototype = {
TimeBufferType: Float32Array,
ValueBufferType: Float32Array,
DefaultInterpolation: InterpolateLinear,
InterpolantFactoryMethodDiscrete: function ( result ) {
return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
},
InterpolantFactoryMethodLinear: function ( result ) {
return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
},
InterpolantFactoryMethodSmooth: function ( result ) {
return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
},
setInterpolation: function ( interpolation ) {
var factoryMethod;
switch ( interpolation ) {
case InterpolateDiscrete:
factoryMethod = this.InterpolantFactoryMethodDiscrete;
break;
case InterpolateLinear:
factoryMethod = this.InterpolantFactoryMethodLinear;
break;
case InterpolateSmooth:
factoryMethod = this.InterpolantFactoryMethodSmooth;
break;
}
if ( factoryMethod === undefined ) {
var message = "unsupported interpolation for " +
this.ValueTypeName + " keyframe track named " + this.name;
if ( this.createInterpolant === undefined ) {
// fall back to default, unless the default itself is messed up
if ( interpolation !== this.DefaultInterpolation ) {
this.setInterpolation( this.DefaultInterpolation );
} else {
throw new Error( message ); // fatal, in this case
}
}
console.warn( 'THREE.KeyframeTrackPrototype:', message );
return;
}
this.createInterpolant = factoryMethod;
},
getInterpolation: function () {
switch ( this.createInterpolant ) {
case this.InterpolantFactoryMethodDiscrete:
return InterpolateDiscrete;
case this.InterpolantFactoryMethodLinear:
return InterpolateLinear;
case this.InterpolantFactoryMethodSmooth:
return InterpolateSmooth;
}
},
getValueSize: function () {
return this.values.length / this.times.length;
},
// move all keyframes either forwards or backwards in time
shift: function ( timeOffset ) {
if ( timeOffset !== 0.0 ) {
var times = this.times;
for ( var i = 0, n = times.length; i !== n; ++ i ) {
times[ i ] += timeOffset;
}
}
return this;
},
// scale all keyframe times by a factor (useful for frame <-> seconds conversions)
scale: function ( timeScale ) {
if ( timeScale !== 1.0 ) {
var times = this.times;
for ( var i = 0, n = times.length; i !== n; ++ i ) {
times[ i ] *= timeScale;
}
}
return this;
},
// 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 ) {
var times = this.times,
nKeys = times.length,
from = 0,
to = nKeys - 1;
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;
var stride = this.getValueSize();
this.times = AnimationUtils.arraySlice( times, from, to );
this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
}
return this;
},
// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
validate: function () {
var valid = true;
var valueSize = this.getValueSize();
if ( valueSize - Math.floor( valueSize ) !== 0 ) {
console.error( 'THREE.KeyframeTrackPrototype: Invalid value size in track.', this );
valid = false;
}
var times = this.times,
values = this.values,
nKeys = times.length;
if ( nKeys === 0 ) {
console.error( 'THREE.KeyframeTrackPrototype: Track is empty.', this );
valid = false;
}
var prevTime = null;
for ( var i = 0; i !== nKeys; i ++ ) {
var currTime = times[ i ];
if ( typeof currTime === 'number' && isNaN( currTime ) ) {
console.error( 'THREE.KeyframeTrackPrototype: Time is not a valid number.', this, i, currTime );
valid = false;
break;
}
if ( prevTime !== null && prevTime > currTime ) {
console.error( 'THREE.KeyframeTrackPrototype: Out of order keys.', this, i, currTime, prevTime );
valid = false;
break;
}
prevTime = currTime;
}
if ( values !== undefined ) {
if ( AnimationUtils.isTypedArray( values ) ) {
for ( var i = 0, n = values.length; i !== n; ++ i ) {
var value = values[ i ];
if ( isNaN( value ) ) {
console.error( 'THREE.KeyframeTrackPrototype: Value is not a valid number.', this, i, value );
valid = false;
break;
}
}
}
}
return valid;
},
// 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 () {
var times = this.times,
values = this.values,
stride = this.getValueSize(),
smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
writeIndex = 1,
lastIndex = times.length - 1;
for ( var i = 1; i < lastIndex; ++ i ) {
var keep = false;
var time = times[ i ];
var timeNext = times[ i + 1 ];
// remove adjacent keyframes scheduled at the same time
if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
if ( ! smoothInterpolation ) {
// remove unnecessary keyframes same as their neighbors
var offset = i * stride,
offsetP = offset - stride,
offsetN = offset + stride;
for ( var j = 0; j !== stride; ++ j ) {
var value = values[ offset + j ];
if ( value !== values[ offsetP + j ] ||
value !== values[ offsetN + j ] ) {
keep = true;
break;
}
}
} else keep = true;
}
// in-place compaction
if ( keep ) {
if ( i !== writeIndex ) {
times[ writeIndex ] = times[ i ];
var readOffset = i * stride,
writeOffset = writeIndex * stride;
for ( var j = 0; j !== stride; ++ j )
values[ writeOffset + j ] = values[ readOffset + j ];
}
++ writeIndex;
}
}
// flush last keyframe (compaction looks ahead)
if ( lastIndex > 0 ) {
times[ writeIndex ] = times[ lastIndex ];
for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j )
values[ writeOffset + j ] = values[ readOffset + j ];
++ writeIndex;
}
if ( writeIndex !== times.length ) {
this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
}
return this;
}
};
export { KeyframeTrackPrototype };
import { InterpolateDiscrete } from '../../constants.js';
import { KeyframeTrackPrototype } from '../KeyframeTrackPrototype.js';
import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
import { KeyframeTrack } from '../KeyframeTrack.js';
/**
*
......@@ -14,11 +13,11 @@ import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
function BooleanKeyframeTrack( name, times, values ) {
KeyframeTrackConstructor.call( this, name, times, values );
KeyframeTrack.call( this, name, times, values );
}
BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
constructor: BooleanKeyframeTrack,
......@@ -36,5 +35,4 @@ BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackProt
} );
export { BooleanKeyframeTrack };
import { KeyframeTrackPrototype } from '../KeyframeTrackPrototype.js';
import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
import { KeyframeTrack } from '../KeyframeTrack.js';
/**
*
......@@ -13,11 +12,11 @@ import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
function ColorKeyframeTrack( name, times, values, interpolation ) {
KeyframeTrackConstructor.call( this, name, times, values, interpolation );
KeyframeTrack.call( this, name, times, values, interpolation );
}
ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
constructor: ColorKeyframeTrack,
......@@ -27,11 +26,9 @@ ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackProtot
// DefaultInterpolation is inherited
// Note: Very basic implementation and nothing special yet.
// However, this is the place for color space parameterization.
} );
export { ColorKeyframeTrack };
import { KeyframeTrackPrototype } from '../KeyframeTrackPrototype.js';
import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
import { KeyframeTrack } from '../KeyframeTrack.js';
/**
*
......@@ -12,11 +11,11 @@ import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
function NumberKeyframeTrack( name, times, values, interpolation ) {
KeyframeTrackConstructor.call( this, name, times, values, interpolation );
KeyframeTrack.call( this, name, times, values, interpolation );
}
NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
constructor: NumberKeyframeTrack,
......@@ -28,5 +27,4 @@ NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackProto
} );
export { NumberKeyframeTrack };
import { InterpolateLinear } from '../../constants.js';
import { KeyframeTrackPrototype } from '../KeyframeTrackPrototype.js';
import { KeyframeTrack } from '../KeyframeTrack.js';
import { QuaternionLinearInterpolant } from '../../math/interpolants/QuaternionLinearInterpolant.js';
import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
/**
*
......@@ -14,11 +13,11 @@ import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
function QuaternionKeyframeTrack( name, times, values, interpolation ) {
KeyframeTrackConstructor.call( this, name, times, values, interpolation );
KeyframeTrack.call( this, name, times, values, interpolation );
}
QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
constructor: QuaternionKeyframeTrack,
......@@ -38,5 +37,4 @@ QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackP
} );
export { QuaternionKeyframeTrack };
import { InterpolateDiscrete } from '../../constants.js';
import { KeyframeTrackPrototype } from '../KeyframeTrackPrototype.js';
import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
import { KeyframeTrack } from '../KeyframeTrack.js';
/**
*
......@@ -14,11 +13,11 @@ import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
function StringKeyframeTrack( name, times, values, interpolation ) {
KeyframeTrackConstructor.call( this, name, times, values, interpolation );
KeyframeTrack.call( this, name, times, values, interpolation );
}
StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
constructor: StringKeyframeTrack,
......@@ -33,5 +32,4 @@ StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackProto
} );
export { StringKeyframeTrack };
import { KeyframeTrackPrototype } from '../KeyframeTrackPrototype.js';
import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
import { KeyframeTrack } from '../KeyframeTrack.js';
/**
*
......@@ -13,11 +12,11 @@ import { KeyframeTrackConstructor } from '../KeyframeTrackConstructor.js';
function VectorKeyframeTrack( name, times, values, interpolation ) {
KeyframeTrackConstructor.call( this, name, times, values, interpolation );
KeyframeTrack.call( this, name, times, values, interpolation );
}
VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), {
VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
constructor: VectorKeyframeTrack,
......@@ -29,5 +28,4 @@ VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackProto
} );
export { VectorKeyframeTrack };
......@@ -4,8 +4,6 @@
/* global QUnit */
import { KeyframeTrack } from '../../../../src/animation/KeyframeTrack';
import { KeyframeTrackConstructor } from '../../../../src/animation/KeyframeTrackConstructor'; // Todo: why so pain ?
import { KeyframeTrackPrototype } from '../../../../src/animation/KeyframeTrackPrototype'; // Todo: why so pain ?
export default QUnit.module( 'Animation', () => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册