@msb235/blits 0.14.0 → 0.15.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 +154 -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 +15 -7
- 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.js +7 -3
- 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 +8 -2
- package/dist/crowd.js +4 -5
- 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/drop.d.ts +9 -0
- package/dist/drop.js +84 -0
- package/dist/due.d.ts +24 -5
- package/dist/due.js +113 -33
- package/dist/easing.d.ts +4 -4
- package/dist/easing.js +3 -3
- package/dist/everyone.d.ts +3 -2
- package/dist/everyone.js +3 -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 +51 -19
- 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/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 +36 -6
- package/dist/lane.js +32 -1
- package/dist/lanes.d.ts +23 -8
- package/dist/lanes.js +29 -8
- 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 +90 -4
- package/dist/mixer.d.ts +54 -36
- package/dist/mixer.js +53 -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 +0 -1
- package/dist/named.js +20 -2
- package/dist/numbers.d.ts +2 -0
- package/dist/numbers.js +4 -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 +11 -8
- package/dist/qualify.js +84 -21
- 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/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 +1 -1
- package/dist/sample.d.ts +2 -2
- package/dist/sample.js +33 -7
- 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 +20 -13
- 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/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 +222 -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/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +136 -70
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +1 -0
- package/dist/voice.d.ts +7 -143
- package/dist/voice.js +28 -79
- 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 +212 -34
- package/package.json +2 -2
package/dist/gather.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inside, leave, meetBound } from './bound.js';
|
|
1
2
|
import { foldNumber, lerpNumber } from './channels.js';
|
|
2
3
|
import { shortWay, turnFrom, turnInto, turnToward } from './turns.js';
|
|
3
4
|
/** `xs`, or a copy at least `n` long where it is shorter. */
|
|
@@ -9,12 +10,12 @@ function sized(xs, n) {
|
|
|
9
10
|
return grown;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
|
-
* Every member of a
|
|
13
|
+
* Every member of a role, in voice order, gathered rather than folded: a member's delta for a
|
|
13
14
|
* subject is lerped into what the members before it left there by its share of the weight
|
|
14
|
-
* taken, as the general path's `
|
|
15
|
+
* taken, as the general path's `foldRole` does, so each subject's role can fold in at its
|
|
15
16
|
* first member's place in the order.
|
|
16
17
|
*/
|
|
17
|
-
export function
|
|
18
|
+
export function gatherRole(lanes, g) {
|
|
18
19
|
const cap = lanes.cap;
|
|
19
20
|
if (g.met.length < cap) {
|
|
20
21
|
g.met = sized(g.met, cap);
|
|
@@ -30,6 +31,9 @@ export function gatherLocus(lanes, g) {
|
|
|
30
31
|
if (taken !== undefined)
|
|
31
32
|
g.taken[k] = sized(taken, cap);
|
|
32
33
|
}
|
|
34
|
+
g.inline = inlined(g);
|
|
35
|
+
if (g.inline)
|
|
36
|
+
return;
|
|
33
37
|
lanes.into = g;
|
|
34
38
|
try {
|
|
35
39
|
for (const lane of g.members) {
|
|
@@ -40,10 +44,18 @@ export function gatherLocus(lanes, g) {
|
|
|
40
44
|
lanes.reset(lane);
|
|
41
45
|
lane.elapsedNow = voice.elapsedAt(lanes.now);
|
|
42
46
|
const list = lane.list;
|
|
47
|
+
const b = voice.bound;
|
|
48
|
+
if (b !== null)
|
|
49
|
+
meetBound(lanes, b);
|
|
50
|
+
const fills = lanes.fills;
|
|
43
51
|
for (let p = 0; p < list.length; p++) {
|
|
44
52
|
const slot = list[p];
|
|
53
|
+
if (b !== null && !inside(b, slot, fills)) {
|
|
54
|
+
leave(lanes, lane, p, slot);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
45
57
|
if (lanes.one(lane, p, slot)) {
|
|
46
|
-
|
|
58
|
+
meetRole(lanes, g, slot);
|
|
47
59
|
g.sum[slot] =
|
|
48
60
|
g.sum[slot] + lane.data[p * 8 /* Row.STRIDE */ + 2 /* Row.WEIGHT */];
|
|
49
61
|
}
|
|
@@ -56,24 +68,47 @@ export function gatherLocus(lanes, g) {
|
|
|
56
68
|
lanes.into = null;
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
|
-
/** The first a fill hears of a subject in a
|
|
60
|
-
function
|
|
71
|
+
/** The first a fill hears of a subject in a role: from the member now gathering. */
|
|
72
|
+
function meetRole(lanes, g, slot) {
|
|
61
73
|
if (g.met[slot] === lanes.fills)
|
|
62
74
|
return;
|
|
63
75
|
g.met[slot] = lanes.fills;
|
|
64
76
|
g.first[slot] = lanes.intoId;
|
|
65
77
|
g.sum[slot] = 0;
|
|
66
|
-
|
|
78
|
+
const takens = g.taken;
|
|
79
|
+
for (let k = 0; k < takens.length; k++) {
|
|
80
|
+
const taken = takens[k];
|
|
67
81
|
if (taken !== undefined)
|
|
68
82
|
taken[slot] = 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `gatherInto` for a plain scalar channel and the number at `Arg.VALUE`, which as an argument
|
|
87
|
+
* would be boxed, 16 bytes a channel a member a subject (`role`, bench/allocs.mjs, 2026-10-25).
|
|
88
|
+
*/
|
|
89
|
+
function gatherScalar(lanes, g, ch, slot) {
|
|
90
|
+
const arg = lanes.arg;
|
|
91
|
+
const value = arg[7 /* Arg.VALUE */];
|
|
92
|
+
const values = g.values[ch.index];
|
|
93
|
+
const takenAt = g.taken[ch.index];
|
|
94
|
+
if (values === undefined || takenAt === undefined) {
|
|
95
|
+
gatherInto(lanes, g, ch, slot, value);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const w = arg[0 /* Arg.WEIGHT */];
|
|
99
|
+
meetRole(lanes, g, slot);
|
|
100
|
+
const taken = takenAt[slot];
|
|
101
|
+
const total = taken + w;
|
|
102
|
+
values[slot] = taken === 0 ? value : lerpNumber(values[slot], value, w / total);
|
|
103
|
+
takenAt[slot] = total;
|
|
69
104
|
}
|
|
70
105
|
/**
|
|
71
|
-
* A member's value for one channel of a subject, lerped into its
|
|
72
|
-
* path's `
|
|
106
|
+
* A member's value for one channel of a subject, lerped into its role: the step of the general
|
|
107
|
+
* path's `foldRole`.
|
|
73
108
|
*/
|
|
74
109
|
function gatherInto(lanes, g, ch, slot, value) {
|
|
75
110
|
const w = lanes.arg[0 /* Arg.WEIGHT */];
|
|
76
|
-
|
|
111
|
+
meetRole(lanes, g, slot);
|
|
77
112
|
const k = ch.index;
|
|
78
113
|
let values = g.values[k];
|
|
79
114
|
let takenAt = g.taken[k];
|
|
@@ -86,7 +121,7 @@ function gatherInto(lanes, g, ch, slot, value) {
|
|
|
86
121
|
const taken = takenAt[slot];
|
|
87
122
|
const total = taken + w;
|
|
88
123
|
const u = w / total;
|
|
89
|
-
if (!ch.plain)
|
|
124
|
+
if (!ch.plain || !ch.straight)
|
|
90
125
|
turnToward(ch, values, slot * ch.axes, value, taken, u);
|
|
91
126
|
else if (ch.scalar) {
|
|
92
127
|
values[slot] =
|
|
@@ -103,8 +138,8 @@ function gatherInto(lanes, g, ch, slot, value) {
|
|
|
103
138
|
}
|
|
104
139
|
takenAt[slot] = total;
|
|
105
140
|
}
|
|
106
|
-
/** A
|
|
107
|
-
export function
|
|
141
|
+
/** A role member's turn in the order: each subject it was the first member of takes the role. */
|
|
142
|
+
export function foldRole(lanes, lane, g) {
|
|
108
143
|
const id = lane.voice.id;
|
|
109
144
|
const fills = lanes.fills;
|
|
110
145
|
const list = lane.list;
|
|
@@ -112,28 +147,100 @@ export function foldLocus(lanes, lane, g) {
|
|
|
112
147
|
const slot = list[p];
|
|
113
148
|
if (g.met[slot] !== fills || g.first[slot] !== id)
|
|
114
149
|
continue;
|
|
115
|
-
|
|
116
|
-
|
|
150
|
+
if (g.sum[slot] > 0)
|
|
151
|
+
foldAt(lanes, g, slot);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** Folds what a role gathered for the subject at `slot` into its laned values. */
|
|
155
|
+
function foldAt(lanes, g, slot) {
|
|
156
|
+
for (let k = 0; k < g.values.length; k++) {
|
|
157
|
+
const takenAt = g.taken[k];
|
|
158
|
+
const taken = takenAt === undefined ? 0 : takenAt[slot];
|
|
159
|
+
if (!(taken > 0))
|
|
117
160
|
continue;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
161
|
+
// A channel folds at the weight of the members that wrote it, not the whole role.
|
|
162
|
+
const w = taken > 1 ? 1 : taken;
|
|
163
|
+
const ch = lanes.laned[k];
|
|
164
|
+
const values = g.values[k];
|
|
165
|
+
const into = ch.values;
|
|
166
|
+
const base = slot * ch.axes;
|
|
167
|
+
if (!ch.plain) {
|
|
168
|
+
turnFrom(ch, into, base, values, base, w);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
for (let a = 0; a < ch.axes; a++)
|
|
172
|
+
into[base + a] = foldNumber(ch.op, into[base + a], values[base + a], w);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Whether a fill can run a role a subject at a time: every member playing, on its lane, keeping
|
|
177
|
+
* no state, and over the same subjects in the same places, with no other voice between them.
|
|
178
|
+
*/
|
|
179
|
+
function inlined(g) {
|
|
180
|
+
if (!g.contiguous)
|
|
181
|
+
return false;
|
|
182
|
+
const members = g.members;
|
|
183
|
+
const list = members[0].list;
|
|
184
|
+
for (const lane of members) {
|
|
185
|
+
const voice = lane.voice;
|
|
186
|
+
if (lane.idle || voice.keeping || voice.state === 'pending' || voice.state === 'done')
|
|
187
|
+
return false;
|
|
188
|
+
const own = lane.list;
|
|
189
|
+
if (own === list)
|
|
190
|
+
continue;
|
|
191
|
+
if (own.length !== list.length)
|
|
192
|
+
return false;
|
|
193
|
+
for (let p = 0; p < list.length; p++)
|
|
194
|
+
if (own[p] !== list[p])
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* An inlined role at its first member's turn: each subject's members gathered in voice order, as
|
|
201
|
+
* `gatherRole` gathers them, then folded at once, with no pass of its own and no array of sums.
|
|
202
|
+
*/
|
|
203
|
+
export function runRole(lanes, g) {
|
|
204
|
+
const members = g.members;
|
|
205
|
+
const arg = lanes.arg;
|
|
206
|
+
for (const lane of members) {
|
|
207
|
+
lanes.reset(lane);
|
|
208
|
+
lane.elapsedNow = lane.voice.elapsedAt(lanes.now);
|
|
209
|
+
const b = lane.voice.bound;
|
|
210
|
+
if (b !== null)
|
|
211
|
+
meetBound(lanes, b);
|
|
212
|
+
}
|
|
213
|
+
const fills = lanes.fills;
|
|
214
|
+
const list = members[0].list;
|
|
215
|
+
lanes.into = g;
|
|
216
|
+
try {
|
|
217
|
+
for (let p = 0; p < list.length; p++) {
|
|
218
|
+
const slot = list[p];
|
|
219
|
+
let sum = 0;
|
|
220
|
+
for (let i = 0; i < members.length; i++) {
|
|
221
|
+
const lane = members[i];
|
|
222
|
+
// Its patch kept state on an earlier subject: as `gatherRole` leaves it after that call.
|
|
223
|
+
if (lane.voice.keeping)
|
|
224
|
+
continue;
|
|
225
|
+
const b = lane.voice.bound;
|
|
226
|
+
if (b !== null && !inside(b, slot, fills)) {
|
|
227
|
+
leave(lanes, lane, p, slot);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// Each lane's placing is numbered on its own; the last member placed left its number.
|
|
231
|
+
arg[6 /* Arg.MARK */] = lane.mark;
|
|
232
|
+
if (lanes.one(lane, p, slot)) {
|
|
233
|
+
meetRole(lanes, g, slot);
|
|
234
|
+
sum += lane.data[p * 8 /* Row.STRIDE */ + 2 /* Row.WEIGHT */];
|
|
235
|
+
}
|
|
132
236
|
}
|
|
133
|
-
|
|
134
|
-
|
|
237
|
+
if (sum > 0)
|
|
238
|
+
foldAt(lanes, g, slot);
|
|
135
239
|
}
|
|
136
240
|
}
|
|
241
|
+
finally {
|
|
242
|
+
lanes.into = null;
|
|
243
|
+
}
|
|
137
244
|
}
|
|
138
245
|
/** For a keys voice: the delta's value for each channel it writes, read once per phase. */
|
|
139
246
|
export function gather(lane, delta) {
|
|
@@ -161,9 +268,16 @@ export function foldDelta(chans, slot, delta) {
|
|
|
161
268
|
let ch = chans[0];
|
|
162
269
|
if (n > 0) {
|
|
163
270
|
const v = delta[ch.name];
|
|
164
|
-
if (typeof v === 'number' && ch.scalar && ch.plain
|
|
165
|
-
const
|
|
166
|
-
|
|
271
|
+
if (typeof v === 'number' && ch.scalar && ch.plain) {
|
|
272
|
+
const into = this.into;
|
|
273
|
+
if (into === null) {
|
|
274
|
+
const values = ch.values;
|
|
275
|
+
values[slot] = foldNumber(ch.op, values[slot], v, arg[0 /* Arg.WEIGHT */]);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
arg[7 /* Arg.VALUE */] = v;
|
|
279
|
+
gatherScalar(this, into, ch, slot);
|
|
280
|
+
}
|
|
167
281
|
}
|
|
168
282
|
else
|
|
169
283
|
this.foldInto(ch, slot, v);
|
|
@@ -171,9 +285,16 @@ export function foldDelta(chans, slot, delta) {
|
|
|
171
285
|
ch = chans[1];
|
|
172
286
|
if (n > 1) {
|
|
173
287
|
const v = delta[ch.name];
|
|
174
|
-
if (typeof v === 'number' && ch.scalar && ch.plain
|
|
175
|
-
const
|
|
176
|
-
|
|
288
|
+
if (typeof v === 'number' && ch.scalar && ch.plain) {
|
|
289
|
+
const into = this.into;
|
|
290
|
+
if (into === null) {
|
|
291
|
+
const values = ch.values;
|
|
292
|
+
values[slot] = foldNumber(ch.op, values[slot], v, arg[0 /* Arg.WEIGHT */]);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
arg[7 /* Arg.VALUE */] = v;
|
|
296
|
+
gatherScalar(this, into, ch, slot);
|
|
297
|
+
}
|
|
177
298
|
}
|
|
178
299
|
else
|
|
179
300
|
this.foldInto(ch, slot, v);
|
|
@@ -181,9 +302,16 @@ export function foldDelta(chans, slot, delta) {
|
|
|
181
302
|
ch = chans[2];
|
|
182
303
|
if (n > 2) {
|
|
183
304
|
const v = delta[ch.name];
|
|
184
|
-
if (typeof v === 'number' && ch.scalar && ch.plain
|
|
185
|
-
const
|
|
186
|
-
|
|
305
|
+
if (typeof v === 'number' && ch.scalar && ch.plain) {
|
|
306
|
+
const into = this.into;
|
|
307
|
+
if (into === null) {
|
|
308
|
+
const values = ch.values;
|
|
309
|
+
values[slot] = foldNumber(ch.op, values[slot], v, arg[0 /* Arg.WEIGHT */]);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
arg[7 /* Arg.VALUE */] = v;
|
|
313
|
+
gatherScalar(this, into, ch, slot);
|
|
314
|
+
}
|
|
187
315
|
}
|
|
188
316
|
else
|
|
189
317
|
this.foldInto(ch, slot, v);
|
|
@@ -191,9 +319,16 @@ export function foldDelta(chans, slot, delta) {
|
|
|
191
319
|
for (let i = 3; i < n; i++) {
|
|
192
320
|
ch = chans[i];
|
|
193
321
|
const v = delta[ch.name];
|
|
194
|
-
if (typeof v === 'number' && ch.scalar && ch.plain
|
|
195
|
-
const
|
|
196
|
-
|
|
322
|
+
if (typeof v === 'number' && ch.scalar && ch.plain) {
|
|
323
|
+
const into = this.into;
|
|
324
|
+
if (into === null) {
|
|
325
|
+
const values = ch.values;
|
|
326
|
+
values[slot] = foldNumber(ch.op, values[slot], v, arg[0 /* Arg.WEIGHT */]);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
arg[7 /* Arg.VALUE */] = v;
|
|
330
|
+
gatherScalar(this, into, ch, slot);
|
|
331
|
+
}
|
|
197
332
|
}
|
|
198
333
|
else
|
|
199
334
|
this.foldInto(ch, slot, v);
|
|
@@ -211,7 +346,7 @@ export function foldRun(ch, slot, xs) {
|
|
|
211
346
|
for (let a = 0; a < ch.axes; a++)
|
|
212
347
|
values[base + a] = foldNumber(ch.op, values[base + a], xs[a], w);
|
|
213
348
|
}
|
|
214
|
-
/** Folds one channel's value into a subject's laned values, or its
|
|
349
|
+
/** Folds one channel's value into a subject's laned values, or its role, at `Arg.WEIGHT`. */
|
|
215
350
|
export function foldInto(ch, slot, value) {
|
|
216
351
|
if (value === undefined)
|
|
217
352
|
return;
|
package/dist/gone.js
CHANGED
|
@@ -26,7 +26,7 @@ function add(ix, voice) {
|
|
|
26
26
|
ix.general.push(voice);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
for (const subject of voice.named) {
|
|
29
|
+
for (const subject of voice.named.list) {
|
|
30
30
|
const list = ix.named.get(subject);
|
|
31
31
|
if (list === undefined)
|
|
32
32
|
ix.named.set(subject, [voice]);
|
|
@@ -41,7 +41,7 @@ function remove(ix, out) {
|
|
|
41
41
|
general = true;
|
|
42
42
|
continue;
|
|
43
43
|
}
|
|
44
|
-
for (const subject of v.named) {
|
|
44
|
+
for (const subject of v.named.list) {
|
|
45
45
|
const named = ix.named.get(subject);
|
|
46
46
|
if (named === undefined)
|
|
47
47
|
continue;
|
package/dist/grid.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positions side by side, `x` then `y`, by place, and a uniform grid over them built again at the
|
|
3
|
+
* first query after they move, so a circle finds the places within it without visiting the rest.
|
|
4
|
+
*/
|
|
5
|
+
export declare class Grid {
|
|
6
|
+
xy: Float64Array;
|
|
7
|
+
/** How many places the list had. */
|
|
8
|
+
n: number;
|
|
9
|
+
/** The cell size asked for, NaN for one from the spread; the grid widens it where the positions are spread too thin. */
|
|
10
|
+
cell: number;
|
|
11
|
+
dirty: boolean;
|
|
12
|
+
/** The grid as last built: its origin, cell size and shape. */
|
|
13
|
+
x0: number;
|
|
14
|
+
y0: number;
|
|
15
|
+
size: number;
|
|
16
|
+
cols: number;
|
|
17
|
+
rows: number;
|
|
18
|
+
/** By cell, where its entries start in `order`, one past the last cell included. */
|
|
19
|
+
starts: Int32Array<ArrayBuffer>;
|
|
20
|
+
order: Int32Array<ArrayBuffer>;
|
|
21
|
+
/** By place, the cell the last build put it in, -1 for none. */
|
|
22
|
+
home: Int32Array<ArrayBuffer>;
|
|
23
|
+
/**
|
|
24
|
+
* Places `put` moved out of their cell since the last build, which every query tests on their own
|
|
25
|
+
* and skips in the cells, and by place whether it is one. Past a sixteenth of the places, the grid
|
|
26
|
+
* is built again.
|
|
27
|
+
*/
|
|
28
|
+
strays: number[];
|
|
29
|
+
stray: Uint8Array<ArrayBuffer>;
|
|
30
|
+
/** What the last `query` or `ring` found, the first `count` of it. */
|
|
31
|
+
found: Int32Array<ArrayBuffer>;
|
|
32
|
+
count: number;
|
|
33
|
+
/** Moves place `i` to (`x`, `y`) in `xy`, leaving the grid as it is where the place keeps its cell. */
|
|
34
|
+
put(i: number, x: number, y: number): void;
|
|
35
|
+
private build;
|
|
36
|
+
private push;
|
|
37
|
+
/**
|
|
38
|
+
* Leaves in `found` the places within `r` of (`cx`, `cy`), by the test `inside` makes, `r2`
|
|
39
|
+
* being `r * r`: the cells the circle touches, and one more where an edge falls a hair from one.
|
|
40
|
+
*/
|
|
41
|
+
query(cx: number, cy: number, r: number, r2: number): number;
|
|
42
|
+
/**
|
|
43
|
+
* Leaves in `found` the places beyond `r` of (`cx`, `cy`) and within a cell's width more, from
|
|
44
|
+
* the cells that ring touches.
|
|
45
|
+
*/
|
|
46
|
+
ring(cx: number, cy: number, r: number): number;
|
|
47
|
+
}
|
package/dist/grid.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positions side by side, `x` then `y`, by place, and a uniform grid over them built again at the
|
|
3
|
+
* first query after they move, so a circle finds the places within it without visiting the rest.
|
|
4
|
+
*/
|
|
5
|
+
export class Grid {
|
|
6
|
+
xy = new Float64Array(0);
|
|
7
|
+
/** How many places the list had. */
|
|
8
|
+
n = 0;
|
|
9
|
+
/** The cell size asked for, NaN for one from the spread; the grid widens it where the positions are spread too thin. */
|
|
10
|
+
cell = Number.NaN;
|
|
11
|
+
dirty = true;
|
|
12
|
+
/** The grid as last built: its origin, cell size and shape. */
|
|
13
|
+
x0 = 0;
|
|
14
|
+
y0 = 0;
|
|
15
|
+
size = 1;
|
|
16
|
+
cols = 0;
|
|
17
|
+
rows = 0;
|
|
18
|
+
/** By cell, where its entries start in `order`, one past the last cell included. */
|
|
19
|
+
starts = new Int32Array(1);
|
|
20
|
+
order = new Int32Array(0);
|
|
21
|
+
/** By place, the cell the last build put it in, -1 for none. */
|
|
22
|
+
home = new Int32Array(0);
|
|
23
|
+
/**
|
|
24
|
+
* Places `put` moved out of their cell since the last build, which every query tests on their own
|
|
25
|
+
* and skips in the cells, and by place whether it is one. Past a sixteenth of the places, the grid
|
|
26
|
+
* is built again.
|
|
27
|
+
*/
|
|
28
|
+
strays = [];
|
|
29
|
+
stray = new Uint8Array(0);
|
|
30
|
+
/** What the last `query` or `ring` found, the first `count` of it. */
|
|
31
|
+
found = new Int32Array(64);
|
|
32
|
+
count = 0;
|
|
33
|
+
/** Moves place `i` to (`x`, `y`) in `xy`, leaving the grid as it is where the place keeps its cell. */
|
|
34
|
+
put(i, x, y) {
|
|
35
|
+
this.xy[2 * i] = x;
|
|
36
|
+
this.xy[2 * i + 1] = y;
|
|
37
|
+
if (this.dirty)
|
|
38
|
+
return;
|
|
39
|
+
if (this.cols === 0 || i >= this.home.length) {
|
|
40
|
+
this.dirty = true;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const col = Math.floor((x - this.x0) / this.size);
|
|
44
|
+
const row = Math.floor((y - this.y0) / this.size);
|
|
45
|
+
const c = col >= 0 && col < this.cols && row >= 0 && row < this.rows ? row * this.cols + col : -2;
|
|
46
|
+
if (c === this.home[i] || this.stray[i] === 1)
|
|
47
|
+
return;
|
|
48
|
+
this.stray[i] = 1;
|
|
49
|
+
this.strays.push(i);
|
|
50
|
+
if (this.strays.length > Math.max(32, this.n >> 4))
|
|
51
|
+
this.dirty = true;
|
|
52
|
+
}
|
|
53
|
+
build() {
|
|
54
|
+
this.dirty = false;
|
|
55
|
+
for (const i of this.strays)
|
|
56
|
+
this.stray[i] = 0;
|
|
57
|
+
this.strays.length = 0;
|
|
58
|
+
const xy = this.xy;
|
|
59
|
+
const n = Math.min(this.n, xy.length >> 1);
|
|
60
|
+
let x0 = Number.POSITIVE_INFINITY;
|
|
61
|
+
let y0 = Number.POSITIVE_INFINITY;
|
|
62
|
+
let x1 = Number.NEGATIVE_INFINITY;
|
|
63
|
+
let y1 = Number.NEGATIVE_INFINITY;
|
|
64
|
+
for (let i = 0; i < n; i++) {
|
|
65
|
+
const x = xy[2 * i];
|
|
66
|
+
const y = xy[2 * i + 1];
|
|
67
|
+
if (x < x0)
|
|
68
|
+
x0 = x;
|
|
69
|
+
if (x > x1)
|
|
70
|
+
x1 = x;
|
|
71
|
+
if (y < y0)
|
|
72
|
+
y0 = y;
|
|
73
|
+
if (y > y1)
|
|
74
|
+
y1 = y;
|
|
75
|
+
}
|
|
76
|
+
if (!(x1 >= x0) || !(y1 >= y0)) {
|
|
77
|
+
this.cols = 0;
|
|
78
|
+
this.rows = 0;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Unasked, about two subjects a cell where they are spread evenly.
|
|
82
|
+
let size = Number.isNaN(this.cell)
|
|
83
|
+
? Math.sqrt((2 * Math.max(x1 - x0, 1e-9) * Math.max(y1 - y0, 1e-9)) / n)
|
|
84
|
+
: this.cell;
|
|
85
|
+
// At most about four cells a subject: a few far-flung positions would otherwise ask for millions.
|
|
86
|
+
const most = 4 * n + 64;
|
|
87
|
+
if ((Math.floor((x1 - x0) / size) + 1) * (Math.floor((y1 - y0) / size) + 1) > most)
|
|
88
|
+
size = Math.max(size, Math.sqrt(((x1 - x0) * (y1 - y0)) / n), (x1 - x0 + y1 - y0) / most);
|
|
89
|
+
const cols = Math.floor((x1 - x0) / size) + 1;
|
|
90
|
+
const rows = Math.floor((y1 - y0) / size) + 1;
|
|
91
|
+
this.x0 = x0;
|
|
92
|
+
this.y0 = y0;
|
|
93
|
+
this.size = size;
|
|
94
|
+
this.cols = cols;
|
|
95
|
+
this.rows = rows;
|
|
96
|
+
const cells = cols * rows;
|
|
97
|
+
if (this.starts.length < cells + 1)
|
|
98
|
+
this.starts = new Int32Array(cells + 1);
|
|
99
|
+
else
|
|
100
|
+
this.starts.fill(0, 0, cells + 1);
|
|
101
|
+
if (this.order.length < n)
|
|
102
|
+
this.order = new Int32Array(n);
|
|
103
|
+
if (this.home.length < n) {
|
|
104
|
+
this.home = new Int32Array(n);
|
|
105
|
+
this.stray = new Uint8Array(n);
|
|
106
|
+
}
|
|
107
|
+
const starts = this.starts;
|
|
108
|
+
const order = this.order;
|
|
109
|
+
// A position that is not a number is in no cell, and no circle finds it.
|
|
110
|
+
for (let i = 0; i < n; i++) {
|
|
111
|
+
const c = cellOf(xy, i, x0, y0, size, cols);
|
|
112
|
+
this.home[i] = c;
|
|
113
|
+
if (c >= 0)
|
|
114
|
+
starts[c + 1] = starts[c + 1] + 1;
|
|
115
|
+
}
|
|
116
|
+
for (let c = 0; c < cells; c++)
|
|
117
|
+
starts[c + 1] = starts[c + 1] + starts[c];
|
|
118
|
+
const placed = starts[cells];
|
|
119
|
+
// Filled from each cell's end back to its start: `starts[c + 1]`, the end of cell c, ends at its start.
|
|
120
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
121
|
+
const c = cellOf(xy, i, x0, y0, size, cols);
|
|
122
|
+
if (c < 0)
|
|
123
|
+
continue;
|
|
124
|
+
const k = starts[c + 1] - 1;
|
|
125
|
+
starts[c + 1] = k;
|
|
126
|
+
order[k] = i;
|
|
127
|
+
}
|
|
128
|
+
for (let c = 0; c < cells; c++)
|
|
129
|
+
starts[c] = starts[c + 1];
|
|
130
|
+
starts[cells] = placed;
|
|
131
|
+
}
|
|
132
|
+
push(i, count) {
|
|
133
|
+
if (count === this.found.length) {
|
|
134
|
+
const grown = new Int32Array(this.found.length * 2);
|
|
135
|
+
grown.set(this.found);
|
|
136
|
+
this.found = grown;
|
|
137
|
+
}
|
|
138
|
+
this.found[count] = i;
|
|
139
|
+
return count + 1;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Leaves in `found` the places within `r` of (`cx`, `cy`), by the test `inside` makes, `r2`
|
|
143
|
+
* being `r * r`: the cells the circle touches, and one more where an edge falls a hair from one.
|
|
144
|
+
*/
|
|
145
|
+
query(cx, cy, r, r2) {
|
|
146
|
+
if (this.dirty)
|
|
147
|
+
this.build();
|
|
148
|
+
this.count = 0;
|
|
149
|
+
if (this.cols === 0 || !(r >= 0))
|
|
150
|
+
return 0;
|
|
151
|
+
const size = this.size;
|
|
152
|
+
const c0 = Math.max(0, edge((cx - r - this.x0) / size, -1));
|
|
153
|
+
const c1 = Math.min(this.cols - 1, edge((cx + r - this.x0) / size, 1));
|
|
154
|
+
const r0 = Math.max(0, edge((cy - r - this.y0) / size, -1));
|
|
155
|
+
const r1 = Math.min(this.rows - 1, edge((cy + r - this.y0) / size, 1));
|
|
156
|
+
const xy = this.xy;
|
|
157
|
+
const order = this.order;
|
|
158
|
+
const starts = this.starts;
|
|
159
|
+
const any = this.strays.length > 0;
|
|
160
|
+
let count = 0;
|
|
161
|
+
for (let row = r0; row <= r1 && c0 <= c1; row++) {
|
|
162
|
+
const from = starts[row * this.cols + c0];
|
|
163
|
+
const to = starts[row * this.cols + c1 + 1];
|
|
164
|
+
for (let k = from; k < to; k++) {
|
|
165
|
+
const i = order[k];
|
|
166
|
+
if (any && this.stray[i] === 1)
|
|
167
|
+
continue;
|
|
168
|
+
const dx = xy[2 * i] - cx;
|
|
169
|
+
const dy = xy[2 * i + 1] - cy;
|
|
170
|
+
if (dx * dx + dy * dy <= r2)
|
|
171
|
+
count = this.push(i, count);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const i of this.strays) {
|
|
175
|
+
const dx = xy[2 * i] - cx;
|
|
176
|
+
const dy = xy[2 * i + 1] - cy;
|
|
177
|
+
if (dx * dx + dy * dy <= r2)
|
|
178
|
+
count = this.push(i, count);
|
|
179
|
+
}
|
|
180
|
+
this.count = count;
|
|
181
|
+
return count;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Leaves in `found` the places beyond `r` of (`cx`, `cy`) and within a cell's width more, from
|
|
185
|
+
* the cells that ring touches.
|
|
186
|
+
*/
|
|
187
|
+
ring(cx, cy, r) {
|
|
188
|
+
if (this.dirty)
|
|
189
|
+
this.build();
|
|
190
|
+
this.count = 0;
|
|
191
|
+
if (this.cols === 0 || !(r >= 0))
|
|
192
|
+
return 0;
|
|
193
|
+
const size = this.size;
|
|
194
|
+
const out = r + size;
|
|
195
|
+
const r2 = r * r;
|
|
196
|
+
const out2 = out * out;
|
|
197
|
+
const c0 = Math.max(0, Math.floor((cx - out - this.x0) / size));
|
|
198
|
+
const c1 = Math.min(this.cols - 1, Math.floor((cx + out - this.x0) / size));
|
|
199
|
+
const r0 = Math.max(0, Math.floor((cy - out - this.y0) / size));
|
|
200
|
+
const r1 = Math.min(this.rows - 1, Math.floor((cy + out - this.y0) / size));
|
|
201
|
+
const xy = this.xy;
|
|
202
|
+
let count = 0;
|
|
203
|
+
for (let row = r0; row <= r1; row++)
|
|
204
|
+
for (let col = c0; col <= c1; col++) {
|
|
205
|
+
// A cell wholly inside the circle holds nothing beyond it.
|
|
206
|
+
const ax = this.x0 + col * size - cx;
|
|
207
|
+
const ay = this.y0 + row * size - cy;
|
|
208
|
+
const far = Math.max(ax * ax, (ax + size) ** 2) + Math.max(ay * ay, (ay + size) ** 2);
|
|
209
|
+
if (far <= r2)
|
|
210
|
+
continue;
|
|
211
|
+
const c = row * this.cols + col;
|
|
212
|
+
for (let k = this.starts[c]; k < this.starts[c + 1]; k++) {
|
|
213
|
+
const i = this.order[k];
|
|
214
|
+
if (this.stray[i] === 1)
|
|
215
|
+
continue;
|
|
216
|
+
const dx = xy[2 * i] - cx;
|
|
217
|
+
const dy = xy[2 * i + 1] - cy;
|
|
218
|
+
const d2 = dx * dx + dy * dy;
|
|
219
|
+
if (d2 > r2 && d2 <= out2)
|
|
220
|
+
count = this.push(i, count);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
for (const i of this.strays) {
|
|
224
|
+
const dx = xy[2 * i] - cx;
|
|
225
|
+
const dy = xy[2 * i + 1] - cy;
|
|
226
|
+
const d2 = dx * dx + dy * dy;
|
|
227
|
+
if (d2 > r2 && d2 <= out2)
|
|
228
|
+
count = this.push(i, count);
|
|
229
|
+
}
|
|
230
|
+
this.count = count;
|
|
231
|
+
return count;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function cellOf(xy, i, x0, y0, size, cols) {
|
|
235
|
+
const x = xy[2 * i];
|
|
236
|
+
const y = xy[2 * i + 1];
|
|
237
|
+
if (!(x >= x0) || !(y >= y0))
|
|
238
|
+
return -1;
|
|
239
|
+
return Math.floor((x - x0) / size) + Math.floor((y - y0) / size) * cols;
|
|
240
|
+
}
|
|
241
|
+
/** The cell index `u` falls in, one further out toward `side` where it lies within 1e-6 of an edge. */
|
|
242
|
+
function edge(u, side) {
|
|
243
|
+
const c = Math.floor(u);
|
|
244
|
+
if (side < 0)
|
|
245
|
+
return u - c < 1e-6 ? c - 1 : c;
|
|
246
|
+
return c + 1 - u < 1e-6 ? c + 1 : c;
|
|
247
|
+
}
|
package/dist/handle.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface HandleHost<I, O> {
|
|
|
23
23
|
* it, on the handle it is given: `h`, or a read ahead's handle on its copy of the voice.
|
|
24
24
|
*/
|
|
25
25
|
record(h: VoiceHandle<I, O>, label: string, again: (h: VoiceHandle<I, O>) => void): void;
|
|
26
|
+
/** Whether `record` keeps a write made now, so a handle can skip making one it would drop. */
|
|
27
|
+
taping(): boolean;
|
|
26
28
|
}
|
|
27
29
|
/** Refuses a voice rate no clock can run at. */
|
|
28
30
|
export declare function finiteRate(rate: number): void;
|