physics-animator 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/AnimationSequencer.js +156 -0
- package/dist/cjs/Animator.js +310 -0
- package/{src/Spring.ts → dist/cjs/Spring.js} +38 -80
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/react/index.js +19 -0
- package/dist/cjs/react/useAnimator.js +29 -0
- package/dist/cjs/react/useSpringState.js +16 -0
- package/dist/cjs/react/useSpringValue.js +59 -0
- package/dist/cjs/three/ThreeAnimator.js +453 -0
- package/dist/cjs/three/index.js +17 -0
- package/dist/esm/package.json +1 -0
- package/package.json +20 -13
- package/src/AnimationSequencer.ts +0 -193
- package/src/Animator.ts +0 -377
- package/src/index.ts +0 -3
- package/src/react/index.ts +0 -3
- package/src/react/useAnimator.ts +0 -45
- package/src/react/useSpringState.ts +0 -22
- package/src/react/useSpringValue.ts +0 -81
- package/src/three/ThreeAnimator.ts +0 -605
- package/src/three/index.ts +0 -1
- package/tsconfig.json +0 -14
- /package/dist/{AnimationSequencer.js → esm/AnimationSequencer.js} +0 -0
- /package/dist/{Animator.js → esm/Animator.js} +0 -0
- /package/dist/{Spring.js → esm/Spring.js} +0 -0
- /package/dist/{index.js → esm/index.js} +0 -0
- /package/dist/{react → esm/react}/index.js +0 -0
- /package/dist/{react → esm/react}/useAnimator.js +0 -0
- /package/dist/{react → esm/react}/useSpringState.js +0 -0
- /package/dist/{react → esm/react}/useSpringValue.js +0 -0
- /package/dist/{three → esm/three}/ThreeAnimator.js +0 -0
- /package/dist/{three → esm/three}/index.js +0 -0
- /package/dist/{AnimationSequencer.d.ts → types/AnimationSequencer.d.ts} +0 -0
- /package/dist/{Animator.d.ts → types/Animator.d.ts} +0 -0
- /package/dist/{Spring.d.ts → types/Spring.d.ts} +0 -0
- /package/dist/{index.d.ts → types/index.d.ts} +0 -0
- /package/dist/{react → types/react}/index.d.ts +0 -0
- /package/dist/{react → types/react}/useAnimator.d.ts +0 -0
- /package/dist/{react → types/react}/useSpringState.d.ts +0 -0
- /package/dist/{react → types/react}/useSpringValue.d.ts +0 -0
- /package/dist/{three → types/three}/ThreeAnimator.d.ts +0 -0
- /package/dist/{three → types/three}/index.d.ts +0 -0
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { useRef } from "react";
|
|
2
|
-
import { Animator } from "../Animator.js";
|
|
3
|
-
import { Spring, SpringParameters } from "../Spring.js";
|
|
4
|
-
import { useAnimator } from "./useAnimator.js";
|
|
5
|
-
import { useInitializer } from "use-initializer";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A value that animates to a target value using a spring animation.
|
|
9
|
-
* This will **not** cause a re-render when the value changes.
|
|
10
|
-
*
|
|
11
|
-
* See {@link useSpringState} for a version that does cause re-renders.
|
|
12
|
-
*/
|
|
13
|
-
export function useSpringValue<T extends number | number[] | { [field: PropertyKey]: number }>(
|
|
14
|
-
options: {
|
|
15
|
-
animator?: Animator,
|
|
16
|
-
initial: T;
|
|
17
|
-
target: T;
|
|
18
|
-
} & SpringParameters,
|
|
19
|
-
onChange: (value: T) => void
|
|
20
|
-
) {
|
|
21
|
-
const animator = useAnimator(options.animator);
|
|
22
|
-
|
|
23
|
-
const springValue = useInitializer(() => {
|
|
24
|
-
let value = structuredClone(options.initial);
|
|
25
|
-
return {
|
|
26
|
-
get value() {
|
|
27
|
-
return value;
|
|
28
|
-
},
|
|
29
|
-
set value(newValue: T) {
|
|
30
|
-
value = newValue;
|
|
31
|
-
onChange(value);
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const afterStepListener = useRef<{ remove:() => void } | null>(null);
|
|
37
|
-
|
|
38
|
-
switch (typeof options.initial) {
|
|
39
|
-
case 'number': {
|
|
40
|
-
animator.springTo(
|
|
41
|
-
springValue,
|
|
42
|
-
'value',
|
|
43
|
-
options.target as any,
|
|
44
|
-
options
|
|
45
|
-
);
|
|
46
|
-
} break;
|
|
47
|
-
default: {
|
|
48
|
-
if (Array.isArray(options.initial)) {
|
|
49
|
-
for (let i = 0; i < options.initial.length; i++) {
|
|
50
|
-
animator.springTo(
|
|
51
|
-
springValue.value as number[],
|
|
52
|
-
i,
|
|
53
|
-
(options.target as number[])[i],
|
|
54
|
-
options
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
// assume object, iterate over keys
|
|
59
|
-
for (const key in options.initial) {
|
|
60
|
-
animator.springTo(
|
|
61
|
-
springValue.value,
|
|
62
|
-
key,
|
|
63
|
-
(options.target as any)[key],
|
|
64
|
-
options
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!afterStepListener.current) {
|
|
70
|
-
afterStepListener.current = animator.onAfterStep.addListener(() => {
|
|
71
|
-
onChange(springValue.value);
|
|
72
|
-
});
|
|
73
|
-
animator.onAllComplete(springValue.value, () => {
|
|
74
|
-
afterStepListener.current?.remove();
|
|
75
|
-
afterStepListener.current = null;
|
|
76
|
-
}, 'once');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
} break;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,605 +0,0 @@
|
|
|
1
|
-
import { Euler, Matrix4, Quaternion, Vector2, Vector3, Vector4 } from "three";
|
|
2
|
-
import { Animator, Tween, TweenStepFn } from "../Animator.js";
|
|
3
|
-
import { Spring, SpringParameters } from "../Spring.js";
|
|
4
|
-
|
|
5
|
-
export enum QuaternionSpringMode {
|
|
6
|
-
DirectionRollCartesian,
|
|
7
|
-
YawPitchRoll,
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type SupportedTypes = Vector4 | Vector3 | Vector2 | Quaternion | Euler | number;
|
|
11
|
-
|
|
12
|
-
// type KeysOfType<T, U> = {
|
|
13
|
-
// [K in keyof T]: T[K] extends U ? K : never
|
|
14
|
-
// }[keyof T]
|
|
15
|
-
|
|
16
|
-
type KeysOfType<T, U> = keyof T;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Extends Animator to add support for animating vectors and quaternions
|
|
20
|
-
*/
|
|
21
|
-
export class ThreeAnimator {
|
|
22
|
-
|
|
23
|
-
animator: Animator;
|
|
24
|
-
|
|
25
|
-
get onAfterStep() {
|
|
26
|
-
return this.animator.onAfterStep;
|
|
27
|
-
}
|
|
28
|
-
get onBeforeStep() {
|
|
29
|
-
return this.animator.onBeforeStep;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
quaternionSprings = new Map<Quaternion, {
|
|
33
|
-
q: Quaternion,
|
|
34
|
-
target: Quaternion,
|
|
35
|
-
direction: Vector3,
|
|
36
|
-
directionVelocity: Vector3,
|
|
37
|
-
rollVelocity: number,
|
|
38
|
-
params: Spring.PhysicsParameters | null,
|
|
39
|
-
mode: QuaternionSpringMode,
|
|
40
|
-
}>();
|
|
41
|
-
|
|
42
|
-
constructor(animator: Animator = new Animator()) {
|
|
43
|
-
this.animator = animator;
|
|
44
|
-
this.animator.onBeforeStep.on(e => this.stepQuaternionSprings(e.dt_s));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
setTo<
|
|
48
|
-
Obj,
|
|
49
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
50
|
-
T extends Obj[Name] & SupportedTypes
|
|
51
|
-
>(
|
|
52
|
-
object: Obj,
|
|
53
|
-
field: Name,
|
|
54
|
-
target: T
|
|
55
|
-
) {
|
|
56
|
-
if (target instanceof Vector4) {
|
|
57
|
-
let v = object[field] as Vector4;
|
|
58
|
-
this.animator.setTo(v, 'x', target.x);
|
|
59
|
-
this.animator.setTo(v, 'y', target.y);
|
|
60
|
-
this.animator.setTo(v, 'z', target.z);
|
|
61
|
-
this.animator.setTo(v, 'w', target.w);
|
|
62
|
-
} else if (target instanceof Vector3) {
|
|
63
|
-
let v = object[field] as Vector3;
|
|
64
|
-
this.animator.setTo(v, 'x', target.x);
|
|
65
|
-
this.animator.setTo(v, 'y', target.y);
|
|
66
|
-
this.animator.setTo(v, 'z', target.z);
|
|
67
|
-
} else if (target instanceof Vector2) {
|
|
68
|
-
let v = object[field] as Vector2;
|
|
69
|
-
this.animator.setTo(v, 'x', target.x);
|
|
70
|
-
this.animator.setTo(v, 'y', target.y);
|
|
71
|
-
} else if (target instanceof Quaternion) {
|
|
72
|
-
let q = object[field] as Quaternion;
|
|
73
|
-
this.animator.setTo(q, 'x', target.x);
|
|
74
|
-
this.animator.setTo(q, 'y', target.y);
|
|
75
|
-
this.animator.setTo(q, 'z', target.z);
|
|
76
|
-
this.animator.setTo(q, 'w', target.w);
|
|
77
|
-
} else if (target instanceof Euler) {
|
|
78
|
-
let e = object[field] as Euler;
|
|
79
|
-
this.animator.setTo(e, 'x', target.x);
|
|
80
|
-
this.animator.setTo(e, 'y', target.y);
|
|
81
|
-
this.animator.setTo(e, 'z', target.z);
|
|
82
|
-
e.order = target.order;
|
|
83
|
-
} else { // number
|
|
84
|
-
this.animator.setTo(object, field, target as any);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
springTo<
|
|
89
|
-
Obj,
|
|
90
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
91
|
-
T extends Obj[Name] & SupportedTypes
|
|
92
|
-
>(
|
|
93
|
-
object: Obj,
|
|
94
|
-
field: Name,
|
|
95
|
-
target: T,
|
|
96
|
-
params: SpringParameters = { duration_s: 0.5 },
|
|
97
|
-
mode?: QuaternionSpringMode
|
|
98
|
-
) {
|
|
99
|
-
if (target instanceof Vector4) {
|
|
100
|
-
let v = object[field] as Vector4;
|
|
101
|
-
this.animator.springTo(v, 'x', target.x, params);
|
|
102
|
-
this.animator.springTo(v, 'y', target.y, params);
|
|
103
|
-
this.animator.springTo(v, 'z', target.z, params);
|
|
104
|
-
this.animator.springTo(v, 'w', target.w, params);
|
|
105
|
-
} else if (target instanceof Vector3) {
|
|
106
|
-
let v = object[field] as Vector3;
|
|
107
|
-
this.animator.springTo(v, 'x', target.x, params);
|
|
108
|
-
this.animator.springTo(v, 'y', target.y, params);
|
|
109
|
-
this.animator.springTo(v, 'z', target.z, params);
|
|
110
|
-
} else if (target instanceof Vector2) {
|
|
111
|
-
let v = object[field] as Vector2;
|
|
112
|
-
this.animator.springTo(v, 'x', target.x, params);
|
|
113
|
-
this.animator.springTo(v, 'y', target.y, params);
|
|
114
|
-
} else if (target instanceof Quaternion) {
|
|
115
|
-
let q = object[field] as Quaternion;
|
|
116
|
-
let spring = this.getQuaternionSpring(q);
|
|
117
|
-
|
|
118
|
-
// update
|
|
119
|
-
spring.target.copy(target).normalize();
|
|
120
|
-
spring.params = Spring.getPhysicsParameters(params);
|
|
121
|
-
spring.mode = mode ?? QuaternionSpringMode.DirectionRollCartesian;
|
|
122
|
-
|
|
123
|
-
} else if (target instanceof Euler) {
|
|
124
|
-
let e = object[field] as Euler;
|
|
125
|
-
this.animator.springTo(e, 'x', target.x, params);
|
|
126
|
-
this.animator.springTo(e, 'y', target.y, params);
|
|
127
|
-
this.animator.springTo(e, 'z', target.z, params);
|
|
128
|
-
e.order = target.order;
|
|
129
|
-
|
|
130
|
-
} else { // number
|
|
131
|
-
this.animator.springTo(object, field, target as any, params);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
customTweenTo<
|
|
136
|
-
Obj,
|
|
137
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
138
|
-
T extends Obj[Name] & SupportedTypes
|
|
139
|
-
>(
|
|
140
|
-
object: Obj,
|
|
141
|
-
field: Name,
|
|
142
|
-
target: T,
|
|
143
|
-
duration_s: number,
|
|
144
|
-
step: TweenStepFn
|
|
145
|
-
) {
|
|
146
|
-
if (target instanceof Vector4) {
|
|
147
|
-
let v = object[field] as Vector4;
|
|
148
|
-
this.animator.customTweenTo(v, 'x', target.x, duration_s, step);
|
|
149
|
-
this.animator.customTweenTo(v, 'y', target.y, duration_s, step);
|
|
150
|
-
this.animator.customTweenTo(v, 'z', target.z, duration_s, step);
|
|
151
|
-
this.animator.customTweenTo(v, 'w', target.w, duration_s, step);
|
|
152
|
-
} else if (target instanceof Vector3) {
|
|
153
|
-
let v = object[field] as Vector3;
|
|
154
|
-
this.animator.customTweenTo(v, 'x', target.x, duration_s, step);
|
|
155
|
-
this.animator.customTweenTo(v, 'y', target.y, duration_s, step);
|
|
156
|
-
this.animator.customTweenTo(v, 'z', target.z, duration_s, step);
|
|
157
|
-
} else if (target instanceof Vector2) {
|
|
158
|
-
let v = object[field] as Vector2;
|
|
159
|
-
this.animator.customTweenTo(v, 'x', target.x, duration_s, step);
|
|
160
|
-
this.animator.customTweenTo(v, 'y', target.y, duration_s, step);
|
|
161
|
-
} else if (target instanceof Quaternion) {
|
|
162
|
-
throw new Error('Quaternion customTweenTo not yet supported, try springTo or use Euler');
|
|
163
|
-
} else if (target instanceof Euler) {
|
|
164
|
-
let e = object[field] as Euler;
|
|
165
|
-
this.animator.customTweenTo(e, 'x', target.x, duration_s, step);
|
|
166
|
-
this.animator.customTweenTo(e, 'y', target.y, duration_s, step);
|
|
167
|
-
this.animator.customTweenTo(e, 'z', target.z, duration_s, step);
|
|
168
|
-
e.order = target.order;
|
|
169
|
-
} else { // number
|
|
170
|
-
this.animator.customTweenTo(object, field, target as any, duration_s, step);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
linearTo<
|
|
175
|
-
Obj,
|
|
176
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
177
|
-
T extends Obj[Name] & SupportedTypes
|
|
178
|
-
>(
|
|
179
|
-
object: Obj,
|
|
180
|
-
field: Name,
|
|
181
|
-
target: T,
|
|
182
|
-
duration_s: number
|
|
183
|
-
) {
|
|
184
|
-
this.customTweenTo(object, field, target, duration_s, Tween.linearStep);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
easeInOutTo<
|
|
188
|
-
Obj,
|
|
189
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
190
|
-
T extends Obj[Name] & SupportedTypes
|
|
191
|
-
>(
|
|
192
|
-
object: Obj,
|
|
193
|
-
field: Name,
|
|
194
|
-
target: T,
|
|
195
|
-
duration_s: number
|
|
196
|
-
) {
|
|
197
|
-
this.customTweenTo(object, field, target, duration_s, Tween.easeInOutStep);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
easeInTo<
|
|
201
|
-
Obj,
|
|
202
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
203
|
-
T extends Obj[Name] & SupportedTypes
|
|
204
|
-
>(
|
|
205
|
-
object: Obj,
|
|
206
|
-
field: Name,
|
|
207
|
-
target: T,
|
|
208
|
-
duration_s: number
|
|
209
|
-
) {
|
|
210
|
-
this.customTweenTo(object, field, target, duration_s, Tween.easeInStep);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
easeOutTo<
|
|
214
|
-
Obj,
|
|
215
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
216
|
-
T extends Obj[Name] & SupportedTypes
|
|
217
|
-
>(
|
|
218
|
-
object: Obj,
|
|
219
|
-
field: Name,
|
|
220
|
-
target: T,
|
|
221
|
-
duration_s: number
|
|
222
|
-
) {
|
|
223
|
-
this.customTweenTo(object, field, target, duration_s, Tween.easeOutStep);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
step(dt_s: number) {
|
|
227
|
-
this.animator.step(dt_s);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
tick() {
|
|
231
|
-
this.animator.tick();
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
remove<
|
|
235
|
-
Obj,
|
|
236
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
237
|
-
>(
|
|
238
|
-
object: Obj,
|
|
239
|
-
field: Name
|
|
240
|
-
) {
|
|
241
|
-
let v = object[field];
|
|
242
|
-
if (v instanceof Vector4) {
|
|
243
|
-
this.animator.remove(v, 'x');
|
|
244
|
-
this.animator.remove(v, 'y');
|
|
245
|
-
this.animator.remove(v, 'z');
|
|
246
|
-
this.animator.remove(v, 'w');
|
|
247
|
-
} else if (v instanceof Vector3) {
|
|
248
|
-
this.animator.remove(v, 'x');
|
|
249
|
-
this.animator.remove(v, 'y');
|
|
250
|
-
this.animator.remove(v, 'z');
|
|
251
|
-
} else if (v instanceof Vector2) {
|
|
252
|
-
this.animator.remove(v, 'x');
|
|
253
|
-
this.animator.remove(v, 'y');
|
|
254
|
-
} else if (v instanceof Quaternion) {
|
|
255
|
-
this.quaternionSprings.delete(v);
|
|
256
|
-
} else if (v instanceof Euler) {
|
|
257
|
-
this.animator.remove(v, 'x');
|
|
258
|
-
this.animator.remove(v, 'y');
|
|
259
|
-
this.animator.remove(v, 'z');
|
|
260
|
-
} else { // number
|
|
261
|
-
this.animator.remove(object, field);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
removeAll() {
|
|
266
|
-
this.animator.removeAll();
|
|
267
|
-
this.quaternionSprings.clear();
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
getVelocity<
|
|
271
|
-
Obj,
|
|
272
|
-
Name extends KeysOfType<Obj, SupportedTypes>,
|
|
273
|
-
T extends Obj[Name] &
|
|
274
|
-
(Vector4 | Vector3 | Vector2 | { directionVelocity: Vector3, rollVelocity: number } | Euler | number) // supported types
|
|
275
|
-
>(
|
|
276
|
-
object: Obj,
|
|
277
|
-
field: Name,
|
|
278
|
-
into?: T,
|
|
279
|
-
): T {
|
|
280
|
-
let target = object[field];
|
|
281
|
-
if (target instanceof Vector4) {
|
|
282
|
-
let i = (into as Vector4) ?? new Vector4();
|
|
283
|
-
i.x = this.animator.getVelocity(target, 'x');
|
|
284
|
-
i.y = this.animator.getVelocity(target, 'y');
|
|
285
|
-
i.z = this.animator.getVelocity(target, 'z');
|
|
286
|
-
i.w = this.animator.getVelocity(target, 'w');
|
|
287
|
-
return i as T;
|
|
288
|
-
} else if (target instanceof Vector3) {
|
|
289
|
-
let i = (into as Vector3) ?? new Vector3();
|
|
290
|
-
i.x = this.animator.getVelocity(target, 'x');
|
|
291
|
-
i.y = this.animator.getVelocity(target, 'y');
|
|
292
|
-
i.z = this.animator.getVelocity(target, 'z');
|
|
293
|
-
return i as T;
|
|
294
|
-
} else if (target instanceof Vector2) {
|
|
295
|
-
let i = (into as Vector2) ?? new Vector2();
|
|
296
|
-
i.x = this.animator.getVelocity(target, 'x');
|
|
297
|
-
i.y = this.animator.getVelocity(target, 'y');
|
|
298
|
-
return i as T;
|
|
299
|
-
} else if (target instanceof Quaternion) {
|
|
300
|
-
let spring = this.quaternionSprings.get(target);
|
|
301
|
-
return {
|
|
302
|
-
directionVelocity: spring?.directionVelocity ?? new Vector3(),
|
|
303
|
-
rollVelocity: spring?.rollVelocity ?? 0
|
|
304
|
-
} as T;
|
|
305
|
-
} else if (target instanceof Euler) {
|
|
306
|
-
let i = (into as Euler) ?? new Euler();
|
|
307
|
-
i.x = this.animator.getVelocity(target, 'x');
|
|
308
|
-
i.y = this.animator.getVelocity(target, 'y');
|
|
309
|
-
i.z = this.animator.getVelocity(target, 'z');
|
|
310
|
-
i.order = target.order;
|
|
311
|
-
return i as T;
|
|
312
|
-
} else { // number
|
|
313
|
-
return this.animator.getVelocity(object, field) as T;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
startAnimationFrameLoop() {
|
|
318
|
-
return this.animator.startAnimationFrameLoop();
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
startIntervalLoop(interval_ms?: number) {
|
|
322
|
-
return this.animator.startIntervalLoop(interval_ms);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
stop() {
|
|
326
|
-
this.animator.stop();
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private stepQuaternionSprings(dt_s: number) {
|
|
330
|
-
// step quaternion springs
|
|
331
|
-
this.quaternionSprings.forEach((spring, q) => {
|
|
332
|
-
if (spring.params) {
|
|
333
|
-
if (spring.mode === QuaternionSpringMode.DirectionRollCartesian) {
|
|
334
|
-
stepSpringQuaternion(dt_s, spring, spring.params);
|
|
335
|
-
} else {
|
|
336
|
-
stepSpringQuaternionSpherical(dt_s, spring, spring.params);
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
// copy target
|
|
340
|
-
q.copy(spring.target);
|
|
341
|
-
// zero velocity
|
|
342
|
-
spring.directionVelocity.set(0, 0, 0);
|
|
343
|
-
spring.rollVelocity = 0;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// if quaternions match and velocity close to zero, remove spring
|
|
347
|
-
if (Math.abs(q.dot(spring.target)) > 0.999 && spring.directionVelocity.lengthSq() < 0.0001 && Math.abs(spring.rollVelocity) < 0.0001) {
|
|
348
|
-
this.quaternionSprings.delete(q);
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private getQuaternionSpring(q: Quaternion) {
|
|
354
|
-
let spring = this.quaternionSprings.get(q);
|
|
355
|
-
if (!spring) {
|
|
356
|
-
_m.makeRotationFromQuaternion(q);
|
|
357
|
-
let direction = new Vector3();
|
|
358
|
-
_m.extractBasis(new Vector3(), new Vector3(), direction);
|
|
359
|
-
spring = {
|
|
360
|
-
q: q,
|
|
361
|
-
target: new Quaternion(),
|
|
362
|
-
direction: direction,
|
|
363
|
-
directionVelocity: new Vector3(),
|
|
364
|
-
rollVelocity: 0,
|
|
365
|
-
params: null,
|
|
366
|
-
mode: QuaternionSpringMode.DirectionRollCartesian,
|
|
367
|
-
};
|
|
368
|
-
this.quaternionSprings.set(q, spring);
|
|
369
|
-
}
|
|
370
|
-
return spring;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Analytic quaternion spring
|
|
377
|
-
*
|
|
378
|
-
* Todo:
|
|
379
|
-
* - for cameras we want to prefer rotations in xz plane rather than z
|
|
380
|
-
* - animate direction in spherical space rather than cartesian
|
|
381
|
-
*/
|
|
382
|
-
// working variables to avoid allocations
|
|
383
|
-
const _m = new Matrix4();
|
|
384
|
-
const _x = new Vector3();
|
|
385
|
-
const _y = new Vector3();
|
|
386
|
-
const _z = new Vector3();
|
|
387
|
-
const _qMatrix = new Matrix4();
|
|
388
|
-
const _springState = {x: 0, v: 0, targetX: 0}
|
|
389
|
-
function stepSpringQuaternion(
|
|
390
|
-
dt_s: number,
|
|
391
|
-
state: {
|
|
392
|
-
q: Quaternion,
|
|
393
|
-
target: Quaternion,
|
|
394
|
-
direction: Vector3,
|
|
395
|
-
directionVelocity: Vector3,
|
|
396
|
-
rollVelocity: number,
|
|
397
|
-
},
|
|
398
|
-
parameters: Spring.PhysicsParameters
|
|
399
|
-
) {
|
|
400
|
-
|
|
401
|
-
// step direction spring in cartesian space
|
|
402
|
-
// we should do this in spherical in the future
|
|
403
|
-
let targetDirection = new Vector3();
|
|
404
|
-
let targetYBasis = new Vector3();
|
|
405
|
-
_m.makeRotationFromQuaternion(state.target);
|
|
406
|
-
_m.extractBasis(_x, targetYBasis, targetDirection);
|
|
407
|
-
|
|
408
|
-
let directionBefore = state.direction.clone();
|
|
409
|
-
|
|
410
|
-
// step spring direction
|
|
411
|
-
_springState.x = state.direction.x;
|
|
412
|
-
_springState.v = state.directionVelocity.x;
|
|
413
|
-
_springState.targetX = targetDirection.x;
|
|
414
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
415
|
-
state.direction.x = _springState.x;
|
|
416
|
-
state.directionVelocity.x = _springState.v;
|
|
417
|
-
|
|
418
|
-
_springState.x = state.direction.y;
|
|
419
|
-
_springState.v = state.directionVelocity.y;
|
|
420
|
-
_springState.targetX = targetDirection.y;
|
|
421
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
422
|
-
state.direction.y = _springState.x;
|
|
423
|
-
state.directionVelocity.y = _springState.v;
|
|
424
|
-
|
|
425
|
-
_springState.x = state.direction.z;
|
|
426
|
-
_springState.v = state.directionVelocity.z;
|
|
427
|
-
_springState.targetX = targetDirection.z;
|
|
428
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
429
|
-
state.direction.z = _springState.x;
|
|
430
|
-
state.directionVelocity.z = _springState.v;
|
|
431
|
-
|
|
432
|
-
// update quaternion
|
|
433
|
-
let directionDeltaQuaternion = new Quaternion().setFromUnitVectors(_x.copy(directionBefore).normalize(), _y.copy(state.direction).normalize());
|
|
434
|
-
state.q.premultiply(directionDeltaQuaternion)
|
|
435
|
-
// this forces synchronization of direction and quaternion
|
|
436
|
-
alignQuaternion(state.q, state.direction, _qMatrix);
|
|
437
|
-
|
|
438
|
-
// determine roll required to align yBasis with targetYBasis
|
|
439
|
-
let directionToTargetQuaternion = new Quaternion().setFromUnitVectors(state.direction.clone().normalize(), targetDirection);
|
|
440
|
-
_m.makeRotationFromQuaternion(state.q);
|
|
441
|
-
_m.extractBasis(_x, _y, _z);
|
|
442
|
-
let newYBasis = _y.applyQuaternion(directionToTargetQuaternion).clone();
|
|
443
|
-
let rollQuaternion = new Quaternion().setFromUnitVectors(newYBasis, targetYBasis);
|
|
444
|
-
// to axis angle (clamp w)
|
|
445
|
-
let rollAngle = Math.acos(Math.min(1, Math.max(-1, rollQuaternion.w))) * 2;
|
|
446
|
-
let rollSign = _x.crossVectors(newYBasis, targetYBasis).dot(targetDirection) < 0 ? -1 : 1;
|
|
447
|
-
rollAngle = -rollSign * rollAngle;
|
|
448
|
-
|
|
449
|
-
// step roll spring
|
|
450
|
-
_springState.x = rollAngle;
|
|
451
|
-
_springState.v = state.rollVelocity;
|
|
452
|
-
_springState.targetX = 0;
|
|
453
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
454
|
-
state.rollVelocity = _springState.v;
|
|
455
|
-
let rollAfter = _springState.x;
|
|
456
|
-
let rollDelta = rollAfter - rollAngle;
|
|
457
|
-
|
|
458
|
-
// apply roll correction
|
|
459
|
-
let rollDeltaQuaternion = new Quaternion().setFromAxisAngle(state.direction.clone().normalize(), rollDelta /*-rollAngle * 0.1*/);
|
|
460
|
-
state.q.premultiply(rollDeltaQuaternion);
|
|
461
|
-
state.q.normalize();
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function stepSpringQuaternionSpherical(
|
|
465
|
-
dt_s: number,
|
|
466
|
-
state: {
|
|
467
|
-
q: Quaternion,
|
|
468
|
-
target: Quaternion,
|
|
469
|
-
direction: Vector3,
|
|
470
|
-
directionVelocity: Vector3,
|
|
471
|
-
rollVelocity: number,
|
|
472
|
-
},
|
|
473
|
-
parameters: Spring.PhysicsParameters
|
|
474
|
-
) {
|
|
475
|
-
let azimuthVelocity = state.directionVelocity.x;
|
|
476
|
-
let elevationVelocity = state.directionVelocity.y;
|
|
477
|
-
|
|
478
|
-
// get quaternion in spherical coordinates
|
|
479
|
-
let elAzRoll = quaternionToPitchYawRoll(state.q);
|
|
480
|
-
|
|
481
|
-
// get target quaternion in spherical coordinates
|
|
482
|
-
let targetElAzRoll = quaternionToPitchYawRoll(state.target);
|
|
483
|
-
|
|
484
|
-
// step springs
|
|
485
|
-
_springState.x = elAzRoll.x;
|
|
486
|
-
_springState.v = elevationVelocity;
|
|
487
|
-
_springState.targetX = getAngleContinuous(targetElAzRoll.x, elAzRoll.x);
|
|
488
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
489
|
-
elevationVelocity = _springState.v;
|
|
490
|
-
let elevationAfter = _springState.x;
|
|
491
|
-
|
|
492
|
-
_springState.x = elAzRoll.y;
|
|
493
|
-
_springState.v = azimuthVelocity;
|
|
494
|
-
_springState.targetX = getAngleContinuous(targetElAzRoll.y, elAzRoll.y);
|
|
495
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
496
|
-
azimuthVelocity = _springState.v;
|
|
497
|
-
let azimuthAfter = _springState.x;
|
|
498
|
-
|
|
499
|
-
// update directionVelocity
|
|
500
|
-
state.directionVelocity.x = azimuthVelocity;
|
|
501
|
-
state.directionVelocity.y = elevationVelocity;
|
|
502
|
-
|
|
503
|
-
// compose quaternion from spherical coordinates
|
|
504
|
-
// direction from azimuth and elevation
|
|
505
|
-
let direction = new Vector3(
|
|
506
|
-
Math.cos(azimuthAfter) * Math.cos(elevationAfter),
|
|
507
|
-
Math.sin(elevationAfter),
|
|
508
|
-
Math.sin(azimuthAfter) * Math.cos(elevationAfter)
|
|
509
|
-
).normalize();
|
|
510
|
-
|
|
511
|
-
// roll alignment spring
|
|
512
|
-
_springState.x = elAzRoll.z;
|
|
513
|
-
_springState.v = state.rollVelocity;
|
|
514
|
-
_springState.targetX = getAngleContinuous(targetElAzRoll.z, elAzRoll.z);
|
|
515
|
-
Spring.stepSpring(dt_s, _springState, parameters);
|
|
516
|
-
state.rollVelocity = _springState.v;
|
|
517
|
-
let rollAfter = _springState.x;
|
|
518
|
-
|
|
519
|
-
// compose quaternion from direction and roll
|
|
520
|
-
alignQuaternion(state.q, direction, _qMatrix);
|
|
521
|
-
setRoll(state.q, rollAfter);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
function quaternionToPitchYawRoll(q: Quaternion, out: Vector3 = new Vector3()) {
|
|
525
|
-
// // get quaternion in spherical coordinates
|
|
526
|
-
_m.makeRotationFromQuaternion(q);
|
|
527
|
-
_m.extractBasis(_x, _y, _z);
|
|
528
|
-
// // azimuth and elevation are found from z direction
|
|
529
|
-
let azimuth = Math.atan2(_z.z, _z.x);
|
|
530
|
-
let elevation = Math.atan2(_z.y, Math.sqrt(_z.x * _z.x + _z.z * _z.z));
|
|
531
|
-
|
|
532
|
-
let roll = getRoll(q);
|
|
533
|
-
|
|
534
|
-
out.set(elevation, azimuth, roll);
|
|
535
|
-
return out;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
function setRoll(q: Quaternion, roll: number) {
|
|
539
|
-
let currentRoll_rad = getRoll(q);
|
|
540
|
-
let deltaRoll = roll - currentRoll_rad;
|
|
541
|
-
let objectForward = new Vector3(0, 0, -1).applyQuaternion(q);
|
|
542
|
-
let rotation = new Quaternion().setFromAxisAngle(objectForward, deltaRoll);
|
|
543
|
-
q.premultiply(rotation);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Aligns quaternion
|
|
549
|
-
*/
|
|
550
|
-
const _zBasis = new Vector3();
|
|
551
|
-
function alignQuaternion(q: Quaternion, direction: Vector3, outMatrix: Matrix4 = new Matrix4()) {
|
|
552
|
-
outMatrix.makeRotationFromQuaternion(q);
|
|
553
|
-
outMatrix.extractBasis(_x, _y, _z);
|
|
554
|
-
|
|
555
|
-
// must ensure _x and _y are orthogonal to zBasis
|
|
556
|
-
_zBasis.copy(direction).normalize();
|
|
557
|
-
|
|
558
|
-
_x.crossVectors(_y, _zBasis).normalize();
|
|
559
|
-
_y.crossVectors(_zBasis, _x).normalize();
|
|
560
|
-
|
|
561
|
-
outMatrix.makeBasis(_x, _y, _zBasis);
|
|
562
|
-
|
|
563
|
-
q.setFromRotationMatrix(outMatrix);
|
|
564
|
-
|
|
565
|
-
return outMatrix;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
function getAngleContinuous(a: number, lastAngle: number) {
|
|
569
|
-
const tau = 2 * Math.PI;
|
|
570
|
-
|
|
571
|
-
let u = a / tau + 0.5;
|
|
572
|
-
let uLast = fract(lastAngle / tau + 0.5);
|
|
573
|
-
let du = u - uLast;
|
|
574
|
-
|
|
575
|
-
let angle: number;
|
|
576
|
-
if (Math.abs(du) < 0.5) {
|
|
577
|
-
angle = lastAngle + du * tau;
|
|
578
|
-
} else {
|
|
579
|
-
// passed through 0
|
|
580
|
-
let duSmall = 1 - Math.abs(du);
|
|
581
|
-
angle = lastAngle + -Math.sign(du) * duSmall * tau;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
return angle;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
function fract(x: number) {
|
|
588
|
-
return x - Math.floor(x);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function getRoll(quaternion: Quaternion) {
|
|
592
|
-
return getQuaternionPlaneAngle(quaternion, new Vector3(0, 1, 0), new Vector3(0, 0, -1));
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
function getQuaternionPlaneAngle(quaternion: Quaternion, basisDirection: Vector3, basisPlane: Vector3) {
|
|
596
|
-
let objectDirection = basisDirection.clone().applyQuaternion(quaternion);
|
|
597
|
-
let objectPlane = basisPlane.clone().applyQuaternion(quaternion);
|
|
598
|
-
let objectDirectionProjected = objectDirection.projectOnPlane(objectPlane);
|
|
599
|
-
let worldZeroProjected = basisDirection.clone().projectOnPlane(objectPlane);
|
|
600
|
-
let angle = worldZeroProjected.angleTo(objectDirectionProjected);
|
|
601
|
-
// sign of angle
|
|
602
|
-
let sign = Math.sign(worldZeroProjected.cross(objectDirectionProjected).dot(objectPlane));
|
|
603
|
-
angle *= sign;
|
|
604
|
-
return angle;
|
|
605
|
-
}
|
package/src/three/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './ThreeAnimator.js';
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"strict": true,
|
|
4
|
-
"noEmit": false,
|
|
5
|
-
"module": "NodeNext",
|
|
6
|
-
"moduleResolution": "nodenext",
|
|
7
|
-
"allowArbitraryExtensions": true,
|
|
8
|
-
"declaration": true,
|
|
9
|
-
"jsx": "react",
|
|
10
|
-
"outDir": "./dist",
|
|
11
|
-
"allowJs": true,
|
|
12
|
-
"skipLibCheck": true,
|
|
13
|
-
}
|
|
14
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|