@msb235/blits 0.7.1 → 0.8.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 +193 -0
- package/README.md +22 -4
- package/dist/book.js +4 -4
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +57 -54
- package/dist/channels.d.ts +26 -5
- package/dist/channels.js +12 -8
- package/dist/clock.d.ts +8 -1
- package/dist/clock.js +14 -3
- package/dist/clone.d.ts +8 -0
- package/dist/clone.js +22 -0
- package/dist/closed.d.ts +1 -1
- package/dist/closed.js +3 -2
- package/dist/color.d.ts +0 -11
- package/dist/color.js +4 -24
- package/dist/columns.js +0 -1
- package/dist/copyout.d.ts +6 -0
- package/dist/copyout.js +38 -0
- package/dist/crowd.d.ts +7 -5
- package/dist/crowd.js +32 -18
- package/dist/cue.d.ts +3 -1
- package/dist/cue.js +31 -20
- package/dist/departed.d.ts +34 -0
- package/dist/departed.js +73 -0
- package/dist/easing.d.ts +5 -0
- package/dist/easing.js +31 -2
- package/dist/everyone.d.ts +56 -0
- package/dist/everyone.js +221 -0
- package/dist/fade.d.ts +8 -1
- package/dist/fade.js +34 -6
- package/dist/fill.d.ts +22 -13
- package/dist/fill.js +104 -41
- package/dist/fit.js +2 -1
- package/dist/fold.d.ts +37 -0
- package/dist/fold.js +216 -31
- package/dist/frames.d.ts +21 -0
- package/dist/frames.js +59 -0
- package/dist/gather.d.ts +15 -9
- package/dist/gather.js +60 -25
- package/dist/gone.js +10 -0
- package/dist/handle.d.ts +16 -5
- package/dist/handle.js +56 -6
- package/dist/held.d.ts +8 -5
- package/dist/held.js +56 -58
- package/dist/history.d.ts +17 -13
- package/dist/history.js +47 -28
- package/dist/hosts.js +55 -8
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/keyfill.d.ts +7 -0
- package/dist/keyfill.js +124 -0
- package/dist/keyrows.d.ts +1 -0
- package/dist/keyrows.js +1 -1
- package/dist/lane.d.ts +32 -2
- package/dist/lane.js +5 -0
- package/dist/lanes.d.ts +32 -8
- package/dist/lanes.js +69 -37
- package/dist/leavings.d.ts +26 -0
- package/dist/leavings.js +70 -0
- package/dist/locus.js +2 -2
- package/dist/meet.js +14 -8
- package/dist/mixer.d.ts +39 -7
- package/dist/mixer.js +50 -10
- package/dist/motion.d.ts +2 -0
- package/dist/motion.js +4 -1
- package/dist/motions.d.ts +22 -15
- package/dist/motions.js +40 -34
- package/dist/move.js +23 -16
- package/dist/moved.d.ts +11 -0
- package/dist/moved.js +29 -0
- package/dist/origin.d.ts +19 -0
- package/dist/origin.js +68 -0
- package/dist/owed.d.ts +4 -4
- package/dist/owed.js +8 -8
- package/dist/owner.d.ts +1 -1
- package/dist/owner.js +2 -2
- package/dist/pace.d.ts +13 -5
- package/dist/pace.js +21 -8
- package/dist/pack.d.ts +3 -1
- package/dist/pack.js +15 -24
- package/dist/paging.d.ts +8 -2
- package/dist/paging.js +39 -4
- package/dist/patch.d.ts +14 -9
- package/dist/patch.js +83 -21
- package/dist/place.js +18 -3
- package/dist/project.d.ts +12 -1
- package/dist/project.js +101 -83
- package/dist/pull.d.ts +2 -0
- package/dist/pull.js +8 -0
- package/dist/qualify.js +10 -2
- package/dist/reading.d.ts +5 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +9 -0
- package/dist/record.js +42 -0
- package/dist/relink.d.ts +11 -5
- package/dist/relink.js +7 -5
- package/dist/restored.d.ts +41 -0
- package/dist/restored.js +91 -0
- package/dist/revive.d.ts +10 -3
- package/dist/revive.js +31 -23
- package/dist/roster.d.ts +11 -0
- package/dist/roster.js +46 -0
- package/dist/rotation.d.ts +29 -0
- package/dist/rotation.js +91 -0
- package/dist/rows.d.ts +5 -5
- package/dist/rows.js +5 -5
- package/dist/sample.d.ts +3 -2
- package/dist/sample.js +32 -15
- package/dist/seek.js +97 -114
- package/dist/shared.d.ts +6 -0
- package/dist/shared.js +15 -0
- package/dist/signals.d.ts +24 -3
- package/dist/signals.js +50 -11
- package/dist/spans.js +7 -0
- package/dist/stack.d.ts +11 -0
- package/dist/stack.js +47 -0
- package/dist/standing.d.ts +10 -0
- package/dist/standing.js +79 -0
- package/dist/store.d.ts +3 -3
- package/dist/store.js +3 -3
- package/dist/strays.js +3 -0
- package/dist/tape.d.ts +8 -5
- package/dist/tape.js +21 -13
- package/dist/testing.d.ts +35 -0
- package/dist/testing.js +116 -0
- package/dist/touch.d.ts +6 -0
- package/dist/touch.js +28 -0
- package/dist/transport.d.ts +45 -6
- package/dist/transport.js +64 -19
- package/dist/turns.d.ts +18 -0
- package/dist/turns.js +72 -0
- package/dist/tweened.js +1 -1
- package/dist/types/channel.d.ts +46 -0
- package/dist/types/channel.js +1 -0
- package/dist/types/history.d.ts +139 -0
- package/dist/types/history.js +1 -0
- package/dist/types/mix.d.ts +336 -0
- package/dist/types/mix.js +1 -0
- package/dist/types/patch.d.ts +187 -0
- package/dist/types/patch.js +1 -0
- package/dist/types/place.d.ts +157 -0
- package/dist/types/place.js +1 -0
- package/dist/types/transport.d.ts +71 -0
- package/dist/types/transport.js +1 -0
- package/dist/types/voice.d.ts +323 -0
- package/dist/types/voice.js +1 -0
- package/dist/types.d.ts +7 -1162
- package/dist/unpage.d.ts +2 -2
- package/dist/unpage.js +53 -25
- package/dist/voice.d.ts +90 -22
- package/dist/voice.js +105 -43
- package/dist/wave.d.ts +42 -0
- package/dist/wave.js +62 -0
- package/dist/weigh.js +1 -1
- package/docs/schema.html +42 -36
- package/package.json +8 -3
package/dist/history.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { elapsedWith } from './clock.js';
|
|
2
2
|
import { clone } from './clone.js';
|
|
3
3
|
import { schedule } from './due.js';
|
|
4
|
+
import { Leavings } from './leavings.js';
|
|
4
5
|
import { packHeld } from './pack.js';
|
|
5
6
|
import { pageOut } from './paging.js';
|
|
6
7
|
import { scoreTouched } from './scored.js';
|
|
8
|
+
import { within } from './transport.js';
|
|
7
9
|
import { none } from './voice.js';
|
|
8
|
-
/**
|
|
9
|
-
* The last entry taken before `t`, or at it where `inclusive` says so, as it does for a change a
|
|
10
|
-
* sync made. A host's change between frames is taken strictly: it shows from the next frame on, as
|
|
11
|
-
* it did live.
|
|
12
|
-
*/
|
|
10
|
+
/** The last entry taken before `t`, or at it where `inclusive` says so. */
|
|
13
11
|
export function last(list, t, inclusive) {
|
|
14
12
|
let lo = 0;
|
|
15
13
|
let hi = list.length - 1;
|
|
@@ -26,8 +24,28 @@ export function last(list, t, inclusive) {
|
|
|
26
24
|
}
|
|
27
25
|
return found;
|
|
28
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* The last entry of a list kept in the order it was made that falls inside `cut`, an entry made
|
|
29
|
+
* before its frame was read counting as `early`.
|
|
30
|
+
*/
|
|
31
|
+
export function lastWithin(list, cut) {
|
|
32
|
+
let lo = 0;
|
|
33
|
+
let hi = list.length - 1;
|
|
34
|
+
let found;
|
|
35
|
+
while (lo <= hi) {
|
|
36
|
+
const mid = (lo + hi) >> 1;
|
|
37
|
+
const e = list[mid];
|
|
38
|
+
if (within(cut, e.seq, e.sync)) {
|
|
39
|
+
found = e;
|
|
40
|
+
lo = mid + 1;
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
hi = mid - 1;
|
|
44
|
+
}
|
|
45
|
+
return found;
|
|
46
|
+
}
|
|
29
47
|
/** Deep equality over plain data, for telling whether a host field changed. */
|
|
30
|
-
function same(a, b) {
|
|
48
|
+
export function same(a, b) {
|
|
31
49
|
if (a === b)
|
|
32
50
|
return true;
|
|
33
51
|
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
|
|
@@ -59,7 +77,7 @@ export function noted(mix, voice) {
|
|
|
59
77
|
const log = voice.log;
|
|
60
78
|
if (log === null)
|
|
61
79
|
return;
|
|
62
|
-
voice.note(Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now, mix.moving);
|
|
80
|
+
voice.note(Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now, mix.transport.seq, mix.moving || !mix.looked);
|
|
63
81
|
const reach = mix.now - mix.opts.history.ms;
|
|
64
82
|
let drop = 0;
|
|
65
83
|
while (drop + 1 < log.length && log[drop + 1].at <= reach)
|
|
@@ -108,7 +126,8 @@ export function copyHeld(voice, h) {
|
|
|
108
126
|
export function recordHost(mix, now) {
|
|
109
127
|
const log = mix.hostLog;
|
|
110
128
|
const prev = log[log.length - 1];
|
|
111
|
-
|
|
129
|
+
const seq = mix.transport.seq;
|
|
130
|
+
if (prev !== undefined && prev.seq === seq)
|
|
112
131
|
return;
|
|
113
132
|
const host = mix.opts.host;
|
|
114
133
|
const fields = {};
|
|
@@ -118,7 +137,7 @@ export function recordHost(mix, now) {
|
|
|
118
137
|
fields[f] = clone(host[f]);
|
|
119
138
|
if (prev !== undefined && same(prev.fields, fields))
|
|
120
139
|
return;
|
|
121
|
-
log.push({ at: now, fields });
|
|
140
|
+
log.push({ at: now, seq, fields });
|
|
122
141
|
const reach = now - mix.opts.history.ms;
|
|
123
142
|
pageOut(mix, 'host', undefined, undefined, older(log, reach), (e) => e.fields, reach);
|
|
124
143
|
}
|
|
@@ -135,7 +154,7 @@ export function record(mix, voice, subject, held, value) {
|
|
|
135
154
|
prev.value = value;
|
|
136
155
|
return;
|
|
137
156
|
}
|
|
138
|
-
inputs.push({ at: mix.now, value });
|
|
157
|
+
inputs.push({ at: mix.now, seq: mix.transport.seq, value });
|
|
139
158
|
const reach = mix.now - history.ms;
|
|
140
159
|
const out = older(inputs, reach);
|
|
141
160
|
if (out.length > 0)
|
|
@@ -155,7 +174,7 @@ export function remember(mix, voice, subject, held) {
|
|
|
155
174
|
const prev = snaps[snaps.length - 1];
|
|
156
175
|
if (prev !== undefined && mix.now - prev.at < (history.every ?? 200))
|
|
157
176
|
return;
|
|
158
|
-
snaps.push({ at: mix.now, held: copyHeld(voice, held) });
|
|
177
|
+
snaps.push({ at: mix.now, seq: mix.transport.seq, held: copyHeld(voice, held) });
|
|
159
178
|
const reach = mix.now - history.ms;
|
|
160
179
|
const out = older(snaps, reach);
|
|
161
180
|
if (out.length > 0)
|
|
@@ -170,27 +189,27 @@ export function leave(mix, voice, subject, held) {
|
|
|
170
189
|
if (history === undefined || held === undefined)
|
|
171
190
|
return;
|
|
172
191
|
const reach = mix.now - history.ms;
|
|
173
|
-
const left =
|
|
174
|
-
for (const e of voice.left ?? [])
|
|
175
|
-
if (e.at >= reach)
|
|
176
|
-
left.push(e);
|
|
177
|
-
else if (mix.keys !== null)
|
|
178
|
-
pageOut(mix, 'left', voice.id, mix.keys.key(e.subject), [e], (x) => packHeld(voice, x.held), reach);
|
|
179
|
-
left.push({ subject, at: Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now, held });
|
|
192
|
+
const left = voice.left ?? new Leavings();
|
|
180
193
|
voice.left = left;
|
|
194
|
+
const keys = mix.keys;
|
|
195
|
+
left.expire(reach, keys === null
|
|
196
|
+
? null
|
|
197
|
+
: (e) => pageOut(mix, 'left', voice.id, keys.key(e.subject), [e], (x) => [packHeld(voice, x.held), x.sync], reach));
|
|
198
|
+
left.push({
|
|
199
|
+
subject,
|
|
200
|
+
at: Number.isNaN(mix.now) ? Number.NEGATIVE_INFINITY : mix.now,
|
|
201
|
+
seq: mix.transport.seq,
|
|
202
|
+
sync: mix.syncing,
|
|
203
|
+
held,
|
|
204
|
+
});
|
|
181
205
|
}
|
|
182
|
-
/**
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (e.at > t && Object.is(e.subject, subject) && (found === undefined || e.at < found.at))
|
|
187
|
-
found = e;
|
|
188
|
-
return found?.held;
|
|
189
|
-
}
|
|
190
|
-
/** The mix time a motion patch keeps a released subject from, and how far back, under history. */
|
|
206
|
+
/**
|
|
207
|
+
* The mix time a motion patch keeps a released subject from, how far back, and the frame, under
|
|
208
|
+
* history.
|
|
209
|
+
*/
|
|
191
210
|
export function releasing(mix) {
|
|
192
211
|
const history = mix.opts.history;
|
|
193
212
|
if (history === undefined || Number.isNaN(mix.now))
|
|
194
213
|
return [];
|
|
195
|
-
return [mix.now, mix.now - history.ms];
|
|
214
|
+
return [mix.now, mix.now - history.ms, mix.transport.seq];
|
|
196
215
|
}
|
package/dist/hosts.js
CHANGED
|
@@ -2,15 +2,18 @@ import { Book } from './book.js';
|
|
|
2
2
|
import { numericOf } from './channels.js';
|
|
3
3
|
import { crowdable } from './crowd.js';
|
|
4
4
|
import { schedule } from './due.js';
|
|
5
|
+
import { unshare, unsight, weightAt } from './everyone.js';
|
|
5
6
|
import { beginFade, beginRise, fadeAt, fadeSubject } from './fade.js';
|
|
6
7
|
import { goneIndex } from './gone.js';
|
|
7
8
|
import { VoiceHandle } from './handle.js';
|
|
9
|
+
import { reorigin } from './held.js';
|
|
8
10
|
import { noted, remember } from './history.js';
|
|
9
11
|
import { hostOf, listed, markOf, readingAt } from './marks.js';
|
|
10
12
|
import { nextFrame } from './move.js';
|
|
11
13
|
import { descendants, ownWeight, signalled } from './owner.js';
|
|
12
14
|
import { localNow } from './place.js';
|
|
13
15
|
import { reading } from './reading.js';
|
|
16
|
+
import { heard } from './signals.js';
|
|
14
17
|
import { record } from './tape.js';
|
|
15
18
|
import { unreach } from './unreached.js';
|
|
16
19
|
export function book(mix, opts) {
|
|
@@ -52,12 +55,15 @@ export function laneHost(mix) {
|
|
|
52
55
|
for (let held = head; held !== null; held = held.next)
|
|
53
56
|
if (held.voice === voice)
|
|
54
57
|
return held;
|
|
55
|
-
return mix.held(voice, subject,
|
|
58
|
+
return mix.held(voice, subject, slot);
|
|
56
59
|
},
|
|
57
60
|
forgot: (slot) => {
|
|
58
61
|
// Only a voice over every subject keeps bits, and one gone may still be read back.
|
|
59
|
-
for (const voice of mix.general)
|
|
62
|
+
for (const voice of mix.general) {
|
|
60
63
|
unreach(voice, slot, false);
|
|
64
|
+
if (voice.sighted !== null)
|
|
65
|
+
unsight(voice, slot);
|
|
66
|
+
}
|
|
61
67
|
for (const voice of goneIndex(mix).general)
|
|
62
68
|
unreach(voice, slot, false);
|
|
63
69
|
},
|
|
@@ -110,8 +116,13 @@ export function fits(mix, voice) {
|
|
|
110
116
|
// A fill weighs a voice's owners once for every subject, which a signal on one would not be.
|
|
111
117
|
if (voice.owner !== null && signalled(voice))
|
|
112
118
|
return false;
|
|
113
|
-
//
|
|
114
|
-
|
|
119
|
+
// What can change between two probes without `Lanes.current` hearing of it keeps a voice off:
|
|
120
|
+
// an input signal that does not report its changes, one that history records per probe, and a
|
|
121
|
+
// host field.
|
|
122
|
+
const w = spec.weight;
|
|
123
|
+
if (typeof w === 'function' && w.input && (mix.opts.history?.inputs || !heard(w)))
|
|
124
|
+
return false;
|
|
125
|
+
if (patch.reads !== undefined && patch.reads.length > 0)
|
|
115
126
|
return false;
|
|
116
127
|
// A locus on lanes gathers keys and fn members; a motion member keeps it on the general path.
|
|
117
128
|
if (spec.locus !== undefined && voice.motion !== undefined)
|
|
@@ -126,17 +137,38 @@ export function fits(mix, voice) {
|
|
|
126
137
|
return false;
|
|
127
138
|
if (patch.state !== undefined || patch.step !== undefined)
|
|
128
139
|
return false;
|
|
129
|
-
if (mix.opts.history?.inputs && patch.reads !== undefined && patch.reads.length > 0)
|
|
130
|
-
return false;
|
|
131
140
|
const built = voice.built;
|
|
132
141
|
return (built === null ||
|
|
133
142
|
built.tracks.every((t, i) => t.lerp === undefined || t.lerp === voice.lerps[i]));
|
|
134
143
|
}
|
|
144
|
+
const RANK = { exact: 0, stepped: 1, held: 2 };
|
|
145
|
+
/** How sure a seek leaves a voice's state: kept is held; rebuilt is exact only on a fixed step. */
|
|
146
|
+
function seekDoubt(mix, voice, rebuild) {
|
|
147
|
+
const patch = voice.patch;
|
|
148
|
+
if (patch.state === undefined && patch.step === undefined && !voice.keeping)
|
|
149
|
+
return 'exact';
|
|
150
|
+
if (!rebuild)
|
|
151
|
+
return 'held';
|
|
152
|
+
const weight = voice.spec.weight;
|
|
153
|
+
if ((patch.reads !== undefined && patch.reads.length > 0) ||
|
|
154
|
+
(typeof weight === 'function' && weight.input))
|
|
155
|
+
return 'held';
|
|
156
|
+
if (voice.keeping)
|
|
157
|
+
return 'stepped';
|
|
158
|
+
if (patch.step === undefined)
|
|
159
|
+
return 'exact';
|
|
160
|
+
const tick = mix.opts.stepMs;
|
|
161
|
+
const fixed = tick !== undefined && tick > 0 && !mix.reducedNow && mix.opts.maxDt === undefined;
|
|
162
|
+
return fixed ? 'exact' : 'stepped';
|
|
163
|
+
}
|
|
135
164
|
/** A voice's patch kept state on a record: it is stateful from now on. */
|
|
136
165
|
export function stateful(mix, voice) {
|
|
137
166
|
if (voice.keeping)
|
|
138
167
|
return;
|
|
139
168
|
voice.keeping = true;
|
|
169
|
+
// The call that kept state was for the subject last sent to, whose record the state is on.
|
|
170
|
+
if (voice.sharing)
|
|
171
|
+
unshare(mix, voice, true, mix.sending.subject);
|
|
140
172
|
mix.lanes?.invalidate();
|
|
141
173
|
}
|
|
142
174
|
export function handle(mix, voice) {
|
|
@@ -153,16 +185,28 @@ function handleHost(mix) {
|
|
|
153
185
|
},
|
|
154
186
|
changed: (voice) => {
|
|
155
187
|
mix.frame = nextFrame();
|
|
188
|
+
reorigin(mix, voice, mix.now);
|
|
189
|
+
if (voice.holding !== null)
|
|
190
|
+
descendants(voice, (v) => reorigin(mix, v, mix.now));
|
|
156
191
|
noted(mix, voice);
|
|
157
192
|
mix.lanes?.refill();
|
|
158
193
|
},
|
|
159
194
|
// A delta read before an owner's seek, or a hit booked, is not taken as standing after it.
|
|
160
|
-
sought: (voice) => {
|
|
195
|
+
sought: (voice, rebuild) => {
|
|
196
|
+
if (rebuild)
|
|
197
|
+
voice.rebuilds++;
|
|
198
|
+
let doubt = seekDoubt(mix, voice, rebuild);
|
|
161
199
|
if (voice.holding !== null)
|
|
162
200
|
descendants(voice, (v) => {
|
|
163
201
|
v.seeks++;
|
|
202
|
+
if (rebuild)
|
|
203
|
+
v.rebuilds++;
|
|
204
|
+
const d = seekDoubt(mix, v, rebuild);
|
|
205
|
+
if (RANK[d] > RANK[doubt])
|
|
206
|
+
doubt = d;
|
|
164
207
|
schedule(mix, v);
|
|
165
208
|
});
|
|
209
|
+
return doubt;
|
|
166
210
|
},
|
|
167
211
|
fade: (voice, opts) => {
|
|
168
212
|
if (voice.holding !== null && opts !== undefined && ('subject' in opts || opts.at === 'rest'))
|
|
@@ -180,11 +224,14 @@ function handleHost(mix) {
|
|
|
180
224
|
return 0;
|
|
181
225
|
if (voice.holding !== null)
|
|
182
226
|
return ownWeight(voice, mix.now, mix.reducedNow, (o) => mix.ownerBase(o, subject));
|
|
227
|
+
const slot = mix.lanes === null ? -1 : (mix.chains.get(subject)?.slot ?? -1);
|
|
183
228
|
if (voice.laned && mix.lanes !== null) {
|
|
184
|
-
const w = mix.lanes.weightOf(voice.id,
|
|
229
|
+
const w = mix.lanes.weightOf(voice.id, slot);
|
|
185
230
|
if (w !== undefined)
|
|
186
231
|
return w;
|
|
187
232
|
}
|
|
233
|
+
if (voice.sharing)
|
|
234
|
+
return weightAt(voice, slot);
|
|
188
235
|
return voice.subjects.get(subject)?.weight ?? 0;
|
|
189
236
|
},
|
|
190
237
|
record: (label, again) => record(mix, label, again),
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export type { NumberOptions } from './channels.js';
|
|
1
|
+
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
|
-
export { color, css,
|
|
4
|
+
export { color, css, mixHex, oklab, toHex } from './color.js';
|
|
5
5
|
export type { Claim, Fit, FitPlan, Order, SpanClaim, Strength } from './fit.js';
|
|
6
6
|
export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
|
|
7
7
|
export { mix, mixer } from './mixer.js';
|
|
@@ -10,8 +10,13 @@ export { glide, spring, tween } from './motion.js';
|
|
|
10
10
|
export { HistoryMiss } from './paging.js';
|
|
11
11
|
export type { KeysOptions, PatchOptions } from './patch.js';
|
|
12
12
|
export { keys, patch } from './patch.js';
|
|
13
|
-
export {
|
|
13
|
+
export type { AngleOptions } from './rotation.js';
|
|
14
|
+
export { angle, quat } from './rotation.js';
|
|
15
|
+
export { gate, input, lag, level, peak, slew } from './signals.js';
|
|
16
|
+
export { fold } from './stack.js';
|
|
14
17
|
export type { Ticked, Ticker, TickerOptions } from './ticker.js';
|
|
15
18
|
export { ticker } from './ticker.js';
|
|
16
19
|
export { transport } from './transport.js';
|
|
17
|
-
export type { Anchor, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Keyframe, Kit, Mark, Marked, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Projection, Query, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, Tape, TapeBranch, TapeMaker, TapeOp, Transport, TransportOptions, TransportProjection, VoiceSpec, } from './types.js';
|
|
20
|
+
export type { Anchor, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Keyframe, Kit, Mark, Marked, 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';
|
|
21
|
+
export type { WaveOptions, WaveShape } from './wave.js';
|
|
22
|
+
export { wave, waveAt } from './wave.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export { kit, last, max, mul, sum, vec } from './channels.js';
|
|
2
|
-
export { color, css,
|
|
2
|
+
export { color, css, mixHex, oklab, toHex } from './color.js';
|
|
3
3
|
export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
|
|
4
4
|
export { mix, mixer } from './mixer.js';
|
|
5
5
|
export { glide, spring, tween } from './motion.js';
|
|
6
6
|
export { HistoryMiss } from './paging.js';
|
|
7
7
|
export { keys, patch } from './patch.js';
|
|
8
|
-
export {
|
|
8
|
+
export { angle, quat } from './rotation.js';
|
|
9
|
+
export { gate, input, lag, level, peak, slew } from './signals.js';
|
|
10
|
+
export { fold } from './stack.js';
|
|
9
11
|
export { ticker } from './ticker.js';
|
|
10
12
|
export { transport } from './transport.js';
|
|
13
|
+
export { wave, waveAt } from './wave.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Lane } from './lane.js';
|
|
2
|
+
import type { Lanes } from './lanes.js';
|
|
3
|
+
/**
|
|
4
|
+
* A keys voice's fill outside a locus, its weight a number: what `one` does for each subject, in
|
|
5
|
+
* one loop, so no weight or time crosses a call as a boxed double per subject.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runKeys<I, O>(lanes: Lanes<I, O>, lane: Lane<I, O>): void;
|
package/dist/keyfill.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { foldNumber } from './channels.js';
|
|
2
|
+
import { clampWeight, frozenTime, phaseAt, same, weighed } from './clock.js';
|
|
3
|
+
import { shortWay } from './turns.js';
|
|
4
|
+
/**
|
|
5
|
+
* A keys voice's fill outside a locus, its weight a number: what `one` does for each subject, in
|
|
6
|
+
* one loop, so no weight or time crosses a call as a boxed double per subject.
|
|
7
|
+
*/
|
|
8
|
+
export function runKeys(lanes, lane) {
|
|
9
|
+
const voice = lane.voice;
|
|
10
|
+
const data = lane.data;
|
|
11
|
+
const per = lanes.per;
|
|
12
|
+
const list = lane.list;
|
|
13
|
+
const records = lane.records;
|
|
14
|
+
const chans = lane.chans;
|
|
15
|
+
const read = lane.values;
|
|
16
|
+
const n = chans.length;
|
|
17
|
+
const fills = lanes.fills;
|
|
18
|
+
const lately = lanes.lately;
|
|
19
|
+
const elapsedNow = voice.elapsedAt(lanes.now);
|
|
20
|
+
const duration = voice.duration;
|
|
21
|
+
const passes = voice.passes;
|
|
22
|
+
const before = voice.freezesBefore;
|
|
23
|
+
const after = voice.freezesAfter;
|
|
24
|
+
const span = voice.span;
|
|
25
|
+
const flat = lane.flat;
|
|
26
|
+
const parts = voice.parts !== null;
|
|
27
|
+
const whole = clampWeight(voice.weight);
|
|
28
|
+
let width = 0;
|
|
29
|
+
for (let i = 0; i < n; i++)
|
|
30
|
+
width += chans[i].axes;
|
|
31
|
+
if (lane.nums.length < width)
|
|
32
|
+
lane.nums = new Float64Array(width);
|
|
33
|
+
if (lane.kinds.length < n)
|
|
34
|
+
lane.kinds = new Uint8Array(n);
|
|
35
|
+
const nums = lane.nums;
|
|
36
|
+
const kinds = lane.kinds;
|
|
37
|
+
for (let p = 0; p < list.length; p++) {
|
|
38
|
+
const slot = list[p];
|
|
39
|
+
const o = p * 8 /* Row.STRIDE */;
|
|
40
|
+
if (per[slot * 6 /* Per.SLOT */ + 5 /* Per.LANE_FILL */] === fills - 1)
|
|
41
|
+
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
42
|
+
if (lately >= 0 && lanes.unread(slot))
|
|
43
|
+
continue;
|
|
44
|
+
const elapsed = frozenTime(elapsedNow - data[o + 0 /* Row.DELAY */], before, after, span);
|
|
45
|
+
if (!(elapsed >= 0)) {
|
|
46
|
+
data[o + 2 /* Row.WEIGHT */] = 0;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (!flat) {
|
|
50
|
+
const since = data[o + 1 /* Row.SINCE */];
|
|
51
|
+
if (!lane.weighed || !same(since, lane.weighedSince)) {
|
|
52
|
+
lane.fade = lanes.host.envelope(voice, since);
|
|
53
|
+
lane.weighed = true;
|
|
54
|
+
lane.weighedSince = since;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const w = flat && !parts ? whole : weighed(voice.weight, lane.fade, lanes.parting(voice, slot));
|
|
58
|
+
data[o + 2 /* Row.WEIGHT */] = w;
|
|
59
|
+
if (!lane.read || !same(elapsed, lane.readAt)) {
|
|
60
|
+
lanes.readKeyed(voice, phaseAt(elapsed, duration, passes), lane.delta, lane.scratch);
|
|
61
|
+
lane.read = true;
|
|
62
|
+
lane.readAt = elapsed;
|
|
63
|
+
lanes.gather(lane, lane.delta);
|
|
64
|
+
flatten(chans, read, nums, kinds);
|
|
65
|
+
}
|
|
66
|
+
if (!(w > 0))
|
|
67
|
+
continue;
|
|
68
|
+
let k = 0;
|
|
69
|
+
for (let i = 0; i < n; i++) {
|
|
70
|
+
const ch = chans[i];
|
|
71
|
+
const axes = ch.axes;
|
|
72
|
+
const kind = kinds[i];
|
|
73
|
+
if (kind === 1 /* Kind.NUMBERS */) {
|
|
74
|
+
const values = ch.values;
|
|
75
|
+
const base = slot * axes;
|
|
76
|
+
for (let a = 0; a < axes; a++)
|
|
77
|
+
values[base + a] = foldNumber(ch.op, values[base + a], nums[k + a], w);
|
|
78
|
+
}
|
|
79
|
+
else if (kind === 2 /* Kind.GATED */) {
|
|
80
|
+
lanes.folding = lane;
|
|
81
|
+
lanes.rec = records[p] ?? null;
|
|
82
|
+
lanes.arg[0 /* Arg.WEIGHT */] = w;
|
|
83
|
+
lanes.foldInto(ch, slot, read[i]);
|
|
84
|
+
}
|
|
85
|
+
k += axes;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A keys read's values as the numbers `foldInto` would fold, an array read past its end as the rest:
|
|
91
|
+
* read here once per read, since reading a number out of a delta's array for each subject boxes it.
|
|
92
|
+
*/
|
|
93
|
+
function flatten(chans, read, nums, kinds) {
|
|
94
|
+
let k = 0;
|
|
95
|
+
for (let i = 0; i < chans.length; i++) {
|
|
96
|
+
const ch = chans[i];
|
|
97
|
+
const value = read[i];
|
|
98
|
+
if (value === undefined)
|
|
99
|
+
kinds[i] = 0 /* Kind.ABSENT */;
|
|
100
|
+
else if (!ch.plain)
|
|
101
|
+
kinds[i] = unplain(ch, value, nums, k);
|
|
102
|
+
else {
|
|
103
|
+
kinds[i] = 1 /* Kind.NUMBERS */;
|
|
104
|
+
if (ch.scalar)
|
|
105
|
+
nums[k] = value;
|
|
106
|
+
else {
|
|
107
|
+
const arr = Array.isArray(value) || ArrayBuffer.isView(value) ? value : null;
|
|
108
|
+
for (let a = 0; a < ch.axes; a++)
|
|
109
|
+
nums[k + a] = arr === null ? ch.rest : (arr[a] ?? ch.rest);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
k += ch.axes;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* `flatten` for a channel that is not plain. An angle's number is taken the short way round here,
|
|
117
|
+
* and then folds as the sum it is; a rest-less channel and a quat fold through `foldInto`.
|
|
118
|
+
*/
|
|
119
|
+
function unplain(ch, value, nums, k) {
|
|
120
|
+
if (!(ch.turn > 0))
|
|
121
|
+
return 2 /* Kind.GATED */;
|
|
122
|
+
nums[k] = shortWay(value, ch.turn);
|
|
123
|
+
return 1 /* Kind.NUMBERS */;
|
|
124
|
+
}
|
package/dist/keyrows.d.ts
CHANGED
package/dist/keyrows.js
CHANGED
|
@@ -23,7 +23,7 @@ export class KeyRows {
|
|
|
23
23
|
for (let i = 0; i < built.tracks.length; i++) {
|
|
24
24
|
const track = built.tracks[i];
|
|
25
25
|
const ch = chans[i];
|
|
26
|
-
if (track === undefined || ch === undefined)
|
|
26
|
+
if (track === undefined || ch === undefined || !ch.plain)
|
|
27
27
|
return -1;
|
|
28
28
|
for (const pt of track.all)
|
|
29
29
|
if (!flat(pt.value, ch))
|
package/dist/lane.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Numeric } from './channels.js';
|
|
2
2
|
import type { Motions } from './motions.js';
|
|
3
3
|
import type { Scratch } from './patch.js';
|
|
4
|
+
import type { Channel } from './types.js';
|
|
4
5
|
import type { Subject, Voice } from './voice.js';
|
|
5
6
|
/** One laned channel: its arithmetic, and every numbered subject's folded value, `axes` per subject. */
|
|
6
7
|
export interface Laned {
|
|
@@ -8,6 +9,12 @@ export interface Laned {
|
|
|
8
9
|
/** Its value is a number, its rest a number; otherwise an array, which `vec(1)` is too. */
|
|
9
10
|
scalar: boolean;
|
|
10
11
|
op: Numeric['op'];
|
|
12
|
+
/** Its values fold as its `op` alone: not through a band as `'last'` does, and not a rotation. */
|
|
13
|
+
plain: boolean;
|
|
14
|
+
/** An angle's full turn, and 0 for any other channel. */
|
|
15
|
+
turn: number;
|
|
16
|
+
/** The channel, whose own `fold` and `lerp` an `'own'` lane calls. */
|
|
17
|
+
channel: Channel<unknown>;
|
|
11
18
|
rest: number;
|
|
12
19
|
axes: number;
|
|
13
20
|
values: Float64Array;
|
|
@@ -56,6 +63,24 @@ export declare const enum Begin {
|
|
|
56
63
|
GENERAL = 0,
|
|
57
64
|
READY = 1
|
|
58
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Where each number a fill hands from one of its methods to the next sits in `Lanes.arg`. Passed as
|
|
68
|
+
* an argument, a double is boxed wherever V8 does not inline the call: 16 bytes a number a subject
|
|
69
|
+
* (bench/allocs.mjs, 2026-10-09). The caller writes them just before the call, and the callee reads
|
|
70
|
+
* them before it calls anything else.
|
|
71
|
+
*/
|
|
72
|
+
export declare const enum Arg {
|
|
73
|
+
/** The weight to fold at. */
|
|
74
|
+
WEIGHT = 0,
|
|
75
|
+
/** The subject's voice time, its freezes applied. */
|
|
76
|
+
ELAPSED = 1,
|
|
77
|
+
PHASE = 2,
|
|
78
|
+
PASS = 3,
|
|
79
|
+
DELAY = 4,
|
|
80
|
+
/** The voice's own fade, for `signalled`. */
|
|
81
|
+
FADE = 5,
|
|
82
|
+
SIZE = 6
|
|
83
|
+
}
|
|
59
84
|
/** Where each of a subject's `SLOT` numbers in `Lanes.per` sits. */
|
|
60
85
|
export declare const enum Per {
|
|
61
86
|
SLOT = 6,
|
|
@@ -98,8 +123,8 @@ export interface Paced {
|
|
|
98
123
|
data: Float64Array;
|
|
99
124
|
}
|
|
100
125
|
/**
|
|
101
|
-
* Where a motion voice's per-subject numbers live, by position: a lane of one voice, or
|
|
102
|
-
* of single-subject
|
|
126
|
+
* Where a motion voice's per-subject numbers live, by position: a lane of one voice, or a crowd
|
|
127
|
+
* of single-subject voices, whose positions each belong to a voice of their own.
|
|
103
128
|
*/
|
|
104
129
|
export interface Positions<I, O> {
|
|
105
130
|
/** `STRIDE` numbers per position. */
|
|
@@ -164,6 +189,9 @@ export declare class Lane<I, O> implements Positions<I, O> {
|
|
|
164
189
|
chans: Laned[];
|
|
165
190
|
/** The value read for each of `chans`, for the subject being folded. */
|
|
166
191
|
values: unknown[];
|
|
192
|
+
/** A keys read's `values` as numbers, each channel's axes side by side, and how each folds. */
|
|
193
|
+
nums: Float64Array<ArrayBuffer>;
|
|
194
|
+
kinds: Uint8Array<ArrayBuffer>;
|
|
167
195
|
/**
|
|
168
196
|
* Within one fill, the last elapsed placed and the last `since` weighed, with what they gave, so
|
|
169
197
|
* subjects sharing a delay or a start share the arithmetic: the same inputs give the same bits.
|
|
@@ -179,6 +207,8 @@ export declare class Lane<I, O> implements Positions<I, O> {
|
|
|
179
207
|
fade: number;
|
|
180
208
|
/** Whether the voice has no fade in or out this fill, so its envelope is 1 for every subject. */
|
|
181
209
|
flat: boolean;
|
|
210
|
+
/** The voice's clock at this fill, set before `one` runs its subjects. */
|
|
211
|
+
elapsedNow: number;
|
|
182
212
|
/** A motion patch's state, which the lane samples in place of calling the patch. */
|
|
183
213
|
readonly motion: Motions<I> | undefined;
|
|
184
214
|
/** By the motion patch's number for a subject, its position plus one, 0 where it has none. */
|
package/dist/lane.js
CHANGED
|
@@ -52,6 +52,9 @@ export class Lane {
|
|
|
52
52
|
chans = [];
|
|
53
53
|
/** The value read for each of `chans`, for the subject being folded. */
|
|
54
54
|
values = [];
|
|
55
|
+
/** A keys read's `values` as numbers, each channel's axes side by side, and how each folds. */
|
|
56
|
+
nums = new Float64Array(0);
|
|
57
|
+
kinds = new Uint8Array(0);
|
|
55
58
|
/**
|
|
56
59
|
* Within one fill, the last elapsed placed and the last `since` weighed, with what they gave, so
|
|
57
60
|
* subjects sharing a delay or a start share the arithmetic: the same inputs give the same bits.
|
|
@@ -67,6 +70,8 @@ export class Lane {
|
|
|
67
70
|
fade = 1;
|
|
68
71
|
/** Whether the voice has no fade in or out this fill, so its envelope is 1 for every subject. */
|
|
69
72
|
flat = false;
|
|
73
|
+
/** The voice's clock at this fill, set before `one` runs its subjects. */
|
|
74
|
+
elapsedNow = 0;
|
|
70
75
|
/** A motion patch's state, which the lane samples in place of calling the patch. */
|
|
71
76
|
motion;
|
|
72
77
|
/** By the motion patch's number for a subject, its position plus one, 0 where it has none. */
|