@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.
Files changed (173) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +3 -3
  3. package/dist/ahead.d.ts +26 -0
  4. package/dist/ahead.js +264 -0
  5. package/dist/around.d.ts +11 -0
  6. package/dist/around.js +32 -0
  7. package/dist/bare.js +15 -7
  8. package/dist/blank.d.ts +10 -0
  9. package/dist/blank.js +26 -0
  10. package/dist/blend.d.ts +2 -1
  11. package/dist/blend.js +1 -6
  12. package/dist/bound.d.ts +70 -0
  13. package/dist/bound.js +351 -0
  14. package/dist/chain.d.ts +2 -1
  15. package/dist/chain.js +7 -5
  16. package/dist/changes.d.ts +24 -0
  17. package/dist/changes.js +189 -0
  18. package/dist/channels.d.ts +39 -13
  19. package/dist/channels.js +14 -5
  20. package/dist/color.d.ts +12 -10
  21. package/dist/color.js +25 -25
  22. package/dist/columns.js +7 -3
  23. package/dist/contribution.d.ts +30 -0
  24. package/dist/contribution.js +374 -0
  25. package/dist/copyout.d.ts +10 -0
  26. package/dist/copyout.js +69 -24
  27. package/dist/crowd.d.ts +8 -2
  28. package/dist/crowd.js +4 -5
  29. package/dist/cue.d.ts +8 -1
  30. package/dist/cue.js +39 -13
  31. package/dist/cut.d.ts +8 -0
  32. package/dist/cut.js +4 -0
  33. package/dist/drop.d.ts +9 -0
  34. package/dist/drop.js +84 -0
  35. package/dist/due.d.ts +24 -5
  36. package/dist/due.js +113 -33
  37. package/dist/easing.d.ts +4 -4
  38. package/dist/easing.js +3 -3
  39. package/dist/everyone.d.ts +3 -2
  40. package/dist/everyone.js +3 -3
  41. package/dist/fade.d.ts +2 -1
  42. package/dist/fade.js +2 -1
  43. package/dist/fill.d.ts +3 -2
  44. package/dist/fill.js +51 -19
  45. package/dist/fit.d.ts +44 -26
  46. package/dist/fit.js +10 -9
  47. package/dist/fold.d.ts +4 -37
  48. package/dist/fold.js +5 -513
  49. package/dist/gather.d.ts +12 -7
  50. package/dist/gather.js +179 -44
  51. package/dist/gone.js +2 -2
  52. package/dist/grid.d.ts +47 -0
  53. package/dist/grid.js +247 -0
  54. package/dist/handle.d.ts +2 -0
  55. package/dist/handle.js +14 -8
  56. package/dist/held.d.ts +2 -1
  57. package/dist/history.d.ts +6 -5
  58. package/dist/history.js +7 -9
  59. package/dist/hosts.js +9 -15
  60. package/dist/index.d.ts +5 -4
  61. package/dist/keyfill.d.ts +1 -1
  62. package/dist/keyfill.js +5 -3
  63. package/dist/keyrows.d.ts +12 -0
  64. package/dist/keyrows.js +99 -12
  65. package/dist/lane.d.ts +36 -6
  66. package/dist/lane.js +32 -1
  67. package/dist/lanes.d.ts +23 -8
  68. package/dist/lanes.js +29 -8
  69. package/dist/leavings.d.ts +2 -2
  70. package/dist/leavings.js +1 -1
  71. package/dist/lives.d.ts +47 -0
  72. package/dist/lives.js +180 -0
  73. package/dist/meet.d.ts +2 -2
  74. package/dist/meet.js +90 -4
  75. package/dist/mixer.d.ts +54 -36
  76. package/dist/mixer.js +53 -171
  77. package/dist/motion.d.ts +14 -118
  78. package/dist/motion.js +16 -8
  79. package/dist/motions.d.ts +62 -155
  80. package/dist/motions.js +56 -370
  81. package/dist/moved.d.ts +1 -1
  82. package/dist/moved.js +16 -7
  83. package/dist/named.d.ts +0 -1
  84. package/dist/named.js +20 -2
  85. package/dist/numbers.d.ts +2 -0
  86. package/dist/numbers.js +4 -0
  87. package/dist/owed.d.ts +2 -0
  88. package/dist/owed.js +4 -0
  89. package/dist/pack.d.ts +2 -1
  90. package/dist/pack.js +2 -3
  91. package/dist/patch.d.ts +7 -3
  92. package/dist/patch.js +6 -2
  93. package/dist/places.d.ts +53 -0
  94. package/dist/places.js +171 -0
  95. package/dist/plot.d.ts +12 -0
  96. package/dist/plot.js +52 -0
  97. package/dist/project.d.ts +4 -4
  98. package/dist/project.js +9 -212
  99. package/dist/pull.js +11 -8
  100. package/dist/qualify.js +84 -21
  101. package/dist/reading.d.ts +6 -0
  102. package/dist/reading.js +5 -0
  103. package/dist/record.d.ts +5 -1
  104. package/dist/record.js +3 -2
  105. package/dist/relink.d.ts +1 -1
  106. package/dist/restored.d.ts +2 -2
  107. package/dist/restored.js +1 -1
  108. package/dist/rests.d.ts +9 -0
  109. package/dist/rests.js +76 -0
  110. package/dist/revive.d.ts +3 -2
  111. package/dist/revive.js +2 -2
  112. package/dist/{locus.d.ts → role.d.ts} +10 -9
  113. package/dist/{locus.js → role.js} +3 -3
  114. package/dist/roles.d.ts +10 -0
  115. package/dist/roles.js +158 -0
  116. package/dist/roster.js +3 -3
  117. package/dist/rotation.d.ts +1 -1
  118. package/dist/rotation.js +1 -1
  119. package/dist/rows.js +1 -1
  120. package/dist/sample.d.ts +2 -2
  121. package/dist/sample.js +33 -7
  122. package/dist/seek.js +5 -5
  123. package/dist/setting.d.ts +27 -0
  124. package/dist/setting.js +55 -0
  125. package/dist/shared.d.ts +1 -1
  126. package/dist/shared.js +1 -1
  127. package/dist/signals.d.ts +64 -32
  128. package/dist/signals.js +20 -18
  129. package/dist/stack.d.ts +5 -5
  130. package/dist/stack.js +5 -5
  131. package/dist/standing.d.ts +2 -1
  132. package/dist/standing.js +3 -2
  133. package/dist/store.d.ts +9 -0
  134. package/dist/store.js +24 -0
  135. package/dist/strays.d.ts +2 -1
  136. package/dist/strays.js +20 -13
  137. package/dist/stretch.d.ts +112 -0
  138. package/dist/stretch.js +9 -0
  139. package/dist/subject.d.ts +142 -0
  140. package/dist/subject.js +1 -0
  141. package/dist/tape.d.ts +9 -5
  142. package/dist/tape.js +5 -1
  143. package/dist/then.d.ts +9 -0
  144. package/dist/then.js +57 -0
  145. package/dist/ticker.d.ts +4 -4
  146. package/dist/transport.d.ts +0 -8
  147. package/dist/transport.js +0 -4
  148. package/dist/turns.d.ts +4 -3
  149. package/dist/turns.js +4 -3
  150. package/dist/types/arc.d.ts +12 -11
  151. package/dist/types/channel.d.ts +12 -8
  152. package/dist/types/history.d.ts +35 -19
  153. package/dist/types/mix.d.ts +222 -163
  154. package/dist/types/motion.d.ts +151 -0
  155. package/dist/types/motion.js +1 -0
  156. package/dist/types/patch.d.ts +67 -34
  157. package/dist/types/place.d.ts +51 -36
  158. package/dist/types/transport.d.ts +20 -18
  159. package/dist/types/voice.d.ts +136 -70
  160. package/dist/types/watch.d.ts +88 -40
  161. package/dist/types.d.ts +1 -0
  162. package/dist/voice.d.ts +7 -143
  163. package/dist/voice.js +28 -79
  164. package/dist/warn.d.ts +2 -0
  165. package/dist/warn.js +1 -1
  166. package/dist/watch.d.ts +2 -2
  167. package/dist/watch.js +5 -3
  168. package/dist/wave.d.ts +4 -3
  169. package/dist/wave.js +3 -2
  170. package/dist/weigh.d.ts +7 -1
  171. package/dist/weigh.js +27 -0
  172. package/docs/schema.html +212 -34
  173. package/package.json +2 -2
package/dist/mixer.d.ts CHANGED
@@ -1,31 +1,40 @@
1
1
  import { type Arc } from './arcs.js';
2
+ import { around } from './around.js';
2
3
  import { blended, laneRead, shareKept } from './blend.js';
3
4
  import type { Book } from './book.js';
4
5
  import { aims, chain, linkable, relink } from './chain.js';
5
6
  import { type LerpInto } from './channels.js';
6
7
  import type { Column } from './columns.js';
8
+ import { baseFor, contribution, isRest, keyed, read, slopeFor, tick } from './contribution.js';
7
9
  import { DepartedIndex } from './departed.js';
8
- import type { Due } from './due.js';
10
+ import { drop, forgetIn, recordDrop, releaseIn } from './drop.js';
11
+ import { DueQueue } from './due.js';
9
12
  import { parting } from './fade.js';
10
- import { apply, baseFor, clamp, contribution, fold, foldDetour, folded, foldLoci, foldSharers, foldWith, isRest, type Key, keyed, linked, owedBy, passes, read, shared, slopeFor, tick } from './fold.js';
13
+ import { apply, clamp, fold, foldDetour, folded, foldSharers, foldWith, type Key, linked, owedBy, passes, shared } from './fold.js';
11
14
  import { GoneIndex } from './gone.js';
12
15
  import type { HandleHost } from './handle.js';
13
16
  import { held, shownOf, sinceOf, unreachedOf } from './held.js';
14
17
  import { Lanes } from './lanes.js';
15
- import { foldLocus, type LocusScratch } from './locus.js';
16
- import type { MotionOwner, Motions } from './motions.js';
18
+ import type { Motions } from './motions.js';
17
19
  import type { Pace } from './pace.js';
18
20
  import { Keys } from './paging.js';
21
+ import type { Places } from './places.js';
22
+ import { moved, place, plot, put } from './plot.js';
19
23
  import { keep } from './pull.js';
20
24
  import { Steps } from './relink.js';
25
+ import { atRest, restful, rests, restsKey, restsLaned, still } from './rests.js';
21
26
  import { Outs } from './revive.js';
27
+ import { foldRole, type RoleScratch } from './role.js';
28
+ import { foldRoles } from './roles.js';
22
29
  import { ScoreIndex } from './scored.js';
23
30
  import { Fitting } from './spans.js';
24
31
  import { Store } from './store.js';
32
+ import type { MotionOwner } from './stretch.js';
33
+ import type { Subject } from './subject.js';
25
34
  import { type Announced, Transport } from './transport.js';
26
35
  import type { ArcHandle, ArcSpec, BlendMember, Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, VoiceSpec } from './types.js';
27
- import { type Subject, type Voice } from './voice.js';
28
- import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, weigh, weighOwned } from './weigh.js';
36
+ import { type Voice } from './voice.js';
37
+ import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, primeLaned, weigh, weighOwned } from './weigh.js';
29
38
  /**
30
39
  * What a mix takes where its options say nothing. Not exported from the package: the suite turns
31
40
  * `lanes` off to run every test on the general path, which is what makes each one an equivalence check.
@@ -67,7 +76,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
67
76
  fields: Record<string, unknown>;
68
77
  } | undefined;
69
78
  /** Each subject's last two poses and when they were probed, for `from: 'current'`. */
70
- pose: Store<I, {
79
+ lastPoses: Store<I, {
71
80
  pose: O;
72
81
  at: number;
73
82
  prev: O | undefined;
@@ -91,7 +100,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
91
100
  /** The pose `pull` folds a subject into where it cannot read straight from the lanes. */
92
101
  scratch: O | undefined;
93
102
  /** Live voices by the earliest mix time `moveTo` would change each, a binary heap. */
94
- due: Due<I, O>[];
103
+ due: DueQueue<I, O>;
95
104
  /** Voices retired since `moveTo` last pruned them, in the order they retired. */
96
105
  retired: Voice<I, O>[];
97
106
  /** Visit every voice each sync, as before the due queue; for tests that compare the two. */
@@ -135,6 +144,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
135
144
  */
136
145
  chains: Store<I, Subject<unknown>>;
137
146
  version: number;
147
+ /** The positions `plot` gave, which a voice's `within` reads: see `bound.ts`. */
148
+ places: Places<I> | null;
138
149
  /** What `prime` reads its clock, voice time and pass from: see `Primed`. */
139
150
  readonly primed: Float64Array<ArrayBuffer>;
140
151
  readonly steps: Steps<Voice<I, O>, I>;
@@ -166,8 +177,10 @@ export declare class Mixer<I, O> implements Mix<I, O> {
166
177
  unshared: boolean;
167
178
  /** 0 where a fold is a walk of the chain: see `detour` in `everyone.ts`. */
168
179
  detours: number;
169
- /** How many voices in the list carry a locus; with none, a fold allocates nothing. */
170
- loci: number;
180
+ /** How many voices in the list carry a role; with none, a fold allocates nothing. */
181
+ roleVoices: number;
182
+ /** The tags the mix's options declare roles. */
183
+ readonly roleTags: readonly string[] | undefined;
171
184
  /** What every motion patch it plays asks for its voice's time, holding this mix weakly. */
172
185
  owner: MotionOwner | null;
173
186
  /** How many voices in the list are anchored, so a sync with none skips placing them. */
@@ -191,9 +204,9 @@ export declare class Mixer<I, O> implements Mix<I, O> {
191
204
  readonly lerpsInto: (LerpInto | undefined)[];
192
205
  /** By slot, a stock scalar channel's arithmetic, which a fold applies without calling the channel. */
193
206
  readonly scalars: ('sum' | 'mul' | 'max' | undefined)[];
194
- /** What a fold with a locus in play gathers into, one per depth of folds under way. */
195
- readonly locusScratch: LocusScratch<I, O>[];
196
- locusDepth: number;
207
+ /** What a fold with a role in play gathers into, one per depth of folds under way. */
208
+ readonly roleScratch: RoleScratch<I, O>[];
209
+ roleDepth: number;
197
210
  readonly slotOf: Map<string, number>;
198
211
  lanes: Lanes<I, O> | null;
199
212
  handles: HandleHost<I, O> | null;
@@ -209,6 +222,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
209
222
  keys: Keys | null;
210
223
  /** With a history store, the voices it paged out whose handles are still held; null without one. */
211
224
  outs: Outs | null;
225
+ readonly pose: () => O;
212
226
  constructor(kit: Kit<O>, opts: MixOptions,
213
227
  /** The lanes an entry was built with; null in one that leaves them out. */
214
228
  lanesOf: typeof Lanes | null);
@@ -237,7 +251,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
237
251
  off: number;
238
252
  };
239
253
  cue(spec: VoiceSpec<I, O>): Handle<I>;
240
- blend(members: readonly (Patch<I, O, unknown> | BlendMember<I, O>)[], by: Signal<I>, spec?: Omit<VoiceSpec<I, O>, 'patch' | 'weight' | 'locus'>): Handle<I>[];
254
+ blend(members: readonly (Patch<I, O, unknown> | BlendMember<I, O>)[], by: Signal<I>, spec?: Omit<VoiceSpec<I, O>, 'patch' | 'weight' | 'role' | 'locus' | 'within'>): Handle<I>[];
241
255
  owns(spec: OwnerSpec<I>): Handle<I>;
242
256
  span(spec: SpanSpec<I>): SpanHandle<I>;
243
257
  arc(spec: ArcSpec<I, O>): ArcHandle<I>;
@@ -260,27 +274,17 @@ export declare class Mixer<I, O> implements Mix<I, O> {
260
274
  /** Throws for a mix taken off its transport. */
261
275
  attached(): void;
262
276
  marks(from: number, to: number): Marked[];
263
- atRest(subject: I): boolean;
264
- rests(pose: O): boolean;
265
277
  rebase(): void;
266
278
  voices(tag?: string): Handle<I>[];
267
279
  get live(): boolean;
268
280
  get inert(): boolean;
269
- private restful;
270
281
  get rate(): number;
271
282
  set rate(r: number);
272
283
  ramp(rate: number, over: number): void;
273
- /** Whether a voice will change no pose from `now` on, short of a change made to it. */
274
- still(voice: Voice<I, O>, now: number): boolean;
275
284
  close(): void;
276
285
  mute(opts?: {
277
286
  over?: number;
278
287
  }): void;
279
- drop(subject: I): void;
280
- /** Lets `voice` go of a dropped subject. Methods rather than closures, so a drop makes neither. */
281
- private forgetIn;
282
- private releaseIn;
283
- private recordDrop;
284
288
  touch(subject?: I): void;
285
289
  drain<E = unknown>(tag?: string): Sent<I, E>[];
286
290
  /** Voices whose stop 0 is mid-computation, so a fold for one cannot re-enter itself. */
@@ -292,15 +296,13 @@ export declare class Mixer<I, O> implements Mix<I, O> {
292
296
  */
293
297
  restStamps: Store<I, number> | null;
294
298
  /** What a probe's record of rest is good for: moved by a new frame, version, relink, or stir. */
295
- private restsEpoch;
296
- private restsFrame;
297
- private restsVersion;
298
- private restsRelinks;
299
- private restsStirs;
299
+ restsEpoch: number;
300
+ restsFrame: number;
301
+ restsVersion: number;
302
+ restsRelinks: number;
303
+ restsStirs: number;
300
304
  /** The pose `atRest` folds into where no probe this frame answers. */
301
- private restScratch;
302
- private restsLaned;
303
- private restsKey;
305
+ restScratch: O | undefined;
304
306
  }
305
307
  declare const methods: {
306
308
  fold: typeof fold;
@@ -308,7 +310,7 @@ declare const methods: {
308
310
  linked: typeof linked;
309
311
  foldWith: typeof foldWith;
310
312
  owedBy: typeof owedBy;
311
- foldLoci: typeof foldLoci;
313
+ foldRoles: typeof foldRoles;
312
314
  foldDetour: typeof foldDetour;
313
315
  foldSharers: typeof foldSharers;
314
316
  shared: typeof shared;
@@ -323,6 +325,7 @@ declare const methods: {
323
325
  baseFor: typeof baseFor;
324
326
  slopeFor: typeof slopeFor;
325
327
  prime: typeof prime;
328
+ primeLaned: typeof primeLaned;
326
329
  capped: typeof capped;
327
330
  horizonFor: typeof horizonFor;
328
331
  weigh: typeof weigh;
@@ -344,7 +347,22 @@ declare const methods: {
344
347
  sinceOf: typeof sinceOf;
345
348
  shownOf: typeof shownOf;
346
349
  keep: typeof keep;
347
- foldLocus: typeof foldLocus;
350
+ foldRole: typeof foldRole;
351
+ atRest: typeof atRest;
352
+ rests: typeof rests;
353
+ restful: typeof restful;
354
+ still: typeof still;
355
+ restsLaned: typeof restsLaned;
356
+ restsKey: typeof restsKey;
357
+ plot: typeof plot;
358
+ place: typeof place;
359
+ moved: typeof moved;
360
+ put: typeof put;
361
+ around: typeof around;
362
+ drop: typeof drop;
363
+ forgetIn: typeof forgetIn;
364
+ releaseIn: typeof releaseIn;
365
+ recordDrop: typeof recordDrop;
348
366
  };
349
367
  type Methods = typeof methods;
350
368
  export interface Mixer<I, O> extends Methods {
@@ -352,13 +370,13 @@ export interface Mixer<I, O> extends Methods {
352
370
  /** Makes a mix the host asked for, with the registry's history where it names none, and lists it there. */
353
371
  export declare function made<I, O, H>(kit: Kit<O>, opts: MixOptions<H>, lanes: typeof Lanes | null): Mix<I, O, H>;
354
372
  /**
355
- * The one that ships: every form on the CPU, probed per subject on demand.
373
+ * The engine that ships, running every form on the CPU. It probes per subject on demand.
356
374
  *
357
375
  * @category engine
358
376
  */
359
377
  export declare const mixer: Engine;
360
378
  /**
361
- * Makes a mix over a kit, on the engine the options name or the stock one.
379
+ * Makes a mix over a kit. It runs on the engine the options name, or the stock one.
362
380
  *
363
381
  * @category mix
364
382
  */
package/dist/mixer.js CHANGED
@@ -1,35 +1,42 @@
1
1
  import { cueArc } from './arcs.js';
2
+ import { around } from './around.js';
3
+ import { blanks } from './blank.js';
2
4
  import { blended, blendOf, laneRead, shareKept } from './blend.js';
3
- import { aims, chain, headOf, linkable, relink, unlink } from './chain.js';
5
+ import { aims, chain, linkable, relink } from './chain.js';
4
6
  import { lerpInto, STOCK_BAND, scalarOp } from './channels.js';
5
- import { cue } from './cue.js';
7
+ import { baseFor, contribution, isRest, keyed, read, slopeFor, tick } from './contribution.js';
8
+ import { cue, roleTags } from './cue.js';
6
9
  import { DepartedIndex } from './departed.js';
7
- import { beginFade, leaveVoice, parting } from './fade.js';
8
- import { apply, baseFor, clamp, contribution, fold, foldDetour, folded, foldLoci, foldSharers, foldWith, isRest, keyed, linked, near, owedBy, passes, read, shared, slopeFor, tick, } from './fold.js';
9
- import { GoneIndex, goneIndex } from './gone.js';
10
+ import { drop, forgetIn, recordDrop, releaseIn } from './drop.js';
11
+ import { DueQueue } from './due.js';
12
+ import { beginFade, parting } from './fade.js';
13
+ import { apply, clamp, fold, foldDetour, folded, foldSharers, foldWith, linked, owedBy, passes, shared, } from './fold.js';
14
+ import { GoneIndex } from './gone.js';
10
15
  import { held, shownOf, sinceOf, unreachedOf } from './held.js';
11
- import { releasing } from './history.js';
12
16
  import { book, laneHost } from './hosts.js';
13
17
  import { Lanes } from './lanes.js';
14
- import { foldLocus } from './locus.js';
15
18
  import { listed } from './marks.js';
16
19
  import { nextFrame } from './move.js';
17
20
  import { ownerPatch } from './owner.js';
18
21
  import { Keys } from './paging.js';
22
+ import { moved, place, plot, put } from './plot.js';
19
23
  import { projectAll } from './project.js';
20
24
  import { keep, pull } from './pull.js';
21
25
  import { Steps } from './relink.js';
26
+ import { atRest, restful, rests, restsKey, restsLaned, still } from './rests.js';
22
27
  import { Outs } from './revive.js';
28
+ import { foldRole } from './role.js';
29
+ import { foldRoles } from './roles.js';
23
30
  import { ScoreIndex } from './scored.js';
24
31
  import { seek } from './seek.js';
25
32
  import { Fitting, refit } from './spans.js';
26
- import { Masked, Store } from './store.js';
27
- import { record, unplayable } from './tape.js';
33
+ import { Store } from './store.js';
34
+ import { record } from './tape.js';
28
35
  import { touch } from './touch.js';
29
36
  import { announce, Transport } from './transport.js';
30
37
  import { none } from './voice.js';
31
38
  import { gave, historyFor, register, unregister } from './watch.js';
32
- import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, weigh, weighOwned, } from './weigh.js';
39
+ import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, primeLaned, weigh, weighOwned, } from './weigh.js';
33
40
  /**
34
41
  * What a mix takes where its options say nothing. Not exported from the package: the suite turns
35
42
  * `lanes` off to run every test on the general path, which is what makes each one an equivalence check.
@@ -63,7 +70,7 @@ export class Mixer {
63
70
  hostLog = [];
64
71
  hostThen;
65
72
  /** Each subject's last two poses and when they were probed, for `from: 'current'`. */
66
- pose = new Store();
73
+ lastPoses = new Store();
67
74
  /** The clock this mix plays on, which also holds its tape and the marks announced on it. */
68
75
  transport;
69
76
  /** Its place among the mixes on its transport, in the order they joined. */
@@ -82,7 +89,7 @@ export class Mixer {
82
89
  /** The pose `pull` folds a subject into where it cannot read straight from the lanes. */
83
90
  scratch;
84
91
  /** Live voices by the earliest mix time `moveTo` would change each, a binary heap. */
85
- due = [];
92
+ due = new DueQueue();
86
93
  /** Voices retired since `moveTo` last pruned them, in the order they retired. */
87
94
  retired = [];
88
95
  /** Visit every voice each sync, as before the due queue; for tests that compare the two. */
@@ -151,6 +158,8 @@ export class Mixer {
151
158
  */
152
159
  chains = new Store();
153
160
  version = 0;
161
+ /** The positions `plot` gave, which a voice's `within` reads: see `bound.ts`. */
162
+ places = null;
154
163
  /** What `prime` reads its clock, voice time and pass from: see `Primed`. */
155
164
  primed = new Float64Array(3 /* Primed.SIZE */);
156
165
  steps = new Steps();
@@ -182,8 +191,10 @@ export class Mixer {
182
191
  unshared = false;
183
192
  /** 0 where a fold is a walk of the chain: see `detour` in `everyone.ts`. */
184
193
  detours = 0;
185
- /** How many voices in the list carry a locus; with none, a fold allocates nothing. */
186
- loci = 0;
194
+ /** How many voices in the list carry a role; with none, a fold allocates nothing. */
195
+ roleVoices = 0;
196
+ /** The tags the mix's options declare roles. */
197
+ roleTags;
187
198
  /** What every motion patch it plays asks for its voice's time, holding this mix weakly. */
188
199
  owner = null;
189
200
  /** How many voices in the list are anchored, so a sync with none skips placing them. */
@@ -218,9 +229,9 @@ export class Mixer {
218
229
  lerpsInto;
219
230
  /** By slot, a stock scalar channel's arithmetic, which a fold applies without calling the channel. */
220
231
  scalars;
221
- /** What a fold with a locus in play gathers into, one per depth of folds under way. */
222
- locusScratch = [];
223
- locusDepth = 0;
232
+ /** What a fold with a role in play gathers into, one per depth of folds under way. */
233
+ roleScratch = [];
234
+ roleDepth = 0;
224
235
  slotOf = new Map();
225
236
  lanes;
226
237
  handles = null;
@@ -236,6 +247,7 @@ export class Mixer {
236
247
  keys = null;
237
248
  /** With a history store, the voices it paged out whose handles are still held; null without one. */
238
249
  outs = null;
250
+ pose;
239
251
  constructor(kit, opts,
240
252
  /** The lanes an entry was built with; null in one that leaves them out. */
241
253
  lanesOf) {
@@ -246,6 +258,7 @@ export class Mixer {
246
258
  this.channels = this.names.map((k) => kit[k]);
247
259
  this.bounded = this.channels.flatMap((c, i) => (c.bounds ? [i] : []));
248
260
  this.lerpsInto = this.channels.map((c) => lerpInto(c));
261
+ this.pose = blanks(this.names, this.channels);
249
262
  this.scalars = this.channels.map((c) => scalarOp(c));
250
263
  this.names.forEach((k, i) => {
251
264
  this.slotOf.set(k, i);
@@ -255,6 +268,7 @@ export class Mixer {
255
268
  this.lanes =
256
269
  lanesOf !== null && (opts.lanes ?? defaults.lanes) ? new lanesOf(laneHost(this)) : null;
257
270
  this.name = opts.name;
271
+ this.roleTags = roleTags(opts);
258
272
  const shared = opts.transport;
259
273
  if (shared !== undefined) {
260
274
  if (opts.history !== undefined)
@@ -345,14 +359,14 @@ export class Mixer {
345
359
  return cue(this, spec);
346
360
  }
347
361
  blend(members, by, spec = {}) {
348
- const locus = `blend:${this.nextId}`;
362
+ const role = `blend:${this.nextId}`;
349
363
  const of = blendOf(by, members.length - 1);
350
364
  return members.map((member, i) => {
351
365
  const own = {
352
366
  ...spec,
353
367
  patch: member,
354
368
  weight: by,
355
- locus,
369
+ role,
356
370
  };
357
371
  if ('patch' in member) {
358
372
  own.patch = member.patch;
@@ -433,38 +447,6 @@ export class Mixer {
433
447
  marks(from, to) {
434
448
  return listed(this, from, to).map(({ order: _, ...m }) => m);
435
449
  }
436
- atRest(subject) {
437
- this.restStamps ??= new Store();
438
- const head = this.linked(subject);
439
- const laned = this.linkedLaned;
440
- const lanes = this.lanes;
441
- // Every voice on lanes and nothing to clamp: the pose a fold would make is the lanes' values.
442
- if (laned && this.restsLaned()) {
443
- const slot = head.slot;
444
- if (!lanes.owes(slot))
445
- return lanes.rests(slot);
446
- }
447
- // A probe this frame answers for the pose it gave the host.
448
- const since = (this.restStamps.get(subject) ?? Number.NaN) - 2 * this.restsKey();
449
- if (since >= 0)
450
- return since === 1;
451
- this.restScratch ??= {};
452
- const pose = this.foldWith(subject, this.restScratch, head, laned, true);
453
- return this.rests((this.bounded.length === 0 ? pose : this.clamp(pose)));
454
- }
455
- rests(pose) {
456
- for (let i = 0; i < this.names.length; i++) {
457
- const channel = this.channels[i];
458
- const value = pose[this.names[i]];
459
- if (channel.rest === undefined) {
460
- if (value !== undefined)
461
- return false;
462
- }
463
- else if (!near(value, channel.rest))
464
- return false;
465
- }
466
- return true;
467
- }
468
450
  rebase() {
469
451
  this.own('rebase').rebase();
470
452
  }
@@ -486,16 +468,6 @@ export class Mixer {
486
468
  this.asleep = true;
487
469
  return inert;
488
470
  }
489
- restful() {
490
- // Standing still, a voice changes no pose but by its weight signal, or by an anchor or start
491
- // that host time may yet reach.
492
- if (this.pace?.stopped(this.u))
493
- return this.cued.every((v) => v.state === 'done' ||
494
- (v.state !== 'pending' &&
495
- typeof v.spec.weight !== 'function' &&
496
- v.spec.anchor === undefined));
497
- return this.cued.every((v) => this.still(v, this.now));
498
- }
499
471
  get rate() {
500
472
  return this.transport.rate;
501
473
  }
@@ -505,22 +477,6 @@ export class Mixer {
505
477
  ramp(rate, over) {
506
478
  this.own('ramp').ramp(rate, over);
507
479
  }
508
- /** Whether a voice will change no pose from `now` on, short of a change made to it. */
509
- still(voice, now) {
510
- if (voice.state === 'done')
511
- return true;
512
- if (voice.state !== 'frozen' && voice.state !== 'live')
513
- return false;
514
- if (typeof voice.spec.weight === 'function' || voice.parts !== null)
515
- return false;
516
- const fadeIn = this.reducedNow ? 0 : (voice.fade.in ?? 0);
517
- if (fadeIn > 0 && now - this.sinceOf(voice, voice.latest) < fadeIn)
518
- return false;
519
- if (voice.state === 'frozen' || voice.holding !== null)
520
- return true;
521
- const motion = voice.motion;
522
- return motion !== undefined && voice.elapsedAt(now) >= voice.latest && motion.landed();
523
- }
524
480
  close() {
525
481
  unregister(this);
526
482
  }
@@ -531,80 +487,6 @@ export class Mixer {
531
487
  }
532
488
  record(this, 'mute', (stage) => stage.mix(this)?.mute(opts));
533
489
  }
534
- drop(subject) {
535
- this.frame = nextFrame();
536
- const head = headOf(this, subject);
537
- // Nothing that kept this head, such as `pull`'s remembered list, may take it as current again.
538
- if (head !== undefined)
539
- unlink(this, head);
540
- // A number a seek carried over is released even where its chain is not linked since.
541
- const slot = this.chains.get(subject)?.slot ?? -1;
542
- if (slot >= 0 && this.lanes !== null)
543
- this.lanes.release(slot);
544
- // A read ahead's copy reads the live mix's poses, and so hides one where the mix deletes it.
545
- if (this.projecting && !(this.pose instanceof Masked))
546
- this.pose = new Masked(this.pose);
547
- this.pose.delete(subject);
548
- this.restStamps?.delete(subject);
549
- this.chains.delete(subject);
550
- this.stir();
551
- // A record of it is only in a voice over every subject, one naming it, or one gone; a ramp out
552
- // of a voice that does not name it is in `parters`, and a patch's own state in `motions`.
553
- const kept = releasing(this);
554
- for (const voice of this.general)
555
- this.forgetIn(voice, subject, kept);
556
- const named = this.named.get(subject);
557
- if (named !== undefined)
558
- for (const voice of [...named])
559
- this.forgetIn(voice, subject, kept);
560
- const gone = goneIndex(this);
561
- for (const voice of gone.general)
562
- this.forgetIn(voice, subject, kept);
563
- const goneNaming = gone.named.get(subject);
564
- if (goneNaming !== undefined)
565
- for (const voice of goneNaming)
566
- this.forgetIn(voice, subject, kept);
567
- if (this.owners !== null)
568
- for (const voice of this.owners)
569
- this.forgetIn(voice, subject, kept);
570
- for (const voice of this.parters) {
571
- this.forgetIn(voice, subject, kept);
572
- if (voice.parts?.size === 0)
573
- voice.parts = null;
574
- if (voice.parted?.size === 0)
575
- voice.parted = null;
576
- if (voice.parts === null && voice.parted === null)
577
- this.parters.delete(voice);
578
- }
579
- const strays = this.strays.get(subject);
580
- if (strays !== undefined)
581
- for (const motion of strays)
582
- this.releaseIn(motion, subject, kept);
583
- this.strays.delete(subject);
584
- for (const motion of this.strayAll)
585
- this.releaseIn(motion, subject, kept);
586
- this.recordDrop(subject);
587
- }
588
- /** Lets `voice` go of a dropped subject. Methods rather than closures, so a drop makes neither. */
589
- forgetIn(voice, subject, kept) {
590
- leaveVoice(this, voice, subject);
591
- voice.subjects.delete(subject);
592
- if (voice.motion !== undefined)
593
- this.releaseIn(voice.motion, subject, kept);
594
- voice.parts?.delete(subject);
595
- voice.parted?.delete(subject);
596
- voice.blend?.of.reads.delete(subject);
597
- }
598
- // A motion patch's state is the live mix's alone: a read ahead cannot start a subject afresh in it.
599
- releaseIn(motion, subject, kept) {
600
- if (!this.projecting)
601
- motion.release(subject, kept[0], kept[1], kept[2]);
602
- else if (motion.holds(subject))
603
- unplayable('a drop of a subject a motion patch holds');
604
- }
605
- recordDrop(subject) {
606
- record(this, 'drop', (stage) => stage.mix(this)?.drop(subject));
607
- }
608
490
  touch(subject) {
609
491
  this.attached();
610
492
  touch(this, subject);
@@ -641,22 +523,6 @@ export class Mixer {
641
523
  restsStirs = -1;
642
524
  /** The pose `atRest` folds into where no probe this frame answers. */
643
525
  restScratch;
644
- restsLaned() {
645
- return this.lanes.whole && this.bounded.length === 0;
646
- }
647
- restsKey() {
648
- if (this.frame !== this.restsFrame ||
649
- this.version !== this.restsVersion ||
650
- this.relinks !== this.restsRelinks ||
651
- this.stirs !== this.restsStirs) {
652
- this.restsEpoch++;
653
- this.restsFrame = this.frame;
654
- this.restsVersion = this.version;
655
- this.restsRelinks = this.relinks;
656
- this.restsStirs = this.stirs;
657
- }
658
- return this.restsEpoch;
659
- }
660
526
  }
661
527
  // The probe's hot path is methods, installed here from the modules that own them: as functions
662
528
  // called directly they made probes and fills up to 9% slower (bench/ab.sh on teitou, 2026-10-05).
@@ -666,7 +532,7 @@ const methods = {
666
532
  linked,
667
533
  foldWith,
668
534
  owedBy,
669
- foldLoci,
535
+ foldRoles,
670
536
  foldDetour,
671
537
  foldSharers,
672
538
  shared,
@@ -681,6 +547,7 @@ const methods = {
681
547
  baseFor,
682
548
  slopeFor,
683
549
  prime,
550
+ primeLaned,
684
551
  capped,
685
552
  horizonFor,
686
553
  weigh,
@@ -702,7 +569,22 @@ const methods = {
702
569
  sinceOf,
703
570
  shownOf,
704
571
  keep,
705
- foldLocus,
572
+ foldRole,
573
+ atRest,
574
+ rests,
575
+ restful,
576
+ still,
577
+ restsLaned,
578
+ restsKey,
579
+ plot,
580
+ place,
581
+ moved,
582
+ put,
583
+ around,
584
+ drop,
585
+ forgetIn,
586
+ releaseIn,
587
+ recordDrop,
706
588
  };
707
589
  for (const [name, value] of Object.entries(methods))
708
590
  Object.defineProperty(Mixer.prototype, name, { value, writable: true, configurable: true });
@@ -724,7 +606,7 @@ export function made(kit, opts, lanes) {
724
606
  return m;
725
607
  }
726
608
  /**
727
- * The one that ships: every form on the CPU, probed per subject on demand.
609
+ * The engine that ships, running every form on the CPU. It probes per subject on demand.
728
610
  *
729
611
  * @category engine
730
612
  */
@@ -736,7 +618,7 @@ export const mixer = {
736
618
  },
737
619
  };
738
620
  /**
739
- * Makes a mix over a kit, on the engine the options name or the stock one.
621
+ * Makes a mix over a kit. It runs on the engine the options name, or the stock one.
740
622
  *
741
623
  * @category mix
742
624
  */