import Curve from "./Curve"; import Type from "./Type"; import Time from "./Time"; import Interval from "./Interval"; import Invariance from "./Invariance"; import AnimUtil from "./AnimUtil"; /** * @summary キーフレームによる階段関数 * * @classdesc * <p>あるキーフレームから次のキーフレームの直前まで一定の値を返す階段関数である。</p> * <p>構築子により任意の関数値の型を指定することができる。</p> * * @memberof mapray.animation * @extends mapray.animation.Curve */ class KFStepCurve extends Curve { /** * @desc * <p>type 型の階段関数を keyframes により生成する。</p> * <p>type は任意の型を指定することができる。</p> * <p>keyframes を省略したときは type 型の既定値を返す定数関数と同等になる。keyframes の形式に関しては * {@link mapray.animation.KFStepCurve#setKeyFrames setKeyFrames()} を参照のこと。</p> * * @param {mapray.animation.Type} type 関数値の型 * @param {object[]} [keyframes] 初期キーフレーム */ constructor( type, keyframes ) { super(); this._value_type = type; // any type this._num_keyframes = undefined; // >= 1 this._key_times = undefined; // Time[] this._key_values = undefined; // object[] if ( keyframes !== undefined ) { // 初期のキーフレームを設定 this.setKeyFrames( keyframes ); } else { // 既定のキーフレームを設定 const t0 = Time.fromNumber( 0 ); const dv = type.getDefaultValue(); this.setKeyFrames( [t0, dv] ); } } /** * @summary キーフレーム設定 * * @desc * <p>keyframes により、すべてのキーフレームを指定する。</p> * * <p> * 条件1: keyframes.length >= 2 (キーフレーム数 >= 1)<br> * 条件2: すべての i, j において、i < j ⇔ 時刻i < 時刻j<br> * 条件3: すべての i において、値i は構築子の type 引数で指定した型のインスタンス * </p> * * @param {object[]} keyframes [時刻0, 値0, 時刻1, 値1, ...] */ setKeyFrames( keyframes ) { this._num_keyframes = keyframes.length / 2; this._key_times = new Array( this._num_keyframes ); this._key_values = new Array( this._num_keyframes ); // キーフレームを設定 for ( let i = 0; i < this._num_keyframes; ++i ) { const time = keyframes[2*i ]; const value = keyframes[2*i + 1]; this._key_times[i] = time; this._key_values[i] = this._value_type.getCloneValue( value ); } // 全時刻の値が変化 this.notifyValueChange( Interval.UNIVERSAL ); } /** * @override */ isTypeSupported( type ) { const from_type = this._value_type; return type.isConvertible( from_type ); } /** * @override */ getValue( time, type ) { const from_type = this._value_type; const from_value = this._getInterpolatedValue( time ); return type.convertValue( from_type, from_value ); } /** * @override */ getInvariance( interval ) { if ( this._num_keyframes == 1 ) { // キーフレームが 1 個のときは ConstantCurve と同じく、全時間で一定値 // (UNIVERSAL と非空区間は必ず交差するので interval の参照は不要) return new Invariance().write( Interval.UNIVERSAL ); } else { // assert: this._num_keyframes >= 2 const invr = new Invariance(); // 最初から2番目のキー時刻より前は一定値 const first = this._key_times[1]; invr.write( new Interval( first, first ).getPrecedings() ); // 最後のキー時刻とその後は一定値 const lastL = this._key_times[this._num_keyframes - 2]; const lastU = this._key_times[this._num_keyframes - 1]; invr.write( new Interval( lastL, lastU, false, true ).getFollowings() ); // interval 範囲に絞って返す return invr.getNarrowed( interval ); } } /** * @summary time での補間値を取得 * * @param {mapray.animation.Time} time * * @return {object} 補間値 (this._value_type に適応した型) * * @private */ _getInterpolatedValue( time ) { // this._key_times に time より後の時刻が存在すれば、その中で最小のインデックス // そのような時刻が存在しなければ this._num_keyframes const found = AnimUtil.findKeyFrameIndex( time, this._key_times, 0, this._num_keyframes ); // キー値のインデックス const index = (found > 0) ? found - 1 : 0; // 補間値を生成 return this._value_type.getCloneValue( this._key_values[index] ); } } export default KFStepCurve;