@msb235/blits 0.4.0 → 0.5.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/CHANGELOG.md +262 -0
- package/README.md +2 -1
- package/dist/bare.d.ts +29 -0
- package/dist/bare.js +105 -0
- package/dist/blend.d.ts +34 -0
- package/dist/blend.js +102 -0
- package/dist/book.d.ts +53 -0
- package/dist/book.js +218 -0
- package/dist/chain.d.ts +22 -0
- package/dist/chain.js +150 -0
- package/dist/channels.d.ts +2 -0
- package/dist/channels.js +3 -1
- package/dist/clock.d.ts +50 -3
- package/dist/clock.js +102 -9
- package/dist/clone.d.ts +7 -0
- package/dist/clone.js +57 -0
- package/dist/columns.d.ts +29 -0
- package/dist/columns.js +183 -0
- package/dist/crowd.d.ts +128 -0
- package/dist/crowd.js +354 -0
- package/dist/cue.d.ts +3 -0
- package/dist/cue.js +115 -0
- package/dist/due.d.ts +12 -0
- package/dist/due.js +93 -0
- package/dist/fade.d.ts +19 -0
- package/dist/fade.js +133 -0
- package/dist/fill.d.ts +42 -0
- package/dist/fill.js +381 -0
- package/dist/fold.d.ts +51 -0
- package/dist/fold.js +475 -0
- package/dist/gather.d.ts +24 -0
- package/dist/gather.js +186 -0
- package/dist/handle.d.ts +31 -0
- package/dist/handle.js +56 -0
- package/dist/held.d.ts +14 -0
- package/dist/held.js +117 -0
- package/dist/history.d.ts +22 -0
- package/dist/history.js +149 -0
- package/dist/hosts.d.ts +16 -0
- package/dist/hosts.js +178 -0
- package/dist/index.d.ts +1 -1
- package/dist/keyrows.d.ts +36 -0
- package/dist/keyrows.js +138 -0
- package/dist/lane.d.ts +210 -0
- package/dist/lane.js +187 -0
- package/dist/lanes.d.ts +121 -246
- package/dist/lanes.js +150 -1578
- package/dist/locus.d.ts +40 -0
- package/dist/locus.js +89 -0
- package/dist/marks.d.ts +12 -0
- package/dist/marks.js +80 -0
- package/dist/meet.d.ts +16 -0
- package/dist/meet.js +113 -0
- package/dist/mixer.d.ts +244 -196
- package/dist/mixer.js +242 -2220
- package/dist/motion.d.ts +66 -21
- package/dist/motion.js +325 -165
- package/dist/move.d.ts +9 -0
- package/dist/move.js +190 -0
- package/dist/named.d.ts +12 -0
- package/dist/named.js +39 -0
- package/dist/numbers.d.ts +18 -6
- package/dist/numbers.js +59 -31
- package/dist/owed.d.ts +24 -0
- package/dist/owed.js +51 -0
- package/dist/owner.d.ts +43 -0
- package/dist/owner.js +117 -0
- package/dist/pace.d.ts +40 -0
- package/dist/pace.js +113 -0
- package/dist/patch.d.ts +51 -10
- package/dist/patch.js +140 -45
- package/dist/place.d.ts +20 -0
- package/dist/place.js +196 -0
- package/dist/project.d.ts +4 -0
- package/dist/project.js +233 -0
- package/dist/pull.d.ts +5 -0
- package/dist/pull.js +175 -0
- package/dist/qualify.d.ts +31 -0
- package/dist/qualify.js +345 -0
- package/dist/relink.d.ts +25 -0
- package/dist/relink.js +59 -0
- package/dist/rows.d.ts +33 -0
- package/dist/rows.js +282 -0
- package/dist/sample.d.ts +21 -0
- package/dist/sample.js +232 -0
- package/dist/signals.d.ts +12 -9
- package/dist/signals.js +8 -5
- package/dist/store.d.ts +10 -3
- package/dist/store.js +44 -9
- package/dist/strays.d.ts +21 -0
- package/dist/strays.js +84 -0
- package/dist/tweened.d.ts +11 -0
- package/dist/tweened.js +48 -0
- package/dist/types.d.ts +217 -34
- package/dist/unreached.d.ts +5 -0
- package/dist/unreached.js +24 -0
- package/dist/voice.d.ts +240 -0
- package/dist/voice.js +322 -0
- package/dist/weigh.d.ts +26 -0
- package/dist/weigh.js +89 -0
- package/docs/schema.html +118 -40
- package/package.json +5 -2
package/dist/qualify.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { numericOf } from './channels.js';
|
|
2
|
+
import { flush } from './columns.js';
|
|
3
|
+
import { crowdable } from './crowd.js';
|
|
4
|
+
import { wake } from './fill.js';
|
|
5
|
+
import { Lane } from './lane.js';
|
|
6
|
+
import { bury, compactSparse, join, regroup, retouchRow, sameCrowds } from './rows.js';
|
|
7
|
+
import { settle } from './sample.js';
|
|
8
|
+
/**
|
|
9
|
+
* Which channels run as lanes and which voices run on them: the fixed point where every voice
|
|
10
|
+
* writing a laned channel fits and writes only laned channels. Only channels some laned voice
|
|
11
|
+
* writes are laned.
|
|
12
|
+
*/
|
|
13
|
+
export function qualify(numeric, voices) {
|
|
14
|
+
const open = numeric.slice();
|
|
15
|
+
let changed = true;
|
|
16
|
+
while (changed) {
|
|
17
|
+
changed = false;
|
|
18
|
+
for (const v of voices) {
|
|
19
|
+
if (v.fits && v.slots.every((s) => open[s]))
|
|
20
|
+
continue;
|
|
21
|
+
for (const s of v.slots)
|
|
22
|
+
if (open[s]) {
|
|
23
|
+
open[s] = false;
|
|
24
|
+
changed = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const laned = new Set();
|
|
29
|
+
const channels = numeric.map(() => false);
|
|
30
|
+
for (const v of voices) {
|
|
31
|
+
if (!v.fits || !v.slots.every((s) => open[s]))
|
|
32
|
+
continue;
|
|
33
|
+
laned.add(v.id);
|
|
34
|
+
for (const s of v.slots)
|
|
35
|
+
channels[s] = true;
|
|
36
|
+
}
|
|
37
|
+
return { channels, voices: laned };
|
|
38
|
+
}
|
|
39
|
+
export function requalify(lanes, version) {
|
|
40
|
+
flush(lanes);
|
|
41
|
+
const host = lanes.host;
|
|
42
|
+
lanes.qualifiedVersion = version;
|
|
43
|
+
lanes.filledVersion = Number.NaN;
|
|
44
|
+
const touched = lanes.touched.slice();
|
|
45
|
+
lanes.touched.length = 0;
|
|
46
|
+
// An owner writes nothing and no fold meets it, so it neither takes a lane nor keeps one whole.
|
|
47
|
+
const present = host.voices.filter((v) => v.state !== 'done' && v.holding === null);
|
|
48
|
+
for (const v of present)
|
|
49
|
+
if (v.id > lanes.known)
|
|
50
|
+
lanes.known = v.id;
|
|
51
|
+
const numeric = host.channels.map((c) => numericOf(c) !== undefined);
|
|
52
|
+
// A voice writing nothing has no lane to fill, so it stays where its calls are made.
|
|
53
|
+
const fits = present.map((v) => v.slots.length > 0 && host.fits(v));
|
|
54
|
+
const candidates = () => present.map((v, i) => ({ id: v.id, fits: fits[i], slots: v.slots }));
|
|
55
|
+
let { channels, voices } = qualify(numeric, candidates());
|
|
56
|
+
// A locus folds its members together, so they run on lanes all together or not at all.
|
|
57
|
+
for (let split = true; split;) {
|
|
58
|
+
split = false;
|
|
59
|
+
const loci = new Map();
|
|
60
|
+
present.forEach((v, i) => {
|
|
61
|
+
const name = v.spec.locus;
|
|
62
|
+
if (name === undefined)
|
|
63
|
+
return;
|
|
64
|
+
const at = loci.get(name);
|
|
65
|
+
if (at === undefined)
|
|
66
|
+
loci.set(name, [i]);
|
|
67
|
+
else
|
|
68
|
+
at.push(i);
|
|
69
|
+
});
|
|
70
|
+
for (const at of loci.values()) {
|
|
71
|
+
const on = at.filter((i) => voices.has(present[i].id)).length;
|
|
72
|
+
if (on === 0 || on === at.length)
|
|
73
|
+
continue;
|
|
74
|
+
for (const i of at)
|
|
75
|
+
fits[i] = false;
|
|
76
|
+
split = true;
|
|
77
|
+
}
|
|
78
|
+
if (split)
|
|
79
|
+
({ channels, voices } = qualify(numeric, candidates()));
|
|
80
|
+
}
|
|
81
|
+
for (const lane of lanes.lanes)
|
|
82
|
+
if (!voices.has(lane.voice.id))
|
|
83
|
+
leave(lanes, lane);
|
|
84
|
+
const kept = [];
|
|
85
|
+
let crowded = 0;
|
|
86
|
+
for (const v of present) {
|
|
87
|
+
if (!voices.has(v.id))
|
|
88
|
+
continue;
|
|
89
|
+
if (crowdable(v)) {
|
|
90
|
+
crowded++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const lane = lanes.byId.get(v.id) ?? new Lane(v);
|
|
94
|
+
v.laned = true;
|
|
95
|
+
if (lane.epoch === 0 && v.state !== 'pending')
|
|
96
|
+
lane.epoch = open(lanes, v);
|
|
97
|
+
kept.push(lane);
|
|
98
|
+
}
|
|
99
|
+
lanes.lanes = kept;
|
|
100
|
+
lanes.loci = [];
|
|
101
|
+
const loci = new Map();
|
|
102
|
+
for (const lane of kept) {
|
|
103
|
+
lane.group = null;
|
|
104
|
+
const name = lane.voice.spec.locus;
|
|
105
|
+
if (name === undefined)
|
|
106
|
+
continue;
|
|
107
|
+
let g = loci.get(name);
|
|
108
|
+
if (g === undefined) {
|
|
109
|
+
g = {
|
|
110
|
+
members: [],
|
|
111
|
+
met: new Float64Array(lanes.cap),
|
|
112
|
+
first: new Float64Array(lanes.cap),
|
|
113
|
+
sum: new Float64Array(lanes.cap),
|
|
114
|
+
values: [],
|
|
115
|
+
taken: [],
|
|
116
|
+
};
|
|
117
|
+
loci.set(name, g);
|
|
118
|
+
lanes.loci.push(g);
|
|
119
|
+
}
|
|
120
|
+
g.members.push(lane);
|
|
121
|
+
lane.group = g;
|
|
122
|
+
}
|
|
123
|
+
lanes.dense = kept
|
|
124
|
+
.filter((l) => l.voice.named === null && l.epoch > 0)
|
|
125
|
+
.sort((a, b) => a.epoch - b.epoch);
|
|
126
|
+
lanes.whole = kept.length + crowded === present.length;
|
|
127
|
+
lanes.byId.clear();
|
|
128
|
+
for (const lane of kept)
|
|
129
|
+
lanes.byId.set(lane.voice.id, lane);
|
|
130
|
+
// With no lane left no fill comes to let go of the subjects the last probes held.
|
|
131
|
+
if (kept.length === 0 && crowded === 0)
|
|
132
|
+
lanes.subjects = [];
|
|
133
|
+
const members = present.filter((v) => voices.has(v.id) && crowdable(v));
|
|
134
|
+
// The same channels laned and the same voices crowded, as when a voice over every subject comes
|
|
135
|
+
// or goes among a crowd of thousands: what a rebuild would make is what is there.
|
|
136
|
+
if (sameChannels(lanes, channels) && sameCrowds(lanes, members)) {
|
|
137
|
+
for (const lane of kept) {
|
|
138
|
+
lane.chans = lane.voice.slots.map((s) => lanes.bySlot[s]);
|
|
139
|
+
lane.values = lane.chans.map(() => undefined);
|
|
140
|
+
}
|
|
141
|
+
for (const c of lanes.crowds)
|
|
142
|
+
for (let p = 0; p < c.size; p++) {
|
|
143
|
+
const v = c.voices[p];
|
|
144
|
+
if (c.rowOf.get(v.id) === p && v.state === 'done')
|
|
145
|
+
bury(lanes, c, p);
|
|
146
|
+
}
|
|
147
|
+
// A crowd voice that started or began fading takes it up on its row, as `retouch` does.
|
|
148
|
+
for (const v of touched) {
|
|
149
|
+
if (v.state === 'done')
|
|
150
|
+
continue;
|
|
151
|
+
const c = lanes.crowdOf.get(v.id);
|
|
152
|
+
const p = c?.rowOf.get(v.id);
|
|
153
|
+
if (c !== undefined && p !== undefined)
|
|
154
|
+
retouchRow(lanes, c, p, v);
|
|
155
|
+
}
|
|
156
|
+
compactSparse(lanes);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
lanes.laned = [];
|
|
160
|
+
lanes.bySlot = host.channels.map(() => undefined);
|
|
161
|
+
channels.forEach((on, slot) => {
|
|
162
|
+
if (!on)
|
|
163
|
+
return;
|
|
164
|
+
const channel = host.channels[slot];
|
|
165
|
+
const n = numericOf(channel);
|
|
166
|
+
const rest = channel.rest;
|
|
167
|
+
const ch = {
|
|
168
|
+
name: host.names[slot],
|
|
169
|
+
op: n.op,
|
|
170
|
+
scalar: typeof rest === 'number',
|
|
171
|
+
rest: typeof rest === 'number' ? rest : rest[0],
|
|
172
|
+
axes: n.axes,
|
|
173
|
+
values: new Float64Array(lanes.cap * n.axes),
|
|
174
|
+
start: typeof rest === 'number' ? [] : rest,
|
|
175
|
+
index: lanes.laned.length,
|
|
176
|
+
};
|
|
177
|
+
lanes.laned.push(ch);
|
|
178
|
+
lanes.bySlot[slot] = ch;
|
|
179
|
+
});
|
|
180
|
+
lanes.copies.length = 0;
|
|
181
|
+
for (const ch of lanes.bySlot)
|
|
182
|
+
lanes.copies.push(ch !== undefined);
|
|
183
|
+
for (const lane of kept) {
|
|
184
|
+
lane.chans = lane.voice.slots.map((s) => lanes.bySlot[s]);
|
|
185
|
+
lane.values = lane.chans.map(() => undefined);
|
|
186
|
+
}
|
|
187
|
+
regroup(lanes, members);
|
|
188
|
+
}
|
|
189
|
+
/** Whether qualifying laned exactly the channels laned now. */
|
|
190
|
+
function sameChannels(lanes, channels) {
|
|
191
|
+
const by = lanes.bySlot;
|
|
192
|
+
if (by.length !== channels.length)
|
|
193
|
+
return false;
|
|
194
|
+
for (let i = 0; i < by.length; i++)
|
|
195
|
+
if ((by[i] !== undefined) !== channels[i])
|
|
196
|
+
return false;
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A voice starts playing on a lane or crowd: its epoch. Every subject meets a voice over all of
|
|
201
|
+
* them on its next probe; a voice naming its subjects marks only those.
|
|
202
|
+
*/
|
|
203
|
+
export function open(lanes, v) {
|
|
204
|
+
const epoch = ++lanes.epochs;
|
|
205
|
+
if (v.named === null) {
|
|
206
|
+
lanes.wide = epoch;
|
|
207
|
+
return epoch;
|
|
208
|
+
}
|
|
209
|
+
for (const subject of v.named) {
|
|
210
|
+
const slot = lanes.host.slotOf(subject);
|
|
211
|
+
if (slot < 0 || slot >= lanes.cap)
|
|
212
|
+
continue;
|
|
213
|
+
const i = slot * 6 /* Per.SLOT */ + 1 /* Per.SEEN */;
|
|
214
|
+
const seen = lanes.per[i];
|
|
215
|
+
const from = seen < 0 ? -1 - seen : seen;
|
|
216
|
+
lanes.per[i] = -1 - Math.min(from, epoch - 1);
|
|
217
|
+
}
|
|
218
|
+
return epoch;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Takes the voices `touch` named on or off their crowds, each as it now stands; false at the
|
|
222
|
+
* first that cannot be, which needs a qualify: one that leaves a lane or the general path, which
|
|
223
|
+
* may open a channel, or one that joins anything but a crowd on a laned channel. Neither a voice
|
|
224
|
+
* that fits joining laned channels nor a laned one leaving moves the fixed point `qualify` finds.
|
|
225
|
+
* A crowd a quarter empty rows is compacted.
|
|
226
|
+
*/
|
|
227
|
+
export function retouch(lanes) {
|
|
228
|
+
const touched = [...new Set(lanes.touched)].sort((a, b) => a.id - b.id);
|
|
229
|
+
lanes.touched.length = 0;
|
|
230
|
+
// The qualify a false answer calls takes up every touched voice, crowd rows starting included.
|
|
231
|
+
const qualify = () => {
|
|
232
|
+
lanes.touched = touched;
|
|
233
|
+
return false;
|
|
234
|
+
};
|
|
235
|
+
for (const v of touched) {
|
|
236
|
+
const known = v.id <= lanes.known;
|
|
237
|
+
if (v.id > lanes.known)
|
|
238
|
+
lanes.known = v.id;
|
|
239
|
+
const c = lanes.crowdOf.get(v.id);
|
|
240
|
+
const p = c?.rowOf.get(v.id);
|
|
241
|
+
if (c !== undefined && p !== undefined) {
|
|
242
|
+
if (v.state === 'done') {
|
|
243
|
+
bury(lanes, c, p);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
retouchRow(lanes, c, p, v);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (v.state === 'done' && !known)
|
|
250
|
+
continue;
|
|
251
|
+
const lane = lanes.byId.get(v.id);
|
|
252
|
+
if (lane !== undefined) {
|
|
253
|
+
if (!relane(lanes, lane))
|
|
254
|
+
return qualify();
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (v.state === 'done')
|
|
258
|
+
return qualify();
|
|
259
|
+
// A locus's members are on lanes together or not at all, so one joining may move the others.
|
|
260
|
+
if (v.spec.locus !== undefined)
|
|
261
|
+
return qualify();
|
|
262
|
+
const laned = v.slots.some((slot) => lanes.bySlot[slot] !== undefined);
|
|
263
|
+
const fits = v.slots.length > 0 && lanes.host.fits(v);
|
|
264
|
+
if (!laned && !fits) {
|
|
265
|
+
lanes.whole = false;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (known || !fits || !laned || v.slots.some((slot) => lanes.bySlot[slot] === undefined))
|
|
269
|
+
return qualify();
|
|
270
|
+
if (crowdable(v))
|
|
271
|
+
join(lanes, v, undefined, undefined, lanes.crowds);
|
|
272
|
+
else
|
|
273
|
+
enlane(lanes, v);
|
|
274
|
+
}
|
|
275
|
+
compactSparse(lanes);
|
|
276
|
+
// As a qualify leaving nothing on lanes: no fill comes to let go of what the last probes held.
|
|
277
|
+
if (lanes.lanes.length === 0 && lanes.crowds.every((c) => c.dead === c.size))
|
|
278
|
+
lanes.subjects = [];
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* A new voice that fits and writes only laned channels, and that no crowd takes, gets a lane at
|
|
283
|
+
* the end of the order, its id being past every other: what a qualify would give it, since such a
|
|
284
|
+
* voice joining cannot unlane a channel.
|
|
285
|
+
*/
|
|
286
|
+
function enlane(lanes, v) {
|
|
287
|
+
const lane = new Lane(v);
|
|
288
|
+
v.laned = true;
|
|
289
|
+
if (v.state !== 'pending')
|
|
290
|
+
lane.epoch = open(lanes, v);
|
|
291
|
+
lane.chans = v.slots.map((s) => lanes.bySlot[s]);
|
|
292
|
+
lane.values = lane.chans.map(() => undefined);
|
|
293
|
+
lanes.lanes.push(lane);
|
|
294
|
+
lanes.byId.set(v.id, lane);
|
|
295
|
+
if (v.named === null && lane.epoch > 0)
|
|
296
|
+
lanes.dense.push(lane);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* A laned voice that started playing or finished, as a qualify would take it: a lane leaving
|
|
300
|
+
* cannot lane or unlane another channel. False for a locus member, which a qualify must take.
|
|
301
|
+
*/
|
|
302
|
+
function relane(lanes, lane) {
|
|
303
|
+
const v = lane.voice;
|
|
304
|
+
if (v.state !== 'done') {
|
|
305
|
+
if (lane.epoch === 0 && v.state !== 'pending') {
|
|
306
|
+
lane.epoch = open(lanes, v);
|
|
307
|
+
if (v.named === null)
|
|
308
|
+
lanes.dense.push(lane);
|
|
309
|
+
}
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
if (lane.group !== null)
|
|
313
|
+
return false;
|
|
314
|
+
leave(lanes, lane);
|
|
315
|
+
lanes.lanes.splice(lanes.lanes.indexOf(lane), 1);
|
|
316
|
+
const d = lanes.dense.indexOf(lane);
|
|
317
|
+
if (d >= 0)
|
|
318
|
+
lanes.dense.splice(d, 1);
|
|
319
|
+
lanes.byId.delete(v.id);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
/** Hands a voice back to the general path, with the weights `weightOf` reports kept on its records. */
|
|
323
|
+
function leave(lanes, lane) {
|
|
324
|
+
if (lane.idle)
|
|
325
|
+
wake(lanes, lane);
|
|
326
|
+
// A retired voice reports no weight, so only a motion's samples are left to settle.
|
|
327
|
+
const done = lane.voice.state === 'done';
|
|
328
|
+
if (!done || lane.motion !== undefined)
|
|
329
|
+
for (let p = 0; p < lane.list.length; p++) {
|
|
330
|
+
const rec = lane.records[p];
|
|
331
|
+
const slot = lane.list[p];
|
|
332
|
+
const w = done ? undefined : lanes.reported(lane, slot);
|
|
333
|
+
if (rec !== undefined && w !== undefined)
|
|
334
|
+
rec.weight = w;
|
|
335
|
+
if (rec !== undefined && lane.motion !== undefined)
|
|
336
|
+
settle(lanes, lane, p, slot, rec);
|
|
337
|
+
}
|
|
338
|
+
const run = lane.motion;
|
|
339
|
+
if (run !== undefined && run.watcher === lanes && run.watchId === lane.voice.id) {
|
|
340
|
+
for (let p = 0; p < lane.list.length; p++)
|
|
341
|
+
lane.fix(p);
|
|
342
|
+
run.watcher = null;
|
|
343
|
+
}
|
|
344
|
+
lane.voice.laned = false;
|
|
345
|
+
}
|
package/dist/relink.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Subject } from './voice.js';
|
|
2
|
+
/** What `patch` needs of a voice: its place in voice order. */
|
|
3
|
+
interface Ordered {
|
|
4
|
+
readonly id: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The voices over every subject behind each step of a mix's `version`, so a chain linked a few
|
|
8
|
+
* steps back can be brought up to date by those voices alone; null for a step that changed
|
|
9
|
+
* anything else, after which every chain links afresh.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Steps<V extends Ordered> {
|
|
12
|
+
private list;
|
|
13
|
+
/** The version before `list[0]`. */
|
|
14
|
+
private from;
|
|
15
|
+
/** Records the step to `version`, made by `voice`. */
|
|
16
|
+
push(voice: V | null, version: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* A chain linked at `version`, its first record `head` (null where it holds none), with each voice
|
|
19
|
+
* stepped since taken off it or put on at its place in voice order by `reach`, which gives the
|
|
20
|
+
* voice's record where a full relink would link it and null where it would not. undefined where
|
|
21
|
+
* the steps since do not say, so the chain must link afresh.
|
|
22
|
+
*/
|
|
23
|
+
patch(head: Subject<unknown> | null, version: number, reach: (voice: V) => Subject<unknown> | null): Subject<unknown> | null | undefined;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
package/dist/relink.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The voices over every subject behind each step of a mix's `version`, so a chain linked a few
|
|
3
|
+
* steps back can be brought up to date by those voices alone; null for a step that changed
|
|
4
|
+
* anything else, after which every chain links afresh.
|
|
5
|
+
*/
|
|
6
|
+
export class Steps {
|
|
7
|
+
list = [];
|
|
8
|
+
/** The version before `list[0]`. */
|
|
9
|
+
from = 0;
|
|
10
|
+
/** Records the step to `version`, made by `voice`. */
|
|
11
|
+
push(voice, version) {
|
|
12
|
+
if (this.list.length >= 64) {
|
|
13
|
+
this.list = this.list.slice(32);
|
|
14
|
+
this.from += 32;
|
|
15
|
+
}
|
|
16
|
+
if (this.from + this.list.length !== version - 1) {
|
|
17
|
+
this.list = [];
|
|
18
|
+
this.from = version - 1;
|
|
19
|
+
}
|
|
20
|
+
this.list.push(voice);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A chain linked at `version`, its first record `head` (null where it holds none), with each voice
|
|
24
|
+
* stepped since taken off it or put on at its place in voice order by `reach`, which gives the
|
|
25
|
+
* voice's record where a full relink would link it and null where it would not. undefined where
|
|
26
|
+
* the steps since do not say, so the chain must link afresh.
|
|
27
|
+
*/
|
|
28
|
+
patch(head, version, reach) {
|
|
29
|
+
if (!(version >= this.from))
|
|
30
|
+
return undefined;
|
|
31
|
+
const list = this.list;
|
|
32
|
+
for (let k = version - this.from; k < list.length; k++) {
|
|
33
|
+
const voice = list[k];
|
|
34
|
+
if (voice === null || voice === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
let prev = null;
|
|
37
|
+
let at = head;
|
|
38
|
+
while (at !== null && at.voice.id < voice.id) {
|
|
39
|
+
prev = at;
|
|
40
|
+
at = at.next;
|
|
41
|
+
}
|
|
42
|
+
const here = at !== null && at.voice === voice ? at : null;
|
|
43
|
+
const want = reach(voice);
|
|
44
|
+
if (want === here)
|
|
45
|
+
continue;
|
|
46
|
+
const after = here === null ? at : here.next;
|
|
47
|
+
if (want !== null) {
|
|
48
|
+
want.voice = voice;
|
|
49
|
+
want.next = after;
|
|
50
|
+
}
|
|
51
|
+
const link = want ?? after;
|
|
52
|
+
if (prev === null)
|
|
53
|
+
head = link;
|
|
54
|
+
else
|
|
55
|
+
prev.next = link;
|
|
56
|
+
}
|
|
57
|
+
return head;
|
|
58
|
+
}
|
|
59
|
+
}
|
package/dist/rows.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Crowd } from './crowd.js';
|
|
2
|
+
import type { Lanes } from './lanes.js';
|
|
3
|
+
import type { Voice } from './voice.js';
|
|
4
|
+
/** Takes a crowd row off its subject, to be placed again if a probe meets it again. */
|
|
5
|
+
export declare function unplace<I, O>(lanes: Lanes<I, O>, c: Crowd<I, O>, p: number): void;
|
|
6
|
+
/**
|
|
7
|
+
* Whether every voice qualifying for a crowd has its row, and every row's voice either qualified
|
|
8
|
+
* or is done, so the crowds stand as a rebuild would leave them, departed rows aside.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sameCrowds<I, O>(lanes: Lanes<I, O>, members: readonly Voice<I, O>[]): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Rebuilds the crowds from the voices that belong in one, in voice order: a voice already in a
|
|
13
|
+
* crowd keeps its row's numbers, a new one gets a row a probe will place, and a voice no longer in
|
|
14
|
+
* one is handed back to the general path, as a lane's voice is when it leaves.
|
|
15
|
+
*/
|
|
16
|
+
export declare function regroup<I, O>(lanes: Lanes<I, O>, members: Voice<I, O>[]): void;
|
|
17
|
+
/**
|
|
18
|
+
* Gives a voice the next row of its channel's crowd, made if there is none: a copy of the row it
|
|
19
|
+
* had in `from` at `at`, or a new one. `old` holds the crowds being replaced, whose idle state a
|
|
20
|
+
* new crowd on the same channel keeps. Rows stay in voice order only because voices join in it.
|
|
21
|
+
*/
|
|
22
|
+
export declare function join<I, O>(lanes: Lanes<I, O>, v: Voice<I, O>, from: Crowd<I, O> | undefined, at: number | undefined, old: readonly Crowd<I, O>[]): void;
|
|
23
|
+
/** A crowd row takes up its voice as it now stands: opened once it starts, its fields copied again. */
|
|
24
|
+
export declare function retouchRow<I, O>(lanes: Lanes<I, O>, c: Crowd<I, O>, p: number, v: Voice<I, O>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Compacts every crowd a quarter or more empty rows: each fill walks every row, and at half a
|
|
27
|
+
* crowd replacing a voice a frame carried as many empty rows as live ones.
|
|
28
|
+
*/
|
|
29
|
+
export declare function compactSparse<I, O>(lanes: Lanes<I, O>): void;
|
|
30
|
+
/** A crowd voice left: its row stays, empty, until the crowds are next rebuilt. */
|
|
31
|
+
export declare function bury<I, O>(lanes: Lanes<I, O>, c: Crowd<I, O>, p: number): void;
|
|
32
|
+
/** A crowd row's weight as `reported` reads a lane position's. */
|
|
33
|
+
export declare function reportedRow<I, O>(lanes: Lanes<I, O>, c: Crowd<I, O>, p: number, slot: number): number | undefined;
|