@msb235/blits 0.7.0 → 0.8.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.
Files changed (156) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/README.md +22 -4
  3. package/dist/book.js +4 -4
  4. package/dist/chain.d.ts +1 -1
  5. package/dist/chain.js +57 -54
  6. package/dist/channels.d.ts +26 -5
  7. package/dist/channels.js +12 -8
  8. package/dist/clock.d.ts +8 -1
  9. package/dist/clock.js +14 -3
  10. package/dist/clone.d.ts +3 -0
  11. package/dist/clone.js +16 -1
  12. package/dist/closed.d.ts +1 -1
  13. package/dist/closed.js +3 -2
  14. package/dist/color.d.ts +0 -11
  15. package/dist/color.js +4 -24
  16. package/dist/columns.js +0 -1
  17. package/dist/copyout.d.ts +6 -0
  18. package/dist/copyout.js +38 -0
  19. package/dist/crowd.d.ts +7 -5
  20. package/dist/crowd.js +32 -18
  21. package/dist/cue.d.ts +3 -1
  22. package/dist/cue.js +33 -20
  23. package/dist/departed.d.ts +34 -0
  24. package/dist/departed.js +73 -0
  25. package/dist/easing.d.ts +5 -0
  26. package/dist/easing.js +31 -2
  27. package/dist/everyone.d.ts +56 -0
  28. package/dist/everyone.js +221 -0
  29. package/dist/fade.d.ts +8 -1
  30. package/dist/fade.js +36 -6
  31. package/dist/fill.d.ts +22 -13
  32. package/dist/fill.js +104 -41
  33. package/dist/fit.js +2 -1
  34. package/dist/fold.d.ts +37 -0
  35. package/dist/fold.js +213 -28
  36. package/dist/gather.d.ts +15 -9
  37. package/dist/gather.js +60 -25
  38. package/dist/gone.d.ts +32 -0
  39. package/dist/gone.js +144 -0
  40. package/dist/handle.d.ts +16 -5
  41. package/dist/handle.js +56 -6
  42. package/dist/held.d.ts +8 -5
  43. package/dist/held.js +56 -58
  44. package/dist/history.d.ts +17 -13
  45. package/dist/history.js +49 -28
  46. package/dist/hosts.js +57 -9
  47. package/dist/index.d.ts +9 -4
  48. package/dist/index.js +5 -2
  49. package/dist/keyfill.d.ts +7 -0
  50. package/dist/keyfill.js +124 -0
  51. package/dist/keyrows.d.ts +1 -0
  52. package/dist/keyrows.js +1 -1
  53. package/dist/lane.d.ts +32 -2
  54. package/dist/lane.js +5 -0
  55. package/dist/lanes.d.ts +32 -8
  56. package/dist/lanes.js +69 -37
  57. package/dist/leavings.d.ts +26 -0
  58. package/dist/leavings.js +70 -0
  59. package/dist/meet.js +14 -8
  60. package/dist/mixer.d.ts +43 -7
  61. package/dist/mixer.js +60 -11
  62. package/dist/motion.d.ts +2 -0
  63. package/dist/motion.js +4 -1
  64. package/dist/motions.d.ts +22 -15
  65. package/dist/motions.js +40 -34
  66. package/dist/move.js +31 -20
  67. package/dist/moved.d.ts +11 -0
  68. package/dist/moved.js +29 -0
  69. package/dist/origin.d.ts +19 -0
  70. package/dist/origin.js +68 -0
  71. package/dist/owed.d.ts +4 -4
  72. package/dist/owed.js +8 -8
  73. package/dist/owner.d.ts +1 -1
  74. package/dist/owner.js +2 -2
  75. package/dist/pace.d.ts +13 -5
  76. package/dist/pace.js +21 -8
  77. package/dist/pack.d.ts +3 -1
  78. package/dist/pack.js +15 -24
  79. package/dist/paging.d.ts +8 -2
  80. package/dist/paging.js +44 -4
  81. package/dist/patch.d.ts +14 -9
  82. package/dist/patch.js +83 -21
  83. package/dist/place.js +188 -30
  84. package/dist/project.d.ts +12 -1
  85. package/dist/project.js +101 -83
  86. package/dist/pull.d.ts +2 -0
  87. package/dist/pull.js +8 -0
  88. package/dist/qualify.js +10 -2
  89. package/dist/reading.d.ts +5 -0
  90. package/dist/reading.js +5 -0
  91. package/dist/record.d.ts +9 -0
  92. package/dist/record.js +42 -0
  93. package/dist/relink.d.ts +11 -5
  94. package/dist/relink.js +7 -5
  95. package/dist/restored.d.ts +41 -0
  96. package/dist/restored.js +91 -0
  97. package/dist/revive.d.ts +10 -3
  98. package/dist/revive.js +31 -23
  99. package/dist/roster.d.ts +11 -0
  100. package/dist/roster.js +46 -0
  101. package/dist/rotation.d.ts +29 -0
  102. package/dist/rotation.js +91 -0
  103. package/dist/rows.d.ts +5 -5
  104. package/dist/rows.js +5 -5
  105. package/dist/sample.d.ts +3 -2
  106. package/dist/sample.js +32 -15
  107. package/dist/scored.d.ts +57 -0
  108. package/dist/scored.js +196 -0
  109. package/dist/seek.js +97 -114
  110. package/dist/shared.d.ts +6 -0
  111. package/dist/shared.js +15 -0
  112. package/dist/signals.d.ts +24 -3
  113. package/dist/signals.js +50 -11
  114. package/dist/spans.js +7 -0
  115. package/dist/stack.d.ts +11 -0
  116. package/dist/stack.js +47 -0
  117. package/dist/standing.d.ts +10 -0
  118. package/dist/standing.js +79 -0
  119. package/dist/store.d.ts +3 -3
  120. package/dist/store.js +3 -3
  121. package/dist/strays.js +3 -0
  122. package/dist/tape.d.ts +8 -5
  123. package/dist/tape.js +21 -13
  124. package/dist/testing.d.ts +35 -0
  125. package/dist/testing.js +116 -0
  126. package/dist/touch.d.ts +6 -0
  127. package/dist/touch.js +28 -0
  128. package/dist/transport.d.ts +53 -6
  129. package/dist/transport.js +102 -20
  130. package/dist/turns.d.ts +18 -0
  131. package/dist/turns.js +72 -0
  132. package/dist/tweened.js +1 -1
  133. package/dist/types/channel.d.ts +46 -0
  134. package/dist/types/channel.js +1 -0
  135. package/dist/types/history.d.ts +139 -0
  136. package/dist/types/history.js +1 -0
  137. package/dist/types/mix.d.ts +336 -0
  138. package/dist/types/mix.js +1 -0
  139. package/dist/types/patch.d.ts +187 -0
  140. package/dist/types/patch.js +1 -0
  141. package/dist/types/place.d.ts +157 -0
  142. package/dist/types/place.js +1 -0
  143. package/dist/types/transport.d.ts +71 -0
  144. package/dist/types/transport.js +1 -0
  145. package/dist/types/voice.d.ts +323 -0
  146. package/dist/types/voice.js +1 -0
  147. package/dist/types.d.ts +7 -1162
  148. package/dist/unpage.d.ts +2 -2
  149. package/dist/unpage.js +55 -25
  150. package/dist/voice.d.ts +92 -22
  151. package/dist/voice.js +108 -43
  152. package/dist/wave.d.ts +42 -0
  153. package/dist/wave.js +62 -0
  154. package/dist/weigh.js +1 -1
  155. package/docs/schema.html +42 -36
  156. package/package.json +8 -3
package/dist/hosts.js CHANGED
@@ -2,14 +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';
7
+ import { goneIndex } from './gone.js';
6
8
  import { VoiceHandle } from './handle.js';
9
+ import { reorigin } from './held.js';
7
10
  import { noted, remember } from './history.js';
8
11
  import { hostOf, listed, markOf, readingAt } from './marks.js';
9
12
  import { nextFrame } from './move.js';
10
13
  import { descendants, ownWeight, signalled } from './owner.js';
11
14
  import { localNow } from './place.js';
12
15
  import { reading } from './reading.js';
16
+ import { heard } from './signals.js';
13
17
  import { record } from './tape.js';
14
18
  import { unreach } from './unreached.js';
15
19
  export function book(mix, opts) {
@@ -51,13 +55,16 @@ export function laneHost(mix) {
51
55
  for (let held = head; held !== null; held = held.next)
52
56
  if (held.voice === voice)
53
57
  return held;
54
- return mix.held(voice, subject, mix.now, slot);
58
+ return mix.held(voice, subject, slot);
55
59
  },
56
60
  forgot: (slot) => {
57
61
  // Only a voice over every subject keeps bits, and one gone may still be read back.
58
- for (const voice of mix.general)
62
+ for (const voice of mix.general) {
59
63
  unreach(voice, slot, false);
60
- for (const voice of mix.gone)
64
+ if (voice.sighted !== null)
65
+ unsight(voice, slot);
66
+ }
67
+ for (const voice of goneIndex(mix).general)
61
68
  unreach(voice, slot, false);
62
69
  },
63
70
  passes: (was, w) => mix.passes(was, w),
@@ -109,8 +116,13 @@ export function fits(mix, voice) {
109
116
  // A fill weighs a voice's owners once for every subject, which a signal on one would not be.
110
117
  if (voice.owner !== null && signalled(voice))
111
118
  return false;
112
- // A signal reading host input records it per probe under history, which a fill cannot.
113
- if (typeof spec.weight === 'function' && spec.weight.input && mix.opts.history?.inputs)
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)
114
126
  return false;
115
127
  // A locus on lanes gathers keys and fn members; a motion member keeps it on the general path.
116
128
  if (spec.locus !== undefined && voice.motion !== undefined)
@@ -125,17 +137,38 @@ export function fits(mix, voice) {
125
137
  return false;
126
138
  if (patch.state !== undefined || patch.step !== undefined)
127
139
  return false;
128
- if (mix.opts.history?.inputs && patch.reads !== undefined && patch.reads.length > 0)
129
- return false;
130
140
  const built = voice.built;
131
141
  return (built === null ||
132
142
  built.tracks.every((t, i) => t.lerp === undefined || t.lerp === voice.lerps[i]));
133
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
+ }
134
164
  /** A voice's patch kept state on a record: it is stateful from now on. */
135
165
  export function stateful(mix, voice) {
136
166
  if (voice.keeping)
137
167
  return;
138
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);
139
172
  mix.lanes?.invalidate();
140
173
  }
141
174
  export function handle(mix, voice) {
@@ -152,16 +185,28 @@ function handleHost(mix) {
152
185
  },
153
186
  changed: (voice) => {
154
187
  mix.frame = nextFrame();
188
+ reorigin(mix, voice, mix.now);
189
+ if (voice.holding !== null)
190
+ descendants(voice, (v) => reorigin(mix, v, mix.now));
155
191
  noted(mix, voice);
156
192
  mix.lanes?.refill();
157
193
  },
158
194
  // A delta read before an owner's seek, or a hit booked, is not taken as standing after it.
159
- sought: (voice) => {
195
+ sought: (voice, rebuild) => {
196
+ if (rebuild)
197
+ voice.rebuilds++;
198
+ let doubt = seekDoubt(mix, voice, rebuild);
160
199
  if (voice.holding !== null)
161
200
  descendants(voice, (v) => {
162
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;
163
207
  schedule(mix, v);
164
208
  });
209
+ return doubt;
165
210
  },
166
211
  fade: (voice, opts) => {
167
212
  if (voice.holding !== null && opts !== undefined && ('subject' in opts || opts.at === 'rest'))
@@ -179,11 +224,14 @@ function handleHost(mix) {
179
224
  return 0;
180
225
  if (voice.holding !== null)
181
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);
182
228
  if (voice.laned && mix.lanes !== null) {
183
- const w = mix.lanes.weightOf(voice.id, mix.chains.get(subject)?.slot ?? -1);
229
+ const w = mix.lanes.weightOf(voice.id, slot);
184
230
  if (w !== undefined)
185
231
  return w;
186
232
  }
233
+ if (voice.sharing)
234
+ return weightAt(voice, slot);
187
235
  return voice.subjects.get(subject)?.weight ?? 0;
188
236
  },
189
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, hex, mixHex, oklab, toHex } from './color.js';
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 { gate, lag, level, peak, slew } from './signals.js';
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, hex, mixHex, oklab, toHex } from './color.js';
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 { gate, lag, level, peak, slew } from './signals.js';
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;
@@ -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
@@ -5,6 +5,7 @@ import { type Built } from './patch.js';
5
5
  export interface KeyChannel {
6
6
  scalar: boolean;
7
7
  op: Numeric['op'];
8
+ plain: boolean;
8
9
  rest: number;
9
10
  axes: number;
10
11
  values: Float64Array;
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 the crowd
102
- * of single-subject motion voices, whose positions each belong to a voice of their own.
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. */
package/dist/lanes.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import type { Column } from './columns.js';
2
+ import { copy } from './copyout.js';
2
3
  import { type Crowd, row } from './crowd.js';
3
- import { call, foldKeys, one, parting, readKeyed, reset, run, signalled, subjectAt } from './fill.js';
4
- import { fold, foldDelta, foldInto, foldRun, gate, gather } from './gather.js';
4
+ import { call, foldKeys, one, parting, readKeyed, reset, run, runEveryone, signalled, subjectAt } from './fill.js';
5
+ import { fold, foldDelta, foldInto, foldRun, gate, gather, unplain } from './gather.js';
5
6
  import { type Lane, type Laned, type Locus } from './lane.js';
6
7
  import type { Watcher } from './motions.js';
7
- import { type absent, Numbers } from './numbers.js';
8
+ import { absent, Numbers } from './numbers.js';
8
9
  import { Owed } from './owed.js';
9
10
  import type { Channel } from './types.js';
10
11
  import type { Subject, Voice } from './voice.js';
@@ -61,7 +62,7 @@ export declare class Lanes<I, O> implements Watcher {
61
62
  /** The lanes over every subject that have started playing, by epoch. */
62
63
  dense: Lane<I, O>[];
63
64
  readonly byId: Map<number, Lane<I, O>>;
64
- /** The crowds of single-subject motion voices, one per channel, and which holds each voice. */
65
+ /** The crowds of single-subject voices, one per list of channels, and which holds each voice. */
65
66
  crowds: Crowd<I, O>[];
66
67
  readonly crowdOf: Map<number, Crowd<I, O>>;
67
68
  /** Whether two crowds share a channel, so their rows must interleave in voice order. */
@@ -71,13 +72,20 @@ export declare class Lanes<I, O> implements Watcher {
71
72
  /** The lane and record `one` is folding, whose band state a rest-less channel reads. */
72
73
  folding: Lane<I, O> | null;
73
74
  rec: Subject<unknown> | null;
75
+ /** The numbers a fill's methods hand each other: see `Arg`. */
76
+ readonly arg: Float64Array<ArrayBuffer>;
74
77
  /** While a fill gathers a locus: that locus and the member gathering, where folds go instead. */
75
78
  into: Locus<I, O> | null;
76
79
  intoId: number;
77
80
  readonly lawsByKey: Map<string, Float64Array<ArrayBufferLike>>;
78
81
  lastLaw: Float64Array | null;
79
- /** The lanes the last `meet` gave a position, and whether it placed a crowd row. */
82
+ /**
83
+ * The lanes the last `meet` gave a position, the first `metN` of `met`, and whether it placed a
84
+ * crowd row. Counted, not cut to length: an array emptied by its length takes new room at its
85
+ * next push, about 150 B a subject met (the `swap` row, bench/allocs.mjs, 2026-10-09).
86
+ */
80
87
  readonly met: number[];
88
+ metN: number;
81
89
  metCrowd: boolean;
82
90
  /** By subject number, the laned voices its probes fold on the general path this fill. */
83
91
  readonly owed: Owed;
@@ -137,8 +145,9 @@ export declare class Lanes<I, O> implements Watcher {
137
145
  keeps: boolean;
138
146
  now: number;
139
147
  filledAt: number;
140
- /** `reading.moved` at the last fill, so a retarget or push between two probes refills. */
148
+ /** `reading.moved` and `reading.inputs` at the last fill: see `current`. */
141
149
  moved: number;
150
+ inputs: number;
142
151
  /** The `now` of the latest probe, and the probe count before its first, to tell a probe this frame. */
143
152
  frameAt: number;
144
153
  frameProbes: number;
@@ -191,6 +200,13 @@ export declare class Lanes<I, O> implements Watcher {
191
200
  prepare(slot: number, subject: I, version: number, head: Subject<unknown> | null): boolean;
192
201
  /** Whether this frame's lanes are filled and nothing since asks `begin` to look again. */
193
202
  filled(now: number, version: number): boolean;
203
+ /**
204
+ * Whether the last fill still stands: everything a fill reads that can change between two probes,
205
+ * each stamped by `fillAll`. The clock; the mix's version, which voices coming, going, starting
206
+ * and stopping move; a motion's retargets and pushes; and a `level` set. Whatever else can change
207
+ * between probes keeps its voice off lanes in `fits`.
208
+ */
209
+ current(now: number, version: number): boolean;
194
210
  /**
195
211
  * What a probe does before its subject, once a frame's lanes are settled: qualifies and fills as
196
212
  * needed. Says whether the subject takes the general path or the lanes are filled.
@@ -215,8 +231,6 @@ export declare class Lanes<I, O> implements Watcher {
215
231
  * would write: each number within 1e-9 of its rest.
216
232
  */
217
233
  rests(slot: number): boolean;
218
- /** Writes a subject's laned values into a pose, each array channel into a new array. */
219
- copy(slot: number, pose: Record<string, unknown>): void;
220
234
  forget(slot: number): void;
221
235
  grow(size: number): void;
222
236
  /**
@@ -224,16 +238,25 @@ export declare class Lanes<I, O> implements Watcher {
224
238
  * next fill, and a lane keeps the sample the old one gave.
225
239
  */
226
240
  stretchChanged(id: number, s: number): void;
241
+ /**
242
+ * A voice that kept one record for every subject keeps one each from now on: each of its lane's
243
+ * positions takes the record `of` gives its subject.
244
+ */
245
+ rerecord(id: number, of: (subject: I, slot: number) => Subject<unknown>): void;
246
+ /** A voice's clock moved the `shown` of records it had already placed: copies them again. */
247
+ reshown(id: number): void;
227
248
  /** Something on a voice changed: a crowd copies it again at the next fill. */
228
249
  voiceChanged(id: number): void;
229
250
  }
230
251
  declare const methods: {
252
+ copy: typeof copy;
231
253
  row: typeof row;
232
254
  signalled: typeof signalled;
233
255
  parting: typeof parting;
234
256
  subjectAt: typeof subjectAt;
235
257
  reset: typeof reset;
236
258
  run: typeof run;
259
+ runEveryone: typeof runEveryone;
237
260
  one: typeof one;
238
261
  foldKeys: typeof foldKeys;
239
262
  readKeyed: typeof readKeyed;
@@ -244,6 +267,7 @@ declare const methods: {
244
267
  foldRun: typeof foldRun;
245
268
  foldInto: typeof foldInto;
246
269
  gate: typeof gate;
270
+ unplain: typeof unplain;
247
271
  };
248
272
  type Methods = typeof methods;
249
273
  export interface Lanes<I, O> extends Methods {