@msb235/blits 0.13.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 (178) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/README.md +20 -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/closed.d.ts +1 -0
  21. package/dist/closed.js +45 -1
  22. package/dist/color.d.ts +12 -10
  23. package/dist/color.js +25 -25
  24. package/dist/columns.js +7 -3
  25. package/dist/contribution.d.ts +30 -0
  26. package/dist/contribution.js +374 -0
  27. package/dist/copyout.d.ts +10 -0
  28. package/dist/copyout.js +69 -24
  29. package/dist/crowd.d.ts +8 -2
  30. package/dist/crowd.js +4 -5
  31. package/dist/cue.d.ts +8 -1
  32. package/dist/cue.js +47 -14
  33. package/dist/cut.d.ts +8 -0
  34. package/dist/cut.js +4 -0
  35. package/dist/drop.d.ts +9 -0
  36. package/dist/drop.js +84 -0
  37. package/dist/due.d.ts +24 -5
  38. package/dist/due.js +113 -33
  39. package/dist/easing.d.ts +4 -4
  40. package/dist/easing.js +3 -3
  41. package/dist/everyone.d.ts +3 -2
  42. package/dist/everyone.js +3 -3
  43. package/dist/fade.d.ts +2 -1
  44. package/dist/fade.js +2 -1
  45. package/dist/fill.d.ts +3 -2
  46. package/dist/fill.js +51 -19
  47. package/dist/fit.d.ts +44 -26
  48. package/dist/fit.js +10 -9
  49. package/dist/fling.d.ts +66 -0
  50. package/dist/fling.js +162 -0
  51. package/dist/fold.d.ts +4 -37
  52. package/dist/fold.js +5 -512
  53. package/dist/gather.d.ts +12 -7
  54. package/dist/gather.js +179 -44
  55. package/dist/gone.js +2 -2
  56. package/dist/grid.d.ts +47 -0
  57. package/dist/grid.js +247 -0
  58. package/dist/handle.d.ts +3 -0
  59. package/dist/handle.js +19 -8
  60. package/dist/held.d.ts +2 -1
  61. package/dist/history.d.ts +6 -5
  62. package/dist/history.js +9 -9
  63. package/dist/hosts.js +9 -15
  64. package/dist/index.d.ts +5 -4
  65. package/dist/keyfill.d.ts +1 -1
  66. package/dist/keyfill.js +5 -3
  67. package/dist/keyrows.d.ts +12 -0
  68. package/dist/keyrows.js +99 -12
  69. package/dist/lane.d.ts +36 -6
  70. package/dist/lane.js +32 -1
  71. package/dist/lanes.d.ts +23 -8
  72. package/dist/lanes.js +29 -8
  73. package/dist/leavings.d.ts +2 -2
  74. package/dist/leavings.js +1 -1
  75. package/dist/lives.d.ts +47 -0
  76. package/dist/lives.js +180 -0
  77. package/dist/meet.d.ts +2 -2
  78. package/dist/meet.js +90 -4
  79. package/dist/mixer.d.ts +61 -35
  80. package/dist/mixer.js +64 -169
  81. package/dist/motion.d.ts +19 -106
  82. package/dist/motion.js +60 -13
  83. package/dist/motions.d.ts +62 -150
  84. package/dist/motions.js +57 -369
  85. package/dist/move.js +24 -1
  86. package/dist/moved.d.ts +1 -1
  87. package/dist/moved.js +16 -7
  88. package/dist/named.d.ts +0 -1
  89. package/dist/named.js +20 -2
  90. package/dist/numbers.d.ts +2 -0
  91. package/dist/numbers.js +4 -0
  92. package/dist/owed.d.ts +2 -0
  93. package/dist/owed.js +4 -0
  94. package/dist/pack.d.ts +3 -1
  95. package/dist/pack.js +4 -3
  96. package/dist/patch.d.ts +7 -3
  97. package/dist/patch.js +6 -2
  98. package/dist/places.d.ts +53 -0
  99. package/dist/places.js +171 -0
  100. package/dist/plot.d.ts +12 -0
  101. package/dist/plot.js +52 -0
  102. package/dist/project.d.ts +4 -4
  103. package/dist/project.js +9 -211
  104. package/dist/pull.js +11 -8
  105. package/dist/qualify.js +84 -21
  106. package/dist/reading.d.ts +6 -0
  107. package/dist/reading.js +5 -0
  108. package/dist/record.d.ts +5 -1
  109. package/dist/record.js +4 -2
  110. package/dist/relink.d.ts +1 -1
  111. package/dist/restored.d.ts +2 -2
  112. package/dist/restored.js +1 -1
  113. package/dist/rests.d.ts +9 -0
  114. package/dist/rests.js +76 -0
  115. package/dist/revive.d.ts +3 -2
  116. package/dist/revive.js +2 -2
  117. package/dist/{locus.d.ts → role.d.ts} +10 -9
  118. package/dist/{locus.js → role.js} +3 -3
  119. package/dist/roles.d.ts +10 -0
  120. package/dist/roles.js +158 -0
  121. package/dist/roster.js +3 -3
  122. package/dist/rotation.d.ts +1 -1
  123. package/dist/rotation.js +1 -1
  124. package/dist/rows.js +1 -1
  125. package/dist/sample.d.ts +2 -2
  126. package/dist/sample.js +33 -7
  127. package/dist/seek.js +13 -5
  128. package/dist/setting.d.ts +27 -0
  129. package/dist/setting.js +55 -0
  130. package/dist/shared.d.ts +1 -1
  131. package/dist/shared.js +1 -1
  132. package/dist/signals.d.ts +64 -32
  133. package/dist/signals.js +20 -18
  134. package/dist/stack.d.ts +5 -5
  135. package/dist/stack.js +5 -5
  136. package/dist/standing.d.ts +2 -1
  137. package/dist/standing.js +3 -2
  138. package/dist/store.d.ts +9 -0
  139. package/dist/store.js +24 -0
  140. package/dist/strays.d.ts +2 -1
  141. package/dist/strays.js +21 -14
  142. package/dist/stretch.d.ts +112 -0
  143. package/dist/stretch.js +9 -0
  144. package/dist/subject.d.ts +142 -0
  145. package/dist/subject.js +1 -0
  146. package/dist/tape.d.ts +9 -5
  147. package/dist/tape.js +5 -1
  148. package/dist/then.d.ts +9 -0
  149. package/dist/then.js +57 -0
  150. package/dist/ticker.d.ts +4 -4
  151. package/dist/transport.d.ts +0 -8
  152. package/dist/transport.js +0 -4
  153. package/dist/turns.d.ts +4 -3
  154. package/dist/turns.js +4 -3
  155. package/dist/types/arc.d.ts +12 -11
  156. package/dist/types/channel.d.ts +12 -8
  157. package/dist/types/history.d.ts +35 -19
  158. package/dist/types/mix.d.ts +222 -161
  159. package/dist/types/motion.d.ts +151 -0
  160. package/dist/types/motion.js +1 -0
  161. package/dist/types/patch.d.ts +69 -34
  162. package/dist/types/place.d.ts +51 -36
  163. package/dist/types/transport.d.ts +20 -18
  164. package/dist/types/voice.d.ts +145 -72
  165. package/dist/types/watch.d.ts +88 -40
  166. package/dist/types.d.ts +1 -0
  167. package/dist/voice.d.ts +9 -141
  168. package/dist/voice.js +30 -79
  169. package/dist/warn.d.ts +2 -0
  170. package/dist/warn.js +1 -1
  171. package/dist/watch.d.ts +2 -2
  172. package/dist/watch.js +5 -3
  173. package/dist/wave.d.ts +4 -3
  174. package/dist/wave.js +3 -2
  175. package/dist/weigh.d.ts +7 -1
  176. package/dist/weigh.js +27 -0
  177. package/docs/schema.html +223 -37
  178. package/package.json +6 -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. */
@@ -114,6 +123,13 @@ export declare class Mixer<I, O> implements Mix<I, O> {
114
123
  reducedNow: boolean;
115
124
  /** Something was changed since the last sync, so the next frame may differ from this one. */
116
125
  stirred: boolean;
126
+ /**
127
+ * The host read `inert` as true since the last sync, so its frame loop may be asleep until a
128
+ * change wakes it; a host that never asks keeps no loop asleep on it.
129
+ */
130
+ asleep: boolean;
131
+ /** Some voice cued while the mix slept waits for the next sync to set its start. */
132
+ waking: boolean;
117
133
  /** Counts the changes `stir` records, which a frame's record of a probe is current only before. */
118
134
  stirs: number;
119
135
  syncing: boolean;
@@ -128,6 +144,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
128
144
  */
129
145
  chains: Store<I, Subject<unknown>>;
130
146
  version: number;
147
+ /** The positions `plot` gave, which a voice's `within` reads: see `bound.ts`. */
148
+ places: Places<I> | null;
131
149
  /** What `prime` reads its clock, voice time and pass from: see `Primed`. */
132
150
  readonly primed: Float64Array<ArrayBuffer>;
133
151
  readonly steps: Steps<Voice<I, O>, I>;
@@ -159,8 +177,10 @@ export declare class Mixer<I, O> implements Mix<I, O> {
159
177
  unshared: boolean;
160
178
  /** 0 where a fold is a walk of the chain: see `detour` in `everyone.ts`. */
161
179
  detours: number;
162
- /** How many voices in the list carry a locus; with none, a fold allocates nothing. */
163
- 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;
164
184
  /** What every motion patch it plays asks for its voice's time, holding this mix weakly. */
165
185
  owner: MotionOwner | null;
166
186
  /** How many voices in the list are anchored, so a sync with none skips placing them. */
@@ -184,9 +204,9 @@ export declare class Mixer<I, O> implements Mix<I, O> {
184
204
  readonly lerpsInto: (LerpInto | undefined)[];
185
205
  /** By slot, a stock scalar channel's arithmetic, which a fold applies without calling the channel. */
186
206
  readonly scalars: ('sum' | 'mul' | 'max' | undefined)[];
187
- /** What a fold with a locus in play gathers into, one per depth of folds under way. */
188
- readonly locusScratch: LocusScratch<I, O>[];
189
- 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;
190
210
  readonly slotOf: Map<string, number>;
191
211
  lanes: Lanes<I, O> | null;
192
212
  handles: HandleHost<I, O> | null;
@@ -202,6 +222,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
202
222
  keys: Keys | null;
203
223
  /** With a history store, the voices it paged out whose handles are still held; null without one. */
204
224
  outs: Outs | null;
225
+ readonly pose: () => O;
205
226
  constructor(kit: Kit<O>, opts: MixOptions,
206
227
  /** The lanes an entry was built with; null in one that leaves them out. */
207
228
  lanesOf: typeof Lanes | null);
@@ -230,7 +251,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
230
251
  off: number;
231
252
  };
232
253
  cue(spec: VoiceSpec<I, O>): Handle<I>;
233
- 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>[];
234
255
  owns(spec: OwnerSpec<I>): Handle<I>;
235
256
  span(spec: SpanSpec<I>): SpanHandle<I>;
236
257
  arc(spec: ArcSpec<I, O>): ArcHandle<I>;
@@ -253,8 +274,6 @@ export declare class Mixer<I, O> implements Mix<I, O> {
253
274
  /** Throws for a mix taken off its transport. */
254
275
  attached(): void;
255
276
  marks(from: number, to: number): Marked[];
256
- atRest(subject: I): boolean;
257
- rests(pose: O): boolean;
258
277
  rebase(): void;
259
278
  voices(tag?: string): Handle<I>[];
260
279
  get live(): boolean;
@@ -262,17 +281,10 @@ export declare class Mixer<I, O> implements Mix<I, O> {
262
281
  get rate(): number;
263
282
  set rate(r: number);
264
283
  ramp(rate: number, over: number): void;
265
- /** Whether a voice will change no pose from `now` on, short of a change made to it. */
266
- still(voice: Voice<I, O>, now: number): boolean;
267
284
  close(): void;
268
285
  mute(opts?: {
269
286
  over?: number;
270
287
  }): void;
271
- drop(subject: I): void;
272
- /** Lets `voice` go of a dropped subject. Methods rather than closures, so a drop makes neither. */
273
- private forgetIn;
274
- private releaseIn;
275
- private recordDrop;
276
288
  touch(subject?: I): void;
277
289
  drain<E = unknown>(tag?: string): Sent<I, E>[];
278
290
  /** Voices whose stop 0 is mid-computation, so a fold for one cannot re-enter itself. */
@@ -284,15 +296,13 @@ export declare class Mixer<I, O> implements Mix<I, O> {
284
296
  */
285
297
  restStamps: Store<I, number> | null;
286
298
  /** What a probe's record of rest is good for: moved by a new frame, version, relink, or stir. */
287
- private restsEpoch;
288
- private restsFrame;
289
- private restsVersion;
290
- private restsRelinks;
291
- private restsStirs;
299
+ restsEpoch: number;
300
+ restsFrame: number;
301
+ restsVersion: number;
302
+ restsRelinks: number;
303
+ restsStirs: number;
292
304
  /** The pose `atRest` folds into where no probe this frame answers. */
293
- private restScratch;
294
- private restsLaned;
295
- private restsKey;
305
+ restScratch: O | undefined;
296
306
  }
297
307
  declare const methods: {
298
308
  fold: typeof fold;
@@ -300,7 +310,7 @@ declare const methods: {
300
310
  linked: typeof linked;
301
311
  foldWith: typeof foldWith;
302
312
  owedBy: typeof owedBy;
303
- foldLoci: typeof foldLoci;
313
+ foldRoles: typeof foldRoles;
304
314
  foldDetour: typeof foldDetour;
305
315
  foldSharers: typeof foldSharers;
306
316
  shared: typeof shared;
@@ -315,6 +325,7 @@ declare const methods: {
315
325
  baseFor: typeof baseFor;
316
326
  slopeFor: typeof slopeFor;
317
327
  prime: typeof prime;
328
+ primeLaned: typeof primeLaned;
318
329
  capped: typeof capped;
319
330
  horizonFor: typeof horizonFor;
320
331
  weigh: typeof weigh;
@@ -336,7 +347,22 @@ declare const methods: {
336
347
  sinceOf: typeof sinceOf;
337
348
  shownOf: typeof shownOf;
338
349
  keep: typeof keep;
339
- 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;
340
366
  };
341
367
  type Methods = typeof methods;
342
368
  export interface Mixer<I, O> extends Methods {
@@ -344,13 +370,13 @@ export interface Mixer<I, O> extends Methods {
344
370
  /** Makes a mix the host asked for, with the registry's history where it names none, and lists it there. */
345
371
  export declare function made<I, O, H>(kit: Kit<O>, opts: MixOptions<H>, lanes: typeof Lanes | null): Mix<I, O, H>;
346
372
  /**
347
- * 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.
348
374
  *
349
375
  * @category engine
350
376
  */
351
377
  export declare const mixer: Engine;
352
378
  /**
353
- * 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.
354
380
  *
355
381
  * @category mix
356
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. */
@@ -105,6 +112,13 @@ export class Mixer {
105
112
  reducedNow = false;
106
113
  /** Something was changed since the last sync, so the next frame may differ from this one. */
107
114
  stirred = false;
115
+ /**
116
+ * The host read `inert` as true since the last sync, so its frame loop may be asleep until a
117
+ * change wakes it; a host that never asks keeps no loop asleep on it.
118
+ */
119
+ asleep = false;
120
+ /** Some voice cued while the mix slept waits for the next sync to set its start. */
121
+ waking = false;
108
122
  /** Counts the changes `stir` records, which a frame's record of a probe is current only before. */
109
123
  stirs = 0;
110
124
  syncing = false;
@@ -144,6 +158,8 @@ export class Mixer {
144
158
  */
145
159
  chains = new Store();
146
160
  version = 0;
161
+ /** The positions `plot` gave, which a voice's `within` reads: see `bound.ts`. */
162
+ places = null;
147
163
  /** What `prime` reads its clock, voice time and pass from: see `Primed`. */
148
164
  primed = new Float64Array(3 /* Primed.SIZE */);
149
165
  steps = new Steps();
@@ -175,8 +191,10 @@ export class Mixer {
175
191
  unshared = false;
176
192
  /** 0 where a fold is a walk of the chain: see `detour` in `everyone.ts`. */
177
193
  detours = 0;
178
- /** How many voices in the list carry a locus; with none, a fold allocates nothing. */
179
- 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;
180
198
  /** What every motion patch it plays asks for its voice's time, holding this mix weakly. */
181
199
  owner = null;
182
200
  /** How many voices in the list are anchored, so a sync with none skips placing them. */
@@ -211,9 +229,9 @@ export class Mixer {
211
229
  lerpsInto;
212
230
  /** By slot, a stock scalar channel's arithmetic, which a fold applies without calling the channel. */
213
231
  scalars;
214
- /** What a fold with a locus in play gathers into, one per depth of folds under way. */
215
- locusScratch = [];
216
- 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;
217
235
  slotOf = new Map();
218
236
  lanes;
219
237
  handles = null;
@@ -229,6 +247,7 @@ export class Mixer {
229
247
  keys = null;
230
248
  /** With a history store, the voices it paged out whose handles are still held; null without one. */
231
249
  outs = null;
250
+ pose;
232
251
  constructor(kit, opts,
233
252
  /** The lanes an entry was built with; null in one that leaves them out. */
234
253
  lanesOf) {
@@ -239,6 +258,7 @@ export class Mixer {
239
258
  this.channels = this.names.map((k) => kit[k]);
240
259
  this.bounded = this.channels.flatMap((c, i) => (c.bounds ? [i] : []));
241
260
  this.lerpsInto = this.channels.map((c) => lerpInto(c));
261
+ this.pose = blanks(this.names, this.channels);
242
262
  this.scalars = this.channels.map((c) => scalarOp(c));
243
263
  this.names.forEach((k, i) => {
244
264
  this.slotOf.set(k, i);
@@ -248,6 +268,7 @@ export class Mixer {
248
268
  this.lanes =
249
269
  lanesOf !== null && (opts.lanes ?? defaults.lanes) ? new lanesOf(laneHost(this)) : null;
250
270
  this.name = opts.name;
271
+ this.roleTags = roleTags(opts);
251
272
  const shared = opts.transport;
252
273
  if (shared !== undefined) {
253
274
  if (opts.history !== undefined)
@@ -338,14 +359,14 @@ export class Mixer {
338
359
  return cue(this, spec);
339
360
  }
340
361
  blend(members, by, spec = {}) {
341
- const locus = `blend:${this.nextId}`;
362
+ const role = `blend:${this.nextId}`;
342
363
  const of = blendOf(by, members.length - 1);
343
364
  return members.map((member, i) => {
344
365
  const own = {
345
366
  ...spec,
346
367
  patch: member,
347
368
  weight: by,
348
- locus,
369
+ role,
349
370
  };
350
371
  if ('patch' in member) {
351
372
  own.patch = member.patch;
@@ -426,38 +447,6 @@ export class Mixer {
426
447
  marks(from, to) {
427
448
  return listed(this, from, to).map(({ order: _, ...m }) => m);
428
449
  }
429
- atRest(subject) {
430
- this.restStamps ??= new Store();
431
- const head = this.linked(subject);
432
- const laned = this.linkedLaned;
433
- const lanes = this.lanes;
434
- // Every voice on lanes and nothing to clamp: the pose a fold would make is the lanes' values.
435
- if (laned && this.restsLaned()) {
436
- const slot = head.slot;
437
- if (!lanes.owes(slot))
438
- return lanes.rests(slot);
439
- }
440
- // A probe this frame answers for the pose it gave the host.
441
- const since = (this.restStamps.get(subject) ?? Number.NaN) - 2 * this.restsKey();
442
- if (since >= 0)
443
- return since === 1;
444
- this.restScratch ??= {};
445
- const pose = this.foldWith(subject, this.restScratch, head, laned, true);
446
- return this.rests((this.bounded.length === 0 ? pose : this.clamp(pose)));
447
- }
448
- rests(pose) {
449
- for (let i = 0; i < this.names.length; i++) {
450
- const channel = this.channels[i];
451
- const value = pose[this.names[i]];
452
- if (channel.rest === undefined) {
453
- if (value !== undefined)
454
- return false;
455
- }
456
- else if (!near(value, channel.rest))
457
- return false;
458
- }
459
- return true;
460
- }
461
450
  rebase() {
462
451
  this.own('rebase').rebase();
463
452
  }
@@ -474,14 +463,10 @@ export class Mixer {
474
463
  get inert() {
475
464
  if (this.stirred)
476
465
  return false;
477
- // Standing still, a voice changes no pose but by its weight signal, or by an anchor or start
478
- // that host time may yet reach.
479
- if (this.pace?.stopped(this.u))
480
- return this.cued.every((v) => v.state === 'done' ||
481
- (v.state !== 'pending' &&
482
- typeof v.spec.weight !== 'function' &&
483
- v.spec.anchor === undefined));
484
- return this.cued.every((v) => this.still(v, this.now));
466
+ const inert = this.restful();
467
+ if (inert && !Number.isNaN(this.now))
468
+ this.asleep = true;
469
+ return inert;
485
470
  }
486
471
  get rate() {
487
472
  return this.transport.rate;
@@ -492,22 +477,6 @@ export class Mixer {
492
477
  ramp(rate, over) {
493
478
  this.own('ramp').ramp(rate, over);
494
479
  }
495
- /** Whether a voice will change no pose from `now` on, short of a change made to it. */
496
- still(voice, now) {
497
- if (voice.state === 'done')
498
- return true;
499
- if (voice.state !== 'frozen' && voice.state !== 'live')
500
- return false;
501
- if (typeof voice.spec.weight === 'function' || voice.parts !== null)
502
- return false;
503
- const fadeIn = this.reducedNow ? 0 : (voice.fade.in ?? 0);
504
- if (fadeIn > 0 && now - this.sinceOf(voice, voice.latest) < fadeIn)
505
- return false;
506
- if (voice.state === 'frozen' || voice.holding !== null)
507
- return true;
508
- const motion = voice.motion;
509
- return motion !== undefined && voice.elapsedAt(now) >= voice.latest && motion.landed();
510
- }
511
480
  close() {
512
481
  unregister(this);
513
482
  }
@@ -518,80 +487,6 @@ export class Mixer {
518
487
  }
519
488
  record(this, 'mute', (stage) => stage.mix(this)?.mute(opts));
520
489
  }
521
- drop(subject) {
522
- this.frame = nextFrame();
523
- const head = headOf(this, subject);
524
- // Nothing that kept this head, such as `pull`'s remembered list, may take it as current again.
525
- if (head !== undefined)
526
- unlink(this, head);
527
- // A number a seek carried over is released even where its chain is not linked since.
528
- const slot = this.chains.get(subject)?.slot ?? -1;
529
- if (slot >= 0 && this.lanes !== null)
530
- this.lanes.release(slot);
531
- // A read ahead's copy reads the live mix's poses, and so hides one where the mix deletes it.
532
- if (this.projecting && !(this.pose instanceof Masked))
533
- this.pose = new Masked(this.pose);
534
- this.pose.delete(subject);
535
- this.restStamps?.delete(subject);
536
- this.chains.delete(subject);
537
- this.stir();
538
- // A record of it is only in a voice over every subject, one naming it, or one gone; a ramp out
539
- // of a voice that does not name it is in `parters`, and a patch's own state in `motions`.
540
- const kept = releasing(this);
541
- for (const voice of this.general)
542
- this.forgetIn(voice, subject, kept);
543
- const named = this.named.get(subject);
544
- if (named !== undefined)
545
- for (const voice of [...named])
546
- this.forgetIn(voice, subject, kept);
547
- const gone = goneIndex(this);
548
- for (const voice of gone.general)
549
- this.forgetIn(voice, subject, kept);
550
- const goneNaming = gone.named.get(subject);
551
- if (goneNaming !== undefined)
552
- for (const voice of goneNaming)
553
- this.forgetIn(voice, subject, kept);
554
- if (this.owners !== null)
555
- for (const voice of this.owners)
556
- this.forgetIn(voice, subject, kept);
557
- for (const voice of this.parters) {
558
- this.forgetIn(voice, subject, kept);
559
- if (voice.parts?.size === 0)
560
- voice.parts = null;
561
- if (voice.parted?.size === 0)
562
- voice.parted = null;
563
- if (voice.parts === null && voice.parted === null)
564
- this.parters.delete(voice);
565
- }
566
- const strays = this.strays.get(subject);
567
- if (strays !== undefined)
568
- for (const motion of strays)
569
- this.releaseIn(motion, subject, kept);
570
- this.strays.delete(subject);
571
- for (const motion of this.strayAll)
572
- this.releaseIn(motion, subject, kept);
573
- this.recordDrop(subject);
574
- }
575
- /** Lets `voice` go of a dropped subject. Methods rather than closures, so a drop makes neither. */
576
- forgetIn(voice, subject, kept) {
577
- leaveVoice(this, voice, subject);
578
- voice.subjects.delete(subject);
579
- if (voice.motion !== undefined)
580
- this.releaseIn(voice.motion, subject, kept);
581
- voice.parts?.delete(subject);
582
- voice.parted?.delete(subject);
583
- voice.blend?.of.reads.delete(subject);
584
- }
585
- // A motion patch's state is the live mix's alone: a read ahead cannot start a subject afresh in it.
586
- releaseIn(motion, subject, kept) {
587
- if (!this.projecting)
588
- motion.release(subject, kept[0], kept[1], kept[2]);
589
- else if (motion.holds(subject))
590
- unplayable('a drop of a subject a motion patch holds');
591
- }
592
- recordDrop(subject) {
593
- record(this, 'drop', (stage) => stage.mix(this)?.drop(subject));
594
- }
595
490
  touch(subject) {
596
491
  this.attached();
597
492
  touch(this, subject);
@@ -628,22 +523,6 @@ export class Mixer {
628
523
  restsStirs = -1;
629
524
  /** The pose `atRest` folds into where no probe this frame answers. */
630
525
  restScratch;
631
- restsLaned() {
632
- return this.lanes.whole && this.bounded.length === 0;
633
- }
634
- restsKey() {
635
- if (this.frame !== this.restsFrame ||
636
- this.version !== this.restsVersion ||
637
- this.relinks !== this.restsRelinks ||
638
- this.stirs !== this.restsStirs) {
639
- this.restsEpoch++;
640
- this.restsFrame = this.frame;
641
- this.restsVersion = this.version;
642
- this.restsRelinks = this.relinks;
643
- this.restsStirs = this.stirs;
644
- }
645
- return this.restsEpoch;
646
- }
647
526
  }
648
527
  // The probe's hot path is methods, installed here from the modules that own them: as functions
649
528
  // called directly they made probes and fills up to 9% slower (bench/ab.sh on teitou, 2026-10-05).
@@ -653,7 +532,7 @@ const methods = {
653
532
  linked,
654
533
  foldWith,
655
534
  owedBy,
656
- foldLoci,
535
+ foldRoles,
657
536
  foldDetour,
658
537
  foldSharers,
659
538
  shared,
@@ -668,6 +547,7 @@ const methods = {
668
547
  baseFor,
669
548
  slopeFor,
670
549
  prime,
550
+ primeLaned,
671
551
  capped,
672
552
  horizonFor,
673
553
  weigh,
@@ -689,7 +569,22 @@ const methods = {
689
569
  sinceOf,
690
570
  shownOf,
691
571
  keep,
692
- 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,
693
588
  };
694
589
  for (const [name, value] of Object.entries(methods))
695
590
  Object.defineProperty(Mixer.prototype, name, { value, writable: true, configurable: true });
@@ -711,7 +606,7 @@ export function made(kit, opts, lanes) {
711
606
  return m;
712
607
  }
713
608
  /**
714
- * 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.
715
610
  *
716
611
  * @category engine
717
612
  */
@@ -723,7 +618,7 @@ export const mixer = {
723
618
  },
724
619
  };
725
620
  /**
726
- * 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.
727
622
  *
728
623
  * @category mix
729
624
  */