physics-animator 0.2.1 → 0.9.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.
@@ -1,453 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ThreeAnimator = exports.QuaternionSpringMode = void 0;
4
- const three_1 = require("three");
5
- const Animator_js_1 = require("../Animator.js");
6
- const Spring_js_1 = require("../Spring.js");
7
- var QuaternionSpringMode;
8
- (function (QuaternionSpringMode) {
9
- QuaternionSpringMode[QuaternionSpringMode["DirectionRollCartesian"] = 0] = "DirectionRollCartesian";
10
- QuaternionSpringMode[QuaternionSpringMode["YawPitchRoll"] = 1] = "YawPitchRoll";
11
- })(QuaternionSpringMode || (exports.QuaternionSpringMode = QuaternionSpringMode = {}));
12
- /**
13
- * Extends Animator to add support for animating vectors and quaternions
14
- */
15
- class ThreeAnimator {
16
- get onAfterStep() {
17
- return this.animator.onAfterStep;
18
- }
19
- get onBeforeStep() {
20
- return this.animator.onBeforeStep;
21
- }
22
- constructor(animator = new Animator_js_1.Animator()) {
23
- this.quaternionSprings = new Map();
24
- this.animator = animator;
25
- this.animator.onBeforeStep.on(e => this.stepQuaternionSprings(e.dt_s));
26
- }
27
- setTo(object, field, target) {
28
- if (target instanceof three_1.Vector4) {
29
- let v = object[field];
30
- this.animator.setTo(v, 'x', target.x);
31
- this.animator.setTo(v, 'y', target.y);
32
- this.animator.setTo(v, 'z', target.z);
33
- this.animator.setTo(v, 'w', target.w);
34
- }
35
- else if (target instanceof three_1.Vector3) {
36
- let v = object[field];
37
- this.animator.setTo(v, 'x', target.x);
38
- this.animator.setTo(v, 'y', target.y);
39
- this.animator.setTo(v, 'z', target.z);
40
- }
41
- else if (target instanceof three_1.Vector2) {
42
- let v = object[field];
43
- this.animator.setTo(v, 'x', target.x);
44
- this.animator.setTo(v, 'y', target.y);
45
- }
46
- else if (target instanceof three_1.Quaternion) {
47
- let q = object[field];
48
- this.animator.setTo(q, 'x', target.x);
49
- this.animator.setTo(q, 'y', target.y);
50
- this.animator.setTo(q, 'z', target.z);
51
- this.animator.setTo(q, 'w', target.w);
52
- }
53
- else if (target instanceof three_1.Euler) {
54
- let e = object[field];
55
- this.animator.setTo(e, 'x', target.x);
56
- this.animator.setTo(e, 'y', target.y);
57
- this.animator.setTo(e, 'z', target.z);
58
- e.order = target.order;
59
- }
60
- else { // number
61
- this.animator.setTo(object, field, target);
62
- }
63
- }
64
- springTo(object, field, target, params = { duration_s: 0.5 }, mode) {
65
- if (target instanceof three_1.Vector4) {
66
- let v = object[field];
67
- this.animator.springTo(v, 'x', target.x, params);
68
- this.animator.springTo(v, 'y', target.y, params);
69
- this.animator.springTo(v, 'z', target.z, params);
70
- this.animator.springTo(v, 'w', target.w, params);
71
- }
72
- else if (target instanceof three_1.Vector3) {
73
- let v = object[field];
74
- this.animator.springTo(v, 'x', target.x, params);
75
- this.animator.springTo(v, 'y', target.y, params);
76
- this.animator.springTo(v, 'z', target.z, params);
77
- }
78
- else if (target instanceof three_1.Vector2) {
79
- let v = object[field];
80
- this.animator.springTo(v, 'x', target.x, params);
81
- this.animator.springTo(v, 'y', target.y, params);
82
- }
83
- else if (target instanceof three_1.Quaternion) {
84
- let q = object[field];
85
- let spring = this.getQuaternionSpring(q);
86
- // update
87
- spring.target.copy(target).normalize();
88
- spring.params = Spring_js_1.Spring.getPhysicsParameters(params);
89
- spring.mode = mode ?? QuaternionSpringMode.DirectionRollCartesian;
90
- }
91
- else if (target instanceof three_1.Euler) {
92
- let e = object[field];
93
- this.animator.springTo(e, 'x', target.x, params);
94
- this.animator.springTo(e, 'y', target.y, params);
95
- this.animator.springTo(e, 'z', target.z, params);
96
- e.order = target.order;
97
- }
98
- else { // number
99
- this.animator.springTo(object, field, target, params);
100
- }
101
- }
102
- customTweenTo(object, field, target, duration_s, step) {
103
- if (target instanceof three_1.Vector4) {
104
- let v = object[field];
105
- this.animator.customTweenTo(v, 'x', target.x, duration_s, step);
106
- this.animator.customTweenTo(v, 'y', target.y, duration_s, step);
107
- this.animator.customTweenTo(v, 'z', target.z, duration_s, step);
108
- this.animator.customTweenTo(v, 'w', target.w, duration_s, step);
109
- }
110
- else if (target instanceof three_1.Vector3) {
111
- let v = object[field];
112
- this.animator.customTweenTo(v, 'x', target.x, duration_s, step);
113
- this.animator.customTweenTo(v, 'y', target.y, duration_s, step);
114
- this.animator.customTweenTo(v, 'z', target.z, duration_s, step);
115
- }
116
- else if (target instanceof three_1.Vector2) {
117
- let v = object[field];
118
- this.animator.customTweenTo(v, 'x', target.x, duration_s, step);
119
- this.animator.customTweenTo(v, 'y', target.y, duration_s, step);
120
- }
121
- else if (target instanceof three_1.Quaternion) {
122
- throw new Error('Quaternion customTweenTo not yet supported, try springTo or use Euler');
123
- }
124
- else if (target instanceof three_1.Euler) {
125
- let e = object[field];
126
- this.animator.customTweenTo(e, 'x', target.x, duration_s, step);
127
- this.animator.customTweenTo(e, 'y', target.y, duration_s, step);
128
- this.animator.customTweenTo(e, 'z', target.z, duration_s, step);
129
- e.order = target.order;
130
- }
131
- else { // number
132
- this.animator.customTweenTo(object, field, target, duration_s, step);
133
- }
134
- }
135
- linearTo(object, field, target, duration_s) {
136
- this.customTweenTo(object, field, target, duration_s, Animator_js_1.Tween.linearStep);
137
- }
138
- easeInOutTo(object, field, target, duration_s) {
139
- this.customTweenTo(object, field, target, duration_s, Animator_js_1.Tween.easeInOutStep);
140
- }
141
- easeInTo(object, field, target, duration_s) {
142
- this.customTweenTo(object, field, target, duration_s, Animator_js_1.Tween.easeInStep);
143
- }
144
- easeOutTo(object, field, target, duration_s) {
145
- this.customTweenTo(object, field, target, duration_s, Animator_js_1.Tween.easeOutStep);
146
- }
147
- step(dt_s) {
148
- this.animator.step(dt_s);
149
- }
150
- tick() {
151
- this.animator.tick();
152
- }
153
- remove(object, field) {
154
- let v = object[field];
155
- if (v instanceof three_1.Vector4) {
156
- this.animator.remove(v, 'x');
157
- this.animator.remove(v, 'y');
158
- this.animator.remove(v, 'z');
159
- this.animator.remove(v, 'w');
160
- }
161
- else if (v instanceof three_1.Vector3) {
162
- this.animator.remove(v, 'x');
163
- this.animator.remove(v, 'y');
164
- this.animator.remove(v, 'z');
165
- }
166
- else if (v instanceof three_1.Vector2) {
167
- this.animator.remove(v, 'x');
168
- this.animator.remove(v, 'y');
169
- }
170
- else if (v instanceof three_1.Quaternion) {
171
- this.quaternionSprings.delete(v);
172
- }
173
- else if (v instanceof three_1.Euler) {
174
- this.animator.remove(v, 'x');
175
- this.animator.remove(v, 'y');
176
- this.animator.remove(v, 'z');
177
- }
178
- else { // number
179
- this.animator.remove(object, field);
180
- }
181
- }
182
- removeAll() {
183
- this.animator.removeAll();
184
- this.quaternionSprings.clear();
185
- }
186
- getVelocity(object, field, into) {
187
- let target = object[field];
188
- if (target instanceof three_1.Vector4) {
189
- let i = into ?? new three_1.Vector4();
190
- i.x = this.animator.getVelocity(target, 'x');
191
- i.y = this.animator.getVelocity(target, 'y');
192
- i.z = this.animator.getVelocity(target, 'z');
193
- i.w = this.animator.getVelocity(target, 'w');
194
- return i;
195
- }
196
- else if (target instanceof three_1.Vector3) {
197
- let i = into ?? new three_1.Vector3();
198
- i.x = this.animator.getVelocity(target, 'x');
199
- i.y = this.animator.getVelocity(target, 'y');
200
- i.z = this.animator.getVelocity(target, 'z');
201
- return i;
202
- }
203
- else if (target instanceof three_1.Vector2) {
204
- let i = into ?? new three_1.Vector2();
205
- i.x = this.animator.getVelocity(target, 'x');
206
- i.y = this.animator.getVelocity(target, 'y');
207
- return i;
208
- }
209
- else if (target instanceof three_1.Quaternion) {
210
- let spring = this.quaternionSprings.get(target);
211
- return {
212
- directionVelocity: spring?.directionVelocity ?? new three_1.Vector3(),
213
- rollVelocity: spring?.rollVelocity ?? 0
214
- };
215
- }
216
- else if (target instanceof three_1.Euler) {
217
- let i = into ?? new three_1.Euler();
218
- i.x = this.animator.getVelocity(target, 'x');
219
- i.y = this.animator.getVelocity(target, 'y');
220
- i.z = this.animator.getVelocity(target, 'z');
221
- i.order = target.order;
222
- return i;
223
- }
224
- else { // number
225
- return this.animator.getVelocity(object, field);
226
- }
227
- }
228
- startAnimationFrameLoop() {
229
- return this.animator.startAnimationFrameLoop();
230
- }
231
- startIntervalLoop(interval_ms) {
232
- return this.animator.startIntervalLoop(interval_ms);
233
- }
234
- stop() {
235
- this.animator.stop();
236
- }
237
- stepQuaternionSprings(dt_s) {
238
- // step quaternion springs
239
- this.quaternionSprings.forEach((spring, q) => {
240
- if (spring.params) {
241
- if (spring.mode === QuaternionSpringMode.DirectionRollCartesian) {
242
- stepSpringQuaternion(dt_s, spring, spring.params);
243
- }
244
- else {
245
- stepSpringQuaternionSpherical(dt_s, spring, spring.params);
246
- }
247
- }
248
- else {
249
- // copy target
250
- q.copy(spring.target);
251
- // zero velocity
252
- spring.directionVelocity.set(0, 0, 0);
253
- spring.rollVelocity = 0;
254
- }
255
- // if quaternions match and velocity close to zero, remove spring
256
- if (Math.abs(q.dot(spring.target)) > 0.999 && spring.directionVelocity.lengthSq() < 0.0001 && Math.abs(spring.rollVelocity) < 0.0001) {
257
- this.quaternionSprings.delete(q);
258
- }
259
- });
260
- }
261
- getQuaternionSpring(q) {
262
- let spring = this.quaternionSprings.get(q);
263
- if (!spring) {
264
- _m.makeRotationFromQuaternion(q);
265
- let direction = new three_1.Vector3();
266
- _m.extractBasis(new three_1.Vector3(), new three_1.Vector3(), direction);
267
- spring = {
268
- q: q,
269
- target: new three_1.Quaternion(),
270
- direction: direction,
271
- directionVelocity: new three_1.Vector3(),
272
- rollVelocity: 0,
273
- params: null,
274
- mode: QuaternionSpringMode.DirectionRollCartesian,
275
- };
276
- this.quaternionSprings.set(q, spring);
277
- }
278
- return spring;
279
- }
280
- }
281
- exports.ThreeAnimator = ThreeAnimator;
282
- /**
283
- * Analytic quaternion spring
284
- *
285
- * Todo:
286
- * - for cameras we want to prefer rotations in xz plane rather than z
287
- * - animate direction in spherical space rather than cartesian
288
- */
289
- // working variables to avoid allocations
290
- const _m = new three_1.Matrix4();
291
- const _x = new three_1.Vector3();
292
- const _y = new three_1.Vector3();
293
- const _z = new three_1.Vector3();
294
- const _qMatrix = new three_1.Matrix4();
295
- const _springState = { x: 0, v: 0, targetX: 0 };
296
- function stepSpringQuaternion(dt_s, state, parameters) {
297
- // step direction spring in cartesian space
298
- // we should do this in spherical in the future
299
- let targetDirection = new three_1.Vector3();
300
- let targetYBasis = new three_1.Vector3();
301
- _m.makeRotationFromQuaternion(state.target);
302
- _m.extractBasis(_x, targetYBasis, targetDirection);
303
- let directionBefore = state.direction.clone();
304
- // step spring direction
305
- _springState.x = state.direction.x;
306
- _springState.v = state.directionVelocity.x;
307
- _springState.targetX = targetDirection.x;
308
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
309
- state.direction.x = _springState.x;
310
- state.directionVelocity.x = _springState.v;
311
- _springState.x = state.direction.y;
312
- _springState.v = state.directionVelocity.y;
313
- _springState.targetX = targetDirection.y;
314
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
315
- state.direction.y = _springState.x;
316
- state.directionVelocity.y = _springState.v;
317
- _springState.x = state.direction.z;
318
- _springState.v = state.directionVelocity.z;
319
- _springState.targetX = targetDirection.z;
320
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
321
- state.direction.z = _springState.x;
322
- state.directionVelocity.z = _springState.v;
323
- // update quaternion
324
- let directionDeltaQuaternion = new three_1.Quaternion().setFromUnitVectors(_x.copy(directionBefore).normalize(), _y.copy(state.direction).normalize());
325
- state.q.premultiply(directionDeltaQuaternion);
326
- // this forces synchronization of direction and quaternion
327
- alignQuaternion(state.q, state.direction, _qMatrix);
328
- // determine roll required to align yBasis with targetYBasis
329
- let directionToTargetQuaternion = new three_1.Quaternion().setFromUnitVectors(state.direction.clone().normalize(), targetDirection);
330
- _m.makeRotationFromQuaternion(state.q);
331
- _m.extractBasis(_x, _y, _z);
332
- let newYBasis = _y.applyQuaternion(directionToTargetQuaternion).clone();
333
- let rollQuaternion = new three_1.Quaternion().setFromUnitVectors(newYBasis, targetYBasis);
334
- // to axis angle (clamp w)
335
- let rollAngle = Math.acos(Math.min(1, Math.max(-1, rollQuaternion.w))) * 2;
336
- let rollSign = _x.crossVectors(newYBasis, targetYBasis).dot(targetDirection) < 0 ? -1 : 1;
337
- rollAngle = -rollSign * rollAngle;
338
- // step roll spring
339
- _springState.x = rollAngle;
340
- _springState.v = state.rollVelocity;
341
- _springState.targetX = 0;
342
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
343
- state.rollVelocity = _springState.v;
344
- let rollAfter = _springState.x;
345
- let rollDelta = rollAfter - rollAngle;
346
- // apply roll correction
347
- let rollDeltaQuaternion = new three_1.Quaternion().setFromAxisAngle(state.direction.clone().normalize(), rollDelta /*-rollAngle * 0.1*/);
348
- state.q.premultiply(rollDeltaQuaternion);
349
- state.q.normalize();
350
- }
351
- function stepSpringQuaternionSpherical(dt_s, state, parameters) {
352
- let azimuthVelocity = state.directionVelocity.x;
353
- let elevationVelocity = state.directionVelocity.y;
354
- // get quaternion in spherical coordinates
355
- let elAzRoll = quaternionToPitchYawRoll(state.q);
356
- // get target quaternion in spherical coordinates
357
- let targetElAzRoll = quaternionToPitchYawRoll(state.target);
358
- // step springs
359
- _springState.x = elAzRoll.x;
360
- _springState.v = elevationVelocity;
361
- _springState.targetX = getAngleContinuous(targetElAzRoll.x, elAzRoll.x);
362
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
363
- elevationVelocity = _springState.v;
364
- let elevationAfter = _springState.x;
365
- _springState.x = elAzRoll.y;
366
- _springState.v = azimuthVelocity;
367
- _springState.targetX = getAngleContinuous(targetElAzRoll.y, elAzRoll.y);
368
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
369
- azimuthVelocity = _springState.v;
370
- let azimuthAfter = _springState.x;
371
- // update directionVelocity
372
- state.directionVelocity.x = azimuthVelocity;
373
- state.directionVelocity.y = elevationVelocity;
374
- // compose quaternion from spherical coordinates
375
- // direction from azimuth and elevation
376
- let direction = new three_1.Vector3(Math.cos(azimuthAfter) * Math.cos(elevationAfter), Math.sin(elevationAfter), Math.sin(azimuthAfter) * Math.cos(elevationAfter)).normalize();
377
- // roll alignment spring
378
- _springState.x = elAzRoll.z;
379
- _springState.v = state.rollVelocity;
380
- _springState.targetX = getAngleContinuous(targetElAzRoll.z, elAzRoll.z);
381
- Spring_js_1.Spring.stepSpring(dt_s, _springState, parameters);
382
- state.rollVelocity = _springState.v;
383
- let rollAfter = _springState.x;
384
- // compose quaternion from direction and roll
385
- alignQuaternion(state.q, direction, _qMatrix);
386
- setRoll(state.q, rollAfter);
387
- }
388
- function quaternionToPitchYawRoll(q, out = new three_1.Vector3()) {
389
- // // get quaternion in spherical coordinates
390
- _m.makeRotationFromQuaternion(q);
391
- _m.extractBasis(_x, _y, _z);
392
- // // azimuth and elevation are found from z direction
393
- let azimuth = Math.atan2(_z.z, _z.x);
394
- let elevation = Math.atan2(_z.y, Math.sqrt(_z.x * _z.x + _z.z * _z.z));
395
- let roll = getRoll(q);
396
- out.set(elevation, azimuth, roll);
397
- return out;
398
- }
399
- function setRoll(q, roll) {
400
- let currentRoll_rad = getRoll(q);
401
- let deltaRoll = roll - currentRoll_rad;
402
- let objectForward = new three_1.Vector3(0, 0, -1).applyQuaternion(q);
403
- let rotation = new three_1.Quaternion().setFromAxisAngle(objectForward, deltaRoll);
404
- q.premultiply(rotation);
405
- }
406
- /**
407
- * Aligns quaternion
408
- */
409
- const _zBasis = new three_1.Vector3();
410
- function alignQuaternion(q, direction, outMatrix = new three_1.Matrix4()) {
411
- outMatrix.makeRotationFromQuaternion(q);
412
- outMatrix.extractBasis(_x, _y, _z);
413
- // must ensure _x and _y are orthogonal to zBasis
414
- _zBasis.copy(direction).normalize();
415
- _x.crossVectors(_y, _zBasis).normalize();
416
- _y.crossVectors(_zBasis, _x).normalize();
417
- outMatrix.makeBasis(_x, _y, _zBasis);
418
- q.setFromRotationMatrix(outMatrix);
419
- return outMatrix;
420
- }
421
- function getAngleContinuous(a, lastAngle) {
422
- const tau = 2 * Math.PI;
423
- let u = a / tau + 0.5;
424
- let uLast = fract(lastAngle / tau + 0.5);
425
- let du = u - uLast;
426
- let angle;
427
- if (Math.abs(du) < 0.5) {
428
- angle = lastAngle + du * tau;
429
- }
430
- else {
431
- // passed through 0
432
- let duSmall = 1 - Math.abs(du);
433
- angle = lastAngle + -Math.sign(du) * duSmall * tau;
434
- }
435
- return angle;
436
- }
437
- function fract(x) {
438
- return x - Math.floor(x);
439
- }
440
- function getRoll(quaternion) {
441
- return getQuaternionPlaneAngle(quaternion, new three_1.Vector3(0, 1, 0), new three_1.Vector3(0, 0, -1));
442
- }
443
- function getQuaternionPlaneAngle(quaternion, basisDirection, basisPlane) {
444
- let objectDirection = basisDirection.clone().applyQuaternion(quaternion);
445
- let objectPlane = basisPlane.clone().applyQuaternion(quaternion);
446
- let objectDirectionProjected = objectDirection.projectOnPlane(objectPlane);
447
- let worldZeroProjected = basisDirection.clone().projectOnPlane(objectPlane);
448
- let angle = worldZeroProjected.angleTo(objectDirectionProjected);
449
- // sign of angle
450
- let sign = Math.sign(worldZeroProjected.cross(objectDirectionProjected).dot(objectPlane));
451
- angle *= sign;
452
- return angle;
453
- }
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./ThreeAnimator.js"), exports);