@weasel-js/animator 1.9.4 → 1.10.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.
- package/README.md +20 -2
- package/dist/index.d.ts +212 -150
- package/dist/index.js +557 -235
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { useRef, useCallback, useMemo, useInsertionEffect, useEffect } from 'react';
|
|
2
2
|
import { useLatest, useVisibleRaf } from '@weasel-js/react';
|
|
3
|
-
import { isPlainObject } from '@weasel-js/registry';
|
|
3
|
+
import { isPlainObject, createEmitter } from '@weasel-js/registry';
|
|
4
4
|
import { keys, patch, tween, glide, spring, mix, kit, vec, sum } from '@msb235/blits';
|
|
5
|
-
import { EASINGS, resolveEasing, mat3, PATH_M, PATH_L, PATH_Z, easeOut, SPRING_PRESETS, linear } from '@weasel-js/geom';
|
|
5
|
+
import { EASINGS, resolveEasing, mat3, PATH_M, PATH_L, PATH_Z, reviveTypedArrays, tagTypedArrays, easeOut, SPRING_PRESETS, linear } from '@weasel-js/geom';
|
|
6
6
|
import { hslToSrgb, lerpColorArray } from '@weasel-js/paint';
|
|
7
7
|
|
|
8
8
|
// src/useAnimator.ts
|
|
9
|
+
|
|
10
|
+
// src/axisNames.ts
|
|
11
|
+
var LISTED = 8;
|
|
12
|
+
function axisIndexes(axes, wanted, what = "animator: `axes`") {
|
|
13
|
+
const { count, names } = axes;
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const w of wanted) {
|
|
16
|
+
if (typeof w === "number") {
|
|
17
|
+
if (!Number.isInteger(w) || w < 0 || w >= count) throw new Error(`${what} names axis ${w} of a target with ${count}`);
|
|
18
|
+
out.push(w);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (!names) throw new Error(`${what} names "${w}" on a target whose axes have no names`);
|
|
22
|
+
const before = out.length;
|
|
23
|
+
const under = `${w}.`;
|
|
24
|
+
for (let i = 0; i < names.length; i++) {
|
|
25
|
+
if (names[i] === w || names[i].startsWith(under)) out.push(i);
|
|
26
|
+
}
|
|
27
|
+
if (out.length === before) {
|
|
28
|
+
const some = names.slice(0, LISTED).map((n) => `"${n}"`).join(", ");
|
|
29
|
+
throw new Error(`${what} names "${w}", and the target's axes are ${some}${names.length > LISTED ? ", \u2026" : ""}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
9
34
|
function fieldByField(keys) {
|
|
10
35
|
const [a, b, c, e] = keys;
|
|
11
36
|
switch (keys.length) {
|
|
@@ -87,6 +112,7 @@ function axesOf(sample) {
|
|
|
87
112
|
return {
|
|
88
113
|
count: keys.length,
|
|
89
114
|
shape: `object:${JSON.stringify(keys)}`,
|
|
115
|
+
names: keys,
|
|
90
116
|
to: (v) => keys.map((k) => v[k]),
|
|
91
117
|
from: (a, at = 0) => {
|
|
92
118
|
const out = {};
|
|
@@ -170,6 +196,9 @@ function readTargetInto(s, out, at) {
|
|
|
170
196
|
out[at + i] = out[at + i] + (slot ? slot.cols[slot.at + i] : 0) + adjust[i] + (beside ? beside[i] : 0) - (hid ? hid[i] : 0);
|
|
171
197
|
}
|
|
172
198
|
}
|
|
199
|
+
function isTarget(x) {
|
|
200
|
+
return typeof x === "object" && x !== null && states.has(x);
|
|
201
|
+
}
|
|
173
202
|
function stateOf(target) {
|
|
174
203
|
const s = states.get(target);
|
|
175
204
|
if (!s) throw new Error("animator: `target` was not made by createTarget");
|
|
@@ -206,6 +235,7 @@ function createTarget(opts) {
|
|
|
206
235
|
readInto: (out, at = 0) => readTargetInto(s, out, at),
|
|
207
236
|
axisCount: axes.count,
|
|
208
237
|
axes,
|
|
238
|
+
axisIndexes: (wanted) => axisIndexes(axes, wanted),
|
|
209
239
|
committed: opts.get,
|
|
210
240
|
commit: opts.set,
|
|
211
241
|
get active() {
|
|
@@ -231,11 +261,33 @@ function put(into, n, row, value) {
|
|
|
231
261
|
if (value.length !== n) throw new Error(`target table: a row is ${n} numbers, and this value is ${value.length}`);
|
|
232
262
|
for (let k = 0; k < n; k++) into[row * n + k] = value[k];
|
|
233
263
|
}
|
|
264
|
+
function rowTarget(n, row, rows, committed, commit) {
|
|
265
|
+
const target = createTarget({
|
|
266
|
+
get: () => {
|
|
267
|
+
const from = committed();
|
|
268
|
+
const value = new Array(n);
|
|
269
|
+
for (let k = 0; k < n; k++) value[k] = from[row * n + k];
|
|
270
|
+
return value;
|
|
271
|
+
},
|
|
272
|
+
set: (value) => {
|
|
273
|
+
committed();
|
|
274
|
+
commit(row, value);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
const state = stateOf(target);
|
|
278
|
+
state.row = { table: rows, index: row };
|
|
279
|
+
state.write = (out, at) => {
|
|
280
|
+
const from = committed();
|
|
281
|
+
for (let k = 0; k < n; k++) out[at + k] = from[row * n + k];
|
|
282
|
+
};
|
|
283
|
+
return target;
|
|
284
|
+
}
|
|
234
285
|
function createTargetTable(opts) {
|
|
235
286
|
const n = opts.axes;
|
|
287
|
+
const Shown = opts.shown ?? Float64Array;
|
|
236
288
|
if (!Number.isInteger(n) || n < 1) throw new Error("createTargetTable: `axes` is a whole number of at least 1");
|
|
237
289
|
let committed = new Float64Array(Math.max(opts.capacity ?? 64, 1) * n);
|
|
238
|
-
let shown = new
|
|
290
|
+
let shown = new Shown(committed.length);
|
|
239
291
|
let high = 0;
|
|
240
292
|
let size = 0;
|
|
241
293
|
const free = [];
|
|
@@ -297,7 +349,7 @@ function createTargetTable(opts) {
|
|
|
297
349
|
const grown = new Float64Array(Math.max(committed.length * 2, (row + 1) * n));
|
|
298
350
|
grown.set(committed);
|
|
299
351
|
committed = grown;
|
|
300
|
-
const wider = new
|
|
352
|
+
const wider = new Shown(grown.length);
|
|
301
353
|
wider.set(shown);
|
|
302
354
|
shown = wider;
|
|
303
355
|
}
|
|
@@ -323,26 +375,12 @@ function createTargetTable(opts) {
|
|
|
323
375
|
const mine = () => {
|
|
324
376
|
if (removals[row] !== made) throw new Error(`target table: row ${row} was removed, and this is its old target`);
|
|
325
377
|
};
|
|
326
|
-
const target =
|
|
327
|
-
get: () => {
|
|
328
|
-
mine();
|
|
329
|
-
const value = new Array(n);
|
|
330
|
-
for (let k = 0; k < n; k++) value[k] = committed[row * n + k];
|
|
331
|
-
return value;
|
|
332
|
-
},
|
|
333
|
-
set: (value) => {
|
|
334
|
-
mine();
|
|
335
|
-
commit(row, value);
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
const state = stateOf(target);
|
|
339
|
-
state.row = { table: rows, index: row };
|
|
340
|
-
state.write = (out, at) => {
|
|
378
|
+
const target = rowTarget(n, row, rows, () => {
|
|
341
379
|
mine();
|
|
342
|
-
|
|
343
|
-
};
|
|
380
|
+
return committed;
|
|
381
|
+
}, commit);
|
|
344
382
|
targets[row] = target;
|
|
345
|
-
states2[row] =
|
|
383
|
+
states2[row] = stateOf(target);
|
|
346
384
|
return target;
|
|
347
385
|
}
|
|
348
386
|
};
|
|
@@ -495,27 +533,16 @@ function shownOf(slot) {
|
|
|
495
533
|
for (let i = 0; i < out.length; i++) out[i] = shown[at + i];
|
|
496
534
|
return out;
|
|
497
535
|
}
|
|
498
|
-
|
|
499
|
-
// src/observe.ts
|
|
500
536
|
var AnimatorEventHub = class {
|
|
501
|
-
|
|
537
|
+
events = createEmitter();
|
|
502
538
|
get watched() {
|
|
503
|
-
return this.
|
|
539
|
+
return this.events.size > 0;
|
|
504
540
|
}
|
|
505
541
|
add(listener) {
|
|
506
|
-
this.
|
|
507
|
-
return () => {
|
|
508
|
-
this.listeners.delete(listener);
|
|
509
|
-
};
|
|
542
|
+
return this.events.subscribe(listener);
|
|
510
543
|
}
|
|
511
544
|
emit(event) {
|
|
512
|
-
|
|
513
|
-
try {
|
|
514
|
-
listener(event);
|
|
515
|
-
} catch (err) {
|
|
516
|
-
console.error("useAnimator: watch listener threw", err);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
545
|
+
this.events.emit(event);
|
|
519
546
|
}
|
|
520
547
|
};
|
|
521
548
|
|
|
@@ -713,25 +740,49 @@ function runStagger(createSupervisor, watchCompletion, items, delay, factory, op
|
|
|
713
740
|
function resolvePerItem(v, item, i) {
|
|
714
741
|
return typeof v === "function" ? v(item, i) : v;
|
|
715
742
|
}
|
|
743
|
+
function perItem(opts, item, i) {
|
|
744
|
+
const target = opts.target !== void 0 ? resolvePerItem(opts.target, item, i) : isTarget(item) ? item : null;
|
|
745
|
+
return {
|
|
746
|
+
target,
|
|
747
|
+
from: opts.from === void 0 ? void 0 : resolvePerItem(opts.from, item, i),
|
|
748
|
+
to: resolvePerItem(opts.to, item, i),
|
|
749
|
+
onTick: opts.onTick && ((v) => opts.onTick(v, item, i)),
|
|
750
|
+
onDone: opts.onDone && (() => opts.onDone(item, i))
|
|
751
|
+
};
|
|
752
|
+
}
|
|
716
753
|
function makeBuilder(animator, createSupervisor, watchCompletion, items, delay) {
|
|
754
|
+
const run = (method, opts, start) => {
|
|
755
|
+
if (opts.target === void 0 && !opts.onTick && !items.every(isTarget)) {
|
|
756
|
+
throw new Error(`animator.stagger: \`.${method}\` needs a \`target\`, items that are targets, or an \`onTick\``);
|
|
757
|
+
}
|
|
758
|
+
return runStagger(createSupervisor, watchCompletion, items, delay, (item, i) => start(perItem(opts, item, i), item, i), opts);
|
|
759
|
+
};
|
|
717
760
|
return {
|
|
718
761
|
each: (factory, opts) => runStagger(createSupervisor, watchCompletion, items, delay, factory, opts),
|
|
719
|
-
tween: (opts) =>
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
items,
|
|
723
|
-
delay,
|
|
724
|
-
(item, i) => animator.tween({
|
|
725
|
-
from: resolvePerItem(opts.from, item, i),
|
|
726
|
-
to: resolvePerItem(opts.to, item, i),
|
|
762
|
+
tween: ((opts) => run("tween", opts, ({ target, ...own }, item, i) => {
|
|
763
|
+
const shared = {
|
|
764
|
+
...own,
|
|
727
765
|
ms: resolvePerItem(opts.ms, item, i),
|
|
728
766
|
easing: opts.easing,
|
|
729
767
|
interpolate: opts.interpolate,
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
})
|
|
733
|
-
|
|
734
|
-
)
|
|
768
|
+
interpolator: opts.interpolator
|
|
769
|
+
};
|
|
770
|
+
return target ? animator.tween({ ...shared, target }) : animator.tween(shared);
|
|
771
|
+
})),
|
|
772
|
+
spring: ((opts) => run("spring", opts, ({ target, ...own }, item, i) => {
|
|
773
|
+
const shared = {
|
|
774
|
+
...own,
|
|
775
|
+
velocity: opts.velocity === void 0 ? void 0 : resolvePerItem(opts.velocity, item, i),
|
|
776
|
+
preset: opts.preset,
|
|
777
|
+
stiffness: opts.stiffness,
|
|
778
|
+
damping: opts.damping,
|
|
779
|
+
mass: opts.mass,
|
|
780
|
+
restThreshold: opts.restThreshold
|
|
781
|
+
};
|
|
782
|
+
if (target) return animator.spring({ ...shared, target });
|
|
783
|
+
const { interpolate, add, subtract, scale, magnitude } = opts;
|
|
784
|
+
return animator.spring({ ...shared, interpolate, add, subtract, scale, magnitude });
|
|
785
|
+
}))
|
|
735
786
|
};
|
|
736
787
|
}
|
|
737
788
|
|
|
@@ -1027,7 +1078,7 @@ function createTargetBook(owner) {
|
|
|
1027
1078
|
const token = { ...owner, adjusted: (s) => {
|
|
1028
1079
|
adjusted.push(s);
|
|
1029
1080
|
} };
|
|
1030
|
-
const
|
|
1081
|
+
const showing = /* @__PURE__ */ new Set();
|
|
1031
1082
|
const rested = /* @__PURE__ */ new Set();
|
|
1032
1083
|
const publishes = /* @__PURE__ */ new Set();
|
|
1033
1084
|
const tables = /* @__PURE__ */ new Map();
|
|
@@ -1046,7 +1097,7 @@ function createTargetBook(owner) {
|
|
|
1046
1097
|
}
|
|
1047
1098
|
s.owner = token;
|
|
1048
1099
|
s.live += 1;
|
|
1049
|
-
|
|
1100
|
+
if (s.show) showing.add(s);
|
|
1050
1101
|
rested.delete(s);
|
|
1051
1102
|
if (s.row) {
|
|
1052
1103
|
tables.set(s.row.table, (tables.get(s.row.table) ?? 0) + 1);
|
|
@@ -1067,8 +1118,7 @@ function createTargetBook(owner) {
|
|
|
1067
1118
|
s.dimmed = false;
|
|
1068
1119
|
s.adjust.fill(0);
|
|
1069
1120
|
s.stale = false;
|
|
1070
|
-
|
|
1071
|
-
if (s.show) rested.add(s);
|
|
1121
|
+
if (s.show && showing.delete(s)) rested.add(s);
|
|
1072
1122
|
},
|
|
1073
1123
|
settle() {
|
|
1074
1124
|
for (const s of adjusted) {
|
|
@@ -1085,7 +1135,7 @@ function createTargetBook(owner) {
|
|
|
1085
1135
|
s.row?.table.mark(s.row.index);
|
|
1086
1136
|
},
|
|
1087
1137
|
show() {
|
|
1088
|
-
for (const s of
|
|
1138
|
+
for (const s of showing) shown(s, readTarget(s));
|
|
1089
1139
|
for (const s of rested) shown(s, s.get());
|
|
1090
1140
|
rested.clear();
|
|
1091
1141
|
for (const [t, moving] of tables) {
|
|
@@ -1317,14 +1367,13 @@ function tracksEnd(tracks, explicit) {
|
|
|
1317
1367
|
for (const t of tracks) max = Math.max(max, trackEnd(t));
|
|
1318
1368
|
return max;
|
|
1319
1369
|
}
|
|
1320
|
-
function
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
console.error("timeline: subscriber threw", err);
|
|
1326
|
-
}
|
|
1370
|
+
function crossLaps(playhead, duration, loopsLeft, take) {
|
|
1371
|
+
if (duration <= 0 || playhead < duration) return;
|
|
1372
|
+
if (loopsLeft === Infinity) {
|
|
1373
|
+
take(Math.floor(playhead / duration));
|
|
1374
|
+
return;
|
|
1327
1375
|
}
|
|
1376
|
+
for (let at = playhead, left = loopsLeft; at >= duration && left > 0; at -= duration, left -= 1) take(1);
|
|
1328
1377
|
}
|
|
1329
1378
|
function createTimeline(register, id, opts, report, hold) {
|
|
1330
1379
|
let duration = tracksEnd(opts.tracks, opts.duration);
|
|
@@ -1339,9 +1388,9 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1339
1388
|
let cancelled = false;
|
|
1340
1389
|
let wantPaused = opts.autoplay === false;
|
|
1341
1390
|
let wantScale = 1;
|
|
1342
|
-
const
|
|
1343
|
-
const
|
|
1344
|
-
const playbackChanged =
|
|
1391
|
+
const edited = createEmitter();
|
|
1392
|
+
const playback = createEmitter();
|
|
1393
|
+
const playbackChanged = playback.emit;
|
|
1345
1394
|
const loopOpt = opts.loop ?? false;
|
|
1346
1395
|
let loopsLeft = loopOpt === true ? Infinity : loopOpt === false ? 0 : loopOpt;
|
|
1347
1396
|
let lap = 0;
|
|
@@ -1362,27 +1411,20 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1362
1411
|
};
|
|
1363
1412
|
const tick = (virtualNow, scale) => {
|
|
1364
1413
|
lastVirtual = virtualNow;
|
|
1365
|
-
playhead = virtualNow + offset;
|
|
1366
1414
|
const rate = scale ?? (base.isPaused() ? 0 : base.timeScale());
|
|
1367
1415
|
held = rate === 0;
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1416
|
+
const parked = held && duration > 0 && playhead === duration && loopsLeft > 0;
|
|
1417
|
+
if (!parked) {
|
|
1418
|
+
playhead = virtualNow + offset;
|
|
1419
|
+
crossLaps(playhead, duration, loopsLeft, (laps) => {
|
|
1420
|
+
loopsLeft -= laps;
|
|
1371
1421
|
offset -= laps * duration;
|
|
1372
1422
|
playhead -= laps * duration;
|
|
1373
1423
|
lap += laps;
|
|
1374
1424
|
onWrap();
|
|
1375
|
-
}
|
|
1376
|
-
while (playhead >= duration && loopsLeft > 0) {
|
|
1377
|
-
loopsLeft -= 1;
|
|
1378
|
-
offset -= duration;
|
|
1379
|
-
playhead -= duration;
|
|
1380
|
-
lap += 1;
|
|
1381
|
-
onWrap();
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1425
|
+
});
|
|
1384
1426
|
}
|
|
1385
|
-
const finished = playhead >= duration;
|
|
1427
|
+
const finished = playhead >= duration && !parked;
|
|
1386
1428
|
playhead = Math.min(playhead, duration);
|
|
1387
1429
|
sampler.apply(opts.tracks, playhead);
|
|
1388
1430
|
if (!held) {
|
|
@@ -1396,7 +1438,7 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1396
1438
|
opts.onDone?.();
|
|
1397
1439
|
playbackChanged();
|
|
1398
1440
|
}
|
|
1399
|
-
if (playhead < duration) return false;
|
|
1441
|
+
if (playhead < duration || parked) return false;
|
|
1400
1442
|
live = false;
|
|
1401
1443
|
sampler.release();
|
|
1402
1444
|
return true;
|
|
@@ -1482,20 +1524,10 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1482
1524
|
booker?.retract();
|
|
1483
1525
|
rearm();
|
|
1484
1526
|
sampleNow();
|
|
1485
|
-
|
|
1486
|
-
},
|
|
1487
|
-
subscribe(cb) {
|
|
1488
|
-
subscribers.add(cb);
|
|
1489
|
-
return () => {
|
|
1490
|
-
subscribers.delete(cb);
|
|
1491
|
-
};
|
|
1527
|
+
edited.emit();
|
|
1492
1528
|
},
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
return () => {
|
|
1496
|
-
playbackSubscribers.delete(cb);
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1529
|
+
subscribe: edited.subscribe,
|
|
1530
|
+
subscribePlayback: playback.subscribe
|
|
1499
1531
|
};
|
|
1500
1532
|
}
|
|
1501
1533
|
|
|
@@ -1591,15 +1623,10 @@ function dim(s, before, after) {
|
|
|
1591
1623
|
}
|
|
1592
1624
|
}
|
|
1593
1625
|
}
|
|
1594
|
-
function coverOf(
|
|
1595
|
-
if (!
|
|
1596
|
-
const covers = new Uint8Array(count);
|
|
1597
|
-
for (const a of axes)
|
|
1598
|
-
if (!Number.isInteger(a) || a < 0 || a >= count) {
|
|
1599
|
-
throw new Error(`animator: \`axes\` names axis ${a} of a target with ${count}`);
|
|
1600
|
-
}
|
|
1601
|
-
covers[a] = 1;
|
|
1602
|
-
}
|
|
1626
|
+
function coverOf(wanted, axes) {
|
|
1627
|
+
if (!wanted) return null;
|
|
1628
|
+
const covers = new Uint8Array(axes.count);
|
|
1629
|
+
for (const a of axisIndexes(axes, wanted)) covers[a] = 1;
|
|
1603
1630
|
return covers;
|
|
1604
1631
|
}
|
|
1605
1632
|
function openHold(ctx, o) {
|
|
@@ -1615,7 +1642,8 @@ function openHold(ctx, o) {
|
|
|
1615
1642
|
return buffer;
|
|
1616
1643
|
} : () => ax.to(o.value()),
|
|
1617
1644
|
weight: () => gone ? 0 : share(o.weight()),
|
|
1618
|
-
covers: coverOf(o.axes, ax
|
|
1645
|
+
covers: coverOf(o.axes, ax),
|
|
1646
|
+
fixed: o.fixed === true,
|
|
1619
1647
|
w: 0,
|
|
1620
1648
|
v: buffer
|
|
1621
1649
|
};
|
|
@@ -1633,6 +1661,7 @@ function openHold(ctx, o) {
|
|
|
1633
1661
|
id: made.voice,
|
|
1634
1662
|
on: s,
|
|
1635
1663
|
axes: ax.count,
|
|
1664
|
+
still: member.fixed,
|
|
1636
1665
|
at: () => {
|
|
1637
1666
|
const committed = s.heldAt ??= new Array(ax.count).fill(0);
|
|
1638
1667
|
committedInto(s, committed, 0);
|
|
@@ -1646,6 +1675,8 @@ function openHold(ctx, o) {
|
|
|
1646
1675
|
}
|
|
1647
1676
|
const { members, voice } = group;
|
|
1648
1677
|
members.push(member);
|
|
1678
|
+
const still = () => ctx.codec.still(voice, members.every((m) => m.fixed));
|
|
1679
|
+
still();
|
|
1649
1680
|
ctx.touch(state, true);
|
|
1650
1681
|
return {
|
|
1651
1682
|
touch: () => {
|
|
@@ -1670,7 +1701,7 @@ function openHold(ctx, o) {
|
|
|
1670
1701
|
const beside = s.beside;
|
|
1671
1702
|
s.beside = null;
|
|
1672
1703
|
if (beside) absorb(s, beside);
|
|
1673
|
-
}
|
|
1704
|
+
} else still();
|
|
1674
1705
|
ctx.targets.detach(state);
|
|
1675
1706
|
ctx.touch(state, true);
|
|
1676
1707
|
}
|
|
@@ -1683,6 +1714,7 @@ function startHold(ctx, o) {
|
|
|
1683
1714
|
const target = stateOf(o.target);
|
|
1684
1715
|
const id = ctx.nextId();
|
|
1685
1716
|
const { value, weight = 1 } = o;
|
|
1717
|
+
const fixed = !isFn(value) && !isFn(weight);
|
|
1686
1718
|
const seed = {
|
|
1687
1719
|
id,
|
|
1688
1720
|
kind: "hold",
|
|
@@ -1690,15 +1722,17 @@ function startHold(ctx, o) {
|
|
|
1690
1722
|
label: o.label,
|
|
1691
1723
|
target,
|
|
1692
1724
|
keepExisting: o.interrupt === false,
|
|
1693
|
-
|
|
1694
|
-
|
|
1725
|
+
idle: fixed,
|
|
1726
|
+
// It leaves when released or canceled, and has nothing to do on a frame.
|
|
1727
|
+
due: Infinity
|
|
1695
1728
|
};
|
|
1696
1729
|
if (o.cancelKey != null && o.interrupt !== false) ctx.interrupt(o.cancelKey, seed);
|
|
1697
1730
|
const held = openHold(ctx, {
|
|
1698
1731
|
target,
|
|
1699
1732
|
value: isFn(value) ? value : () => value,
|
|
1700
1733
|
weight: isFn(weight) ? weight : () => weight,
|
|
1701
|
-
axes: o.axes
|
|
1734
|
+
axes: o.axes,
|
|
1735
|
+
fixed
|
|
1702
1736
|
});
|
|
1703
1737
|
seed.onCancel = () => held.leave(true);
|
|
1704
1738
|
seed.tick = () => {
|
|
@@ -1840,6 +1874,15 @@ function startPhysics(ctx, o, kind) {
|
|
|
1840
1874
|
};
|
|
1841
1875
|
}
|
|
1842
1876
|
|
|
1877
|
+
// src/engine/springRest.ts
|
|
1878
|
+
function earliestRest(value, velocity, stiffness, damping, mass, threshold) {
|
|
1879
|
+
let energy = 0;
|
|
1880
|
+
for (let i = 0; i < value.length; i++) energy += stiffness * value[i] ** 2 + mass * velocity[i] ** 2;
|
|
1881
|
+
const atRest = (stiffness + mass) * threshold ** 2;
|
|
1882
|
+
if (!(energy > atRest)) return 0;
|
|
1883
|
+
return 1e3 * mass / (2 * damping) * Math.log(energy / atRest);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1843
1886
|
// src/engine/springVoices.ts
|
|
1844
1887
|
function createSpringVoices(codec, s, o) {
|
|
1845
1888
|
const n = s.axes.count;
|
|
@@ -1914,19 +1957,37 @@ function createSpringVoices(codec, s, o) {
|
|
|
1914
1957
|
|
|
1915
1958
|
// src/engine/offsetPhysics.ts
|
|
1916
1959
|
var any = (a) => a.some((x) => x !== 0);
|
|
1960
|
+
function constantsFor(o, kind, toward) {
|
|
1961
|
+
const c = resolveSpringConstants(o);
|
|
1962
|
+
if (!(c.damping > 0) || !(c.mass > 0) || toward && !(c.stiffness > 0)) {
|
|
1963
|
+
throw new Error(`${kind}: an animation on a target needs positive damping and mass, and stiffness to reach \`to\``);
|
|
1964
|
+
}
|
|
1965
|
+
return c;
|
|
1966
|
+
}
|
|
1967
|
+
function restWaits(ctx, id, quiet, c, threshold) {
|
|
1968
|
+
const until = (value, velocity, coast) => earliestRest(value, velocity, coast ? 0 : c.stiffness, c.damping, c.mass, threshold);
|
|
1969
|
+
return {
|
|
1970
|
+
/** The `due` it registers with. */
|
|
1971
|
+
first: (value, velocity, coast) => quiet ? until(value, velocity, coast) : void 0,
|
|
1972
|
+
/** Leaves it unticked until then. Answers false, as the tick of one still moving does. */
|
|
1973
|
+
again(value, velocity, coast) {
|
|
1974
|
+
if (quiet) ctx.defer(id, until(value, velocity, coast));
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1917
1979
|
function startOffsetPhysics(ctx, o, kind) {
|
|
1918
1980
|
const s = stateOf(o.target);
|
|
1919
1981
|
const ax = s.axes;
|
|
1920
|
-
const { stiffness, damping, mass } = resolveSpringConstants(o);
|
|
1921
1982
|
let dest = o.to == null ? null : axesFor(ax, o.to, "to");
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
}
|
|
1983
|
+
const constants = constantsFor(o, kind, dest != null);
|
|
1984
|
+
const { stiffness, damping, mass } = constants;
|
|
1925
1985
|
const given = o.from === void 0 ? null : axesFor(ax, o.from, "from");
|
|
1926
1986
|
let velocity = o.velocity === void 0 ? null : axesFor(ax, o.velocity, "velocity");
|
|
1927
1987
|
const restThreshold = o.restThreshold ?? 0.01;
|
|
1928
1988
|
const zeros = () => new Array(ax.count).fill(0);
|
|
1929
1989
|
const id = ctx.nextId();
|
|
1990
|
+
const waits = restWaits(ctx, id, !o.onTick, constants, restThreshold);
|
|
1930
1991
|
const interrupts = o.interrupt !== false;
|
|
1931
1992
|
const seed = {
|
|
1932
1993
|
id,
|
|
@@ -1968,6 +2029,7 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
1968
2029
|
o.onDone?.();
|
|
1969
2030
|
};
|
|
1970
2031
|
let ticked = false;
|
|
2032
|
+
seed.due = waits.first(start, velocity ?? zeros(), dest == null);
|
|
1971
2033
|
seed.onCancel = () => leave(true);
|
|
1972
2034
|
seed.onRate = (paused, timeScale) => voices.rate(paused ? 0 : timeScale);
|
|
1973
2035
|
seed.velocity = () => voices.own().velocity;
|
|
@@ -1984,7 +2046,9 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
1984
2046
|
ticked = true;
|
|
1985
2047
|
o.onTick?.(ax.from(plus(dest ?? ax.to(s.get()), own.value)));
|
|
1986
2048
|
if (!ctx.has(id)) return true;
|
|
1987
|
-
if (norm(own.velocity) >= restThreshold || dest && norm(own.value) >= restThreshold)
|
|
2049
|
+
if (norm(own.velocity) >= restThreshold || dest && norm(own.value) >= restThreshold) {
|
|
2050
|
+
return waits.again(own.value, own.velocity, dest == null);
|
|
2051
|
+
}
|
|
1988
2052
|
if (dest) o.onTick?.(ax.from(dest));
|
|
1989
2053
|
if (!ctx.has(id)) return true;
|
|
1990
2054
|
finish();
|
|
@@ -2003,6 +2067,7 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
2003
2067
|
dest = null;
|
|
2004
2068
|
voices.rest(minus(own.value, moved), own.velocity, true);
|
|
2005
2069
|
voices.settleMove();
|
|
2070
|
+
waits.again(own.value, own.velocity, true);
|
|
2006
2071
|
return;
|
|
2007
2072
|
}
|
|
2008
2073
|
const to = axesFor(ax, next, "setTarget");
|
|
@@ -2012,11 +2077,14 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
2012
2077
|
if (dest == null) voices.rest(own.value, own.velocity, false);
|
|
2013
2078
|
dest = to;
|
|
2014
2079
|
voices.move(shift);
|
|
2080
|
+
waits.again(plus(own.value, shift), own.velocity, false);
|
|
2015
2081
|
ctx.targets.showNow(s);
|
|
2016
2082
|
},
|
|
2017
2083
|
setVelocity(v) {
|
|
2018
2084
|
if (left) return;
|
|
2019
|
-
|
|
2085
|
+
const pushed = axesFor(ax, v, "setVelocity");
|
|
2086
|
+
waits.again(voices.own().value, pushed, dest == null);
|
|
2087
|
+
voices.push(pushed);
|
|
2020
2088
|
}
|
|
2021
2089
|
};
|
|
2022
2090
|
}
|
|
@@ -2072,6 +2140,7 @@ function startOffsetTween(ctx, o) {
|
|
|
2072
2140
|
ctx.targets.detach(s);
|
|
2073
2141
|
};
|
|
2074
2142
|
seed.progress = (virtualNow) => o.ms <= 0 ? 1 : Math.min(1, virtualNow / o.ms);
|
|
2143
|
+
if (!o.onTick) seed.due = Math.max(o.ms, 0);
|
|
2075
2144
|
seed.onCancel = () => leave(true);
|
|
2076
2145
|
seed.tick = (nowMs) => {
|
|
2077
2146
|
if (ctx.tripwire()) {
|
|
@@ -2120,6 +2189,199 @@ function createCompletions(has) {
|
|
|
2120
2189
|
};
|
|
2121
2190
|
}
|
|
2122
2191
|
|
|
2192
|
+
// src/engine/wakes.ts
|
|
2193
|
+
function createWakes() {
|
|
2194
|
+
const heap = [];
|
|
2195
|
+
const place = (w, i) => {
|
|
2196
|
+
heap[i] = w;
|
|
2197
|
+
w.heapAt = i;
|
|
2198
|
+
};
|
|
2199
|
+
const up = (w, i) => {
|
|
2200
|
+
while (i > 0) {
|
|
2201
|
+
const parent = i - 1 >> 1;
|
|
2202
|
+
const p = heap[parent];
|
|
2203
|
+
if (p.wakeAt <= w.wakeAt) break;
|
|
2204
|
+
place(p, i);
|
|
2205
|
+
i = parent;
|
|
2206
|
+
}
|
|
2207
|
+
place(w, i);
|
|
2208
|
+
};
|
|
2209
|
+
const down = (w, i) => {
|
|
2210
|
+
const n = heap.length;
|
|
2211
|
+
for (; ; ) {
|
|
2212
|
+
let child = 2 * i + 1;
|
|
2213
|
+
if (child >= n) break;
|
|
2214
|
+
if (child + 1 < n && heap[child + 1].wakeAt < heap[child].wakeAt) child += 1;
|
|
2215
|
+
const c = heap[child];
|
|
2216
|
+
if (c.wakeAt >= w.wakeAt) break;
|
|
2217
|
+
place(c, i);
|
|
2218
|
+
i = child;
|
|
2219
|
+
}
|
|
2220
|
+
place(w, i);
|
|
2221
|
+
};
|
|
2222
|
+
const remove = (w) => {
|
|
2223
|
+
const i = w.heapAt;
|
|
2224
|
+
if (i < 0) return;
|
|
2225
|
+
w.heapAt = -1;
|
|
2226
|
+
const last = heap.pop();
|
|
2227
|
+
if (last === w) return;
|
|
2228
|
+
if (i > 0 && last.wakeAt < heap[i - 1 >> 1].wakeAt) up(last, i);
|
|
2229
|
+
else down(last, i);
|
|
2230
|
+
};
|
|
2231
|
+
return {
|
|
2232
|
+
set(w, at) {
|
|
2233
|
+
remove(w);
|
|
2234
|
+
w.wakeAt = at;
|
|
2235
|
+
heap.push(w);
|
|
2236
|
+
up(w, heap.length - 1);
|
|
2237
|
+
},
|
|
2238
|
+
remove,
|
|
2239
|
+
due(now) {
|
|
2240
|
+
const out = [];
|
|
2241
|
+
while (heap.length > 0 && heap[0].wakeAt <= now) {
|
|
2242
|
+
const w = heap[0];
|
|
2243
|
+
remove(w);
|
|
2244
|
+
out.push(w);
|
|
2245
|
+
}
|
|
2246
|
+
if (out.length > 1) out.sort((a, b) => a.seq - b.seq);
|
|
2247
|
+
return out;
|
|
2248
|
+
},
|
|
2249
|
+
clear() {
|
|
2250
|
+
for (const w of heap) w.heapAt = -1;
|
|
2251
|
+
heap.length = 0;
|
|
2252
|
+
},
|
|
2253
|
+
get size() {
|
|
2254
|
+
return heap.length;
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/engine/schedule.ts
|
|
2260
|
+
function createSchedule(elapsed) {
|
|
2261
|
+
const every = /* @__PURE__ */ new Set();
|
|
2262
|
+
const wakes = createWakes();
|
|
2263
|
+
let seq = 0;
|
|
2264
|
+
let awake = 0;
|
|
2265
|
+
let waiting = 0;
|
|
2266
|
+
const rateOf = (a) => a.paused ? 0 : a.timeScale;
|
|
2267
|
+
const clockOf = (a) => a.base + (elapsed() - a.anchor) * rateOf(a);
|
|
2268
|
+
const wait = (a) => {
|
|
2269
|
+
const rate = rateOf(a);
|
|
2270
|
+
if (rate > 0 && a.due < Infinity) wakes.set(a, a.anchor + (a.due - a.base) / rate);
|
|
2271
|
+
else wakes.remove(a);
|
|
2272
|
+
};
|
|
2273
|
+
return {
|
|
2274
|
+
clockOf,
|
|
2275
|
+
add(a) {
|
|
2276
|
+
a.base = 0;
|
|
2277
|
+
a.anchor = elapsed();
|
|
2278
|
+
a.seq = seq++;
|
|
2279
|
+
a.heapAt = -1;
|
|
2280
|
+
a.wakeAt = Infinity;
|
|
2281
|
+
if (!a.idle) awake += 1;
|
|
2282
|
+
if (a.due === void 0) {
|
|
2283
|
+
every.add(a);
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
waiting += 1;
|
|
2287
|
+
wait(a);
|
|
2288
|
+
},
|
|
2289
|
+
remove(a) {
|
|
2290
|
+
if (!a.idle) awake -= 1;
|
|
2291
|
+
if (every.delete(a)) return;
|
|
2292
|
+
waiting -= 1;
|
|
2293
|
+
wakes.remove(a);
|
|
2294
|
+
},
|
|
2295
|
+
pace(a, paused, timeScale) {
|
|
2296
|
+
a.base = clockOf(a);
|
|
2297
|
+
a.anchor = elapsed();
|
|
2298
|
+
a.paused = paused;
|
|
2299
|
+
a.timeScale = timeScale;
|
|
2300
|
+
if (!every.has(a)) wait(a);
|
|
2301
|
+
},
|
|
2302
|
+
defer(a, ms) {
|
|
2303
|
+
if (!a) return;
|
|
2304
|
+
a.due = clockOf(a) + ms;
|
|
2305
|
+
if (every.delete(a)) waiting += 1;
|
|
2306
|
+
wait(a);
|
|
2307
|
+
},
|
|
2308
|
+
ticks() {
|
|
2309
|
+
const steady = [...every];
|
|
2310
|
+
const woke = wakes.due(elapsed());
|
|
2311
|
+
if (woke.length === 0) return steady;
|
|
2312
|
+
waiting -= woke.length;
|
|
2313
|
+
const out = [];
|
|
2314
|
+
let i = 0;
|
|
2315
|
+
for (const a of woke) {
|
|
2316
|
+
a.base = Math.max(clockOf(a), a.due);
|
|
2317
|
+
a.anchor = elapsed();
|
|
2318
|
+
every.add(a);
|
|
2319
|
+
while (i < steady.length && steady[i].seq < a.seq) out.push(steady[i++]);
|
|
2320
|
+
out.push(a);
|
|
2321
|
+
}
|
|
2322
|
+
while (i < steady.length) out.push(steady[i++]);
|
|
2323
|
+
return out;
|
|
2324
|
+
},
|
|
2325
|
+
get awake() {
|
|
2326
|
+
return awake > 0;
|
|
2327
|
+
},
|
|
2328
|
+
get waiting() {
|
|
2329
|
+
return waiting;
|
|
2330
|
+
},
|
|
2331
|
+
clear() {
|
|
2332
|
+
every.clear();
|
|
2333
|
+
wakes.clear();
|
|
2334
|
+
awake = 0;
|
|
2335
|
+
waiting = 0;
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// src/engine/roster.ts
|
|
2341
|
+
var NONE = [];
|
|
2342
|
+
function file(index, key, a, on) {
|
|
2343
|
+
let set = index.get(key);
|
|
2344
|
+
if (on) {
|
|
2345
|
+
if (!set) index.set(key, set = /* @__PURE__ */ new Set());
|
|
2346
|
+
set.add(a);
|
|
2347
|
+
} else if (set?.delete(a) && set.size === 0) index.delete(key);
|
|
2348
|
+
}
|
|
2349
|
+
function createRoster(elapsed) {
|
|
2350
|
+
const animations = /* @__PURE__ */ new Map();
|
|
2351
|
+
const schedule = createSchedule(elapsed);
|
|
2352
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2353
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
2354
|
+
const index = (a, on) => {
|
|
2355
|
+
if (a.cancelKey != null) file(byKey, a.cancelKey, a, on);
|
|
2356
|
+
if (a.target) file(byTarget, a.target, a, on);
|
|
2357
|
+
};
|
|
2358
|
+
const remove = (a) => {
|
|
2359
|
+
animations.delete(a.id);
|
|
2360
|
+
schedule.remove(a);
|
|
2361
|
+
index(a, false);
|
|
2362
|
+
};
|
|
2363
|
+
return {
|
|
2364
|
+
animations,
|
|
2365
|
+
schedule,
|
|
2366
|
+
under: (key) => byKey.get(key) ?? NONE,
|
|
2367
|
+
on: (target) => byTarget.get(target) ?? NONE,
|
|
2368
|
+
add(a) {
|
|
2369
|
+
const had = animations.get(a.id);
|
|
2370
|
+
if (had) remove(had);
|
|
2371
|
+
animations.set(a.id, a);
|
|
2372
|
+
schedule.add(a);
|
|
2373
|
+
index(a, true);
|
|
2374
|
+
},
|
|
2375
|
+
remove,
|
|
2376
|
+
clear() {
|
|
2377
|
+
animations.clear();
|
|
2378
|
+
schedule.clear();
|
|
2379
|
+
byKey.clear();
|
|
2380
|
+
byTarget.clear();
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2123
2385
|
// src/engine/table.ts
|
|
2124
2386
|
function timeScaleOf(scale) {
|
|
2125
2387
|
if (!(scale >= 0)) {
|
|
@@ -2129,7 +2391,8 @@ function timeScaleOf(scale) {
|
|
|
2129
2391
|
}
|
|
2130
2392
|
function createTable(host) {
|
|
2131
2393
|
const { codec, hub } = host;
|
|
2132
|
-
const
|
|
2394
|
+
const roster = createRoster(host.elapsed);
|
|
2395
|
+
const { animations, schedule, remove } = roster;
|
|
2133
2396
|
const { fire: fireCompletion, watch: watchCompletion } = createCompletions((id) => animations.has(id));
|
|
2134
2397
|
const infoOf = (a) => {
|
|
2135
2398
|
if (!a.info) {
|
|
@@ -2151,31 +2414,27 @@ function createTable(host) {
|
|
|
2151
2414
|
if (!a) return;
|
|
2152
2415
|
a.onCancel?.();
|
|
2153
2416
|
codec.stop(id);
|
|
2154
|
-
|
|
2417
|
+
remove(a);
|
|
2155
2418
|
if (host.resting()) host.requestFrame();
|
|
2156
2419
|
fireCompletion(id);
|
|
2157
2420
|
if (!hub.watched || a.ended || error) return;
|
|
2158
2421
|
hub.emit(by ? { type: "interrupt", animation: infoOf(a), by: infoOf(by) } : { type: "cancel", animation: infoOf(a) });
|
|
2159
2422
|
};
|
|
2160
2423
|
const cancelByKey = (key, by) => {
|
|
2161
|
-
const
|
|
2162
|
-
for (const anim of animations.values()) {
|
|
2163
|
-
if (anim.cancelKey === key) ids.push(anim.id);
|
|
2164
|
-
}
|
|
2165
|
-
for (const id of ids) retire(id, by);
|
|
2424
|
+
for (const a of [...roster.under(key)]) retire(a.id, by);
|
|
2166
2425
|
};
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
a.
|
|
2426
|
+
const pace = (a, paused, timeScale) => {
|
|
2427
|
+
schedule.pace(a, paused, timeScale);
|
|
2428
|
+
if (codec.has(a.id)) codec.rate(a.id, paused ? 0 : timeScale);
|
|
2429
|
+
a.onRate?.(paused, timeScale);
|
|
2170
2430
|
};
|
|
2171
2431
|
const register = (seed) => {
|
|
2172
2432
|
const anim = seed;
|
|
2173
2433
|
if (seed.cancelKey != null && !seed.keepExisting) cancelByKey(seed.cancelKey, anim);
|
|
2174
2434
|
anim.paused = false;
|
|
2175
2435
|
anim.timeScale = 1;
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
animations.set(anim.id, anim);
|
|
2436
|
+
host.seedClock();
|
|
2437
|
+
roster.add(anim);
|
|
2179
2438
|
if (anim.target) host.showNow(anim.target);
|
|
2180
2439
|
if (hub.watched) hub.emit({ type: "start", animation: infoOf(anim) });
|
|
2181
2440
|
if (animations.size > 0 && !host.framing()) host.requestFrame();
|
|
@@ -2185,24 +2444,15 @@ function createTable(host) {
|
|
|
2185
2444
|
cancel: () => retire(anim.id),
|
|
2186
2445
|
pause: () => {
|
|
2187
2446
|
const a = live();
|
|
2188
|
-
if (a)
|
|
2189
|
-
a.paused = true;
|
|
2190
|
-
rateOf(a);
|
|
2191
|
-
}
|
|
2447
|
+
if (a) pace(a, true, a.timeScale);
|
|
2192
2448
|
},
|
|
2193
2449
|
resume: () => {
|
|
2194
2450
|
const a = live();
|
|
2195
|
-
if (a)
|
|
2196
|
-
a.paused = false;
|
|
2197
|
-
rateOf(a);
|
|
2198
|
-
}
|
|
2451
|
+
if (a) pace(a, false, a.timeScale);
|
|
2199
2452
|
},
|
|
2200
2453
|
setTimeScale: (s) => {
|
|
2201
2454
|
const a = live();
|
|
2202
|
-
if (a)
|
|
2203
|
-
a.timeScale = timeScaleOf(s);
|
|
2204
|
-
rateOf(a);
|
|
2205
|
-
}
|
|
2455
|
+
if (a) pace(a, a.paused, timeScaleOf(s));
|
|
2206
2456
|
},
|
|
2207
2457
|
timeScale: () => live()?.timeScale ?? 1,
|
|
2208
2458
|
isPaused: () => live()?.paused ?? false
|
|
@@ -2214,16 +2464,28 @@ function createTable(host) {
|
|
|
2214
2464
|
reportEnd,
|
|
2215
2465
|
retire,
|
|
2216
2466
|
cancelByKey,
|
|
2217
|
-
|
|
2467
|
+
under: roster.under,
|
|
2468
|
+
on: roster.on,
|
|
2469
|
+
pace,
|
|
2470
|
+
clockOf: schedule.clockOf,
|
|
2471
|
+
defer: (id, ms) => schedule.defer(animations.get(id), ms),
|
|
2472
|
+
ticks: schedule.ticks,
|
|
2473
|
+
get awake() {
|
|
2474
|
+
return schedule.awake;
|
|
2475
|
+
},
|
|
2476
|
+
get waiting() {
|
|
2477
|
+
return schedule.waiting;
|
|
2478
|
+
},
|
|
2218
2479
|
register,
|
|
2219
2480
|
finish(id) {
|
|
2220
|
-
|
|
2481
|
+
const a = animations.get(id);
|
|
2482
|
+
if (!a) return;
|
|
2221
2483
|
reportEnd(id);
|
|
2222
|
-
|
|
2484
|
+
remove(a);
|
|
2223
2485
|
fireCompletion(id);
|
|
2224
2486
|
},
|
|
2225
2487
|
drop(a) {
|
|
2226
|
-
|
|
2488
|
+
remove(a);
|
|
2227
2489
|
fireCompletion(a.id);
|
|
2228
2490
|
},
|
|
2229
2491
|
cancelAll(cleared) {
|
|
@@ -2233,7 +2495,7 @@ function createTable(host) {
|
|
|
2233
2495
|
codec.stop(a.id);
|
|
2234
2496
|
retired.push(a);
|
|
2235
2497
|
}
|
|
2236
|
-
|
|
2498
|
+
roster.clear();
|
|
2237
2499
|
cleared();
|
|
2238
2500
|
for (const a of retired) fireCompletion(a.id);
|
|
2239
2501
|
if (hub.watched) for (const a of retired) hub.emit({ type: "cancel", animation: infoOf(a) });
|
|
@@ -2298,12 +2560,8 @@ function startTween(ctx, o) {
|
|
|
2298
2560
|
}
|
|
2299
2561
|
|
|
2300
2562
|
// src/engine/animatorApi.ts
|
|
2301
|
-
function eachUnder(table, key,
|
|
2302
|
-
for (const a of table.
|
|
2303
|
-
if (a.cancelKey !== key) continue;
|
|
2304
|
-
set(a);
|
|
2305
|
-
table.rateOf(a);
|
|
2306
|
-
}
|
|
2563
|
+
function eachUnder(table, key, pace) {
|
|
2564
|
+
for (const a of table.under(key)) table.pace(a, ...pace(a));
|
|
2307
2565
|
}
|
|
2308
2566
|
function liveRows(table) {
|
|
2309
2567
|
const rows = [];
|
|
@@ -2312,8 +2570,8 @@ function liveRows(table) {
|
|
|
2312
2570
|
...table.infoOf(a),
|
|
2313
2571
|
paused: a.paused,
|
|
2314
2572
|
timeScale: a.timeScale,
|
|
2315
|
-
elapsed: a
|
|
2316
|
-
...a.progress ? { progress: a.progress(a
|
|
2573
|
+
elapsed: table.clockOf(a),
|
|
2574
|
+
...a.progress ? { progress: a.progress(table.clockOf(a)) } : {}
|
|
2317
2575
|
});
|
|
2318
2576
|
}
|
|
2319
2577
|
return rows;
|
|
@@ -2367,9 +2625,7 @@ function createAnimatorApi(host) {
|
|
|
2367
2625
|
},
|
|
2368
2626
|
isActive: (key) => {
|
|
2369
2627
|
if (key == null) return animations.size > 0;
|
|
2370
|
-
for (const
|
|
2371
|
-
if (a.cancelKey === key) return true;
|
|
2372
|
-
}
|
|
2628
|
+
for (const _ of table.under(key)) return true;
|
|
2373
2629
|
return false;
|
|
2374
2630
|
},
|
|
2375
2631
|
isTicking: host.ticking,
|
|
@@ -2384,17 +2640,11 @@ function createAnimatorApi(host) {
|
|
|
2384
2640
|
clock.timeScale = timeScaleOf(s);
|
|
2385
2641
|
},
|
|
2386
2642
|
timeScale: () => clock.timeScale,
|
|
2387
|
-
pauseKey: (key) => eachUnder(table, key, (a) =>
|
|
2388
|
-
|
|
2389
|
-
}),
|
|
2390
|
-
resumeKey: (key) => eachUnder(table, key, (a) => {
|
|
2391
|
-
a.paused = false;
|
|
2392
|
-
}),
|
|
2643
|
+
pauseKey: (key) => eachUnder(table, key, (a) => [true, a.timeScale]),
|
|
2644
|
+
resumeKey: (key) => eachUnder(table, key, (a) => [false, a.timeScale]),
|
|
2393
2645
|
setTimeScaleByKey: (key, s) => {
|
|
2394
2646
|
const scale = timeScaleOf(s);
|
|
2395
|
-
eachUnder(table, key, (a) =>
|
|
2396
|
-
a.timeScale = scale;
|
|
2397
|
-
});
|
|
2647
|
+
eachUnder(table, key, (a) => [a.paused, scale]);
|
|
2398
2648
|
},
|
|
2399
2649
|
loop: (factory, loopOpts) => createLoop(createSupervisor, factory, loopOpts),
|
|
2400
2650
|
tweenLoop: (tweenLoopOpts) => createTweenLoop(api, createSupervisor, tweenLoopOpts),
|
|
@@ -2409,12 +2659,7 @@ function createAnimatorApi(host) {
|
|
|
2409
2659
|
)),
|
|
2410
2660
|
timeline: (o) => startTimeline(host, o),
|
|
2411
2661
|
colorOverrides: host.colorOverrides,
|
|
2412
|
-
onTick: (cb) =>
|
|
2413
|
-
host.subscribers.add(cb);
|
|
2414
|
-
return () => {
|
|
2415
|
-
host.subscribers.delete(cb);
|
|
2416
|
-
};
|
|
2417
|
-
},
|
|
2662
|
+
onTick: (cb) => host.subscribers.subscribe(cb),
|
|
2418
2663
|
keepAlive: () => {
|
|
2419
2664
|
const sup = createSupervisor({ kind: "keepAlive" });
|
|
2420
2665
|
return () => sup.cancel();
|
|
@@ -2424,6 +2669,75 @@ function createAnimatorApi(host) {
|
|
|
2424
2669
|
};
|
|
2425
2670
|
return api;
|
|
2426
2671
|
}
|
|
2672
|
+
function createHoldVoices() {
|
|
2673
|
+
const asked = /* @__PURE__ */ new Set();
|
|
2674
|
+
const stills = /* @__PURE__ */ new Map();
|
|
2675
|
+
const stale2 = /* @__PURE__ */ new Set();
|
|
2676
|
+
const cue = (v) => {
|
|
2677
|
+
const { start } = v;
|
|
2678
|
+
const { lane, key } = v.slot;
|
|
2679
|
+
v.handle?.fade({ over: 0 });
|
|
2680
|
+
stale2.delete(v);
|
|
2681
|
+
if (start.still) {
|
|
2682
|
+
v.last = start.at();
|
|
2683
|
+
const rest = tween("v", { from: v.last, to: v.last.slice(), ms: 1 });
|
|
2684
|
+
v.handle = lane.mix.cue({ patch: rest, subjects: [key] });
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
const each = patch(
|
|
2688
|
+
1,
|
|
2689
|
+
() => {
|
|
2690
|
+
v.last = start.at();
|
|
2691
|
+
return { v: v.last };
|
|
2692
|
+
},
|
|
2693
|
+
{ writes: ["v"] }
|
|
2694
|
+
);
|
|
2695
|
+
v.handle = lane.mix.cue({ patch: each, subjects: [key], loop: true });
|
|
2696
|
+
};
|
|
2697
|
+
const settle2 = () => {
|
|
2698
|
+
for (const v of stale2) cue(v);
|
|
2699
|
+
};
|
|
2700
|
+
return {
|
|
2701
|
+
add(slot, start) {
|
|
2702
|
+
const v = { slot, start: { ...start } };
|
|
2703
|
+
if (start.still) {
|
|
2704
|
+
stills.set(start.on, v);
|
|
2705
|
+
stale2.add(v);
|
|
2706
|
+
} else {
|
|
2707
|
+
cue(v);
|
|
2708
|
+
asked.add(v);
|
|
2709
|
+
}
|
|
2710
|
+
return v;
|
|
2711
|
+
},
|
|
2712
|
+
still(v, still) {
|
|
2713
|
+
if (!v.start.still === !still) return;
|
|
2714
|
+
v.start.still = still;
|
|
2715
|
+
if (still) {
|
|
2716
|
+
asked.delete(v);
|
|
2717
|
+
stills.set(v.start.on, v);
|
|
2718
|
+
} else {
|
|
2719
|
+
stills.delete(v.start.on);
|
|
2720
|
+
asked.add(v);
|
|
2721
|
+
}
|
|
2722
|
+
cue(v);
|
|
2723
|
+
},
|
|
2724
|
+
touch(on) {
|
|
2725
|
+
const v = stills.get(on);
|
|
2726
|
+
if (v) stale2.add(v);
|
|
2727
|
+
},
|
|
2728
|
+
settle: settle2,
|
|
2729
|
+
frame() {
|
|
2730
|
+
for (const v of asked) v.slot.lane.mix.touch(v.slot.key);
|
|
2731
|
+
settle2();
|
|
2732
|
+
},
|
|
2733
|
+
remove(v) {
|
|
2734
|
+
v.handle?.fade({ over: 0 });
|
|
2735
|
+
asked.delete(v);
|
|
2736
|
+
stale2.delete(v);
|
|
2737
|
+
if (stills.get(v.start.on) === v) stills.delete(v.start.on);
|
|
2738
|
+
}
|
|
2739
|
+
};
|
|
2740
|
+
}
|
|
2427
2741
|
function depart(s) {
|
|
2428
2742
|
const l = s.lane;
|
|
2429
2743
|
l.dirty = true;
|
|
@@ -2563,7 +2877,7 @@ function createCodec(placeOf) {
|
|
|
2563
2877
|
const lanes = createLanes(placeOf);
|
|
2564
2878
|
const voices = /* @__PURE__ */ new Map();
|
|
2565
2879
|
const queue = [];
|
|
2566
|
-
const holds =
|
|
2880
|
+
const holds = createHoldVoices();
|
|
2567
2881
|
let time = 0;
|
|
2568
2882
|
const springVoice = (slot, sp) => {
|
|
2569
2883
|
const patch = sp.to == null ? glide("v", { from: sp.from, ms: 1e3 * sp.mass / sp.damping }) : spring("v", {
|
|
@@ -2596,9 +2910,9 @@ function createCodec(placeOf) {
|
|
|
2596
2910
|
function stop(id) {
|
|
2597
2911
|
const v = voices.get(id);
|
|
2598
2912
|
if (!v) return;
|
|
2599
|
-
if (v.
|
|
2913
|
+
if (v.held) holds.remove(v.held);
|
|
2914
|
+
else if (v.queued) v.queued = void 0;
|
|
2600
2915
|
else v.handle.fade({ over: 0 });
|
|
2601
|
-
holds.delete(v);
|
|
2602
2916
|
lanes.leave(v.slot);
|
|
2603
2917
|
voices.delete(id);
|
|
2604
2918
|
}
|
|
@@ -2616,7 +2930,7 @@ function createCodec(placeOf) {
|
|
|
2616
2930
|
if (v.queuedRate !== void 0) v.handle.rate = v.queuedRate;
|
|
2617
2931
|
}
|
|
2618
2932
|
queue.length = 0;
|
|
2619
|
-
|
|
2933
|
+
holds.frame();
|
|
2620
2934
|
time += dtMs;
|
|
2621
2935
|
lanes.frame(time);
|
|
2622
2936
|
},
|
|
@@ -2662,22 +2976,20 @@ function createCodec(placeOf) {
|
|
|
2662
2976
|
hold(s) {
|
|
2663
2977
|
const zeros = new Array(s.axes).fill(0);
|
|
2664
2978
|
const slot = lanes.join(s.axes, s.on, zeros);
|
|
2665
|
-
|
|
2666
|
-
const patch$1 = patch(
|
|
2667
|
-
1,
|
|
2668
|
-
() => {
|
|
2669
|
-
v.last = s.at();
|
|
2670
|
-
return { v: v.last };
|
|
2671
|
-
},
|
|
2672
|
-
{ writes: ["v"] }
|
|
2673
|
-
);
|
|
2674
|
-
v.handle = slot.lane.mix.cue({ patch: patch$1, subjects: [slot.key], loop: true });
|
|
2675
|
-
voices.set(s.id, v);
|
|
2676
|
-
holds.add(v);
|
|
2979
|
+
voices.set(s.id, { kind: "hold", slot, from: zeros, held: holds.add(slot, s) });
|
|
2677
2980
|
return slot;
|
|
2678
2981
|
},
|
|
2679
|
-
|
|
2982
|
+
still(id, still) {
|
|
2983
|
+
const v = get(id);
|
|
2984
|
+
if (!v.held) throw new Error(`codec: subject ${id} is a ${v.kind}, not a hold`);
|
|
2985
|
+
holds.still(v.held, still);
|
|
2986
|
+
},
|
|
2987
|
+
touch(on) {
|
|
2988
|
+
holds.touch(on);
|
|
2989
|
+
lanes.touch(on);
|
|
2990
|
+
},
|
|
2680
2991
|
refresh(reread) {
|
|
2992
|
+
holds.settle();
|
|
2681
2993
|
lanes.refresh((slot) => {
|
|
2682
2994
|
const queued = new Array(slot.lane.axes).fill(0);
|
|
2683
2995
|
for (const v of queue) {
|
|
@@ -2723,7 +3035,9 @@ function createCodec(placeOf) {
|
|
|
2723
3035
|
const v = voices.get(id);
|
|
2724
3036
|
if (!v) return void 0;
|
|
2725
3037
|
if (v.kind === "spring") return current(v);
|
|
2726
|
-
if (v.kind === "path" || v.kind === "hold")
|
|
3038
|
+
if (v.kind === "path" || v.kind === "hold") {
|
|
3039
|
+
return { value: v.held?.last ?? v.last ?? v.from, velocity: new Array(v.from.length).fill(0) };
|
|
3040
|
+
}
|
|
2727
3041
|
return !v.queued && v.patch.read(v.slot.key) || { value: v.from, velocity: new Array(v.from.length).fill(0) };
|
|
2728
3042
|
},
|
|
2729
3043
|
voiceCount: () => voices.size,
|
|
@@ -2737,18 +3051,16 @@ function createFrame(host) {
|
|
|
2737
3051
|
const { animations } = table;
|
|
2738
3052
|
let framing = false;
|
|
2739
3053
|
let tickDepth = 0;
|
|
2740
|
-
const tickEach = (
|
|
3054
|
+
const tickEach = () => {
|
|
2741
3055
|
const finished = [];
|
|
2742
3056
|
const threw = [];
|
|
2743
|
-
|
|
3057
|
+
const all = table.waiting > 0 && host.tripwire();
|
|
3058
|
+
for (const anim of all ? [...animations.values()] : table.ticks()) {
|
|
2744
3059
|
if (animations.get(anim.id) !== anim) continue;
|
|
2745
|
-
const realDt = anim.lastRealNow == null ? 0 : Math.max(0, t - anim.lastRealNow);
|
|
2746
|
-
anim.lastRealNow = t;
|
|
2747
3060
|
const scale = clock.paused ? 0 : clock.timeScale * (anim.paused ? 0 : anim.timeScale);
|
|
2748
|
-
anim.virtualNow += realDt * scale;
|
|
2749
3061
|
tickDepth += 1;
|
|
2750
3062
|
try {
|
|
2751
|
-
if (anim.tick(anim
|
|
3063
|
+
if (anim.tick(table.clockOf(anim), scale)) finished.push(anim);
|
|
2752
3064
|
} catch (error) {
|
|
2753
3065
|
threw.push([anim, error]);
|
|
2754
3066
|
} finally {
|
|
@@ -2764,18 +3076,16 @@ function createFrame(host) {
|
|
|
2764
3076
|
}
|
|
2765
3077
|
return finished;
|
|
2766
3078
|
};
|
|
2767
|
-
const awake = () => {
|
|
2768
|
-
for (const a of animations.values()) if (!a.idle) return true;
|
|
2769
|
-
return false;
|
|
2770
|
-
};
|
|
2771
3079
|
return {
|
|
2772
3080
|
tick(t) {
|
|
2773
3081
|
const frameDt = clock.frameT == null ? 0 : Math.max(0, t - clock.frameT);
|
|
2774
3082
|
clock.frameT = t;
|
|
2775
3083
|
framing = true;
|
|
2776
|
-
|
|
3084
|
+
const dt = frameDt * (clock.paused ? 0 : clock.timeScale);
|
|
3085
|
+
clock.elapsed += dt;
|
|
3086
|
+
host.codec.frame(dt);
|
|
2777
3087
|
host.targets.settle();
|
|
2778
|
-
for (const anim of tickEach(
|
|
3088
|
+
for (const anim of tickEach()) {
|
|
2779
3089
|
if (animations.get(anim.id) === anim) table.finish(anim.id);
|
|
2780
3090
|
}
|
|
2781
3091
|
for (const cb of host.afterTicks.splice(0)) {
|
|
@@ -2788,41 +3098,36 @@ function createFrame(host) {
|
|
|
2788
3098
|
host.reread();
|
|
2789
3099
|
framing = false;
|
|
2790
3100
|
host.targets.show();
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
sub();
|
|
2794
|
-
} catch (err) {
|
|
2795
|
-
console.error("useAnimator: onTick subscriber threw", err);
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2798
|
-
if (awake()) {
|
|
3101
|
+
host.subscribers.emit();
|
|
3102
|
+
if (table.awake) {
|
|
2799
3103
|
host.requestFrame();
|
|
2800
3104
|
return;
|
|
2801
3105
|
}
|
|
2802
3106
|
clock.frameT = null;
|
|
2803
|
-
for (const anim of animations.values()) anim.lastRealNow = null;
|
|
2804
3107
|
},
|
|
2805
3108
|
framing: () => framing,
|
|
2806
3109
|
ticking: () => tickDepth > 0
|
|
2807
3110
|
};
|
|
2808
3111
|
}
|
|
2809
|
-
|
|
2810
|
-
// src/engine/core.ts
|
|
2811
3112
|
function createAnimatorCore(host) {
|
|
2812
|
-
const clock = { frameT: null, timeScale: 1, paused: false };
|
|
3113
|
+
const clock = { frameT: null, elapsed: 0, timeScale: 1, paused: false };
|
|
2813
3114
|
const hub = new AnimatorEventHub();
|
|
2814
3115
|
const codec = createCodec((on) => {
|
|
2815
3116
|
const { row } = on;
|
|
2816
3117
|
return row ? { group: row.table, tag: row.index } : void 0;
|
|
2817
3118
|
});
|
|
2818
3119
|
const afterTicks = [];
|
|
2819
|
-
const subscribers =
|
|
3120
|
+
const subscribers = createEmitter();
|
|
2820
3121
|
let nextId = 1;
|
|
3122
|
+
const seedClock = () => {
|
|
3123
|
+
if (clock.frameT == null) clock.frameT = host.now();
|
|
3124
|
+
};
|
|
2821
3125
|
const reread = () => codec.refresh((on, queued) => targets.reread(on, queued));
|
|
2822
3126
|
const table = createTable({
|
|
2823
3127
|
codec,
|
|
2824
3128
|
hub,
|
|
2825
|
-
|
|
3129
|
+
elapsed: () => clock.elapsed,
|
|
3130
|
+
seedClock,
|
|
2826
3131
|
showNow: (target) => targets.showNow(target),
|
|
2827
3132
|
resting: () => targets.resting,
|
|
2828
3133
|
framing: () => frame.framing(),
|
|
@@ -2830,7 +3135,7 @@ function createAnimatorCore(host) {
|
|
|
2830
3135
|
});
|
|
2831
3136
|
const targets = createTargetBook({
|
|
2832
3137
|
stop: (s) => {
|
|
2833
|
-
for (const a of [...table.
|
|
3138
|
+
for (const a of [...table.on(s)]) table.retire(a.id);
|
|
2834
3139
|
},
|
|
2835
3140
|
reread: (s) => {
|
|
2836
3141
|
codec.touch(s);
|
|
@@ -2847,17 +3152,17 @@ function createAnimatorCore(host) {
|
|
|
2847
3152
|
afterTicks,
|
|
2848
3153
|
subscribers,
|
|
2849
3154
|
reread,
|
|
2850
|
-
requestFrame: host.requestFrame
|
|
3155
|
+
requestFrame: host.requestFrame,
|
|
3156
|
+
tripwire: host.tripwire
|
|
2851
3157
|
});
|
|
2852
3158
|
const ctx = {
|
|
2853
3159
|
codec,
|
|
2854
3160
|
targets,
|
|
2855
3161
|
nextId: () => nextId++,
|
|
2856
|
-
seedClock
|
|
2857
|
-
if (clock.frameT == null) clock.frameT = host.now();
|
|
2858
|
-
},
|
|
3162
|
+
seedClock,
|
|
2859
3163
|
register: table.register,
|
|
2860
3164
|
has: (id) => table.animations.has(id),
|
|
3165
|
+
defer: table.defer,
|
|
2861
3166
|
reportEnd: table.reportEnd,
|
|
2862
3167
|
finish: table.finish,
|
|
2863
3168
|
touch: (target, at) => {
|
|
@@ -2870,8 +3175,8 @@ function createAnimatorCore(host) {
|
|
|
2870
3175
|
interrupt: (key, by) => table.cancelByKey(key, by),
|
|
2871
3176
|
velocityUnder: (key, target) => {
|
|
2872
3177
|
let sum2 = null;
|
|
2873
|
-
for (const a of table.
|
|
2874
|
-
if (a.
|
|
3178
|
+
for (const a of table.under(key)) {
|
|
3179
|
+
if (a.target !== target) continue;
|
|
2875
3180
|
const v = a.velocity?.() ?? codec.own(a.id)?.velocity;
|
|
2876
3181
|
if (v) sum2 = sum2 ? sum2.map((x, i) => x + v[i]) : v.slice();
|
|
2877
3182
|
}
|
|
@@ -2895,7 +3200,6 @@ function createAnimatorCore(host) {
|
|
|
2895
3200
|
api,
|
|
2896
3201
|
tick: frame.tick,
|
|
2897
3202
|
resumed() {
|
|
2898
|
-
for (const anim of table.animations.values()) anim.lastRealNow = null;
|
|
2899
3203
|
clock.frameT = null;
|
|
2900
3204
|
}
|
|
2901
3205
|
};
|
|
@@ -3162,15 +3466,30 @@ function bindTrack(track, binder) {
|
|
|
3162
3466
|
return defined({
|
|
3163
3467
|
kind: "sampled",
|
|
3164
3468
|
label: track.label,
|
|
3165
|
-
keys: track.keys.map((k) => ({ ...k })),
|
|
3469
|
+
keys: track.keys.map((k) => ({ ...k, value: reviveTypedArrays(k.value) })),
|
|
3166
3470
|
target,
|
|
3167
3471
|
targetName: track.target,
|
|
3168
3472
|
interpolate,
|
|
3169
3473
|
interpolateName: track.interpolate,
|
|
3170
|
-
|
|
3171
|
-
axes: track
|
|
3474
|
+
...bindWeight(track, binder),
|
|
3475
|
+
axes: bindAxes(track, target)
|
|
3172
3476
|
});
|
|
3173
3477
|
}
|
|
3478
|
+
function bindWeight(track, binder) {
|
|
3479
|
+
const { weight } = track;
|
|
3480
|
+
if (typeof weight !== "string") return { weight };
|
|
3481
|
+
const read = binder.weight?.(weight);
|
|
3482
|
+
if (!read) throw new Error(`bindClip: the binder has no weight named "${weight}".`);
|
|
3483
|
+
return { weight: read, weightName: weight };
|
|
3484
|
+
}
|
|
3485
|
+
function bindAxes(track, target) {
|
|
3486
|
+
const { axes } = track;
|
|
3487
|
+
if (!axes) return void 0;
|
|
3488
|
+
const where = `bindClip: track "${track.label ?? ""}"`;
|
|
3489
|
+
if (!target) throw new Error(`${where} names axes and no target.`);
|
|
3490
|
+
axisIndexes(target.axes, axes, `${where} on the target "${track.target}"`);
|
|
3491
|
+
return [...axes];
|
|
3492
|
+
}
|
|
3174
3493
|
function bindTimeline(timeline, binder) {
|
|
3175
3494
|
return defined({ tracks: timeline.tracks.map((t) => bindTrack(t, binder)), duration: timeline.duration });
|
|
3176
3495
|
}
|
|
@@ -3200,17 +3519,20 @@ function sampledToClip(track, where) {
|
|
|
3200
3519
|
if (track.target && track.targetName === void 0) throw new Error(`toClip: ${where} has a target and no targetName.`);
|
|
3201
3520
|
if (track.interpolator) throw new Error(`toClip: ${where} has an interpolator, which a clip cannot hold.`);
|
|
3202
3521
|
if (track.interpolate && track.interpolateName === void 0) throw new Error(`toClip: ${where} has an interpolate and no interpolateName.`);
|
|
3203
|
-
if (typeof track.weight === "function"
|
|
3522
|
+
if (typeof track.weight === "function" && track.weightName === void 0) {
|
|
3523
|
+
throw new Error(`toClip: ${where} has a function for its weight and no weightName.`);
|
|
3524
|
+
}
|
|
3204
3525
|
return defined({
|
|
3205
3526
|
kind: "sampled",
|
|
3206
3527
|
label: track.label,
|
|
3207
3528
|
target: track.targetName,
|
|
3208
3529
|
keys: track.keys.map((k) => {
|
|
3209
|
-
|
|
3210
|
-
|
|
3530
|
+
const value = tagTypedArrays(k.value);
|
|
3531
|
+
if (!isClipValue(value)) throw new Error(`toClip: ${where} holds a value JSON cannot round-trip, at t = ${k.t}.`);
|
|
3532
|
+
return defined({ t: k.t, value, easing: easingOf(k.easing, where) });
|
|
3211
3533
|
}),
|
|
3212
3534
|
interpolate: track.interpolateName,
|
|
3213
|
-
weight: track.weight,
|
|
3535
|
+
weight: typeof track.weight === "function" ? track.weightName : track.weight,
|
|
3214
3536
|
axes: track.axes && [...track.axes]
|
|
3215
3537
|
});
|
|
3216
3538
|
}
|
|
@@ -3310,13 +3632,12 @@ var IDENTITY_JOINT = {
|
|
|
3310
3632
|
// src/rig/skeletonAxes.ts
|
|
3311
3633
|
var FIELDS = ["x", "y", "rotation", "scaleX", "scaleY"];
|
|
3312
3634
|
var ROTATION = FIELDS.indexOf("rotation");
|
|
3635
|
+
function axisNames(skeleton) {
|
|
3636
|
+
return skeleton.joints.flatMap((joint) => FIELDS.map((field) => `${joint.name}.${field}`));
|
|
3637
|
+
}
|
|
3313
3638
|
function jointAxes(skeleton, joints) {
|
|
3314
|
-
const
|
|
3315
|
-
return
|
|
3316
|
-
const j = skeleton.joints.findIndex((joint) => joint.name === name);
|
|
3317
|
-
if (j < 0) throw new Error(`jointAxes: the skeleton has no joint "${name}"`);
|
|
3318
|
-
return FIELDS.map((_, f) => j * n + f);
|
|
3319
|
-
});
|
|
3639
|
+
const names = axisNames(skeleton);
|
|
3640
|
+
return axisIndexes({ count: names.length, names }, joints, "jointAxes: `joints`");
|
|
3320
3641
|
}
|
|
3321
3642
|
function skeletonAxes(skeleton) {
|
|
3322
3643
|
const names = skeleton.joints.map((j) => j.name);
|
|
@@ -3324,6 +3645,7 @@ function skeletonAxes(skeleton) {
|
|
|
3324
3645
|
return {
|
|
3325
3646
|
count: names.length * n,
|
|
3326
3647
|
angles: names.map((_, j) => j * n + ROTATION),
|
|
3648
|
+
names: axisNames(skeleton),
|
|
3327
3649
|
to(pose) {
|
|
3328
3650
|
const out = new Array(names.length * n);
|
|
3329
3651
|
for (let j = 0; j < names.length; j++) {
|
|
@@ -3525,7 +3847,7 @@ function skinPath(path, skin, motions) {
|
|
|
3525
3847
|
coords[2 * i] = t > 0 ? sum2[2 * i] / t : from[2 * i];
|
|
3526
3848
|
coords[2 * i + 1] = t > 0 ? sum2[2 * i + 1] / t : from[2 * i + 1];
|
|
3527
3849
|
}
|
|
3528
|
-
return {
|
|
3850
|
+
return { ...rest, coords };
|
|
3529
3851
|
}
|
|
3530
3852
|
function distanceToSegment(px, py, ax, ay, bx, by) {
|
|
3531
3853
|
const dx = bx - ax;
|