-
Notifications
You must be signed in to change notification settings - Fork 23
/
KFLinearCurve.ts
243 lines (203 loc) · 7.59 KB
/
KFLinearCurve.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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";
import AnimationError from "./AnimationError";
/**
* キーフレームによる線形関数
*
* キーフレーム間を数値またはベクトルを線形に補間する関数である。
*
* 関数値の型は構築子のパラメータにより number, vector2, vector3 または vector4 を指定する。
*/
class KFLinearCurve extends Curve
{
/** number | vector2 | vector3 | vector4 */
private _value_type: Type;
/** 1〜4 */
private _dimension: number;
/** >= 2 */
private _num_keyframes!: number;
private _key_times!: Time[];
private _key_values!: Float64Array;
/**
* type 型の関数を keyframes により生成する。
*
* type は number, vector2, vector3 または vector4 を指定することができる。
*
* keyframes を省略したときは type 型の既定値を返す定数関数と同等になる。keyframes の形式に関しては
* [[KFLinearCurve.setKeyFrames setKeyFrames()]] を参照のこと。
*
* @param type 関数値の型
* @param {object[]} [keyframes] 初期キーフレーム
*/
constructor( type: Type, keyframes?: (Time | number | Vector2 | Vector3 | Vector4)[] )
{
super();
const dimension = AnimUtil.getDimension( type );
if ( dimension == 0 ) {
throw new AnimationError( "unsupported type" );
}
this._value_type = type;
this._dimension = dimension;
if ( keyframes !== undefined ) {
// 初期のキーフレームを設定
this.setKeyFrames( keyframes );
}
else {
// 既定のキーフレームを設定
const t0 = Time.fromNumber( 0 );
const t1 = Time.fromNumber( 1 );
const dv = type.getDefaultValue();
this.setKeyFrames( [t0, dv, t1, dv] );
}
}
/**
* キーフレーム設定
*
* keyframes により、すべてのキーフレームを指定する。
*
* 条件
* - keyframes.length >= 4 (キーフレーム数 >= 2)
* -: すべての i, j において、i < j ⇔ 時刻i < 時刻j
* -: すべての i において、値i は構築子の type 引数で指定した型のインスタンス
*
* @param {object[]} keyframes [時刻0, 値0, 時刻1, 値1, ...]
*/
setKeyFrames( keyframes: any )
{
const dimension = this._dimension;
this._num_keyframes = keyframes.length / 2;
this._key_times = new Array( this._num_keyframes );
this._key_values = new Float64Array( this._num_keyframes * dimension );
// キーフレームを設定
for ( let ti = 0, vi = 0; ti < this._num_keyframes; ++ti, vi += dimension ) {
const time = keyframes[2*ti ];
const value = keyframes[2*ti + 1];
// 時刻を配列に設定
this._key_times[ti] = time;
// 値を配列に設定
if ( dimension == 1 ) {
// スカラー
this._key_values[vi] = value;
}
else {
// ベクトル
for ( let j = 0; j < dimension; ++j ) {
this._key_values[vi + j] = value[j];
}
}
}
// 全時刻の値が変化
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
{
const first_time = this._key_times[0];
const last_time = this._key_times[this._num_keyframes - 1];
const ival_inner = new Interval( first_time, last_time, true, true );
// 全体の不変性情報 (2区間程度なので毎回生成)
const invr_full = new Invariance();
invr_full.write( ival_inner.getPrecedings() ); // 最初のキーの時刻とその前の区間
invr_full.write( ival_inner.getFollowings() ); // 最後のキーの時刻とその後の区間
// interval 範囲に絞って返す
return invr_full.getNarrowed( interval );
}
/**
* time での補間値を取得
*
* @param time
*
* @return 補間値 (this._value_type に適応した型)
*/
private _getInterpolatedValue( time: Time ): any
{
// this._key_times に time より後の時刻が存在すれば、その中で最小のインデックス
// そのような時刻が存在しなければ this._num_keyframes
const index = AnimUtil.findKeyFrameIndex( time, this._key_times, 0, this._num_keyframes );
if ( index == 0 ) {
// time が最初のキー時刻と同じか、その前のときは最初のキー値で一定
return this._createKeyFrameValue( 0 );
}
else if ( index == this._num_keyframes ) {
// time が最後のキー時刻と同じか、その後のときは最後のキー値で一定
return this._createKeyFrameValue( index - 1 );
}
else {
// その他のときは前後のキー値で線形補間
return this._createValueBy2Keys( index - 1, index, time );
}
}
/**
* キーフレーム値を生成
*
* @param index キーフレームのインデックス
*
* @return キーフレーム値 (this._value_type に適応した型)
*/
private _createKeyFrameValue( index: number )
{
const dimension = this._dimension;
const key_values = this._key_values;
if ( dimension == 1 ) {
// スカラー
return key_values[index];
}
else {
// ベクトル
const vi = dimension * index;
const vec = new Float64Array( dimension );
for ( let i = 0; i < dimension; ++i ) {
vec[i] = key_values[vi + i];
}
return vec;
}
}
/**
* キーフレーム間の補間値を生成
*
* @param i0 先キーフレームのインデックス
* @param i1 後キーフレームのインデックス
* @param time
*
* @return 補間値 (this._value_type に適応した型)
*/
private _createValueBy2Keys( i0: number, i1: number, time: Time ): any
{
const x0 = this._key_times[i0].toNumber();
const x1 = this._key_times[i1].toNumber();
const r1 = (time.toNumber() - x0) / (x1 - x0);
const r0 = 1 - r1;
const dimension = this._dimension;
const key_values = this._key_values;
if ( dimension == 1 ) {
// スカラー
return r0*key_values[i0] + r1*key_values[i1];
}
else {
// ベクトル
let vi0 = dimension * i0;
let vi1 = dimension * i1;
let vec = new Float64Array( dimension );
for ( let i = 0; i < dimension; ++i ) {
vec[i] = r0*key_values[vi0 + i] + r1*key_values[vi1 + i];
}
return vec;
}
}
}
export default KFLinearCurve;