import { Vector2, Vector3, Vector4 } from "../GeoMath";
import Curve from "./Curve";
import Type from "./Type";
import Time from "./Time";
import Interval from "./Interval";
import Invariance from "./Invariance";
import AnimUtil from "./AnimUtil";
/**
* キーフレームによる階段関数
*
* あるキーフレームから次のキーフレームの直前まで一定の値を返す階段関数である。
*
* 構築子により任意の関数値の型を指定することができる。
*/
class KFStepCurve extends Curve
{
/** number | vector2 | vector3 | vector4 */
private _value_type: Type;
/** >= 1 */
private _num_keyframes!: number;
private _key_times!: Time[];
private _key_values!: object[];
/**
* type 型の階段関数を keyframes により生成する。
*
* type は任意の型を指定することができる。
*
* keyframes を省略したときは type 型の既定値を返す定数関数と同等になる。keyframes の形式に関しては
* [[KFStepCurve.setKeyFrames setKeyFrames()]] を参照のこと。
*
* @param {mapray.animation.Type} type 関数値の型
* @param {object[]} [keyframes] 初期キーフレーム
*/
constructor( type: Type, keyframes?: (Time | number | Vector2 | Vector3 | Vector4)[] )
{
super();
this._value_type = type;
if ( keyframes !== undefined ) {
// 初期のキーフレームを設定
this.setKeyFrames( keyframes );
}
else {
// 既定のキーフレームを設定
const t0 = Time.fromNumber( 0 );
const dv = type.getDefaultValue();
this.setKeyFrames( [t0, dv] );
}
}
/**
* キーフレーム設定
*
* keyframes により、すべてのキーフレームを指定する。
*
* 条件
* - keyframes.length >= 2 (キーフレーム数 >= 1)
* - すべての i, j において、i < j ⇔ 時刻i < 時刻j
* - すべての i において、値i は構築子の type 引数で指定した型のインスタンス
*
* @param keyframes [時刻0, 値0, 時刻1, 値1, ...]
*/
setKeyFrames( keyframes: (Time | any)[] )
{
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: Type )
{
const from_type = this._value_type;
return type.isConvertible( from_type );
}
override getValue( time: Time, type: Type )
{
const from_type = this._value_type;
const from_value = this._getInterpolatedValue( time );
return type.convertValue( from_type, from_value );
}
override getInvariance( interval: Interval ): Invariance
{
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 );
}
}
/**
* time での補間値を取得
*
* @param time
*
* @return 補間値 (this._value_type に適応した型)
*/
private _getInterpolatedValue( time: Time ): any
{
// 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;