@msb235/blits 0.11.0 → 0.12.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 +97 -0
- package/dist/arcs.d.ts +40 -0
- package/dist/arcs.js +239 -0
- package/dist/chain.d.ts +6 -1
- package/dist/chain.js +33 -13
- package/dist/channels.d.ts +2 -0
- package/dist/channels.js +12 -1
- package/dist/clone.js +16 -4
- package/dist/cue.js +11 -2
- package/dist/departed.d.ts +5 -1
- package/dist/departed.js +71 -27
- package/dist/easing.d.ts +12 -1
- package/dist/easing.js +7 -1
- package/dist/everyone.d.ts +14 -1
- package/dist/everyone.js +27 -2
- package/dist/fade.js +3 -3
- package/dist/fill.js +16 -9
- package/dist/fit.d.ts +8 -1
- package/dist/fit.js +7 -3
- package/dist/fold.js +69 -13
- package/dist/gather.js +38 -7
- package/dist/handle.d.ts +2 -0
- package/dist/handle.js +8 -0
- package/dist/held.d.ts +5 -2
- package/dist/held.js +6 -3
- package/dist/history.d.ts +19 -2
- package/dist/history.js +149 -31
- package/dist/hosts.js +27 -11
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/lanes.d.ts +23 -7
- package/dist/lanes.js +36 -2
- package/dist/lean.js +2 -2
- package/dist/marks.js +3 -0
- package/dist/mixer.d.ts +15 -1
- package/dist/mixer.js +57 -30
- package/dist/move.js +7 -2
- package/dist/numbers.d.ts +5 -1
- package/dist/numbers.js +4 -0
- package/dist/owner.js +6 -1
- package/dist/place.d.ts +8 -0
- package/dist/place.js +16 -8
- package/dist/project.js +3 -3
- package/dist/qualify.d.ts +10 -0
- package/dist/qualify.js +52 -1
- package/dist/reading.d.ts +2 -1
- package/dist/reading.js +2 -1
- package/dist/record.d.ts +6 -0
- package/dist/record.js +1 -0
- package/dist/restored.d.ts +67 -18
- package/dist/restored.js +159 -49
- package/dist/revive.d.ts +1 -0
- package/dist/revive.js +1 -0
- package/dist/roster.js +2 -1
- package/dist/seek.js +72 -18
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/spans.d.ts +5 -0
- package/dist/spans.js +75 -5
- package/dist/store.d.ts +2 -0
- package/dist/store.js +4 -0
- package/dist/touch.js +3 -2
- package/dist/transport.js +6 -1
- package/dist/types/arc.d.ts +55 -0
- package/dist/types/arc.js +1 -0
- package/dist/types/mix.d.ts +18 -0
- package/dist/types/voice.d.ts +10 -1
- package/dist/types/watch.d.ts +126 -0
- package/dist/types/watch.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/voice.d.ts +37 -6
- package/dist/voice.js +17 -0
- package/dist/watch.d.ts +26 -0
- package/dist/watch.js +441 -0
- package/dist/weigh.d.ts +13 -2
- package/dist/weigh.js +10 -3
- package/docs/schema.html +39 -3
- package/package.json +5 -4
package/dist/held.js
CHANGED
|
@@ -17,8 +17,11 @@ export function unreachedOf(voice, now) {
|
|
|
17
17
|
voice.unreached = none;
|
|
18
18
|
return none;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* What this voice holds for this subject, made on first sight with `target` and `stagger` asked
|
|
22
|
+
* once; `guess` is a record it may be, from the subject's chain as last linked.
|
|
23
|
+
*/
|
|
24
|
+
export function held(voice, subject, slot, guess = null) {
|
|
22
25
|
if (voice.sharing) {
|
|
23
26
|
const all = everyoneOf(this, voice);
|
|
24
27
|
if (all !== null)
|
|
@@ -26,7 +29,7 @@ export function held(voice, subject, slot) {
|
|
|
26
29
|
}
|
|
27
30
|
if (unreached(voice, slot))
|
|
28
31
|
return voice.unreached;
|
|
29
|
-
let held = voice.subjects.get(subject);
|
|
32
|
+
let held = (guess === null ? voice.subjects.get(subject) : voice.subjects.find(subject, guess));
|
|
30
33
|
if (held !== undefined)
|
|
31
34
|
return held;
|
|
32
35
|
const now = this.now;
|
package/dist/history.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { type Subject, type Voice } from './voice.js';
|
|
|
5
5
|
export declare function last<T extends {
|
|
6
6
|
at: number;
|
|
7
7
|
}>(list: readonly T[], t: number, inclusive: boolean | ((e: T) => boolean)): T | undefined;
|
|
8
|
+
/** Where `last` finds its entry in the list, -1 where it finds none. */
|
|
9
|
+
export declare function lastIndex<T extends {
|
|
10
|
+
at: number;
|
|
11
|
+
}>(list: readonly T[], t: number, inclusive: boolean | ((e: T) => boolean)): number;
|
|
8
12
|
/**
|
|
9
13
|
* The last entry of a list kept in the order it was made that falls inside `cut`, an entry made
|
|
10
14
|
* before its frame was read counting as `early`.
|
|
@@ -21,8 +25,21 @@ export declare function elapsedThen<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>,
|
|
|
21
25
|
export declare function noted<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>): void;
|
|
22
26
|
/** A copy of the state a voice's patch keeps for a subject. */
|
|
23
27
|
export declare function copyState<I, O>(voice: Voice<I, O>, state: unknown): unknown;
|
|
24
|
-
/**
|
|
25
|
-
|
|
28
|
+
/**
|
|
29
|
+
* A copy of a subject's record that shares nothing a read could change; with `shared`, its state is
|
|
30
|
+
* the record's own, for a copy that takes one of its own before it steps.
|
|
31
|
+
*/
|
|
32
|
+
export declare function copyHeld<I, O>(voice: Voice<I, O>, h: Subject<unknown>, shared?: boolean): Subject<unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* As `copyHeld`, written over `into`, a record nothing else holds, so a copy made in its place
|
|
35
|
+
* leaves nothing new to collect. test/history.test.ts holds this to `copyHeld` field by field.
|
|
36
|
+
*/
|
|
37
|
+
export declare function copyHeldInto<I, O>(voice: Voice<I, O>, h: Subject<unknown>, into: Subject<unknown>, shared?: boolean): Subject<unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Lets go of what a record holds from its last probe and its place in a chain, as a copy of it
|
|
40
|
+
* starts: a seek puts back a record nothing else holds this way, in place of a copy.
|
|
41
|
+
*/
|
|
42
|
+
export declare function unread(h: Subject<unknown>): Subject<unknown>;
|
|
26
43
|
/** Under `history` with `inputs`, copies the host fields any voice reads, once a frame, when they changed. */
|
|
27
44
|
export declare function recordHost<I, O>(mix: Mixer<I, O>, now: number): void;
|
|
28
45
|
/** Under `history` with `inputs`, keeps what an input signal read, each time it changes. */
|
package/dist/history.js
CHANGED
|
@@ -9,14 +9,18 @@ import { within } from './transport.js';
|
|
|
9
9
|
import { none } from './voice.js';
|
|
10
10
|
/** The last entry taken before `t`, or at it where `inclusive` says so. */
|
|
11
11
|
export function last(list, t, inclusive) {
|
|
12
|
+
return list[lastIndex(list, t, inclusive)];
|
|
13
|
+
}
|
|
14
|
+
/** Where `last` finds its entry in the list, -1 where it finds none. */
|
|
15
|
+
export function lastIndex(list, t, inclusive) {
|
|
12
16
|
let lo = 0;
|
|
13
17
|
let hi = list.length - 1;
|
|
14
|
-
let found;
|
|
18
|
+
let found = -1;
|
|
15
19
|
while (lo <= hi) {
|
|
16
20
|
const mid = (lo + hi) >> 1;
|
|
17
21
|
const e = list[mid];
|
|
18
22
|
if (e.at < t || (e.at === t && (typeof inclusive === 'function' ? inclusive(e) : inclusive))) {
|
|
19
|
-
found =
|
|
23
|
+
found = mid;
|
|
20
24
|
lo = mid + 1;
|
|
21
25
|
}
|
|
22
26
|
else
|
|
@@ -86,45 +90,125 @@ export function noted(mix, voice) {
|
|
|
86
90
|
pageOut(mix, 'controls', voice.id, undefined, log.splice(0, drop), copied, reach);
|
|
87
91
|
}
|
|
88
92
|
const copied = (e) => ({ ...e });
|
|
93
|
+
const nothing = Object.freeze([]);
|
|
89
94
|
/** Takes off the front of a list kept by time what a read back to `reach` no longer needs. */
|
|
90
95
|
function older(list, reach) {
|
|
96
|
+
const n = past(list, reach);
|
|
97
|
+
return n === 0 ? nothing : list.splice(0, n);
|
|
98
|
+
}
|
|
99
|
+
/** How many entries at the front of a list kept by time a read back to `reach` no longer needs. */
|
|
100
|
+
function past(list, reach) {
|
|
91
101
|
let n = 0;
|
|
92
102
|
while (n + 1 < list.length && list[n + 1].at <= reach)
|
|
93
103
|
n++;
|
|
94
|
-
return n
|
|
104
|
+
return n;
|
|
95
105
|
}
|
|
96
106
|
/** A copy of the state a voice's patch keeps for a subject. */
|
|
97
107
|
export function copyState(voice, state) {
|
|
98
108
|
const patch = voice.patch;
|
|
99
109
|
return patch.clone ? patch.clone(state) : clone(state);
|
|
100
110
|
}
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
/**
|
|
112
|
+
* A copy of a subject's record that shares nothing a read could change; with `shared`, its state is
|
|
113
|
+
* the record's own, for a copy that takes one of its own before it steps.
|
|
114
|
+
*/
|
|
115
|
+
export function copyHeld(voice, h, shared = false) {
|
|
116
|
+
const kept = keptOf(h);
|
|
117
|
+
// Every field in `record`'s order, written out: a spread with fields after it costs a third more.
|
|
118
|
+
return unread({
|
|
119
|
+
reaches: h.reaches,
|
|
120
|
+
delay: h.delay,
|
|
121
|
+
since: h.since,
|
|
122
|
+
shown: h.shown,
|
|
123
|
+
weight: h.weight,
|
|
124
|
+
rested: h.rested,
|
|
111
125
|
bands: h.bands === null ? null : h.bands.slice(),
|
|
112
|
-
state: h.state === undefined ?
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
probed:
|
|
116
|
-
delta:
|
|
117
|
-
phase:
|
|
118
|
-
seeks:
|
|
126
|
+
state: h.state === undefined || shared ? h.state : copyState(voice, h.state),
|
|
127
|
+
stepped: h.stepped,
|
|
128
|
+
ticks: h.ticks,
|
|
129
|
+
probed: h.probed,
|
|
130
|
+
delta: h.delta,
|
|
131
|
+
phase: h.phase,
|
|
132
|
+
seeks: h.seeks,
|
|
133
|
+
rebuilt: h.rebuilt,
|
|
134
|
+
copied: h.copied,
|
|
119
135
|
base: h.base === undefined ? undefined : clone(h.base),
|
|
120
136
|
slope: h.slope === undefined ? undefined : clone(h.slope),
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
kept,
|
|
138
|
+
unkept: h.unkept?.map(clone),
|
|
139
|
+
snaps: h.snaps,
|
|
140
|
+
from: h.from,
|
|
141
|
+
unknown: h.unknown,
|
|
142
|
+
inputs: h.inputs,
|
|
143
|
+
replay: h.replay,
|
|
144
|
+
voice: h.voice,
|
|
145
|
+
next: h.next,
|
|
146
|
+
version: h.version,
|
|
147
|
+
loci: h.loci,
|
|
148
|
+
slot: h.slot,
|
|
149
|
+
run: h.run,
|
|
150
|
+
freed: h.freed,
|
|
151
|
+
// In no store, so stamped by none: a store trusts its stamp to say a record is still its own.
|
|
152
|
+
restored: null,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** A copy of what a record keeps through `setting.keep`. */
|
|
156
|
+
function keptOf(h) {
|
|
157
|
+
if (h.kept === null)
|
|
158
|
+
return null;
|
|
159
|
+
const kept = new Map();
|
|
160
|
+
for (const [owner, value] of h.kept)
|
|
161
|
+
kept.set(owner, clone(value));
|
|
162
|
+
return kept;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* As `copyHeld`, written over `into`, a record nothing else holds, so a copy made in its place
|
|
166
|
+
* leaves nothing new to collect. test/history.test.ts holds this to `copyHeld` field by field.
|
|
167
|
+
*/
|
|
168
|
+
export function copyHeldInto(voice, h, into, shared = false) {
|
|
169
|
+
into.reaches = h.reaches;
|
|
170
|
+
into.delay = h.delay;
|
|
171
|
+
into.since = h.since;
|
|
172
|
+
into.shown = h.shown;
|
|
173
|
+
into.weight = h.weight;
|
|
174
|
+
into.rested = h.rested;
|
|
175
|
+
into.bands = h.bands === null ? null : h.bands.slice();
|
|
176
|
+
into.state = h.state === undefined || shared ? h.state : copyState(voice, h.state);
|
|
177
|
+
into.stepped = h.stepped;
|
|
178
|
+
into.ticks = h.ticks;
|
|
179
|
+
into.rebuilt = h.rebuilt;
|
|
180
|
+
into.copied = h.copied;
|
|
181
|
+
into.base = h.base === undefined ? undefined : clone(h.base);
|
|
182
|
+
into.slope = h.slope === undefined ? undefined : clone(h.slope);
|
|
183
|
+
into.kept = keptOf(h);
|
|
184
|
+
into.unkept = h.unkept?.map(clone);
|
|
185
|
+
into.from = h.from;
|
|
186
|
+
into.unknown = h.unknown;
|
|
187
|
+
into.replay = h.replay;
|
|
188
|
+
into.voice = h.voice;
|
|
189
|
+
into.slot = h.slot;
|
|
190
|
+
into.run = h.run;
|
|
191
|
+
into.freed = h.freed;
|
|
192
|
+
into.restored = null;
|
|
193
|
+
// `probed`, `delta`, `phase`, `seeks`, `snaps`, `inputs`, `next`, `version` and `loci` as
|
|
194
|
+
// `unread` leaves them.
|
|
195
|
+
return unread(into);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Lets go of what a record holds from its last probe and its place in a chain, as a copy of it
|
|
199
|
+
* starts: a seek puts back a record nothing else holds this way, in place of a copy.
|
|
200
|
+
*/
|
|
201
|
+
export function unread(h) {
|
|
202
|
+
h.probed = Number.NaN;
|
|
203
|
+
h.delta = null;
|
|
204
|
+
h.phase = 0;
|
|
205
|
+
h.seeks = 0;
|
|
206
|
+
h.snaps = undefined;
|
|
207
|
+
h.inputs = undefined;
|
|
208
|
+
h.next = null;
|
|
209
|
+
h.version = Number.NaN;
|
|
210
|
+
h.loci = null;
|
|
211
|
+
return h;
|
|
128
212
|
}
|
|
129
213
|
/** Under `history` with `inputs`, copies the host fields any voice reads, once a frame, when they changed. */
|
|
130
214
|
export function recordHost(mix, now) {
|
|
@@ -168,6 +252,19 @@ export function record(mix, voice, subject, held, value) {
|
|
|
168
252
|
export function apart(history) {
|
|
169
253
|
return history.every ?? 200;
|
|
170
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* A copy of a record to keep now, written over a spare the latest seek back cut, or one just let go
|
|
257
|
+
* of, where it has one.
|
|
258
|
+
*/
|
|
259
|
+
function snapOf(mix, voice, held, gone) {
|
|
260
|
+
const spare = voice.spares?.pop() ?? gone;
|
|
261
|
+
if (spare === undefined)
|
|
262
|
+
return { at: mix.now, seq: mix.transport.seq, held: copyHeld(voice, held) };
|
|
263
|
+
spare.at = mix.now;
|
|
264
|
+
spare.seq = mix.transport.seq;
|
|
265
|
+
copyHeldInto(voice, held, spare.held);
|
|
266
|
+
return spare;
|
|
267
|
+
}
|
|
171
268
|
/** Under `history`, keeps a copy of a stateful voice's record for this subject every so often. */
|
|
172
269
|
export function remember(mix, voice, subject, held) {
|
|
173
270
|
const history = mix.opts.history;
|
|
@@ -182,11 +279,32 @@ export function remember(mix, voice, subject, held) {
|
|
|
182
279
|
const prev = snaps[snaps.length - 1];
|
|
183
280
|
if (prev !== undefined && mix.now - prev.at < apart(history))
|
|
184
281
|
return;
|
|
185
|
-
|
|
282
|
+
// What falls out is the same before the push as after it, since the copy pushed is newer.
|
|
186
283
|
const reach = mix.now - history.ms;
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
284
|
+
const n = past(snaps, reach);
|
|
285
|
+
let gone;
|
|
286
|
+
if (n > 0) {
|
|
287
|
+
if (mix.transport.pager !== null)
|
|
288
|
+
pageOut(mix, 'snap', voice.id, mix.keys?.key(subject), snaps.slice(0, n), (e) => packHeld(voice, e.held), reach);
|
|
289
|
+
gone = reusable(snaps, n);
|
|
290
|
+
// `shift` moves the list's start where `splice` copies what it keeps and allocates what it cuts.
|
|
291
|
+
if (n === 1)
|
|
292
|
+
snaps.shift();
|
|
293
|
+
else
|
|
294
|
+
snaps.splice(0, n);
|
|
295
|
+
}
|
|
296
|
+
snaps.push(snapOf(mix, voice, held, gone));
|
|
297
|
+
}
|
|
298
|
+
/** The first of a list's first `n` copies no other record's list holds, made ready to be written over. */
|
|
299
|
+
function reusable(snaps, n) {
|
|
300
|
+
for (let i = 0; i < n; i++) {
|
|
301
|
+
const snap = snaps[i];
|
|
302
|
+
if (snap.shared)
|
|
303
|
+
continue;
|
|
304
|
+
snap.ahead = undefined;
|
|
305
|
+
return snap;
|
|
306
|
+
}
|
|
307
|
+
return undefined;
|
|
190
308
|
}
|
|
191
309
|
/**
|
|
192
310
|
* Under history, keeps the record a subject had when it left a voice, faded out of it or dropped,
|
package/dist/hosts.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { release } from './arcs.js';
|
|
1
2
|
import { Book } from './book.js';
|
|
3
|
+
import { headOf } from './chain.js';
|
|
2
4
|
import { numericOf } from './channels.js';
|
|
3
5
|
import { stepOf } from './clock.js';
|
|
4
6
|
import { crowdable } from './crowd.js';
|
|
@@ -71,31 +73,44 @@ export function laneHost(mix) {
|
|
|
71
73
|
},
|
|
72
74
|
passes: (was, w) => mix.passes(was, w),
|
|
73
75
|
naming: (subject) => (mix.naming === 0 ? undefined : mix.named.get(subject)),
|
|
74
|
-
slotOf: (subject) => mix
|
|
76
|
+
slotOf: (subject) => headOf(mix, subject)?.slot ?? -1,
|
|
75
77
|
envelope: (voice, since) => voice.owner === null
|
|
76
78
|
? mix.envelope(voice, mix.now, since)
|
|
77
79
|
: mix.envelope(voice, mix.now, since) * mix.ownedBy(voice, undefined),
|
|
78
80
|
parting: (voice, subject) => mix.parting(voice, subject, mix.now),
|
|
79
|
-
ready: (voice, subject, held
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
ready: (voice, subject, held) => {
|
|
82
|
+
const arg = mix.lanes.arg;
|
|
83
|
+
const primed = mix.primed;
|
|
84
|
+
primed[0 /* Primed.NOW */] = mix.now;
|
|
85
|
+
primed[1 /* Primed.ELAPSED */] = arg[1 /* Arg.ELAPSED */];
|
|
86
|
+
primed[2 /* Primed.PASS */] = arg[3 /* Arg.PASS */];
|
|
87
|
+
mix.prime(voice, held);
|
|
88
|
+
voice.setting.weight = arg[0 /* Arg.WEIGHT */];
|
|
82
89
|
mix.sending.voice = voice;
|
|
83
90
|
mix.sending.subject = subject;
|
|
84
91
|
},
|
|
85
|
-
signal: (voice, subject, held,
|
|
92
|
+
signal: (voice, subject, held, slot) => {
|
|
93
|
+
const arg = mix.lanes.arg;
|
|
86
94
|
// A blend member after the first takes the read already made, with no setting to fill.
|
|
87
95
|
const read = voice.blend === null ? Number.NaN : mix.laneRead(voice, slot);
|
|
88
|
-
if (!Number.isNaN(read))
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
if (!Number.isNaN(read)) {
|
|
97
|
+
arg[0 /* Arg.WEIGHT */] = read;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const primed = mix.primed;
|
|
101
|
+
primed[0 /* Primed.NOW */] = mix.now;
|
|
102
|
+
primed[1 /* Primed.ELAPSED */] = arg[1 /* Arg.ELAPSED */];
|
|
103
|
+
primed[2 /* Primed.PASS */] = arg[3 /* Arg.PASS */];
|
|
104
|
+
mix.prime(voice, held);
|
|
91
105
|
const kept = reading.kept;
|
|
92
106
|
const base = mix.base(voice, subject, mix.now, held, slot);
|
|
93
107
|
if (reading.kept !== kept && !voice.keeping)
|
|
94
108
|
stateful(mix, voice);
|
|
95
|
-
|
|
109
|
+
arg[0 /* Arg.WEIGHT */] = base;
|
|
96
110
|
},
|
|
97
111
|
horizon: (voice, delay) => {
|
|
98
|
-
|
|
112
|
+
if (voice.motion !== undefined)
|
|
113
|
+
reading.horizon = mix.horizonFor(voice, delay, mix.now);
|
|
99
114
|
},
|
|
100
115
|
keeps: mix.opts.history !== undefined,
|
|
101
116
|
after: (voice, subject, held) => remember(mix, voice, subject, held),
|
|
@@ -224,12 +239,13 @@ function handleHost(mix) {
|
|
|
224
239
|
beginFade(mix, voice, (opts ?? {}));
|
|
225
240
|
},
|
|
226
241
|
rise: (voice, opts) => beginRise(mix, voice, opts ?? {}),
|
|
242
|
+
release: (voice) => release(mix, voice),
|
|
227
243
|
weightOf: (voice, subject) => {
|
|
228
244
|
if (voice.state === 'done')
|
|
229
245
|
return 0;
|
|
230
246
|
if (voice.holding !== null)
|
|
231
247
|
return ownWeight(voice, mix.now, mix.reducedNow, (o) => mix.ownerBase(o, subject));
|
|
232
|
-
const slot = mix.lanes === null ? -1 : (mix
|
|
248
|
+
const slot = mix.lanes === null ? -1 : (headOf(mix, subject)?.slot ?? -1);
|
|
233
249
|
if (voice.laned && mix.lanes !== null) {
|
|
234
250
|
const w = mix.lanes.weightOf(voice.id, slot);
|
|
235
251
|
if (w !== undefined)
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export type { NumberOptions, Vec } from './channels.js';
|
|
|
2
2
|
export { kit, last, max, mul, sum, vec } from './channels.js';
|
|
3
3
|
export type { ColorOptions } from './color.js';
|
|
4
4
|
export { color, css, mixHex, oklab, toHex } from './color.js';
|
|
5
|
+
export type { Curve } from './easing.js';
|
|
6
|
+
export { curve } from './easing.js';
|
|
5
7
|
export type { Claim, Fit, FitPlan, Order, SpanClaim, Strength } from './fit.js';
|
|
6
8
|
export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
|
|
7
9
|
export { mix, mixer } from './mixer.js';
|
|
@@ -17,6 +19,7 @@ export { fold } from './stack.js';
|
|
|
17
19
|
export type { Ticked, Ticker, TickerOptions } from './ticker.js';
|
|
18
20
|
export { ticker } from './ticker.js';
|
|
19
21
|
export { transport } from './transport.js';
|
|
20
|
-
export type { Anchor, BlendMember, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Keyframe, Kit, Mark, Marked, MarkName, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Projection, Query, SeekOptions, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, Tape, TapeBranch, TapeMaker, TapeOp, Transport, TransportOptions, TransportProjection, VoiceSpec, } from './types.js';
|
|
22
|
+
export type { Anchor, ArcHandle, ArcPart, ArcSpec, ArcVoice, BlendMember, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Hold, Keyframe, Kit, Mark, Marked, MarkName, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Projection, Query, Registry, SeekOptions, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, Tape, TapeBranch, TapeMaker, TapeOp, Transport, TransportOptions, TransportProjection, VoiceSpec, Watched, WatchedFrame, } from './types.js';
|
|
23
|
+
export { watched } from './watch.js';
|
|
21
24
|
export type { WaveOptions, WaveShape } from './wave.js';
|
|
22
25
|
export { wave, waveAt } from './wave.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { kit, last, max, mul, sum, vec } from './channels.js';
|
|
2
2
|
export { color, css, mixHex, oklab, toHex } from './color.js';
|
|
3
|
+
export { curve } from './easing.js';
|
|
3
4
|
export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
|
|
4
5
|
export { mix, mixer } from './mixer.js';
|
|
5
6
|
export { glide, spring, tween } from './motion.js';
|
|
@@ -10,4 +11,5 @@ export { gate, input, lag, level, peak, share, slew } from './signals.js';
|
|
|
10
11
|
export { fold } from './stack.js';
|
|
11
12
|
export { ticker } from './ticker.js';
|
|
12
13
|
export { transport } from './transport.js';
|
|
14
|
+
export { watched } from './watch.js';
|
|
13
15
|
export { wave, waveAt } from './wave.js';
|
package/dist/lanes.d.ts
CHANGED
|
@@ -33,14 +33,18 @@ export interface LaneHost<I, O> {
|
|
|
33
33
|
envelope(voice: Voice<I, O>, since: number): number;
|
|
34
34
|
/** What a subject's own ramp out of the voice leaves of its weight, 0..1. */
|
|
35
35
|
parting(voice: Voice<I, O>, subject: I): number;
|
|
36
|
-
/** Fills the voice's setting and the send target for a call to its patch. */
|
|
37
|
-
ready(voice: Voice<I, O>, subject: I, held: Subject<unknown>, elapsed: number, pass: number, weight: number): void;
|
|
38
36
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* stateful if the signal keeps state. `slot` is the subject's number.
|
|
37
|
+
* Fills the voice's setting and the send target for a call to its patch, at the voice time, pass
|
|
38
|
+
* and weight in `Arg.ELAPSED`, `Arg.PASS` and `Arg.WEIGHT`.
|
|
42
39
|
*/
|
|
43
|
-
|
|
40
|
+
ready(voice: Voice<I, O>, subject: I, held: Subject<unknown>): void;
|
|
41
|
+
/**
|
|
42
|
+
* A voice with a signal weight: the signal's value for a subject the general path has met, at the
|
|
43
|
+
* voice time and pass in `Arg.ELAPSED` and `Arg.PASS`, left in `Arg.WEIGHT` before the fade and
|
|
44
|
+
* ramp `weighed` applies, with the setting filled in for the call; makes the voice stateful if the
|
|
45
|
+
* signal keeps state. `slot` is the subject's number.
|
|
46
|
+
*/
|
|
47
|
+
signal(voice: Voice<I, O>, subject: I, held: Subject<unknown>, slot: number): void;
|
|
44
48
|
/** Whether a rest-less channel's contribution passes the band, as the general path decides it. */
|
|
45
49
|
passes(was: boolean | undefined, w: number): boolean;
|
|
46
50
|
/** Sets `reading.horizon` for the voice and a subject delayed `delay` voice ms. */
|
|
@@ -176,7 +180,12 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
176
180
|
lately: number;
|
|
177
181
|
filledVersion: number;
|
|
178
182
|
qualifiedVersion: number;
|
|
179
|
-
|
|
183
|
+
/**
|
|
184
|
+
* With `from`, the lanes a seek replaces: their subject numbers carry over, each met as newly
|
|
185
|
+
* numbered when its chain is next linked (`renew`), so a seek makes no new weak reference per
|
|
186
|
+
* subject.
|
|
187
|
+
*/
|
|
188
|
+
constructor(host: LaneHost<I, O>, from?: Lanes<I, O>);
|
|
180
189
|
/** A change that may move a voice on or off its lane: qualify again, then fill again. */
|
|
181
190
|
invalidate(): void;
|
|
182
191
|
/** A change to a laned voice's weight or clock between probes: fill again. */
|
|
@@ -189,6 +198,13 @@ export declare class Lanes<I, O> implements Watcher {
|
|
|
189
198
|
/** Numbers a subject the mix sees for the first time. */
|
|
190
199
|
number(subject: I): number;
|
|
191
200
|
release(slot: number): void;
|
|
201
|
+
/** Whether `slot` is a number a seek carried over whose chain has not been linked since. */
|
|
202
|
+
sought(slot: number): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* A chain is being linked for the subject at `slot`: one a seek carried over is from now on as
|
|
205
|
+
* `number` leaves a subject it numbers. Says whether it was one.
|
|
206
|
+
*/
|
|
207
|
+
renew(slot: number): boolean;
|
|
192
208
|
/** A voice faded a subject out of itself alone: its lane lets the subject's position go. */
|
|
193
209
|
part(id: number, slot: number): void;
|
|
194
210
|
/** A voice brought a subject back: its next probe meets every lane again, as it met them first. */
|
package/dist/lanes.js
CHANGED
|
@@ -134,9 +134,27 @@ export class Lanes {
|
|
|
134
134
|
lately = -1;
|
|
135
135
|
filledVersion = Number.NaN;
|
|
136
136
|
qualifiedVersion = Number.NaN;
|
|
137
|
-
|
|
137
|
+
/**
|
|
138
|
+
* With `from`, the lanes a seek replaces: their subject numbers carry over, each met as newly
|
|
139
|
+
* numbered when its chain is next linked (`renew`), so a seek makes no new weak reference per
|
|
140
|
+
* subject.
|
|
141
|
+
*/
|
|
142
|
+
constructor(host, from) {
|
|
138
143
|
this.host = host;
|
|
139
|
-
|
|
144
|
+
if (from === undefined) {
|
|
145
|
+
this.numbers = new Numbers(this);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.numbers = from.numbers;
|
|
149
|
+
this.numbers.adopt(this);
|
|
150
|
+
this.live = from.live;
|
|
151
|
+
const size = this.numbers.size;
|
|
152
|
+
this.grow(size);
|
|
153
|
+
const per = this.per;
|
|
154
|
+
for (let slot = 0; slot < size; slot++) {
|
|
155
|
+
per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = Number.NaN;
|
|
156
|
+
per[slot * 6 /* Per.SLOT */ + 1 /* Per.SEEN */] = -1;
|
|
157
|
+
}
|
|
140
158
|
}
|
|
141
159
|
/** A change that may move a voice on or off its lane: qualify again, then fill again. */
|
|
142
160
|
invalidate() {
|
|
@@ -173,6 +191,22 @@ export class Lanes {
|
|
|
173
191
|
release(slot) {
|
|
174
192
|
this.numbers.release(slot);
|
|
175
193
|
}
|
|
194
|
+
/** Whether `slot` is a number a seek carried over whose chain has not been linked since. */
|
|
195
|
+
sought(slot) {
|
|
196
|
+
return Number.isNaN(this.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */]);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* A chain is being linked for the subject at `slot`: one a seek carried over is from now on as
|
|
200
|
+
* `number` leaves a subject it numbers. Says whether it was one.
|
|
201
|
+
*/
|
|
202
|
+
renew(slot) {
|
|
203
|
+
if (!this.sought(slot))
|
|
204
|
+
return false;
|
|
205
|
+
this.per[slot * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = this.fills;
|
|
206
|
+
if (this.filling)
|
|
207
|
+
this.late.push(slot);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
176
210
|
/** A voice faded a subject out of itself alone: its lane lets the subject's position go. */
|
|
177
211
|
part(id, slot) {
|
|
178
212
|
const lane = this.byId.get(id);
|
package/dist/lean.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { made } from './mixer.js';
|
|
2
2
|
export * from './index.js';
|
|
3
3
|
/**
|
|
4
4
|
* The stock engine without lanes: every voice folds subject by subject, to the same values, and a
|
|
@@ -10,7 +10,7 @@ export const lean = {
|
|
|
10
10
|
name: 'lean',
|
|
11
11
|
runs: new Set(['fn', 'keys', 'motion']),
|
|
12
12
|
create(kit, opts) {
|
|
13
|
-
return
|
|
13
|
+
return made(kit, opts, null);
|
|
14
14
|
},
|
|
15
15
|
};
|
|
16
16
|
/**
|
package/dist/marks.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arcMark } from './arcs.js';
|
|
1
2
|
import { inPass } from './book.js';
|
|
2
3
|
import { startOf } from './place.js';
|
|
3
4
|
import { none } from './voice.js';
|
|
@@ -105,6 +106,8 @@ export function markOf(mix, voice, mark) {
|
|
|
105
106
|
return undefined;
|
|
106
107
|
if (mark === 'start')
|
|
107
108
|
return start;
|
|
109
|
+
if (voice.arc !== null && (mark === 'in' || mark === 'coast' || mark === 'out' || mark === 'end'))
|
|
110
|
+
return arcMark(voice, start, mark);
|
|
108
111
|
if (mark === 'in')
|
|
109
112
|
return start + (voice.fade.in ?? 0);
|
|
110
113
|
if (mark === 'coast')
|
package/dist/mixer.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Arc } from './arcs.js';
|
|
1
2
|
import { blended, laneRead, shareKept } from './blend.js';
|
|
2
3
|
import type { Book } from './book.js';
|
|
3
4
|
import { aims, chain, linkable, relink } from './chain.js';
|
|
@@ -22,7 +23,7 @@ import { ScoreIndex } from './scored.js';
|
|
|
22
23
|
import { Fitting } from './spans.js';
|
|
23
24
|
import { Store } from './store.js';
|
|
24
25
|
import { type Announced, Transport } from './transport.js';
|
|
25
|
-
import type { BlendMember, Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, VoiceSpec } from './types.js';
|
|
26
|
+
import type { ArcHandle, ArcSpec, BlendMember, Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, VoiceSpec } from './types.js';
|
|
26
27
|
import { type Subject, type Voice } from './voice.js';
|
|
27
28
|
import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, weigh, weighOwned } from './weigh.js';
|
|
28
29
|
/**
|
|
@@ -127,6 +128,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
127
128
|
*/
|
|
128
129
|
chains: Store<I, Subject<unknown>>;
|
|
129
130
|
version: number;
|
|
131
|
+
/** What `prime` reads its clock, voice time and pass from: see `Primed`. */
|
|
132
|
+
readonly primed: Float64Array<ArrayBuffer>;
|
|
130
133
|
readonly steps: Steps<Voice<I, O>, I>;
|
|
131
134
|
/** Per subject, the voices whose `subjects` name it, in voice order. */
|
|
132
135
|
named: Store<I, Voice<I, O>[]>;
|
|
@@ -179,6 +182,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
179
182
|
readonly bounded: number[];
|
|
180
183
|
/** By slot, a stock array channel's in-place `lerp`. */
|
|
181
184
|
readonly lerpsInto: (LerpInto | undefined)[];
|
|
185
|
+
/** By slot, a stock scalar channel's arithmetic, which a fold applies without calling the channel. */
|
|
186
|
+
readonly scalars: ('sum' | 'mul' | 'max' | undefined)[];
|
|
182
187
|
/** What a fold with a locus in play gathers into, one per depth of folds under way. */
|
|
183
188
|
readonly locusScratch: LocusScratch<I, O>[];
|
|
184
189
|
locusDepth: number;
|
|
@@ -189,6 +194,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
189
194
|
bookers: Book<I, O>[] | null;
|
|
190
195
|
/** The fit a span being cued takes, which `cue` hands its voice. */
|
|
191
196
|
fitting: Fitting | null;
|
|
197
|
+
/** What an arc being cued keeps, which `cue` hands its voice. */
|
|
198
|
+
arcing: Arc | null;
|
|
192
199
|
/** Every owner still in the mix, null until one is cued. */
|
|
193
200
|
owners: Voice<I, O>[] | null;
|
|
194
201
|
/** With a history store, the key each subject is paged under; null without one. */
|
|
@@ -226,6 +233,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
226
233
|
blend(members: readonly (Patch<I, O, unknown> | BlendMember<I, O>)[], by: Signal<I>, spec?: Omit<VoiceSpec<I, O>, 'patch' | 'weight' | 'locus'>): Handle<I>[];
|
|
227
234
|
owns(spec: OwnerSpec<I>): Handle<I>;
|
|
228
235
|
span(spec: SpanSpec<I>): SpanHandle<I>;
|
|
236
|
+
arc(spec: ArcSpec<I, O>): ArcHandle<I>;
|
|
229
237
|
sync(timestamp: number): void;
|
|
230
238
|
book<E = unknown>(opts: BookOptions<E, I>): Booker;
|
|
231
239
|
probe(subject: I, out?: O): O;
|
|
@@ -260,6 +268,10 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
260
268
|
over?: number;
|
|
261
269
|
}): void;
|
|
262
270
|
drop(subject: I): void;
|
|
271
|
+
/** Lets `voice` go of a dropped subject. Methods rather than closures, so a drop makes neither. */
|
|
272
|
+
private forgetIn;
|
|
273
|
+
private releaseIn;
|
|
274
|
+
private recordDrop;
|
|
263
275
|
touch(subject?: I): void;
|
|
264
276
|
drain<E = unknown>(tag?: string): Sent<I, E>[];
|
|
265
277
|
/** Voices whose stop 0 is mid-computation, so a fold for one cannot re-enter itself. */
|
|
@@ -328,6 +340,8 @@ declare const methods: {
|
|
|
328
340
|
type Methods = typeof methods;
|
|
329
341
|
export interface Mixer<I, O> extends Methods {
|
|
330
342
|
}
|
|
343
|
+
/** Makes a mix the host asked for, with the registry's history where it names none, and lists it there. */
|
|
344
|
+
export declare function made<I, O, H>(kit: Kit<O>, opts: MixOptions<H>, lanes: typeof Lanes | null): Mix<I, O, H>;
|
|
331
345
|
/**
|
|
332
346
|
* The one that ships: every form on the CPU, probed per subject on demand.
|
|
333
347
|
*
|