@msb235/blits 0.14.0 → 0.15.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/CHANGELOG.md +198 -0
- package/README.md +3 -3
- package/dist/ahead.d.ts +26 -0
- package/dist/ahead.js +264 -0
- package/dist/around.d.ts +11 -0
- package/dist/around.js +32 -0
- package/dist/bare.js +37 -14
- package/dist/blank.d.ts +10 -0
- package/dist/blank.js +26 -0
- package/dist/blend.d.ts +2 -1
- package/dist/blend.js +1 -6
- package/dist/bound.d.ts +70 -0
- package/dist/bound.js +351 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -5
- package/dist/changes.d.ts +24 -0
- package/dist/changes.js +189 -0
- package/dist/channels.d.ts +39 -13
- package/dist/channels.js +14 -5
- package/dist/color.d.ts +12 -10
- package/dist/color.js +25 -25
- package/dist/columns.d.ts +2 -1
- package/dist/columns.js +62 -38
- package/dist/contribution.d.ts +30 -0
- package/dist/contribution.js +374 -0
- package/dist/copyout.d.ts +10 -0
- package/dist/copyout.js +69 -24
- package/dist/crowd.d.ts +16 -6
- package/dist/crowd.js +20 -11
- package/dist/cue.d.ts +8 -1
- package/dist/cue.js +39 -13
- package/dist/cut.d.ts +8 -0
- package/dist/cut.js +4 -0
- package/dist/delay.d.ts +3 -0
- package/dist/delay.js +7 -0
- package/dist/drop.d.ts +9 -0
- package/dist/drop.js +84 -0
- package/dist/due.d.ts +24 -5
- package/dist/due.js +114 -34
- package/dist/easing.d.ts +6 -4
- package/dist/easing.js +7 -3
- package/dist/everyone.d.ts +8 -2
- package/dist/everyone.js +33 -3
- package/dist/fade.d.ts +2 -1
- package/dist/fade.js +2 -1
- package/dist/fill.d.ts +3 -2
- package/dist/fill.js +52 -20
- package/dist/fit.d.ts +44 -26
- package/dist/fit.js +10 -9
- package/dist/fold.d.ts +4 -37
- package/dist/fold.js +5 -513
- package/dist/gather.d.ts +12 -7
- package/dist/gather.js +179 -44
- package/dist/gone.js +2 -2
- package/dist/grid.d.ts +47 -0
- package/dist/grid.js +247 -0
- package/dist/handle.d.ts +2 -0
- package/dist/handle.js +14 -8
- package/dist/held.d.ts +2 -1
- package/dist/held.js +2 -1
- package/dist/history.d.ts +6 -5
- package/dist/history.js +7 -9
- package/dist/hosts.js +9 -15
- package/dist/index.d.ts +5 -4
- package/dist/keyfill.d.ts +1 -1
- package/dist/keyfill.js +5 -3
- package/dist/keyrows.d.ts +12 -0
- package/dist/keyrows.js +99 -12
- package/dist/lane.d.ts +38 -11
- package/dist/lane.js +46 -11
- package/dist/lanes.d.ts +30 -12
- package/dist/lanes.js +56 -36
- package/dist/leavings.d.ts +2 -2
- package/dist/leavings.js +1 -1
- package/dist/lives.d.ts +47 -0
- package/dist/lives.js +180 -0
- package/dist/meet.d.ts +2 -2
- package/dist/meet.js +95 -7
- package/dist/mixer.d.ts +56 -37
- package/dist/mixer.js +57 -171
- package/dist/motion.d.ts +14 -118
- package/dist/motion.js +16 -8
- package/dist/motions.d.ts +62 -155
- package/dist/motions.js +56 -370
- package/dist/moved.d.ts +1 -1
- package/dist/moved.js +16 -7
- package/dist/named.d.ts +6 -2
- package/dist/named.js +49 -3
- package/dist/numbers.d.ts +7 -0
- package/dist/numbers.js +33 -0
- package/dist/owed.d.ts +2 -0
- package/dist/owed.js +4 -0
- package/dist/pack.d.ts +2 -1
- package/dist/pack.js +2 -3
- package/dist/patch.d.ts +7 -3
- package/dist/patch.js +6 -2
- package/dist/places.d.ts +53 -0
- package/dist/places.js +171 -0
- package/dist/plot.d.ts +12 -0
- package/dist/plot.js +52 -0
- package/dist/project.d.ts +4 -4
- package/dist/project.js +9 -212
- package/dist/pull.js +22 -9
- package/dist/qualify.js +93 -31
- package/dist/reading.d.ts +6 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +5 -1
- package/dist/record.js +3 -2
- package/dist/relink.d.ts +1 -1
- package/dist/renumber.d.ts +10 -0
- package/dist/renumber.js +75 -0
- package/dist/restored.d.ts +2 -2
- package/dist/restored.js +1 -1
- package/dist/rests.d.ts +9 -0
- package/dist/rests.js +76 -0
- package/dist/revive.d.ts +3 -2
- package/dist/revive.js +2 -2
- package/dist/{locus.d.ts → role.d.ts} +10 -9
- package/dist/{locus.js → role.js} +3 -3
- package/dist/roles.d.ts +10 -0
- package/dist/roles.js +158 -0
- package/dist/roster.js +3 -3
- package/dist/rotation.d.ts +1 -1
- package/dist/rotation.js +1 -1
- package/dist/rows.js +28 -20
- package/dist/sample.d.ts +2 -2
- package/dist/sample.js +33 -7
- package/dist/seat.d.ts +17 -0
- package/dist/seat.js +25 -0
- package/dist/seek.js +5 -5
- package/dist/setting.d.ts +27 -0
- package/dist/setting.js +55 -0
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/signals.d.ts +64 -32
- package/dist/signals.js +20 -18
- package/dist/stack.d.ts +5 -5
- package/dist/stack.js +5 -5
- package/dist/standing.d.ts +2 -1
- package/dist/standing.js +3 -2
- package/dist/store.d.ts +9 -0
- package/dist/store.js +24 -0
- package/dist/strays.d.ts +2 -1
- package/dist/strays.js +23 -15
- package/dist/stretch.d.ts +112 -0
- package/dist/stretch.js +9 -0
- package/dist/subject.d.ts +142 -0
- package/dist/subject.js +1 -0
- package/dist/tape.d.ts +9 -5
- package/dist/tape.js +5 -1
- package/dist/then.d.ts +9 -0
- package/dist/then.js +57 -0
- package/dist/ticker.d.ts +4 -4
- package/dist/trace.d.ts +4 -0
- package/dist/trace.js +255 -0
- package/dist/transport.d.ts +0 -8
- package/dist/transport.js +0 -4
- package/dist/turns.d.ts +4 -3
- package/dist/turns.js +4 -3
- package/dist/types/arc.d.ts +12 -11
- package/dist/types/channel.d.ts +12 -8
- package/dist/types/history.d.ts +35 -19
- package/dist/types/mix.d.ts +237 -163
- package/dist/types/motion.d.ts +151 -0
- package/dist/types/motion.js +1 -0
- package/dist/types/patch.d.ts +67 -34
- package/dist/types/place.d.ts +51 -36
- package/dist/types/trace.d.ts +51 -0
- package/dist/types/trace.js +1 -0
- package/dist/types/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +144 -73
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +2 -0
- package/dist/unreached.d.ts +2 -0
- package/dist/unreached.js +7 -0
- package/dist/voice.d.ts +10 -143
- package/dist/voice.js +37 -82
- package/dist/warn.d.ts +2 -0
- package/dist/warn.js +1 -1
- package/dist/watch.d.ts +2 -2
- package/dist/watch.js +5 -3
- package/dist/wave.d.ts +4 -3
- package/dist/wave.js +3 -2
- package/dist/weigh.d.ts +7 -1
- package/dist/weigh.js +27 -0
- package/docs/schema.html +223 -34
- package/package.json +2 -2
package/dist/motions.js
CHANGED
|
@@ -1,52 +1,20 @@
|
|
|
1
|
+
import { applied, change, commit, cut, dueTo, insert, peek, queue, rewind } from './changes.js';
|
|
1
2
|
import { closed } from './closed.js';
|
|
3
|
+
import { made, pack, prune, reclaim, release, restretch, runAt, runThen, unpack, unrelease, } from './lives.js';
|
|
2
4
|
import { absent, Numbers } from './numbers.js';
|
|
3
5
|
import { reading } from './reading.js';
|
|
4
6
|
import { Store } from './store.js';
|
|
7
|
+
import { numbers, } from './stretch.js';
|
|
5
8
|
import { sloped } from './tweened.js';
|
|
6
|
-
|
|
7
|
-
* A patch's law, `[form, settle, k1, k2, k3]`: which closed form its stretches follow, its `settle`,
|
|
8
|
-
* and up to three constants that form reads. It heads the patch's `runs`, so `solve` finds them in
|
|
9
|
-
* the buffer a sample already reads rather than in an object of their own.
|
|
10
|
-
*/
|
|
11
|
-
const HEAD = 5;
|
|
12
|
-
/** A run that shares nothing with the patch's own. */
|
|
13
|
-
const runOf = (r) => ({
|
|
14
|
-
flags: r.flags,
|
|
15
|
-
stretches: r.stretches.map((seg) => ({ ...seg })),
|
|
16
|
-
pending: r.pending?.map((c) => ({ ...c })),
|
|
17
|
-
});
|
|
18
|
-
/** `segs`, in time order, ahead of `list`: those older than its first, one per time. */
|
|
19
|
-
function ahead(segs, list) {
|
|
20
|
-
const first = list[0].at;
|
|
21
|
-
const out = [];
|
|
22
|
-
for (const seg of [...segs].sort((x, y) => x.at - y.at))
|
|
23
|
-
if (seg.at < first && out[out.length - 1]?.at !== seg.at)
|
|
24
|
-
out.push(seg);
|
|
25
|
-
return out.length === 0 ? list : [...out, ...list];
|
|
26
|
-
}
|
|
9
|
+
const HEAD = 5 /* Law.HEAD */;
|
|
27
10
|
/** Where every patch's `sample` leaves a value on its way to the mix; none outlives its call. */
|
|
28
11
|
let shared = { xs: new Float64Array(4), vs: new Float64Array(4) };
|
|
29
12
|
/** What a patch's `xs` and `vs` are before its first subject sets its axes. */
|
|
30
13
|
const unsized = new Float64Array(0);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
/** `n` numbers of `from` from `at`, as an array of their own. */
|
|
36
|
-
function numbers(from, at, n) {
|
|
37
|
-
// `Array.from` walks a typed array by its iterator: about 500 B a call for three axes.
|
|
38
|
-
for (let i = 0; i < n; i++)
|
|
39
|
-
gathered[i] = from[at + i];
|
|
40
|
-
return gathered.slice(0, n);
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* A subject's flags: its value is a number; it has changes pending; it has older stretches; the
|
|
44
|
-
* stretch it is playing was found landed, which holds until another replaces it.
|
|
45
|
-
*/
|
|
46
|
-
const SCALAR = 1;
|
|
47
|
-
const PENDING = 2;
|
|
48
|
-
const OLDER = 4;
|
|
49
|
-
const LANDED = 8;
|
|
14
|
+
const SCALAR = 1 /* RunFlag.SCALAR */;
|
|
15
|
+
const PENDING = 2 /* RunFlag.PENDING */;
|
|
16
|
+
const OLDER = 4 /* RunFlag.OLDER */;
|
|
17
|
+
const LANDED = 8 /* RunFlag.LANDED */;
|
|
50
18
|
const wrongKind = (channel, holdsNumber) => new Error(`blits: channel ${channel} holds ${holdsNumber ? 'a number' : 'an array'}, and this motion patch gives a subject ${holdsNumber ? 'an array' : 'a number'}`);
|
|
51
19
|
/**
|
|
52
20
|
* A motion patch's subjects: the stretch each is playing, in one flat array by a number the patch
|
|
@@ -58,6 +26,7 @@ const wrongKind = (channel, holdsNumber) => new Error(`blits: channel ${channel}
|
|
|
58
26
|
* 10k springs of one subject each, a buffer per field, a map lookup per sample and the law in an
|
|
59
27
|
* object of its own took a call from 130 ns to 490.
|
|
60
28
|
*/
|
|
29
|
+
// biome-ignore lint/suspicious/noUnsafeDeclarationMerging: every member of `Methods` is installed below
|
|
61
30
|
export class Motions {
|
|
62
31
|
shape;
|
|
63
32
|
numbers = new Numbers(this);
|
|
@@ -68,6 +37,7 @@ export class Motions {
|
|
|
68
37
|
/** Axes per subject; -1 until the first subject sets it. */
|
|
69
38
|
n = -1;
|
|
70
39
|
cap = 0;
|
|
40
|
+
/** Numbers in each subject's run; a run starts at `base`. Written only here. */
|
|
71
41
|
stride = 0;
|
|
72
42
|
/**
|
|
73
43
|
* The law, then each subject's run; empty until the first subject, the law read from `shape`.
|
|
@@ -124,6 +94,16 @@ export class Motions {
|
|
|
124
94
|
const known = this.known(subject);
|
|
125
95
|
if (known !== undefined)
|
|
126
96
|
return known;
|
|
97
|
+
const seg = this.fresh(subject);
|
|
98
|
+
const s = this.take(subject);
|
|
99
|
+
this.runs[this.base(s) + 1] = this.freshFlags;
|
|
100
|
+
this.write(s, seg);
|
|
101
|
+
return s;
|
|
102
|
+
}
|
|
103
|
+
/** The flags of the run `fresh` last started. */
|
|
104
|
+
freshFlags = 0;
|
|
105
|
+
/** The stretch a subject met afresh starts with, its axes and the law set up for it, numbering no one. */
|
|
106
|
+
fresh(subject) {
|
|
127
107
|
const x = this.shape.from(subject);
|
|
128
108
|
const v = this.shape.velocity(subject, x);
|
|
129
109
|
const to = this.shape.aim(x, v, null, subject, false);
|
|
@@ -136,10 +116,14 @@ export class Motions {
|
|
|
136
116
|
throw wrongKind(this.channel, this.holdsNumber);
|
|
137
117
|
if (this.n < 0)
|
|
138
118
|
this.size(n);
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
|
|
142
|
-
|
|
119
|
+
this.grow(1);
|
|
120
|
+
this.freshFlags = scalar ? SCALAR : 0;
|
|
121
|
+
return { at: 0, ms: this.shape.ms?.(subject) ?? 0, x0: x, v0: v, to };
|
|
122
|
+
}
|
|
123
|
+
/** The run `fresh` starts, as plain data. */
|
|
124
|
+
freshRun(subject) {
|
|
125
|
+
const seg = this.fresh(subject);
|
|
126
|
+
return { flags: this.freshFlags, stretches: [seg], pending: undefined };
|
|
143
127
|
}
|
|
144
128
|
/** Sets the axes every subject moves on, at the first. */
|
|
145
129
|
size(n) {
|
|
@@ -175,120 +159,10 @@ export class Motions {
|
|
|
175
159
|
* rewind before the release plays them on from there. Empty without history.
|
|
176
160
|
*/
|
|
177
161
|
released = [];
|
|
178
|
-
/**
|
|
179
|
-
* Forgets a subject, so a later ask starts it afresh from `from`. Given the mix time `at` and frame
|
|
180
|
-
* `seq`, under history, its run is kept for a rewind before then, until `reach` passes it.
|
|
181
|
-
*/
|
|
182
|
-
release(subject, at, reach = Number.NEGATIVE_INFINITY, seq = Number.NaN) {
|
|
183
|
-
const s = this.known(subject);
|
|
184
|
-
if (s === undefined)
|
|
185
|
-
return;
|
|
186
|
-
if (at !== undefined) {
|
|
187
|
-
const kept = [];
|
|
188
|
-
for (const e of this.released)
|
|
189
|
-
if (e.at >= reach)
|
|
190
|
-
kept.push(e);
|
|
191
|
-
else
|
|
192
|
-
this.owner?.page(this.ownerId, 'released', e.subject, [
|
|
193
|
-
{ at: e.at, seq: e.seq, data: runOf(e) },
|
|
194
|
-
]);
|
|
195
|
-
this.released = kept;
|
|
196
|
-
this.released.push({ subject, at, seq, ...this.runAt(s) });
|
|
197
|
-
}
|
|
198
|
-
this.slots.delete(subject);
|
|
199
|
-
this.numbers.release(s);
|
|
200
|
-
}
|
|
201
|
-
/** Puts back each subject released after frame `seq`, as it stood when released. */
|
|
202
|
-
unrelease(seq) {
|
|
203
|
-
const back = this.released.filter((e) => e.seq > seq).sort((a, b) => a.seq - b.seq);
|
|
204
|
-
if (back.length === 0)
|
|
205
|
-
return;
|
|
206
|
-
this.released = this.released.filter((e) => e.seq <= seq);
|
|
207
|
-
const done = new Set();
|
|
208
|
-
for (const e of back) {
|
|
209
|
-
if (done.has(e.subject))
|
|
210
|
-
continue;
|
|
211
|
-
done.add(e.subject);
|
|
212
|
-
this.unpack(e.subject, e);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
/** Subject `s`'s run as it stands. */
|
|
216
|
-
runAt(s) {
|
|
217
|
-
const older = this.older?.[s];
|
|
218
|
-
const pending = this.pending?.[s];
|
|
219
|
-
return {
|
|
220
|
-
flags: this.runs[this.base(s) + 1],
|
|
221
|
-
stretches: older === undefined ? [this.latest(s)] : [...older, this.latest(s)],
|
|
222
|
-
pending: pending === undefined ? undefined : [...pending],
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
162
|
/** Whether the patch holds a run for `subject`. */
|
|
226
163
|
holds(subject) {
|
|
227
164
|
return this.known(subject) !== undefined;
|
|
228
165
|
}
|
|
229
|
-
/** A subject's run as plain data, for a voice leaving memory; undefined where it has none. */
|
|
230
|
-
pack(subject) {
|
|
231
|
-
const s = this.known(subject);
|
|
232
|
-
return s === undefined ? undefined : runOf(this.runAt(s));
|
|
233
|
-
}
|
|
234
|
-
/** Puts a subject's run back as `pack` or a release left it, replacing any it has now. */
|
|
235
|
-
unpack(subject, run) {
|
|
236
|
-
const now = this.known(subject);
|
|
237
|
-
if (now !== undefined) {
|
|
238
|
-
this.slots.delete(subject);
|
|
239
|
-
this.numbers.release(now);
|
|
240
|
-
}
|
|
241
|
-
if (this.n < 0)
|
|
242
|
-
this.size(run.stretches[0].x0.length);
|
|
243
|
-
const s = this.take(subject);
|
|
244
|
-
this.runs[this.base(s) + 1] = run.flags & ~(OLDER | PENDING);
|
|
245
|
-
const latest = run.stretches[run.stretches.length - 1];
|
|
246
|
-
this.write(s, latest);
|
|
247
|
-
if (run.stretches.length > 1) {
|
|
248
|
-
if (this.older === null)
|
|
249
|
-
this.older = [undefined];
|
|
250
|
-
this.older[s] = run.stretches.slice(0, -1);
|
|
251
|
-
this.flag(s, OLDER, true);
|
|
252
|
-
}
|
|
253
|
-
if (run.pending !== undefined) {
|
|
254
|
-
if (this.pending === null)
|
|
255
|
-
this.pending = [undefined];
|
|
256
|
-
this.pending[s] = run.pending;
|
|
257
|
-
this.flag(s, PENDING, true);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
/** A released run a history store gave back, for a rewind to before its release. */
|
|
261
|
-
reclaim(subject, at, seq, run) {
|
|
262
|
-
if (this.released.some((e) => e.seq === seq && Object.is(e.subject, subject)))
|
|
263
|
-
return;
|
|
264
|
-
this.released.push({ subject, at, seq, ...run });
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Stretches a history store gave back, each by the frame it was in force by: each life of the
|
|
268
|
-
* subject, ended by a release, takes those from its own, ahead of the earliest it still keeps.
|
|
269
|
-
*/
|
|
270
|
-
restretch(subject, back) {
|
|
271
|
-
const lives = this.released
|
|
272
|
-
.filter((e) => Object.is(e.subject, subject))
|
|
273
|
-
.sort((x, y) => x.seq - y.seq);
|
|
274
|
-
let from = Number.NEGATIVE_INFINITY;
|
|
275
|
-
const within = (to) => back.filter((r) => r.seq > from && r.seq <= to).map((r) => r.data);
|
|
276
|
-
for (const e of lives) {
|
|
277
|
-
e.stretches = ahead(within(e.seq), e.stretches);
|
|
278
|
-
from = e.seq;
|
|
279
|
-
}
|
|
280
|
-
const s = this.known(subject);
|
|
281
|
-
if (s === undefined)
|
|
282
|
-
return;
|
|
283
|
-
const older = this.older?.[s] ?? [];
|
|
284
|
-
const list = ahead(within(Number.POSITIVE_INFINITY), [...older, this.latest(s)]);
|
|
285
|
-
if (list.length === older.length + 1)
|
|
286
|
-
return;
|
|
287
|
-
if (this.older === null)
|
|
288
|
-
this.older = [undefined];
|
|
289
|
-
this.older[s] = list.slice(0, -1);
|
|
290
|
-
this.flag(s, OLDER, true);
|
|
291
|
-
}
|
|
292
166
|
/**
|
|
293
167
|
* Told, with `watchId`, whenever a subject's stretch or what is pending for it changes, so a mix
|
|
294
168
|
* keeping a copy of it knows to read it again. Set by the mix, cleared when its voice retires. An
|
|
@@ -339,7 +213,11 @@ export class Motions {
|
|
|
339
213
|
}
|
|
340
214
|
/** The value `at` hands the mix: the first axis for a number, a fresh array otherwise. */
|
|
341
215
|
value(s, xs) {
|
|
342
|
-
|
|
216
|
+
return this.valueOf(this.runs[this.base(s) + 1], xs);
|
|
217
|
+
}
|
|
218
|
+
/** The value `at` hands the mix for a run with `flags`. */
|
|
219
|
+
valueOf(flags, xs) {
|
|
220
|
+
if ((flags & SCALAR) !== 0)
|
|
343
221
|
return xs[0];
|
|
344
222
|
const n = this.n;
|
|
345
223
|
const out = new Array(n);
|
|
@@ -464,80 +342,6 @@ export class Motions {
|
|
|
464
342
|
}
|
|
465
343
|
return { x, v, s };
|
|
466
344
|
}
|
|
467
|
-
/**
|
|
468
|
-
* Queues a retarget, set or push, untimed at the latest frame, for `subject`'s next read past its time.
|
|
469
|
-
* The queue keeps the changes with a time in time order, ahead of any still waiting for one.
|
|
470
|
-
*/
|
|
471
|
-
change(subject, c) {
|
|
472
|
-
const back = this.owner === null ? Number.NaN : this.owner.revive(this.ownerId, subject);
|
|
473
|
-
const s = this.slot(subject);
|
|
474
|
-
this.check(c.to, this.n);
|
|
475
|
-
this.check(c.x, this.n);
|
|
476
|
-
this.check(c.v, this.n);
|
|
477
|
-
c.made = this.owner === null ? Number.NaN : this.owner.now();
|
|
478
|
-
c.seq = this.owner === null ? Number.NaN : this.owner.seq();
|
|
479
|
-
if (c.at === undefined) {
|
|
480
|
-
const at = this.frame(subject);
|
|
481
|
-
if (!Number.isNaN(at))
|
|
482
|
-
c.at = at;
|
|
483
|
-
else if (!Number.isNaN(back))
|
|
484
|
-
c.at = back;
|
|
485
|
-
}
|
|
486
|
-
const list = this.pending?.[s];
|
|
487
|
-
if (list === undefined) {
|
|
488
|
-
if (this.pending === null)
|
|
489
|
-
this.pending = [undefined];
|
|
490
|
-
this.pending[s] = [c];
|
|
491
|
-
this.flag(s, PENDING, true);
|
|
492
|
-
}
|
|
493
|
-
else if (c.at === undefined)
|
|
494
|
-
list.push(c);
|
|
495
|
-
else
|
|
496
|
-
this.queue(list, c, c.at);
|
|
497
|
-
reading.moved++;
|
|
498
|
-
const made = { at: c.at, to: c.to, x: c.x, v: c.v };
|
|
499
|
-
this.owner?.changed(this.ownerId, this, subject, made);
|
|
500
|
-
}
|
|
501
|
-
/** Files a change with a time after every one due by then, ahead of those still waiting for one. */
|
|
502
|
-
queue(list, c, at) {
|
|
503
|
-
let i = list.length;
|
|
504
|
-
while (i > 0) {
|
|
505
|
-
const before = list[i - 1].at;
|
|
506
|
-
if (before !== undefined && before <= at)
|
|
507
|
-
break;
|
|
508
|
-
i--;
|
|
509
|
-
}
|
|
510
|
-
list.splice(i, 0, c);
|
|
511
|
-
}
|
|
512
|
-
/** Every change due by `t` applied to a copy of the stretch playing then, committing nothing. */
|
|
513
|
-
peek(s, t, xo, vo) {
|
|
514
|
-
let seg = this.playing(s, t);
|
|
515
|
-
const list = this.dueTo(s);
|
|
516
|
-
if (list !== undefined)
|
|
517
|
-
for (const change of list) {
|
|
518
|
-
const a = change.at;
|
|
519
|
-
if (a === undefined || !(a <= t))
|
|
520
|
-
break;
|
|
521
|
-
if (a >= seg.at)
|
|
522
|
-
seg = this.applied(s, seg, change, a);
|
|
523
|
-
}
|
|
524
|
-
this.evaluateSegment(seg, t, xo, vo);
|
|
525
|
-
}
|
|
526
|
-
/** Subject `s`'s changes waiting, with those a read ahead's tape made filed among them by time. */
|
|
527
|
-
dueTo(s) {
|
|
528
|
-
const own = this.pending?.[s];
|
|
529
|
-
const ahead = reading.ahead?.get(this);
|
|
530
|
-
if (ahead === undefined)
|
|
531
|
-
return own;
|
|
532
|
-
const subject = this.numbers.subject(s);
|
|
533
|
-
const made = subject === absent ? undefined : ahead.get(subject);
|
|
534
|
-
if (made === undefined)
|
|
535
|
-
return own;
|
|
536
|
-
if (own === undefined)
|
|
537
|
-
return made;
|
|
538
|
-
// The patch's own were made first, and a stable sort keeps them first at one time.
|
|
539
|
-
return [...own.filter((c) => c.at !== undefined), ...made].sort((a, b) => a.at - b.at);
|
|
540
|
-
}
|
|
541
345
|
check(a, n) {
|
|
542
346
|
if (a !== undefined && a.length !== n)
|
|
543
347
|
throw new Error('blits: a motion patch moves every subject on the same number of axes');
|
|
@@ -557,54 +361,6 @@ export class Motions {
|
|
|
557
361
|
if (this.causes !== null)
|
|
558
362
|
this.causes[s] = undefined;
|
|
559
363
|
}
|
|
560
|
-
/**
|
|
561
|
-
* Takes back every change made after frame `seq`: each subject plays from its earliest stretch
|
|
562
|
-
* kept, with the changes made by then queued to be applied again.
|
|
563
|
-
*/
|
|
564
|
-
rewind(seq) {
|
|
565
|
-
this.unrelease(seq);
|
|
566
|
-
for (let s = 0; s < this.numbers.size; s++)
|
|
567
|
-
if (this.numbers.subject(s) !== absent)
|
|
568
|
-
this.cut(s, seq);
|
|
569
|
-
}
|
|
570
|
-
cut(s, seq) {
|
|
571
|
-
const older = this.older?.[s];
|
|
572
|
-
const pending = this.pending?.[s];
|
|
573
|
-
const current = this.latest(s);
|
|
574
|
-
const stretches = older === undefined ? [current] : [...older, current];
|
|
575
|
-
const kept = [];
|
|
576
|
-
let cut = false;
|
|
577
|
-
for (const c of [...stretches.slice(1).map((seg) => seg.change), ...(pending ?? [])])
|
|
578
|
-
if (c === undefined || c.seq === undefined || !(c.seq > seq)) {
|
|
579
|
-
if (c !== undefined)
|
|
580
|
-
kept.push(c);
|
|
581
|
-
}
|
|
582
|
-
else
|
|
583
|
-
cut = true;
|
|
584
|
-
if (!cut)
|
|
585
|
-
return;
|
|
586
|
-
if (older !== undefined) {
|
|
587
|
-
this.older[s] = undefined;
|
|
588
|
-
this.flag(s, OLDER, false);
|
|
589
|
-
}
|
|
590
|
-
this.write(s, stretches[0]);
|
|
591
|
-
// Applied again in the order they take effect; one never stamped waits for the next read.
|
|
592
|
-
const timed = kept
|
|
593
|
-
.filter((c) => c.at !== undefined)
|
|
594
|
-
.sort((a, b) => a.at - b.at);
|
|
595
|
-
const list = [...timed, ...kept.filter((c) => c.at === undefined)];
|
|
596
|
-
if (list.length === 0) {
|
|
597
|
-
if (pending !== undefined) {
|
|
598
|
-
this.pending[s] = undefined;
|
|
599
|
-
this.flag(s, PENDING, false);
|
|
600
|
-
}
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
if (this.pending === null)
|
|
604
|
-
this.pending = [undefined];
|
|
605
|
-
this.pending[s] = list;
|
|
606
|
-
this.flag(s, PENDING, true);
|
|
607
|
-
}
|
|
608
364
|
/** Where subject `s`'s run starts in `runs`. */
|
|
609
365
|
base(s) {
|
|
610
366
|
return HEAD + s * this.stride;
|
|
@@ -666,99 +422,6 @@ export class Motions {
|
|
|
666
422
|
}
|
|
667
423
|
return list[lo];
|
|
668
424
|
}
|
|
669
|
-
applied(s, seg, change, at) {
|
|
670
|
-
const n = this.n;
|
|
671
|
-
if (landing.x.length < n)
|
|
672
|
-
landing = { x: new Float64Array(n), v: new Float64Array(n) };
|
|
673
|
-
const { x, v } = landing;
|
|
674
|
-
this.evaluateSegment(seg, at, x, v);
|
|
675
|
-
const xs = change.x ?? numbers(x, 0, n);
|
|
676
|
-
const vs = change.v ?? numbers(v, 0, n);
|
|
677
|
-
const subject = this.numbers.subject(s);
|
|
678
|
-
const known = subject === absent ? undefined : subject;
|
|
679
|
-
const put = change.x !== undefined && change.v === undefined;
|
|
680
|
-
const to = this.shape.aim(xs, vs, change.to ?? seg.to, known, put);
|
|
681
|
-
const ms = subject === absent ? seg.ms : (this.shape.ms?.(known) ?? 0);
|
|
682
|
-
return { at, ms, x0: xs, v0: vs, to, change };
|
|
683
|
-
}
|
|
684
|
-
commit(s, t) {
|
|
685
|
-
const list = this.pending[s];
|
|
686
|
-
// The first read of a subject no frame had met: those waiting for a time take this one.
|
|
687
|
-
if (list[list.length - 1].at === undefined) {
|
|
688
|
-
let i = list.length;
|
|
689
|
-
while (i > 0 && list[i - 1].at === undefined)
|
|
690
|
-
i--;
|
|
691
|
-
for (const c of list.splice(i)) {
|
|
692
|
-
c.at = t;
|
|
693
|
-
this.queue(list, c, t);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
while (list.length > 0) {
|
|
697
|
-
const change = list[0];
|
|
698
|
-
const a = change.at;
|
|
699
|
-
if (a > t)
|
|
700
|
-
break;
|
|
701
|
-
list.shift();
|
|
702
|
-
this.insert(s, this.applied(s, this.playing(s, a), change, a));
|
|
703
|
-
}
|
|
704
|
-
if (list.length === 0) {
|
|
705
|
-
this.pending[s] = undefined;
|
|
706
|
-
this.flag(s, PENDING, false);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
insert(s, seg) {
|
|
710
|
-
let list = this.older?.[s];
|
|
711
|
-
if (list === undefined) {
|
|
712
|
-
list = [];
|
|
713
|
-
if (this.older === null)
|
|
714
|
-
this.older = [undefined];
|
|
715
|
-
this.older[s] = list;
|
|
716
|
-
this.flag(s, OLDER, true);
|
|
717
|
-
}
|
|
718
|
-
if (seg.at >= this.runs[this.base(s)]) {
|
|
719
|
-
list.push(this.latest(s));
|
|
720
|
-
this.write(s, seg);
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
let i = list.length;
|
|
724
|
-
while (i > 0 && list[i - 1].at > seg.at)
|
|
725
|
-
i--;
|
|
726
|
-
list.splice(i, 0, seg);
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* The mix time and frame a stretch was in force by, which a store keys it under: when the change
|
|
730
|
-
* that ended it was made, which falls in the same life of the subject.
|
|
731
|
-
*/
|
|
732
|
-
made(c) {
|
|
733
|
-
const made = c?.made;
|
|
734
|
-
if (made === undefined || Number.isNaN(made))
|
|
735
|
-
return { at: this.owner?.now() ?? Number.NaN, seq: this.owner?.seq() ?? Number.NaN };
|
|
736
|
-
return { at: made, seq: c?.seq ?? Number.NaN };
|
|
737
|
-
}
|
|
738
|
-
/** Lets go of stretches older than the one in force at `reading.horizon`. */
|
|
739
|
-
prune(s) {
|
|
740
|
-
const list = this.older?.[s];
|
|
741
|
-
if (list === undefined)
|
|
742
|
-
return;
|
|
743
|
-
let n = 0;
|
|
744
|
-
while (n < list.length &&
|
|
745
|
-
(n + 1 < list.length ? list[n + 1].at : this.runs[this.base(s)]) <=
|
|
746
|
-
reading.horizon)
|
|
747
|
-
n++;
|
|
748
|
-
if (n === 0)
|
|
749
|
-
return;
|
|
750
|
-
const out = list.splice(0, n);
|
|
751
|
-
const subject = this.numbers.subject(s);
|
|
752
|
-
if (this.owner !== null && subject !== absent)
|
|
753
|
-
this.owner.page(this.ownerId, 'stretch', subject, out.map((seg, i) => {
|
|
754
|
-
const next = out[i + 1] ?? list[0];
|
|
755
|
-
return { ...this.made(next === undefined ? this.causes?.[s] : next.change), data: seg };
|
|
756
|
-
}));
|
|
757
|
-
if (list.length === 0) {
|
|
758
|
-
this.older[s] = undefined;
|
|
759
|
-
this.flag(s, OLDER, false);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
425
|
evaluateSegment(seg, t, xo, vo) {
|
|
763
426
|
this.evaluate(seg.at, seg.ms, seg.x0, 0, seg.v0, 0, seg.to, 0, t, xo, vo);
|
|
764
427
|
}
|
|
@@ -766,3 +429,26 @@ export class Motions {
|
|
|
766
429
|
return closed(this.runs, this.shape.ease, this.n, at, ms, x0, x, v0, v, to, g, t, xo, vo);
|
|
767
430
|
}
|
|
768
431
|
}
|
|
432
|
+
const methods = {
|
|
433
|
+
release,
|
|
434
|
+
unrelease,
|
|
435
|
+
runAt,
|
|
436
|
+
pack,
|
|
437
|
+
unpack,
|
|
438
|
+
reclaim,
|
|
439
|
+
runThen,
|
|
440
|
+
restretch,
|
|
441
|
+
made,
|
|
442
|
+
prune,
|
|
443
|
+
change,
|
|
444
|
+
queue,
|
|
445
|
+
peek,
|
|
446
|
+
dueTo,
|
|
447
|
+
rewind,
|
|
448
|
+
cut,
|
|
449
|
+
applied,
|
|
450
|
+
commit,
|
|
451
|
+
insert,
|
|
452
|
+
};
|
|
453
|
+
for (const [name, value] of Object.entries(methods))
|
|
454
|
+
Object.defineProperty(Motions.prototype, name, { value, writable: true, configurable: true });
|
package/dist/moved.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Motions } from './motions.js';
|
|
2
|
-
import type { Subject } from './
|
|
2
|
+
import type { Subject } from './subject.js';
|
|
3
3
|
/**
|
|
4
4
|
* A motion voice's delta for a subject on the general path: what its patch's `at` gives, written
|
|
5
5
|
* into the record's last delta and the array that holds, where `at` makes an object and an array a
|
package/dist/moved.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { doubles } from './channels.js';
|
|
2
|
+
import { reading } from './reading.js';
|
|
3
|
+
import { sampleThen } from './then.js';
|
|
2
4
|
/**
|
|
3
5
|
* A motion voice's delta for a subject on the general path: what its patch's `at` gives, written
|
|
4
6
|
* into the record's last delta and the array that holds, where `at` makes an object and an array a
|
|
@@ -6,16 +8,23 @@ import { doubles } from './channels.js';
|
|
|
6
8
|
* `setting` has the voice time to sample at.
|
|
7
9
|
*/
|
|
8
10
|
export function moved(run, key, subject, setting, rec) {
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
let scalar;
|
|
12
|
+
const back = reading.back;
|
|
13
|
+
if (back !== null)
|
|
14
|
+
scalar = (sampleThen(run, subject, setting.elapsed, back) & 1 /* RunFlag.SCALAR */) !== 0;
|
|
15
|
+
else {
|
|
16
|
+
let s = rec.run;
|
|
17
|
+
if (rec.freed !== run.freed) {
|
|
18
|
+
s = run.slot(subject);
|
|
19
|
+
rec.run = s;
|
|
20
|
+
rec.freed = run.freed;
|
|
21
|
+
}
|
|
22
|
+
run.sample(s, setting.elapsed, run.xs, run.vs);
|
|
23
|
+
scalar = run.scalar(s);
|
|
14
24
|
}
|
|
15
25
|
const xs = run.xs;
|
|
16
|
-
run.sample(s, setting.elapsed, xs, run.vs);
|
|
17
26
|
const delta = rec.delta ?? {};
|
|
18
|
-
if (
|
|
27
|
+
if (scalar) {
|
|
19
28
|
delta[key] = xs[0];
|
|
20
29
|
return delta;
|
|
21
30
|
}
|
package/dist/named.d.ts
CHANGED
|
@@ -5,8 +5,12 @@
|
|
|
5
5
|
export declare class Named<I> {
|
|
6
6
|
readonly list: readonly I[];
|
|
7
7
|
private readonly set;
|
|
8
|
-
|
|
8
|
+
/** Each subject's `stagger` answer, asked at cue; null for a voice with no stagger. */
|
|
9
|
+
readonly delays: Map<I, number> | null;
|
|
10
|
+
/** The longest of `delays`, 0 at least, and the least, 0 at most: a voice's bounds at cue. */
|
|
11
|
+
readonly latest: number;
|
|
12
|
+
readonly soonest: number;
|
|
13
|
+
constructor(subjects: Iterable<I>, stagger?: (subject: I) => number);
|
|
9
14
|
get size(): number;
|
|
10
15
|
has(subject: I): boolean;
|
|
11
|
-
[Symbol.iterator](): Iterator<I>;
|
|
12
16
|
}
|
package/dist/named.js
CHANGED
|
@@ -7,7 +7,21 @@ const FEW = 8;
|
|
|
7
7
|
export class Named {
|
|
8
8
|
list;
|
|
9
9
|
set;
|
|
10
|
-
|
|
10
|
+
/** Each subject's `stagger` answer, asked at cue; null for a voice with no stagger. */
|
|
11
|
+
delays = null;
|
|
12
|
+
/** The longest of `delays`, 0 at least, and the least, 0 at most: a voice's bounds at cue. */
|
|
13
|
+
latest = 0;
|
|
14
|
+
soonest = 0;
|
|
15
|
+
constructor(subjects, stagger) {
|
|
16
|
+
// An array of distinct subjects, as nearly every voice names, is copied without iterating it.
|
|
17
|
+
if (Array.isArray(subjects) && distinct(subjects)) {
|
|
18
|
+
this.list = subjects.slice();
|
|
19
|
+
this.set = null;
|
|
20
|
+
if (stagger)
|
|
21
|
+
this.delays = delaysOf(this.list, stagger);
|
|
22
|
+
[this.latest, this.soonest] = boundsOf(this.delays);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
11
25
|
const list = [];
|
|
12
26
|
let set = null;
|
|
13
27
|
for (const s of subjects) {
|
|
@@ -26,6 +40,9 @@ export class Named {
|
|
|
26
40
|
// Exactly as long as it is: a list grown by push keeps room for 17.
|
|
27
41
|
this.list = list.slice();
|
|
28
42
|
this.set = set;
|
|
43
|
+
if (stagger)
|
|
44
|
+
this.delays = delaysOf(this.list, stagger);
|
|
45
|
+
[this.latest, this.soonest] = boundsOf(this.delays);
|
|
29
46
|
}
|
|
30
47
|
get size() {
|
|
31
48
|
return this.list.length;
|
|
@@ -33,7 +50,36 @@ export class Named {
|
|
|
33
50
|
has(subject) {
|
|
34
51
|
return this.set === null ? this.list.includes(subject) : this.set.has(subject);
|
|
35
52
|
}
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
}
|
|
54
|
+
function delaysOf(list, stagger) {
|
|
55
|
+
const delays = new Map();
|
|
56
|
+
for (const s of list)
|
|
57
|
+
delays.set(s, stagger(s));
|
|
58
|
+
return delays;
|
|
59
|
+
}
|
|
60
|
+
function boundsOf(delays) {
|
|
61
|
+
let latest = 0;
|
|
62
|
+
let soonest = 0;
|
|
63
|
+
for (const delay of delays?.values() ?? []) {
|
|
64
|
+
if (delay > latest)
|
|
65
|
+
latest = delay;
|
|
66
|
+
if (delay < soonest)
|
|
67
|
+
soonest = delay;
|
|
68
|
+
}
|
|
69
|
+
return [latest, soonest];
|
|
70
|
+
}
|
|
71
|
+
/** Whether a short array names no subject twice, as `includes` compares; a long one says no. */
|
|
72
|
+
function distinct(subjects) {
|
|
73
|
+
const n = subjects.length;
|
|
74
|
+
if (n > FEW)
|
|
75
|
+
return false;
|
|
76
|
+
for (let i = 1; i < n; i++) {
|
|
77
|
+
const a = subjects[i];
|
|
78
|
+
for (let j = 0; j < i; j++) {
|
|
79
|
+
const b = subjects[j];
|
|
80
|
+
if (a === b || (Number.isNaN(a) && Number.isNaN(b)))
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
38
83
|
}
|
|
84
|
+
return true;
|
|
39
85
|
}
|
package/dist/numbers.d.ts
CHANGED
|
@@ -33,6 +33,13 @@ export declare class Numbers<I> {
|
|
|
33
33
|
/** One past the highest number handed out, so a loop over 0..size meets every live one. */
|
|
34
34
|
get size(): number;
|
|
35
35
|
take(subject: I): number;
|
|
36
|
+
/** Whether `slot` is a number in use, its subject perhaps collected and not yet released. */
|
|
37
|
+
has(slot: number): boolean;
|
|
36
38
|
subject(slot: number): I | typeof absent;
|
|
39
|
+
/**
|
|
40
|
+
* Hands the subject numbered `from` the free number `to`, and frees `from` without telling the
|
|
41
|
+
* owner, whose stores the caller moves; false, changing nothing, where either does not hold.
|
|
42
|
+
*/
|
|
43
|
+
move(from: number, to: number): boolean;
|
|
37
44
|
release(slot: number): void;
|
|
38
45
|
}
|