@weasel-js/animator 1.9.4 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -3
- package/dist/index.d.ts +217 -155
- package/dist/index.js +555 -236
- 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,10 @@ 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
|
-
} catch (err) {
|
|
1325
|
-
console.error("timeline: subscriber threw", err);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1370
|
+
function crossLaps(playhead, duration, loopsLeft, take) {
|
|
1371
|
+
if (duration <= 0 || playhead < duration) return;
|
|
1372
|
+
const laps = Math.min(Math.floor(playhead / duration), loopsLeft);
|
|
1373
|
+
if (laps > 0) take(laps);
|
|
1328
1374
|
}
|
|
1329
1375
|
function createTimeline(register, id, opts, report, hold) {
|
|
1330
1376
|
let duration = tracksEnd(opts.tracks, opts.duration);
|
|
@@ -1339,9 +1385,9 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1339
1385
|
let cancelled = false;
|
|
1340
1386
|
let wantPaused = opts.autoplay === false;
|
|
1341
1387
|
let wantScale = 1;
|
|
1342
|
-
const
|
|
1343
|
-
const
|
|
1344
|
-
const playbackChanged =
|
|
1388
|
+
const edited = createEmitter();
|
|
1389
|
+
const playback = createEmitter();
|
|
1390
|
+
const playbackChanged = playback.emit;
|
|
1345
1391
|
const loopOpt = opts.loop ?? false;
|
|
1346
1392
|
let loopsLeft = loopOpt === true ? Infinity : loopOpt === false ? 0 : loopOpt;
|
|
1347
1393
|
let lap = 0;
|
|
@@ -1362,27 +1408,20 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1362
1408
|
};
|
|
1363
1409
|
const tick = (virtualNow, scale) => {
|
|
1364
1410
|
lastVirtual = virtualNow;
|
|
1365
|
-
playhead = virtualNow + offset;
|
|
1366
1411
|
const rate = scale ?? (base.isPaused() ? 0 : base.timeScale());
|
|
1367
1412
|
held = rate === 0;
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1413
|
+
const parked = held && duration > 0 && playhead === duration && loopsLeft > 0;
|
|
1414
|
+
if (!parked) {
|
|
1415
|
+
playhead = virtualNow + offset;
|
|
1416
|
+
crossLaps(playhead, duration, loopsLeft, (laps) => {
|
|
1417
|
+
loopsLeft -= laps;
|
|
1371
1418
|
offset -= laps * duration;
|
|
1372
1419
|
playhead -= laps * duration;
|
|
1373
1420
|
lap += laps;
|
|
1374
1421
|
onWrap();
|
|
1375
|
-
}
|
|
1376
|
-
while (playhead >= duration && loopsLeft > 0) {
|
|
1377
|
-
loopsLeft -= 1;
|
|
1378
|
-
offset -= duration;
|
|
1379
|
-
playhead -= duration;
|
|
1380
|
-
lap += 1;
|
|
1381
|
-
onWrap();
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1422
|
+
});
|
|
1384
1423
|
}
|
|
1385
|
-
const finished = playhead >= duration;
|
|
1424
|
+
const finished = playhead >= duration && !parked;
|
|
1386
1425
|
playhead = Math.min(playhead, duration);
|
|
1387
1426
|
sampler.apply(opts.tracks, playhead);
|
|
1388
1427
|
if (!held) {
|
|
@@ -1396,7 +1435,7 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1396
1435
|
opts.onDone?.();
|
|
1397
1436
|
playbackChanged();
|
|
1398
1437
|
}
|
|
1399
|
-
if (playhead < duration) return false;
|
|
1438
|
+
if (playhead < duration || parked) return false;
|
|
1400
1439
|
live = false;
|
|
1401
1440
|
sampler.release();
|
|
1402
1441
|
return true;
|
|
@@ -1482,20 +1521,10 @@ function createTimeline(register, id, opts, report, hold) {
|
|
|
1482
1521
|
booker?.retract();
|
|
1483
1522
|
rearm();
|
|
1484
1523
|
sampleNow();
|
|
1485
|
-
|
|
1486
|
-
},
|
|
1487
|
-
subscribe(cb) {
|
|
1488
|
-
subscribers.add(cb);
|
|
1489
|
-
return () => {
|
|
1490
|
-
subscribers.delete(cb);
|
|
1491
|
-
};
|
|
1524
|
+
edited.emit();
|
|
1492
1525
|
},
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
return () => {
|
|
1496
|
-
playbackSubscribers.delete(cb);
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1526
|
+
subscribe: edited.subscribe,
|
|
1527
|
+
subscribePlayback: playback.subscribe
|
|
1499
1528
|
};
|
|
1500
1529
|
}
|
|
1501
1530
|
|
|
@@ -1591,15 +1620,10 @@ function dim(s, before, after) {
|
|
|
1591
1620
|
}
|
|
1592
1621
|
}
|
|
1593
1622
|
}
|
|
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
|
-
}
|
|
1623
|
+
function coverOf(wanted, axes) {
|
|
1624
|
+
if (!wanted) return null;
|
|
1625
|
+
const covers = new Uint8Array(axes.count);
|
|
1626
|
+
for (const a of axisIndexes(axes, wanted)) covers[a] = 1;
|
|
1603
1627
|
return covers;
|
|
1604
1628
|
}
|
|
1605
1629
|
function openHold(ctx, o) {
|
|
@@ -1615,7 +1639,8 @@ function openHold(ctx, o) {
|
|
|
1615
1639
|
return buffer;
|
|
1616
1640
|
} : () => ax.to(o.value()),
|
|
1617
1641
|
weight: () => gone ? 0 : share(o.weight()),
|
|
1618
|
-
covers: coverOf(o.axes, ax
|
|
1642
|
+
covers: coverOf(o.axes, ax),
|
|
1643
|
+
fixed: o.fixed === true,
|
|
1619
1644
|
w: 0,
|
|
1620
1645
|
v: buffer
|
|
1621
1646
|
};
|
|
@@ -1633,6 +1658,7 @@ function openHold(ctx, o) {
|
|
|
1633
1658
|
id: made.voice,
|
|
1634
1659
|
on: s,
|
|
1635
1660
|
axes: ax.count,
|
|
1661
|
+
still: member.fixed,
|
|
1636
1662
|
at: () => {
|
|
1637
1663
|
const committed = s.heldAt ??= new Array(ax.count).fill(0);
|
|
1638
1664
|
committedInto(s, committed, 0);
|
|
@@ -1646,6 +1672,8 @@ function openHold(ctx, o) {
|
|
|
1646
1672
|
}
|
|
1647
1673
|
const { members, voice } = group;
|
|
1648
1674
|
members.push(member);
|
|
1675
|
+
const still = () => ctx.codec.still(voice, members.every((m) => m.fixed));
|
|
1676
|
+
still();
|
|
1649
1677
|
ctx.touch(state, true);
|
|
1650
1678
|
return {
|
|
1651
1679
|
touch: () => {
|
|
@@ -1670,7 +1698,7 @@ function openHold(ctx, o) {
|
|
|
1670
1698
|
const beside = s.beside;
|
|
1671
1699
|
s.beside = null;
|
|
1672
1700
|
if (beside) absorb(s, beside);
|
|
1673
|
-
}
|
|
1701
|
+
} else still();
|
|
1674
1702
|
ctx.targets.detach(state);
|
|
1675
1703
|
ctx.touch(state, true);
|
|
1676
1704
|
}
|
|
@@ -1683,6 +1711,7 @@ function startHold(ctx, o) {
|
|
|
1683
1711
|
const target = stateOf(o.target);
|
|
1684
1712
|
const id = ctx.nextId();
|
|
1685
1713
|
const { value, weight = 1 } = o;
|
|
1714
|
+
const fixed = !isFn(value) && !isFn(weight);
|
|
1686
1715
|
const seed = {
|
|
1687
1716
|
id,
|
|
1688
1717
|
kind: "hold",
|
|
@@ -1690,15 +1719,17 @@ function startHold(ctx, o) {
|
|
|
1690
1719
|
label: o.label,
|
|
1691
1720
|
target,
|
|
1692
1721
|
keepExisting: o.interrupt === false,
|
|
1693
|
-
|
|
1694
|
-
|
|
1722
|
+
idle: fixed,
|
|
1723
|
+
// It leaves when released or canceled, and has nothing to do on a frame.
|
|
1724
|
+
due: Infinity
|
|
1695
1725
|
};
|
|
1696
1726
|
if (o.cancelKey != null && o.interrupt !== false) ctx.interrupt(o.cancelKey, seed);
|
|
1697
1727
|
const held = openHold(ctx, {
|
|
1698
1728
|
target,
|
|
1699
1729
|
value: isFn(value) ? value : () => value,
|
|
1700
1730
|
weight: isFn(weight) ? weight : () => weight,
|
|
1701
|
-
axes: o.axes
|
|
1731
|
+
axes: o.axes,
|
|
1732
|
+
fixed
|
|
1702
1733
|
});
|
|
1703
1734
|
seed.onCancel = () => held.leave(true);
|
|
1704
1735
|
seed.tick = () => {
|
|
@@ -1840,6 +1871,15 @@ function startPhysics(ctx, o, kind) {
|
|
|
1840
1871
|
};
|
|
1841
1872
|
}
|
|
1842
1873
|
|
|
1874
|
+
// src/engine/springRest.ts
|
|
1875
|
+
function earliestRest(value, velocity, stiffness, damping, mass, threshold) {
|
|
1876
|
+
let energy = 0;
|
|
1877
|
+
for (let i = 0; i < value.length; i++) energy += stiffness * value[i] ** 2 + mass * velocity[i] ** 2;
|
|
1878
|
+
const atRest = (stiffness + mass) * threshold ** 2;
|
|
1879
|
+
if (!(energy > atRest)) return 0;
|
|
1880
|
+
return 1e3 * mass / (2 * damping) * Math.log(energy / atRest);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1843
1883
|
// src/engine/springVoices.ts
|
|
1844
1884
|
function createSpringVoices(codec, s, o) {
|
|
1845
1885
|
const n = s.axes.count;
|
|
@@ -1914,19 +1954,37 @@ function createSpringVoices(codec, s, o) {
|
|
|
1914
1954
|
|
|
1915
1955
|
// src/engine/offsetPhysics.ts
|
|
1916
1956
|
var any = (a) => a.some((x) => x !== 0);
|
|
1957
|
+
function constantsFor(o, kind, toward) {
|
|
1958
|
+
const c = resolveSpringConstants(o);
|
|
1959
|
+
if (!(c.damping > 0) || !(c.mass > 0) || toward && !(c.stiffness > 0)) {
|
|
1960
|
+
throw new Error(`${kind}: an animation on a target needs positive damping and mass, and stiffness to reach \`to\``);
|
|
1961
|
+
}
|
|
1962
|
+
return c;
|
|
1963
|
+
}
|
|
1964
|
+
function restWaits(ctx, id, quiet, c, threshold) {
|
|
1965
|
+
const until = (value, velocity, coast) => earliestRest(value, velocity, coast ? 0 : c.stiffness, c.damping, c.mass, threshold);
|
|
1966
|
+
return {
|
|
1967
|
+
/** The `due` it registers with. */
|
|
1968
|
+
first: (value, velocity, coast) => quiet ? until(value, velocity, coast) : void 0,
|
|
1969
|
+
/** Leaves it unticked until then. Answers false, as the tick of one still moving does. */
|
|
1970
|
+
again(value, velocity, coast) {
|
|
1971
|
+
if (quiet) ctx.defer(id, until(value, velocity, coast));
|
|
1972
|
+
return false;
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1917
1976
|
function startOffsetPhysics(ctx, o, kind) {
|
|
1918
1977
|
const s = stateOf(o.target);
|
|
1919
1978
|
const ax = s.axes;
|
|
1920
|
-
const { stiffness, damping, mass } = resolveSpringConstants(o);
|
|
1921
1979
|
let dest = o.to == null ? null : axesFor(ax, o.to, "to");
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
}
|
|
1980
|
+
const constants = constantsFor(o, kind, dest != null);
|
|
1981
|
+
const { stiffness, damping, mass } = constants;
|
|
1925
1982
|
const given = o.from === void 0 ? null : axesFor(ax, o.from, "from");
|
|
1926
1983
|
let velocity = o.velocity === void 0 ? null : axesFor(ax, o.velocity, "velocity");
|
|
1927
1984
|
const restThreshold = o.restThreshold ?? 0.01;
|
|
1928
1985
|
const zeros = () => new Array(ax.count).fill(0);
|
|
1929
1986
|
const id = ctx.nextId();
|
|
1987
|
+
const waits = restWaits(ctx, id, !o.onTick, constants, restThreshold);
|
|
1930
1988
|
const interrupts = o.interrupt !== false;
|
|
1931
1989
|
const seed = {
|
|
1932
1990
|
id,
|
|
@@ -1968,6 +2026,7 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
1968
2026
|
o.onDone?.();
|
|
1969
2027
|
};
|
|
1970
2028
|
let ticked = false;
|
|
2029
|
+
seed.due = waits.first(start, velocity ?? zeros(), dest == null);
|
|
1971
2030
|
seed.onCancel = () => leave(true);
|
|
1972
2031
|
seed.onRate = (paused, timeScale) => voices.rate(paused ? 0 : timeScale);
|
|
1973
2032
|
seed.velocity = () => voices.own().velocity;
|
|
@@ -1984,7 +2043,9 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
1984
2043
|
ticked = true;
|
|
1985
2044
|
o.onTick?.(ax.from(plus(dest ?? ax.to(s.get()), own.value)));
|
|
1986
2045
|
if (!ctx.has(id)) return true;
|
|
1987
|
-
if (norm(own.velocity) >= restThreshold || dest && norm(own.value) >= restThreshold)
|
|
2046
|
+
if (norm(own.velocity) >= restThreshold || dest && norm(own.value) >= restThreshold) {
|
|
2047
|
+
return waits.again(own.value, own.velocity, dest == null);
|
|
2048
|
+
}
|
|
1988
2049
|
if (dest) o.onTick?.(ax.from(dest));
|
|
1989
2050
|
if (!ctx.has(id)) return true;
|
|
1990
2051
|
finish();
|
|
@@ -2003,6 +2064,7 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
2003
2064
|
dest = null;
|
|
2004
2065
|
voices.rest(minus(own.value, moved), own.velocity, true);
|
|
2005
2066
|
voices.settleMove();
|
|
2067
|
+
waits.again(own.value, own.velocity, true);
|
|
2006
2068
|
return;
|
|
2007
2069
|
}
|
|
2008
2070
|
const to = axesFor(ax, next, "setTarget");
|
|
@@ -2012,11 +2074,14 @@ function startOffsetPhysics(ctx, o, kind) {
|
|
|
2012
2074
|
if (dest == null) voices.rest(own.value, own.velocity, false);
|
|
2013
2075
|
dest = to;
|
|
2014
2076
|
voices.move(shift);
|
|
2077
|
+
waits.again(plus(own.value, shift), own.velocity, false);
|
|
2015
2078
|
ctx.targets.showNow(s);
|
|
2016
2079
|
},
|
|
2017
2080
|
setVelocity(v) {
|
|
2018
2081
|
if (left) return;
|
|
2019
|
-
|
|
2082
|
+
const pushed = axesFor(ax, v, "setVelocity");
|
|
2083
|
+
waits.again(voices.own().value, pushed, dest == null);
|
|
2084
|
+
voices.push(pushed);
|
|
2020
2085
|
}
|
|
2021
2086
|
};
|
|
2022
2087
|
}
|
|
@@ -2072,6 +2137,7 @@ function startOffsetTween(ctx, o) {
|
|
|
2072
2137
|
ctx.targets.detach(s);
|
|
2073
2138
|
};
|
|
2074
2139
|
seed.progress = (virtualNow) => o.ms <= 0 ? 1 : Math.min(1, virtualNow / o.ms);
|
|
2140
|
+
if (!o.onTick) seed.due = Math.max(o.ms, 0);
|
|
2075
2141
|
seed.onCancel = () => leave(true);
|
|
2076
2142
|
seed.tick = (nowMs) => {
|
|
2077
2143
|
if (ctx.tripwire()) {
|
|
@@ -2120,6 +2186,199 @@ function createCompletions(has) {
|
|
|
2120
2186
|
};
|
|
2121
2187
|
}
|
|
2122
2188
|
|
|
2189
|
+
// src/engine/wakes.ts
|
|
2190
|
+
function createWakes() {
|
|
2191
|
+
const heap = [];
|
|
2192
|
+
const place = (w, i) => {
|
|
2193
|
+
heap[i] = w;
|
|
2194
|
+
w.heapAt = i;
|
|
2195
|
+
};
|
|
2196
|
+
const up = (w, i) => {
|
|
2197
|
+
while (i > 0) {
|
|
2198
|
+
const parent = i - 1 >> 1;
|
|
2199
|
+
const p = heap[parent];
|
|
2200
|
+
if (p.wakeAt <= w.wakeAt) break;
|
|
2201
|
+
place(p, i);
|
|
2202
|
+
i = parent;
|
|
2203
|
+
}
|
|
2204
|
+
place(w, i);
|
|
2205
|
+
};
|
|
2206
|
+
const down = (w, i) => {
|
|
2207
|
+
const n = heap.length;
|
|
2208
|
+
for (; ; ) {
|
|
2209
|
+
let child = 2 * i + 1;
|
|
2210
|
+
if (child >= n) break;
|
|
2211
|
+
if (child + 1 < n && heap[child + 1].wakeAt < heap[child].wakeAt) child += 1;
|
|
2212
|
+
const c = heap[child];
|
|
2213
|
+
if (c.wakeAt >= w.wakeAt) break;
|
|
2214
|
+
place(c, i);
|
|
2215
|
+
i = child;
|
|
2216
|
+
}
|
|
2217
|
+
place(w, i);
|
|
2218
|
+
};
|
|
2219
|
+
const remove = (w) => {
|
|
2220
|
+
const i = w.heapAt;
|
|
2221
|
+
if (i < 0) return;
|
|
2222
|
+
w.heapAt = -1;
|
|
2223
|
+
const last = heap.pop();
|
|
2224
|
+
if (last === w) return;
|
|
2225
|
+
if (i > 0 && last.wakeAt < heap[i - 1 >> 1].wakeAt) up(last, i);
|
|
2226
|
+
else down(last, i);
|
|
2227
|
+
};
|
|
2228
|
+
return {
|
|
2229
|
+
set(w, at) {
|
|
2230
|
+
remove(w);
|
|
2231
|
+
w.wakeAt = at;
|
|
2232
|
+
heap.push(w);
|
|
2233
|
+
up(w, heap.length - 1);
|
|
2234
|
+
},
|
|
2235
|
+
remove,
|
|
2236
|
+
due(now) {
|
|
2237
|
+
const out = [];
|
|
2238
|
+
while (heap.length > 0 && heap[0].wakeAt <= now) {
|
|
2239
|
+
const w = heap[0];
|
|
2240
|
+
remove(w);
|
|
2241
|
+
out.push(w);
|
|
2242
|
+
}
|
|
2243
|
+
if (out.length > 1) out.sort((a, b) => a.seq - b.seq);
|
|
2244
|
+
return out;
|
|
2245
|
+
},
|
|
2246
|
+
clear() {
|
|
2247
|
+
for (const w of heap) w.heapAt = -1;
|
|
2248
|
+
heap.length = 0;
|
|
2249
|
+
},
|
|
2250
|
+
get size() {
|
|
2251
|
+
return heap.length;
|
|
2252
|
+
}
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
// src/engine/schedule.ts
|
|
2257
|
+
function createSchedule(elapsed) {
|
|
2258
|
+
const every = /* @__PURE__ */ new Set();
|
|
2259
|
+
const wakes = createWakes();
|
|
2260
|
+
let seq = 0;
|
|
2261
|
+
let awake = 0;
|
|
2262
|
+
let waiting = 0;
|
|
2263
|
+
const rateOf = (a) => a.paused ? 0 : a.timeScale;
|
|
2264
|
+
const clockOf = (a) => a.base + (elapsed() - a.anchor) * rateOf(a);
|
|
2265
|
+
const wait = (a) => {
|
|
2266
|
+
const rate = rateOf(a);
|
|
2267
|
+
if (rate > 0 && a.due < Infinity) wakes.set(a, a.anchor + (a.due - a.base) / rate);
|
|
2268
|
+
else wakes.remove(a);
|
|
2269
|
+
};
|
|
2270
|
+
return {
|
|
2271
|
+
clockOf,
|
|
2272
|
+
add(a) {
|
|
2273
|
+
a.base = 0;
|
|
2274
|
+
a.anchor = elapsed();
|
|
2275
|
+
a.seq = seq++;
|
|
2276
|
+
a.heapAt = -1;
|
|
2277
|
+
a.wakeAt = Infinity;
|
|
2278
|
+
if (!a.idle) awake += 1;
|
|
2279
|
+
if (a.due === void 0) {
|
|
2280
|
+
every.add(a);
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
waiting += 1;
|
|
2284
|
+
wait(a);
|
|
2285
|
+
},
|
|
2286
|
+
remove(a) {
|
|
2287
|
+
if (!a.idle) awake -= 1;
|
|
2288
|
+
if (every.delete(a)) return;
|
|
2289
|
+
waiting -= 1;
|
|
2290
|
+
wakes.remove(a);
|
|
2291
|
+
},
|
|
2292
|
+
pace(a, paused, timeScale) {
|
|
2293
|
+
a.base = clockOf(a);
|
|
2294
|
+
a.anchor = elapsed();
|
|
2295
|
+
a.paused = paused;
|
|
2296
|
+
a.timeScale = timeScale;
|
|
2297
|
+
if (!every.has(a)) wait(a);
|
|
2298
|
+
},
|
|
2299
|
+
defer(a, ms) {
|
|
2300
|
+
if (!a) return;
|
|
2301
|
+
a.due = clockOf(a) + ms;
|
|
2302
|
+
if (every.delete(a)) waiting += 1;
|
|
2303
|
+
wait(a);
|
|
2304
|
+
},
|
|
2305
|
+
ticks() {
|
|
2306
|
+
const steady = [...every];
|
|
2307
|
+
const woke = wakes.due(elapsed());
|
|
2308
|
+
if (woke.length === 0) return steady;
|
|
2309
|
+
waiting -= woke.length;
|
|
2310
|
+
const out = [];
|
|
2311
|
+
let i = 0;
|
|
2312
|
+
for (const a of woke) {
|
|
2313
|
+
a.base = Math.max(clockOf(a), a.due);
|
|
2314
|
+
a.anchor = elapsed();
|
|
2315
|
+
every.add(a);
|
|
2316
|
+
while (i < steady.length && steady[i].seq < a.seq) out.push(steady[i++]);
|
|
2317
|
+
out.push(a);
|
|
2318
|
+
}
|
|
2319
|
+
while (i < steady.length) out.push(steady[i++]);
|
|
2320
|
+
return out;
|
|
2321
|
+
},
|
|
2322
|
+
get awake() {
|
|
2323
|
+
return awake > 0;
|
|
2324
|
+
},
|
|
2325
|
+
get waiting() {
|
|
2326
|
+
return waiting;
|
|
2327
|
+
},
|
|
2328
|
+
clear() {
|
|
2329
|
+
every.clear();
|
|
2330
|
+
wakes.clear();
|
|
2331
|
+
awake = 0;
|
|
2332
|
+
waiting = 0;
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// src/engine/roster.ts
|
|
2338
|
+
var NONE = [];
|
|
2339
|
+
function file(index, key, a, on) {
|
|
2340
|
+
let set = index.get(key);
|
|
2341
|
+
if (on) {
|
|
2342
|
+
if (!set) index.set(key, set = /* @__PURE__ */ new Set());
|
|
2343
|
+
set.add(a);
|
|
2344
|
+
} else if (set?.delete(a) && set.size === 0) index.delete(key);
|
|
2345
|
+
}
|
|
2346
|
+
function createRoster(elapsed) {
|
|
2347
|
+
const animations = /* @__PURE__ */ new Map();
|
|
2348
|
+
const schedule = createSchedule(elapsed);
|
|
2349
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2350
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
2351
|
+
const index = (a, on) => {
|
|
2352
|
+
if (a.cancelKey != null) file(byKey, a.cancelKey, a, on);
|
|
2353
|
+
if (a.target) file(byTarget, a.target, a, on);
|
|
2354
|
+
};
|
|
2355
|
+
const remove = (a) => {
|
|
2356
|
+
animations.delete(a.id);
|
|
2357
|
+
schedule.remove(a);
|
|
2358
|
+
index(a, false);
|
|
2359
|
+
};
|
|
2360
|
+
return {
|
|
2361
|
+
animations,
|
|
2362
|
+
schedule,
|
|
2363
|
+
under: (key) => byKey.get(key) ?? NONE,
|
|
2364
|
+
on: (target) => byTarget.get(target) ?? NONE,
|
|
2365
|
+
add(a) {
|
|
2366
|
+
const had = animations.get(a.id);
|
|
2367
|
+
if (had) remove(had);
|
|
2368
|
+
animations.set(a.id, a);
|
|
2369
|
+
schedule.add(a);
|
|
2370
|
+
index(a, true);
|
|
2371
|
+
},
|
|
2372
|
+
remove,
|
|
2373
|
+
clear() {
|
|
2374
|
+
animations.clear();
|
|
2375
|
+
schedule.clear();
|
|
2376
|
+
byKey.clear();
|
|
2377
|
+
byTarget.clear();
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2123
2382
|
// src/engine/table.ts
|
|
2124
2383
|
function timeScaleOf(scale) {
|
|
2125
2384
|
if (!(scale >= 0)) {
|
|
@@ -2129,7 +2388,8 @@ function timeScaleOf(scale) {
|
|
|
2129
2388
|
}
|
|
2130
2389
|
function createTable(host) {
|
|
2131
2390
|
const { codec, hub } = host;
|
|
2132
|
-
const
|
|
2391
|
+
const roster = createRoster(host.elapsed);
|
|
2392
|
+
const { animations, schedule, remove } = roster;
|
|
2133
2393
|
const { fire: fireCompletion, watch: watchCompletion } = createCompletions((id) => animations.has(id));
|
|
2134
2394
|
const infoOf = (a) => {
|
|
2135
2395
|
if (!a.info) {
|
|
@@ -2151,31 +2411,27 @@ function createTable(host) {
|
|
|
2151
2411
|
if (!a) return;
|
|
2152
2412
|
a.onCancel?.();
|
|
2153
2413
|
codec.stop(id);
|
|
2154
|
-
|
|
2414
|
+
remove(a);
|
|
2155
2415
|
if (host.resting()) host.requestFrame();
|
|
2156
2416
|
fireCompletion(id);
|
|
2157
2417
|
if (!hub.watched || a.ended || error) return;
|
|
2158
2418
|
hub.emit(by ? { type: "interrupt", animation: infoOf(a), by: infoOf(by) } : { type: "cancel", animation: infoOf(a) });
|
|
2159
2419
|
};
|
|
2160
2420
|
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);
|
|
2421
|
+
for (const a of [...roster.under(key)]) retire(a.id, by);
|
|
2166
2422
|
};
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
a.
|
|
2423
|
+
const pace = (a, paused, timeScale) => {
|
|
2424
|
+
schedule.pace(a, paused, timeScale);
|
|
2425
|
+
if (codec.has(a.id)) codec.rate(a.id, paused ? 0 : timeScale);
|
|
2426
|
+
a.onRate?.(paused, timeScale);
|
|
2170
2427
|
};
|
|
2171
2428
|
const register = (seed) => {
|
|
2172
2429
|
const anim = seed;
|
|
2173
2430
|
if (seed.cancelKey != null && !seed.keepExisting) cancelByKey(seed.cancelKey, anim);
|
|
2174
2431
|
anim.paused = false;
|
|
2175
2432
|
anim.timeScale = 1;
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
animations.set(anim.id, anim);
|
|
2433
|
+
host.seedClock();
|
|
2434
|
+
roster.add(anim);
|
|
2179
2435
|
if (anim.target) host.showNow(anim.target);
|
|
2180
2436
|
if (hub.watched) hub.emit({ type: "start", animation: infoOf(anim) });
|
|
2181
2437
|
if (animations.size > 0 && !host.framing()) host.requestFrame();
|
|
@@ -2185,24 +2441,15 @@ function createTable(host) {
|
|
|
2185
2441
|
cancel: () => retire(anim.id),
|
|
2186
2442
|
pause: () => {
|
|
2187
2443
|
const a = live();
|
|
2188
|
-
if (a)
|
|
2189
|
-
a.paused = true;
|
|
2190
|
-
rateOf(a);
|
|
2191
|
-
}
|
|
2444
|
+
if (a) pace(a, true, a.timeScale);
|
|
2192
2445
|
},
|
|
2193
2446
|
resume: () => {
|
|
2194
2447
|
const a = live();
|
|
2195
|
-
if (a)
|
|
2196
|
-
a.paused = false;
|
|
2197
|
-
rateOf(a);
|
|
2198
|
-
}
|
|
2448
|
+
if (a) pace(a, false, a.timeScale);
|
|
2199
2449
|
},
|
|
2200
2450
|
setTimeScale: (s) => {
|
|
2201
2451
|
const a = live();
|
|
2202
|
-
if (a)
|
|
2203
|
-
a.timeScale = timeScaleOf(s);
|
|
2204
|
-
rateOf(a);
|
|
2205
|
-
}
|
|
2452
|
+
if (a) pace(a, a.paused, timeScaleOf(s));
|
|
2206
2453
|
},
|
|
2207
2454
|
timeScale: () => live()?.timeScale ?? 1,
|
|
2208
2455
|
isPaused: () => live()?.paused ?? false
|
|
@@ -2214,16 +2461,28 @@ function createTable(host) {
|
|
|
2214
2461
|
reportEnd,
|
|
2215
2462
|
retire,
|
|
2216
2463
|
cancelByKey,
|
|
2217
|
-
|
|
2464
|
+
under: roster.under,
|
|
2465
|
+
on: roster.on,
|
|
2466
|
+
pace,
|
|
2467
|
+
clockOf: schedule.clockOf,
|
|
2468
|
+
defer: (id, ms) => schedule.defer(animations.get(id), ms),
|
|
2469
|
+
ticks: schedule.ticks,
|
|
2470
|
+
get awake() {
|
|
2471
|
+
return schedule.awake;
|
|
2472
|
+
},
|
|
2473
|
+
get waiting() {
|
|
2474
|
+
return schedule.waiting;
|
|
2475
|
+
},
|
|
2218
2476
|
register,
|
|
2219
2477
|
finish(id) {
|
|
2220
|
-
|
|
2478
|
+
const a = animations.get(id);
|
|
2479
|
+
if (!a) return;
|
|
2221
2480
|
reportEnd(id);
|
|
2222
|
-
|
|
2481
|
+
remove(a);
|
|
2223
2482
|
fireCompletion(id);
|
|
2224
2483
|
},
|
|
2225
2484
|
drop(a) {
|
|
2226
|
-
|
|
2485
|
+
remove(a);
|
|
2227
2486
|
fireCompletion(a.id);
|
|
2228
2487
|
},
|
|
2229
2488
|
cancelAll(cleared) {
|
|
@@ -2233,7 +2492,7 @@ function createTable(host) {
|
|
|
2233
2492
|
codec.stop(a.id);
|
|
2234
2493
|
retired.push(a);
|
|
2235
2494
|
}
|
|
2236
|
-
|
|
2495
|
+
roster.clear();
|
|
2237
2496
|
cleared();
|
|
2238
2497
|
for (const a of retired) fireCompletion(a.id);
|
|
2239
2498
|
if (hub.watched) for (const a of retired) hub.emit({ type: "cancel", animation: infoOf(a) });
|
|
@@ -2298,12 +2557,8 @@ function startTween(ctx, o) {
|
|
|
2298
2557
|
}
|
|
2299
2558
|
|
|
2300
2559
|
// 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
|
-
}
|
|
2560
|
+
function eachUnder(table, key, pace) {
|
|
2561
|
+
for (const a of table.under(key)) table.pace(a, ...pace(a));
|
|
2307
2562
|
}
|
|
2308
2563
|
function liveRows(table) {
|
|
2309
2564
|
const rows = [];
|
|
@@ -2312,8 +2567,8 @@ function liveRows(table) {
|
|
|
2312
2567
|
...table.infoOf(a),
|
|
2313
2568
|
paused: a.paused,
|
|
2314
2569
|
timeScale: a.timeScale,
|
|
2315
|
-
elapsed: a
|
|
2316
|
-
...a.progress ? { progress: a.progress(a
|
|
2570
|
+
elapsed: table.clockOf(a),
|
|
2571
|
+
...a.progress ? { progress: a.progress(table.clockOf(a)) } : {}
|
|
2317
2572
|
});
|
|
2318
2573
|
}
|
|
2319
2574
|
return rows;
|
|
@@ -2367,9 +2622,7 @@ function createAnimatorApi(host) {
|
|
|
2367
2622
|
},
|
|
2368
2623
|
isActive: (key) => {
|
|
2369
2624
|
if (key == null) return animations.size > 0;
|
|
2370
|
-
for (const
|
|
2371
|
-
if (a.cancelKey === key) return true;
|
|
2372
|
-
}
|
|
2625
|
+
for (const _ of table.under(key)) return true;
|
|
2373
2626
|
return false;
|
|
2374
2627
|
},
|
|
2375
2628
|
isTicking: host.ticking,
|
|
@@ -2384,17 +2637,11 @@ function createAnimatorApi(host) {
|
|
|
2384
2637
|
clock.timeScale = timeScaleOf(s);
|
|
2385
2638
|
},
|
|
2386
2639
|
timeScale: () => clock.timeScale,
|
|
2387
|
-
pauseKey: (key) => eachUnder(table, key, (a) =>
|
|
2388
|
-
|
|
2389
|
-
}),
|
|
2390
|
-
resumeKey: (key) => eachUnder(table, key, (a) => {
|
|
2391
|
-
a.paused = false;
|
|
2392
|
-
}),
|
|
2640
|
+
pauseKey: (key) => eachUnder(table, key, (a) => [true, a.timeScale]),
|
|
2641
|
+
resumeKey: (key) => eachUnder(table, key, (a) => [false, a.timeScale]),
|
|
2393
2642
|
setTimeScaleByKey: (key, s) => {
|
|
2394
2643
|
const scale = timeScaleOf(s);
|
|
2395
|
-
eachUnder(table, key, (a) =>
|
|
2396
|
-
a.timeScale = scale;
|
|
2397
|
-
});
|
|
2644
|
+
eachUnder(table, key, (a) => [a.paused, scale]);
|
|
2398
2645
|
},
|
|
2399
2646
|
loop: (factory, loopOpts) => createLoop(createSupervisor, factory, loopOpts),
|
|
2400
2647
|
tweenLoop: (tweenLoopOpts) => createTweenLoop(api, createSupervisor, tweenLoopOpts),
|
|
@@ -2409,12 +2656,7 @@ function createAnimatorApi(host) {
|
|
|
2409
2656
|
)),
|
|
2410
2657
|
timeline: (o) => startTimeline(host, o),
|
|
2411
2658
|
colorOverrides: host.colorOverrides,
|
|
2412
|
-
onTick: (cb) =>
|
|
2413
|
-
host.subscribers.add(cb);
|
|
2414
|
-
return () => {
|
|
2415
|
-
host.subscribers.delete(cb);
|
|
2416
|
-
};
|
|
2417
|
-
},
|
|
2659
|
+
onTick: (cb) => host.subscribers.subscribe(cb),
|
|
2418
2660
|
keepAlive: () => {
|
|
2419
2661
|
const sup = createSupervisor({ kind: "keepAlive" });
|
|
2420
2662
|
return () => sup.cancel();
|
|
@@ -2424,6 +2666,75 @@ function createAnimatorApi(host) {
|
|
|
2424
2666
|
};
|
|
2425
2667
|
return api;
|
|
2426
2668
|
}
|
|
2669
|
+
function createHoldVoices() {
|
|
2670
|
+
const asked = /* @__PURE__ */ new Set();
|
|
2671
|
+
const stills = /* @__PURE__ */ new Map();
|
|
2672
|
+
const stale2 = /* @__PURE__ */ new Set();
|
|
2673
|
+
const cue = (v) => {
|
|
2674
|
+
const { start } = v;
|
|
2675
|
+
const { lane, key } = v.slot;
|
|
2676
|
+
v.handle?.fade({ over: 0 });
|
|
2677
|
+
stale2.delete(v);
|
|
2678
|
+
if (start.still) {
|
|
2679
|
+
v.last = start.at();
|
|
2680
|
+
const rest = tween("v", { from: v.last, to: v.last.slice(), ms: 1 });
|
|
2681
|
+
v.handle = lane.mix.cue({ patch: rest, subjects: [key] });
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
const each = patch(
|
|
2685
|
+
1,
|
|
2686
|
+
() => {
|
|
2687
|
+
v.last = start.at();
|
|
2688
|
+
return { v: v.last };
|
|
2689
|
+
},
|
|
2690
|
+
{ writes: ["v"] }
|
|
2691
|
+
);
|
|
2692
|
+
v.handle = lane.mix.cue({ patch: each, subjects: [key], loop: true });
|
|
2693
|
+
};
|
|
2694
|
+
const settle2 = () => {
|
|
2695
|
+
for (const v of stale2) cue(v);
|
|
2696
|
+
};
|
|
2697
|
+
return {
|
|
2698
|
+
add(slot, start) {
|
|
2699
|
+
const v = { slot, start: { ...start } };
|
|
2700
|
+
if (start.still) {
|
|
2701
|
+
stills.set(start.on, v);
|
|
2702
|
+
stale2.add(v);
|
|
2703
|
+
} else {
|
|
2704
|
+
cue(v);
|
|
2705
|
+
asked.add(v);
|
|
2706
|
+
}
|
|
2707
|
+
return v;
|
|
2708
|
+
},
|
|
2709
|
+
still(v, still) {
|
|
2710
|
+
if (!v.start.still === !still) return;
|
|
2711
|
+
v.start.still = still;
|
|
2712
|
+
if (still) {
|
|
2713
|
+
asked.delete(v);
|
|
2714
|
+
stills.set(v.start.on, v);
|
|
2715
|
+
} else {
|
|
2716
|
+
stills.delete(v.start.on);
|
|
2717
|
+
asked.add(v);
|
|
2718
|
+
}
|
|
2719
|
+
cue(v);
|
|
2720
|
+
},
|
|
2721
|
+
touch(on) {
|
|
2722
|
+
const v = stills.get(on);
|
|
2723
|
+
if (v) stale2.add(v);
|
|
2724
|
+
},
|
|
2725
|
+
settle: settle2,
|
|
2726
|
+
frame() {
|
|
2727
|
+
for (const v of asked) v.slot.lane.mix.touch(v.slot.key);
|
|
2728
|
+
settle2();
|
|
2729
|
+
},
|
|
2730
|
+
remove(v) {
|
|
2731
|
+
v.handle?.fade({ over: 0 });
|
|
2732
|
+
asked.delete(v);
|
|
2733
|
+
stale2.delete(v);
|
|
2734
|
+
if (stills.get(v.start.on) === v) stills.delete(v.start.on);
|
|
2735
|
+
}
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2427
2738
|
function depart(s) {
|
|
2428
2739
|
const l = s.lane;
|
|
2429
2740
|
l.dirty = true;
|
|
@@ -2563,7 +2874,7 @@ function createCodec(placeOf) {
|
|
|
2563
2874
|
const lanes = createLanes(placeOf);
|
|
2564
2875
|
const voices = /* @__PURE__ */ new Map();
|
|
2565
2876
|
const queue = [];
|
|
2566
|
-
const holds =
|
|
2877
|
+
const holds = createHoldVoices();
|
|
2567
2878
|
let time = 0;
|
|
2568
2879
|
const springVoice = (slot, sp) => {
|
|
2569
2880
|
const patch = sp.to == null ? glide("v", { from: sp.from, ms: 1e3 * sp.mass / sp.damping }) : spring("v", {
|
|
@@ -2596,9 +2907,9 @@ function createCodec(placeOf) {
|
|
|
2596
2907
|
function stop(id) {
|
|
2597
2908
|
const v = voices.get(id);
|
|
2598
2909
|
if (!v) return;
|
|
2599
|
-
if (v.
|
|
2910
|
+
if (v.held) holds.remove(v.held);
|
|
2911
|
+
else if (v.queued) v.queued = void 0;
|
|
2600
2912
|
else v.handle.fade({ over: 0 });
|
|
2601
|
-
holds.delete(v);
|
|
2602
2913
|
lanes.leave(v.slot);
|
|
2603
2914
|
voices.delete(id);
|
|
2604
2915
|
}
|
|
@@ -2616,7 +2927,7 @@ function createCodec(placeOf) {
|
|
|
2616
2927
|
if (v.queuedRate !== void 0) v.handle.rate = v.queuedRate;
|
|
2617
2928
|
}
|
|
2618
2929
|
queue.length = 0;
|
|
2619
|
-
|
|
2930
|
+
holds.frame();
|
|
2620
2931
|
time += dtMs;
|
|
2621
2932
|
lanes.frame(time);
|
|
2622
2933
|
},
|
|
@@ -2662,22 +2973,20 @@ function createCodec(placeOf) {
|
|
|
2662
2973
|
hold(s) {
|
|
2663
2974
|
const zeros = new Array(s.axes).fill(0);
|
|
2664
2975
|
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);
|
|
2976
|
+
voices.set(s.id, { kind: "hold", slot, from: zeros, held: holds.add(slot, s) });
|
|
2677
2977
|
return slot;
|
|
2678
2978
|
},
|
|
2679
|
-
|
|
2979
|
+
still(id, still) {
|
|
2980
|
+
const v = get(id);
|
|
2981
|
+
if (!v.held) throw new Error(`codec: subject ${id} is a ${v.kind}, not a hold`);
|
|
2982
|
+
holds.still(v.held, still);
|
|
2983
|
+
},
|
|
2984
|
+
touch(on) {
|
|
2985
|
+
holds.touch(on);
|
|
2986
|
+
lanes.touch(on);
|
|
2987
|
+
},
|
|
2680
2988
|
refresh(reread) {
|
|
2989
|
+
holds.settle();
|
|
2681
2990
|
lanes.refresh((slot) => {
|
|
2682
2991
|
const queued = new Array(slot.lane.axes).fill(0);
|
|
2683
2992
|
for (const v of queue) {
|
|
@@ -2723,7 +3032,9 @@ function createCodec(placeOf) {
|
|
|
2723
3032
|
const v = voices.get(id);
|
|
2724
3033
|
if (!v) return void 0;
|
|
2725
3034
|
if (v.kind === "spring") return current(v);
|
|
2726
|
-
if (v.kind === "path" || v.kind === "hold")
|
|
3035
|
+
if (v.kind === "path" || v.kind === "hold") {
|
|
3036
|
+
return { value: v.held?.last ?? v.last ?? v.from, velocity: new Array(v.from.length).fill(0) };
|
|
3037
|
+
}
|
|
2727
3038
|
return !v.queued && v.patch.read(v.slot.key) || { value: v.from, velocity: new Array(v.from.length).fill(0) };
|
|
2728
3039
|
},
|
|
2729
3040
|
voiceCount: () => voices.size,
|
|
@@ -2737,18 +3048,16 @@ function createFrame(host) {
|
|
|
2737
3048
|
const { animations } = table;
|
|
2738
3049
|
let framing = false;
|
|
2739
3050
|
let tickDepth = 0;
|
|
2740
|
-
const tickEach = (
|
|
3051
|
+
const tickEach = () => {
|
|
2741
3052
|
const finished = [];
|
|
2742
3053
|
const threw = [];
|
|
2743
|
-
|
|
3054
|
+
const all = table.waiting > 0 && host.tripwire();
|
|
3055
|
+
for (const anim of all ? [...animations.values()] : table.ticks()) {
|
|
2744
3056
|
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
3057
|
const scale = clock.paused ? 0 : clock.timeScale * (anim.paused ? 0 : anim.timeScale);
|
|
2748
|
-
anim.virtualNow += realDt * scale;
|
|
2749
3058
|
tickDepth += 1;
|
|
2750
3059
|
try {
|
|
2751
|
-
if (anim.tick(anim
|
|
3060
|
+
if (anim.tick(table.clockOf(anim), scale)) finished.push(anim);
|
|
2752
3061
|
} catch (error) {
|
|
2753
3062
|
threw.push([anim, error]);
|
|
2754
3063
|
} finally {
|
|
@@ -2764,18 +3073,16 @@ function createFrame(host) {
|
|
|
2764
3073
|
}
|
|
2765
3074
|
return finished;
|
|
2766
3075
|
};
|
|
2767
|
-
const awake = () => {
|
|
2768
|
-
for (const a of animations.values()) if (!a.idle) return true;
|
|
2769
|
-
return false;
|
|
2770
|
-
};
|
|
2771
3076
|
return {
|
|
2772
3077
|
tick(t) {
|
|
2773
3078
|
const frameDt = clock.frameT == null ? 0 : Math.max(0, t - clock.frameT);
|
|
2774
3079
|
clock.frameT = t;
|
|
2775
3080
|
framing = true;
|
|
2776
|
-
|
|
3081
|
+
const dt = frameDt * (clock.paused ? 0 : clock.timeScale);
|
|
3082
|
+
clock.elapsed += dt;
|
|
3083
|
+
host.codec.frame(dt);
|
|
2777
3084
|
host.targets.settle();
|
|
2778
|
-
for (const anim of tickEach(
|
|
3085
|
+
for (const anim of tickEach()) {
|
|
2779
3086
|
if (animations.get(anim.id) === anim) table.finish(anim.id);
|
|
2780
3087
|
}
|
|
2781
3088
|
for (const cb of host.afterTicks.splice(0)) {
|
|
@@ -2788,41 +3095,36 @@ function createFrame(host) {
|
|
|
2788
3095
|
host.reread();
|
|
2789
3096
|
framing = false;
|
|
2790
3097
|
host.targets.show();
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
sub();
|
|
2794
|
-
} catch (err) {
|
|
2795
|
-
console.error("useAnimator: onTick subscriber threw", err);
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2798
|
-
if (awake()) {
|
|
3098
|
+
host.subscribers.emit();
|
|
3099
|
+
if (table.awake) {
|
|
2799
3100
|
host.requestFrame();
|
|
2800
3101
|
return;
|
|
2801
3102
|
}
|
|
2802
3103
|
clock.frameT = null;
|
|
2803
|
-
for (const anim of animations.values()) anim.lastRealNow = null;
|
|
2804
3104
|
},
|
|
2805
3105
|
framing: () => framing,
|
|
2806
3106
|
ticking: () => tickDepth > 0
|
|
2807
3107
|
};
|
|
2808
3108
|
}
|
|
2809
|
-
|
|
2810
|
-
// src/engine/core.ts
|
|
2811
3109
|
function createAnimatorCore(host) {
|
|
2812
|
-
const clock = { frameT: null, timeScale: 1, paused: false };
|
|
3110
|
+
const clock = { frameT: null, elapsed: 0, timeScale: 1, paused: false };
|
|
2813
3111
|
const hub = new AnimatorEventHub();
|
|
2814
3112
|
const codec = createCodec((on) => {
|
|
2815
3113
|
const { row } = on;
|
|
2816
3114
|
return row ? { group: row.table, tag: row.index } : void 0;
|
|
2817
3115
|
});
|
|
2818
3116
|
const afterTicks = [];
|
|
2819
|
-
const subscribers =
|
|
3117
|
+
const subscribers = createEmitter();
|
|
2820
3118
|
let nextId = 1;
|
|
3119
|
+
const seedClock = () => {
|
|
3120
|
+
if (clock.frameT == null) clock.frameT = host.now();
|
|
3121
|
+
};
|
|
2821
3122
|
const reread = () => codec.refresh((on, queued) => targets.reread(on, queued));
|
|
2822
3123
|
const table = createTable({
|
|
2823
3124
|
codec,
|
|
2824
3125
|
hub,
|
|
2825
|
-
|
|
3126
|
+
elapsed: () => clock.elapsed,
|
|
3127
|
+
seedClock,
|
|
2826
3128
|
showNow: (target) => targets.showNow(target),
|
|
2827
3129
|
resting: () => targets.resting,
|
|
2828
3130
|
framing: () => frame.framing(),
|
|
@@ -2830,7 +3132,7 @@ function createAnimatorCore(host) {
|
|
|
2830
3132
|
});
|
|
2831
3133
|
const targets = createTargetBook({
|
|
2832
3134
|
stop: (s) => {
|
|
2833
|
-
for (const a of [...table.
|
|
3135
|
+
for (const a of [...table.on(s)]) table.retire(a.id);
|
|
2834
3136
|
},
|
|
2835
3137
|
reread: (s) => {
|
|
2836
3138
|
codec.touch(s);
|
|
@@ -2847,17 +3149,17 @@ function createAnimatorCore(host) {
|
|
|
2847
3149
|
afterTicks,
|
|
2848
3150
|
subscribers,
|
|
2849
3151
|
reread,
|
|
2850
|
-
requestFrame: host.requestFrame
|
|
3152
|
+
requestFrame: host.requestFrame,
|
|
3153
|
+
tripwire: host.tripwire
|
|
2851
3154
|
});
|
|
2852
3155
|
const ctx = {
|
|
2853
3156
|
codec,
|
|
2854
3157
|
targets,
|
|
2855
3158
|
nextId: () => nextId++,
|
|
2856
|
-
seedClock
|
|
2857
|
-
if (clock.frameT == null) clock.frameT = host.now();
|
|
2858
|
-
},
|
|
3159
|
+
seedClock,
|
|
2859
3160
|
register: table.register,
|
|
2860
3161
|
has: (id) => table.animations.has(id),
|
|
3162
|
+
defer: table.defer,
|
|
2861
3163
|
reportEnd: table.reportEnd,
|
|
2862
3164
|
finish: table.finish,
|
|
2863
3165
|
touch: (target, at) => {
|
|
@@ -2870,8 +3172,8 @@ function createAnimatorCore(host) {
|
|
|
2870
3172
|
interrupt: (key, by) => table.cancelByKey(key, by),
|
|
2871
3173
|
velocityUnder: (key, target) => {
|
|
2872
3174
|
let sum2 = null;
|
|
2873
|
-
for (const a of table.
|
|
2874
|
-
if (a.
|
|
3175
|
+
for (const a of table.under(key)) {
|
|
3176
|
+
if (a.target !== target) continue;
|
|
2875
3177
|
const v = a.velocity?.() ?? codec.own(a.id)?.velocity;
|
|
2876
3178
|
if (v) sum2 = sum2 ? sum2.map((x, i) => x + v[i]) : v.slice();
|
|
2877
3179
|
}
|
|
@@ -2895,7 +3197,6 @@ function createAnimatorCore(host) {
|
|
|
2895
3197
|
api,
|
|
2896
3198
|
tick: frame.tick,
|
|
2897
3199
|
resumed() {
|
|
2898
|
-
for (const anim of table.animations.values()) anim.lastRealNow = null;
|
|
2899
3200
|
clock.frameT = null;
|
|
2900
3201
|
}
|
|
2901
3202
|
};
|
|
@@ -3162,15 +3463,30 @@ function bindTrack(track, binder) {
|
|
|
3162
3463
|
return defined({
|
|
3163
3464
|
kind: "sampled",
|
|
3164
3465
|
label: track.label,
|
|
3165
|
-
keys: track.keys.map((k) => ({ ...k })),
|
|
3466
|
+
keys: track.keys.map((k) => ({ ...k, value: reviveTypedArrays(k.value) })),
|
|
3166
3467
|
target,
|
|
3167
3468
|
targetName: track.target,
|
|
3168
3469
|
interpolate,
|
|
3169
3470
|
interpolateName: track.interpolate,
|
|
3170
|
-
|
|
3171
|
-
axes: track
|
|
3471
|
+
...bindWeight(track, binder),
|
|
3472
|
+
axes: bindAxes(track, target)
|
|
3172
3473
|
});
|
|
3173
3474
|
}
|
|
3475
|
+
function bindWeight(track, binder) {
|
|
3476
|
+
const { weight } = track;
|
|
3477
|
+
if (typeof weight !== "string") return { weight };
|
|
3478
|
+
const read = binder.weight?.(weight);
|
|
3479
|
+
if (!read) throw new Error(`bindClip: the binder has no weight named "${weight}".`);
|
|
3480
|
+
return { weight: read, weightName: weight };
|
|
3481
|
+
}
|
|
3482
|
+
function bindAxes(track, target) {
|
|
3483
|
+
const { axes } = track;
|
|
3484
|
+
if (!axes) return void 0;
|
|
3485
|
+
const where = `bindClip: track "${track.label ?? ""}"`;
|
|
3486
|
+
if (!target) throw new Error(`${where} names axes and no target.`);
|
|
3487
|
+
axisIndexes(target.axes, axes, `${where} on the target "${track.target}"`);
|
|
3488
|
+
return [...axes];
|
|
3489
|
+
}
|
|
3174
3490
|
function bindTimeline(timeline, binder) {
|
|
3175
3491
|
return defined({ tracks: timeline.tracks.map((t) => bindTrack(t, binder)), duration: timeline.duration });
|
|
3176
3492
|
}
|
|
@@ -3200,17 +3516,20 @@ function sampledToClip(track, where) {
|
|
|
3200
3516
|
if (track.target && track.targetName === void 0) throw new Error(`toClip: ${where} has a target and no targetName.`);
|
|
3201
3517
|
if (track.interpolator) throw new Error(`toClip: ${where} has an interpolator, which a clip cannot hold.`);
|
|
3202
3518
|
if (track.interpolate && track.interpolateName === void 0) throw new Error(`toClip: ${where} has an interpolate and no interpolateName.`);
|
|
3203
|
-
if (typeof track.weight === "function"
|
|
3519
|
+
if (typeof track.weight === "function" && track.weightName === void 0) {
|
|
3520
|
+
throw new Error(`toClip: ${where} has a function for its weight and no weightName.`);
|
|
3521
|
+
}
|
|
3204
3522
|
return defined({
|
|
3205
3523
|
kind: "sampled",
|
|
3206
3524
|
label: track.label,
|
|
3207
3525
|
target: track.targetName,
|
|
3208
3526
|
keys: track.keys.map((k) => {
|
|
3209
|
-
|
|
3210
|
-
|
|
3527
|
+
const value = tagTypedArrays(k.value);
|
|
3528
|
+
if (!isClipValue(value)) throw new Error(`toClip: ${where} holds a value JSON cannot round-trip, at t = ${k.t}.`);
|
|
3529
|
+
return defined({ t: k.t, value, easing: easingOf(k.easing, where) });
|
|
3211
3530
|
}),
|
|
3212
3531
|
interpolate: track.interpolateName,
|
|
3213
|
-
weight: track.weight,
|
|
3532
|
+
weight: typeof track.weight === "function" ? track.weightName : track.weight,
|
|
3214
3533
|
axes: track.axes && [...track.axes]
|
|
3215
3534
|
});
|
|
3216
3535
|
}
|
|
@@ -3310,13 +3629,12 @@ var IDENTITY_JOINT = {
|
|
|
3310
3629
|
// src/rig/skeletonAxes.ts
|
|
3311
3630
|
var FIELDS = ["x", "y", "rotation", "scaleX", "scaleY"];
|
|
3312
3631
|
var ROTATION = FIELDS.indexOf("rotation");
|
|
3632
|
+
function axisNames(skeleton) {
|
|
3633
|
+
return skeleton.joints.flatMap((joint) => FIELDS.map((field) => `${joint.name}.${field}`));
|
|
3634
|
+
}
|
|
3313
3635
|
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
|
-
});
|
|
3636
|
+
const names = axisNames(skeleton);
|
|
3637
|
+
return axisIndexes({ count: names.length, names }, joints, "jointAxes: `joints`");
|
|
3320
3638
|
}
|
|
3321
3639
|
function skeletonAxes(skeleton) {
|
|
3322
3640
|
const names = skeleton.joints.map((j) => j.name);
|
|
@@ -3324,6 +3642,7 @@ function skeletonAxes(skeleton) {
|
|
|
3324
3642
|
return {
|
|
3325
3643
|
count: names.length * n,
|
|
3326
3644
|
angles: names.map((_, j) => j * n + ROTATION),
|
|
3645
|
+
names: axisNames(skeleton),
|
|
3327
3646
|
to(pose) {
|
|
3328
3647
|
const out = new Array(names.length * n);
|
|
3329
3648
|
for (let j = 0; j < names.length; j++) {
|
|
@@ -3525,7 +3844,7 @@ function skinPath(path, skin, motions) {
|
|
|
3525
3844
|
coords[2 * i] = t > 0 ? sum2[2 * i] / t : from[2 * i];
|
|
3526
3845
|
coords[2 * i + 1] = t > 0 ? sum2[2 * i + 1] / t : from[2 * i + 1];
|
|
3527
3846
|
}
|
|
3528
|
-
return {
|
|
3847
|
+
return { ...rest, coords };
|
|
3529
3848
|
}
|
|
3530
3849
|
function distanceToSegment(px, py, ax, ay, bx, by) {
|
|
3531
3850
|
const dx = bx - ax;
|