physics-animator 0.1.0 → 0.2.0

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.
Files changed (43) hide show
  1. package/dist/cjs/AnimationSequencer.js +156 -0
  2. package/dist/cjs/Animator.js +310 -0
  3. package/{src/Spring.ts → dist/cjs/Spring.js} +38 -80
  4. package/dist/cjs/index.js +19 -0
  5. package/dist/cjs/package.json +1 -0
  6. package/dist/cjs/react/index.js +19 -0
  7. package/dist/cjs/react/useAnimator.js +29 -0
  8. package/dist/cjs/react/useSpringState.js +16 -0
  9. package/dist/cjs/react/useSpringValue.js +59 -0
  10. package/dist/cjs/three/ThreeAnimator.js +453 -0
  11. package/dist/cjs/three/index.js +17 -0
  12. package/dist/esm/package.json +1 -0
  13. package/package.json +17 -10
  14. package/src/AnimationSequencer.ts +0 -193
  15. package/src/Animator.ts +0 -377
  16. package/src/index.ts +0 -3
  17. package/src/react/index.ts +0 -3
  18. package/src/react/useAnimator.ts +0 -45
  19. package/src/react/useSpringState.ts +0 -22
  20. package/src/react/useSpringValue.ts +0 -81
  21. package/src/three/ThreeAnimator.ts +0 -605
  22. package/src/three/index.ts +0 -1
  23. package/tsconfig.json +0 -14
  24. /package/dist/{AnimationSequencer.js → esm/AnimationSequencer.js} +0 -0
  25. /package/dist/{Animator.js → esm/Animator.js} +0 -0
  26. /package/dist/{Spring.js → esm/Spring.js} +0 -0
  27. /package/dist/{index.js → esm/index.js} +0 -0
  28. /package/dist/{react → esm/react}/index.js +0 -0
  29. /package/dist/{react → esm/react}/useAnimator.js +0 -0
  30. /package/dist/{react → esm/react}/useSpringState.js +0 -0
  31. /package/dist/{react → esm/react}/useSpringValue.js +0 -0
  32. /package/dist/{three → esm/three}/ThreeAnimator.js +0 -0
  33. /package/dist/{three → esm/three}/index.js +0 -0
  34. /package/dist/{AnimationSequencer.d.ts → types/AnimationSequencer.d.ts} +0 -0
  35. /package/dist/{Animator.d.ts → types/Animator.d.ts} +0 -0
  36. /package/dist/{Spring.d.ts → types/Spring.d.ts} +0 -0
  37. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  38. /package/dist/{react → types/react}/index.d.ts +0 -0
  39. /package/dist/{react → types/react}/useAnimator.d.ts +0 -0
  40. /package/dist/{react → types/react}/useSpringState.d.ts +0 -0
  41. /package/dist/{react → types/react}/useSpringValue.d.ts +0 -0
  42. /package/dist/{three → types/three}/ThreeAnimator.d.ts +0 -0
  43. /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
- }
@@ -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