@msb235/blits 0.15.0 → 0.16.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 (80) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +1 -1
  3. package/dist/ahead.js +5 -4
  4. package/dist/bare.js +22 -7
  5. package/dist/chain.js +2 -3
  6. package/dist/color.d.ts +36 -14
  7. package/dist/color.js +186 -58
  8. package/dist/columns.d.ts +3 -12
  9. package/dist/columns.js +56 -41
  10. package/dist/crowd.d.ts +8 -10
  11. package/dist/crowd.js +17 -18
  12. package/dist/cue.js +7 -4
  13. package/dist/delay.d.ts +3 -0
  14. package/dist/delay.js +7 -0
  15. package/dist/due.js +1 -1
  16. package/dist/easing.d.ts +2 -0
  17. package/dist/easing.js +4 -0
  18. package/dist/everyone.d.ts +22 -6
  19. package/dist/everyone.js +47 -8
  20. package/dist/fade.d.ts +11 -0
  21. package/dist/fade.js +44 -8
  22. package/dist/fill.js +1 -1
  23. package/dist/fold.js +4 -7
  24. package/dist/held.js +5 -5
  25. package/dist/hosts.d.ts +7 -0
  26. package/dist/hosts.js +18 -5
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.js +1 -1
  29. package/dist/lane.d.ts +2 -5
  30. package/dist/lane.js +14 -10
  31. package/dist/lanes.d.ts +33 -5
  32. package/dist/lanes.js +34 -28
  33. package/dist/lch.d.ts +23 -0
  34. package/dist/lch.js +126 -0
  35. package/dist/meet.js +5 -3
  36. package/dist/mixer.d.ts +6 -2
  37. package/dist/mixer.js +7 -0
  38. package/dist/motions.d.ts +4 -1
  39. package/dist/motions.js +16 -2
  40. package/dist/move.js +15 -10
  41. package/dist/named.d.ts +6 -1
  42. package/dist/named.js +29 -1
  43. package/dist/numbers.d.ts +5 -0
  44. package/dist/numbers.js +29 -0
  45. package/dist/owner.d.ts +7 -2
  46. package/dist/owner.js +13 -3
  47. package/dist/project.js +3 -8
  48. package/dist/pull.d.ts +11 -0
  49. package/dist/pull.js +18 -4
  50. package/dist/qualify.js +10 -11
  51. package/dist/renumber.d.ts +10 -0
  52. package/dist/renumber.js +75 -0
  53. package/dist/roles.js +1 -3
  54. package/dist/roster.d.ts +6 -0
  55. package/dist/roster.js +8 -1
  56. package/dist/rows.js +27 -19
  57. package/dist/sample.js +2 -1
  58. package/dist/seat.d.ts +17 -0
  59. package/dist/seat.js +25 -0
  60. package/dist/seek.js +1 -13
  61. package/dist/shared.d.ts +1 -1
  62. package/dist/shared.js +1 -1
  63. package/dist/spans.d.ts +4 -1
  64. package/dist/spans.js +6 -3
  65. package/dist/strays.js +3 -2
  66. package/dist/touch.js +1 -2
  67. package/dist/trace.d.ts +4 -0
  68. package/dist/trace.js +255 -0
  69. package/dist/turns.js +2 -1
  70. package/dist/types/mix.d.ts +15 -0
  71. package/dist/types/trace.d.ts +51 -0
  72. package/dist/types/trace.js +1 -0
  73. package/dist/types/voice.d.ts +13 -7
  74. package/dist/types.d.ts +1 -0
  75. package/dist/unreached.d.ts +2 -0
  76. package/dist/unreached.js +7 -0
  77. package/dist/voice.d.ts +22 -0
  78. package/dist/voice.js +44 -4
  79. package/docs/schema.html +48 -11
  80. package/package.json +1 -1
package/dist/held.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { changed } from './chain.js';
2
2
  import { stepOf } from './clock.js';
3
+ import { delayOf } from './delay.js';
3
4
  import { schedule } from './due.js';
4
- import { everyoneOf, inherit, sighted } from './everyone.js';
5
5
  import { originOf } from './origin.js';
6
6
  import { startOf } from './place.js';
7
7
  import { record } from './record.js';
@@ -23,7 +23,7 @@ export function unreachedOf(voice, now) {
23
23
  */
24
24
  export function held(voice, subject, slot, guess = null) {
25
25
  if (voice.sharing) {
26
- const all = everyoneOf(this, voice);
26
+ const all = this.share.everyoneOf(this, voice);
27
27
  if (all !== null)
28
28
  return all;
29
29
  }
@@ -50,7 +50,7 @@ export function held(voice, subject, slot, guess = null) {
50
50
  voice.keyed ??= new Set();
51
51
  voice.keyed.add(this.keys.key(subject));
52
52
  }
53
- const delay = voice.spec.stagger ? voice.spec.stagger(subject) : 0;
53
+ const delay = delayOf(voice, subject);
54
54
  const since = this.sinceOf(voice, delay);
55
55
  const shown = this.shownOf(voice, since);
56
56
  held = record(voice, true, voice.patch.state ? voice.patch.state(subject) : undefined);
@@ -66,8 +66,8 @@ export function held(voice, subject, slot, guess = null) {
66
66
  voice.subjects.set(subject, held);
67
67
  waits(voice, held, now);
68
68
  // A subject a voice saw while it shared one record was counted then.
69
- if (sighted(voice, slot))
70
- inherit(voice, held, slot);
69
+ if (voice.sighted !== null && this.share?.sighted(voice, slot))
70
+ this.share.inherit(voice, held, slot);
71
71
  else
72
72
  voice.seen++;
73
73
  if (delay > voice.latest) {
package/dist/hosts.d.ts CHANGED
@@ -4,6 +4,13 @@ import type { Booker, BookOptions, Handle } from './types.js';
4
4
  import type { Voice } from './voice.js';
5
5
  export declare function book<I, O>(mix: Mixer<I, O>, opts: BookOptions<unknown, I>): Booker;
6
6
  export declare function laneHost<I, O>(mix: Mixer<I, O>): LaneHost<I, O>;
7
+ /**
8
+ * Whether a laned voice belongs in a crowd: one naming a single subject, which shares nothing
9
+ * across subjects for a lane to save. A motion voice writes one channel; a keys or fn voice may
10
+ * write several, and joins the crowd of voices writing those same channels. A motion going round
11
+ * an `ink` color's hue stays out, since a crowd solves its stretches straight across each axis.
12
+ */
13
+ export declare function crowdable<I, O>(v: Voice<I, O>): boolean;
7
14
  /**
8
15
  * Whether a voice's patch and spec can run on a lane, its channels aside. A voice whose patch has
9
16
  * kept state on a record through `setting.keep` is stateful from then on, and leaves its lane;
package/dist/hosts.js CHANGED
@@ -3,9 +3,7 @@ import { Book } from './book.js';
3
3
  import { headOf } from './chain.js';
4
4
  import { numericOf } from './channels.js';
5
5
  import { stepOf } from './clock.js';
6
- import { crowdable } from './crowd.js';
7
6
  import { schedule } from './due.js';
8
- import { unshare, unsight, weightAt } from './everyone.js';
9
7
  import { beginFade, beginRise, fadeAt, fadeSubject } from './fade.js';
10
8
  import { goneIndex } from './gone.js';
11
9
  import { VoiceHandle } from './handle.js';
@@ -67,7 +65,7 @@ export function laneHost(mix) {
67
65
  for (const voice of mix.general) {
68
66
  unreach(voice, slot, false);
69
67
  if (voice.sighted !== null)
70
- unsight(voice, slot);
68
+ mix.share?.unsight(voice, slot);
71
69
  }
72
70
  for (const voice of goneIndex(mix).general)
73
71
  unreach(voice, slot, false);
@@ -110,6 +108,19 @@ export function laneHost(mix) {
110
108
  kept: (voice) => stateful(mix, voice),
111
109
  };
112
110
  }
111
+ /**
112
+ * Whether a laned voice belongs in a crowd: one naming a single subject, which shares nothing
113
+ * across subjects for a lane to save. A motion voice writes one channel; a keys or fn voice may
114
+ * write several, and joins the crowd of voices writing those same channels. A motion going round
115
+ * an `ink` color's hue stays out, since a crowd solves its stretches straight across each axis.
116
+ */
117
+ export function crowdable(v) {
118
+ return (v.named !== null &&
119
+ v.named.size === 1 &&
120
+ v.spec.role === undefined &&
121
+ (v.slots.length === 1 || v.motion === undefined) &&
122
+ v.motion?.round !== true);
123
+ }
113
124
  /**
114
125
  * Whether a voice's patch and spec can run on a lane, its channels aside. A voice whose patch has
115
126
  * kept state on a record through `setting.keep` is stateful from then on, and leaves its lane;
@@ -181,7 +192,7 @@ export function stateful(mix, voice) {
181
192
  voice.keeping = true;
182
193
  // The call that kept state was for the subject last sent to, whose record the state is on.
183
194
  if (voice.sharing)
184
- unshare(mix, voice, true, mix.sending.subject);
195
+ mix.share?.unshare(mix, voice, true, mix.sending.subject);
185
196
  mix.lanes?.invalidate();
186
197
  }
187
198
  export function handle(mix, voice) {
@@ -248,7 +259,9 @@ function handleHost(mix) {
248
259
  ? undefined
249
260
  : voice.subjects.get(subject);
250
261
  // A voice that stopped sharing makes a subject's record at its next read, from what it kept.
251
- return held !== undefined ? held.weight : weightAt(voice, slot);
262
+ if (held !== undefined)
263
+ return held.weight;
264
+ return mix.share === null ? 0 : mix.share.weightAt(voice, slot);
252
265
  },
253
266
  taping: () => taping(mix),
254
267
  record: (h, label, again) => record(mix, label, (stage) => {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type { LastOptions, NumberOptions, NumberTuple, 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, mixHex, oklab, toHex } from './color.js';
4
+ export { color, css, ink, mixHex, oklab, toHex } from './color.js';
5
5
  export type { Curve } from './easing.js';
6
6
  export { curve } from './easing.js';
7
7
  export type { Claim, Fit, FitPlan, Layout, Order, OverrunOptions, SpanClaim, Strength, } from './fit.js';
@@ -20,7 +20,7 @@ export { fold } from './stack.js';
20
20
  export type { Ticked, Ticker, TickerOptions } from './ticker.js';
21
21
  export { ticker } from './ticker.js';
22
22
  export { transport } from './transport.js';
23
- export type { Anchor, AnnounceOptions, ArcHandle, ArcPart, ArcSpec, ArcVoice, Band, BlendMember, BookedHit, Booker, BookOptions, Channel, Circle, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, Finder, FitResult, GlideSpec, Handle, HistoryOptions, HistoryStore, Hit, Hold, Keyframe, Kit, Mark, Marked, MarkName, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Probed, Projection, Pulled, Query, Registry, RegistryChange, SeekOptions, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, SpringSpec, SubjectFade, Taken, Tape, TapeBranch, TapeMaker, TapeOp, TapeOptions, Transport, TransportOptions, TransportProjection, TweenSpec, VoiceFade, VoiceSpec, Watched, WatchedFrame, WatchedHistory, WatchedVoice, } from './types.js';
23
+ export type { Anchor, AnnounceOptions, ArcHandle, ArcPart, ArcSpec, ArcVoice, Band, BlendMember, BookedHit, Booker, BookOptions, Channel, Circle, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, Finder, FitResult, GlideSpec, Handle, HistoryOptions, HistoryStore, Hit, Hold, Keyframe, Kit, Mark, Marked, MarkName, Mix, MixOptions, MotionSpec, NumberChannel, OwnerSpec, Paged, PagedStream, Patch, Placement, Probed, Projection, Pulled, Query, Registry, RegistryChange, Run, SeekOptions, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, SpringSpec, SubjectFade, Taken, Tape, TapeBranch, TapeMaker, TapeOp, TapeOptions, Trace, TraceOptions, Transport, TransportOptions, TransportProjection, TweenSpec, VoiceFade, VoiceSpec, Watched, WatchedFrame, WatchedHistory, WatchedVoice, } from './types.js';
24
24
  export { watched } from './watch.js';
25
25
  export type { WaveOptions, WaveShape } from './wave.js';
26
26
  export { wave, waveAt } from './wave.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { kit, last, max, mul, sum, vec } from './channels.js';
2
- export { color, css, mixHex, oklab, toHex } from './color.js';
2
+ export { color, css, ink, mixHex, oklab, toHex } from './color.js';
3
3
  export { curve } from './easing.js';
4
4
  export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
5
5
  export { mix, mixer } from './mixer.js';
package/dist/lane.d.ts CHANGED
@@ -260,11 +260,8 @@ export declare class Lane<I, O> implements Positions<I, O> {
260
260
  reserve(n: number, cap: number): void;
261
261
  /** Gives a subject the voice reaches a position, from what its record fixed on first sight. */
262
262
  add(slot: number, held: Subject<unknown>): void;
263
+ /** The subject numbered `from` is numbered `to` now, a number no position holds: same position. */
264
+ renumber(from: number, to: number): void;
263
265
  /** A subject lost its number: the last position moves into its place. */
264
266
  remove(slot: number): void;
265
267
  }
266
- /**
267
- * A subject's voice time once its voice's freezes apply: frozen at 0 before it starts and at the end
268
- * of its passes after, and NaN where it shows nothing, as past its end running back.
269
- */
270
- export declare function frozenAt<I, O>(voice: Voice<I, O>, elapsed: number): number;
package/dist/lane.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { unbareLane } from './bare.js';
2
- import { frozenTime } from './clock.js';
3
2
  import { motionOf } from './motion.js';
4
3
  /**
5
4
  * Keeps the weight a probe read from the last fill as `Row.PROBED`, for `weightOf`, before a fill
@@ -185,6 +184,20 @@ export class Lane {
185
184
  this.records[p] = held;
186
185
  this.deltas[p] = null;
187
186
  }
187
+ /** The subject numbered `from` is numbered `to` now, a number no position holds: same position. */
188
+ renumber(from, to) {
189
+ const p = this.positionOf(from);
190
+ if (p < 0)
191
+ return;
192
+ if (to >= this.at.length) {
193
+ const at = new Int32Array(Math.max(to + 1, this.at.length * 2, 64));
194
+ at.set(this.at);
195
+ this.at = at;
196
+ }
197
+ this.list[p] = to;
198
+ this.at[to] = p + 1;
199
+ this.at[from] = 0;
200
+ }
188
201
  /** A subject lost its number: the last position moves into its place. */
189
202
  remove(slot) {
190
203
  const p = this.positionOf(slot);
@@ -214,12 +227,3 @@ export class Lane {
214
227
  this.deltas[last] = null;
215
228
  }
216
229
  }
217
- /**
218
- * A subject's voice time once its voice's freezes apply: frozen at 0 before it starts and at the end
219
- * of its passes after, and NaN where it shows nothing, as past its end running back.
220
- */
221
- export function frozenAt(voice, elapsed) {
222
- if (elapsed > voice.span && voice.hidesPast)
223
- return Number.NaN;
224
- return frozenTime(elapsed, voice.freezesBefore, voice.freezesAfter, voice.span);
225
- }
package/dist/lanes.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Column } from './columns.js';
1
+ import { flush, pullRun, writeLater } from './columns.js';
2
2
  import { type Copier, copy } from './copyout.js';
3
3
  import { type Crowd, row } from './crowd.js';
4
4
  import { call, foldKeys, one, parting, readKeyed, reset, run, runEveryone, signalled, subjectAt } from './fill.js';
@@ -7,6 +7,9 @@ import { type Lane, type Laned, type Role } from './lane.js';
7
7
  import { absent, Numbers } from './numbers.js';
8
8
  import { Owed } from './owed.js';
9
9
  import { Placed } from './placed.js';
10
+ import type { Column } from './pull.js';
11
+ import { renumber } from './renumber.js';
12
+ import { type Seat } from './seat.js';
10
13
  import type { Watcher } from './stretch.js';
11
14
  import type { Subject } from './subject.js';
12
15
  import type { Channel } from './types.js';
@@ -63,6 +66,29 @@ export interface LaneHost<I, O> {
63
66
  */
64
67
  export declare class Lanes<I, O> implements Watcher {
65
68
  readonly host: LaneHost<I, O>;
69
+ /** What the general path calls on a sharing voice, given to a mix by its engine so a lean bundle leaves it out. */
70
+ static readonly share: {
71
+ shares: typeof import("./everyone.js").shares;
72
+ outgrow: typeof import("./everyone.js").outgrow;
73
+ everyoneOf: typeof import("./everyone.js").everyoneOf;
74
+ sighted: typeof import("./everyone.js").sighted;
75
+ sight: typeof import("./everyone.js").sight;
76
+ weightAt: typeof import("./everyone.js").weightAt;
77
+ weighAt: typeof import("./everyone.js").weighAt;
78
+ open: typeof import("./everyone.js").open;
79
+ shut: typeof import("./everyone.js").shut;
80
+ restamp: typeof import("./everyone.js").restamp;
81
+ unsight: typeof import("./everyone.js").unsight;
82
+ unshare: typeof import("./everyone.js").unshare;
83
+ inherit: typeof import("./everyone.js").inherit;
84
+ };
85
+ /** What `pull` calls on lanes alone. */
86
+ readonly pulls: {
87
+ pullRun: typeof pullRun;
88
+ writeLater: typeof writeLater;
89
+ flush: typeof flush;
90
+ renumber: typeof renumber;
91
+ };
66
92
  readonly numbers: Numbers<I>;
67
93
  lanes: Lane<I, O>[];
68
94
  /** The lanes over every subject that have started playing, by epoch. */
@@ -70,7 +96,7 @@ export declare class Lanes<I, O> implements Watcher {
70
96
  readonly byId: Map<number, Lane<I, O>>;
71
97
  /** The crowds of single-subject voices, one per list of channels, and which holds each voice. */
72
98
  crowds: Crowd<I, O>[];
73
- readonly crowdOf: Map<number, Crowd<I, O>>;
99
+ readonly seats: Map<number, Seat<I, O>>;
74
100
  /** Which crowd voices' rows are placed on each subject. */
75
101
  readonly placed: Placed;
76
102
  /**
@@ -161,14 +187,16 @@ export declare class Lanes<I, O> implements Watcher {
161
187
  */
162
188
  readonly late: number[];
163
189
  /**
164
- * What `writeLater` and `pullRun` have queued, for one set of columns, as runs: each run's first
165
- * lane slot, the row it goes to, and how many subjects follow both one apart.
190
+ * What `writeLater` and `pullRun` have queued, for one set of columns, as runs of rows: each
191
+ * run's first row and how many follow, and its first lane slot where its slots follow one apart
192
+ * too, else -1, when each row's slot is read from `queueFrom` by row.
166
193
  */
167
194
  queueSlots: Int32Array<ArrayBuffer>;
168
195
  queueRows: Int32Array<ArrayBuffer>;
169
196
  queueCounts: Int32Array<ArrayBuffer>;
170
197
  queued: number;
171
198
  queuedFor: readonly Column[] | null;
199
+ queueFrom: Int32Array | null;
172
200
  keeps: boolean;
173
201
  now: number;
174
202
  filledAt: number;
@@ -285,7 +313,7 @@ export declare class Lanes<I, O> implements Watcher {
285
313
  /** A voice's clock moved the `shown` of records it had already placed: copies them again. */
286
314
  reshown(id: number): void;
287
315
  /** Something on a voice changed: a crowd copies it again at the next fill. */
288
- voiceChanged(id: number): void;
316
+ voiceChanged(voice: Voice<I, O>): void;
289
317
  }
290
318
  declare const methods: {
291
319
  copy: typeof copy;
package/dist/lanes.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { lit } from './bound.js';
2
+ import { flush, pullRun, writeLater } from './columns.js';
2
3
  import { copy } from './copyout.js';
3
4
  import { row } from './crowd.js';
5
+ import { share } from './everyone.js';
4
6
  import { call, fillAll, foldKeys, one, parting, readKeyed, reset, run, runEveryone, signalled, subjectAt, } from './fill.js';
5
7
  import { fold, foldDelta, foldInto, foldRun, gate, gather, unplain } from './gather.js';
6
8
  import { meet, reach } from './meet.js';
@@ -9,7 +11,9 @@ import { Owed } from './owed.js';
9
11
  import { Placed } from './placed.js';
10
12
  import { requalify, retouch } from './qualify.js';
11
13
  import { reading } from './reading.js';
14
+ import { renumber } from './renumber.js';
12
15
  import { reportedRow, unplace } from './rows.js';
16
+ import { seatOf } from './seat.js';
13
17
  /**
14
18
  * The lanes of one mix: subject numbers, which channels and voices run on lanes, and the values a
15
19
  * frame's fill leaves for probes to copy.
@@ -17,6 +21,10 @@ import { reportedRow, unplace } from './rows.js';
17
21
  // biome-ignore lint/suspicious/noUnsafeDeclarationMerging: every member of `Methods` is installed below
18
22
  export class Lanes {
19
23
  host;
24
+ /** What the general path calls on a sharing voice, given to a mix by its engine so a lean bundle leaves it out. */
25
+ static share = share;
26
+ /** What `pull` calls on lanes alone. */
27
+ pulls = { pullRun, writeLater, flush, renumber };
20
28
  numbers;
21
29
  lanes = [];
22
30
  /** The lanes over every subject that have started playing, by epoch. */
@@ -24,7 +32,7 @@ export class Lanes {
24
32
  byId = new Map();
25
33
  /** The crowds of single-subject voices, one per list of channels, and which holds each voice. */
26
34
  crowds = [];
27
- crowdOf = new Map();
35
+ seats = new Map();
28
36
  /** Which crowd voices' rows are placed on each subject. */
29
37
  placed = new Placed();
30
38
  /**
@@ -115,14 +123,16 @@ export class Lanes {
115
123
  */
116
124
  late = [];
117
125
  /**
118
- * What `writeLater` and `pullRun` have queued, for one set of columns, as runs: each run's first
119
- * lane slot, the row it goes to, and how many subjects follow both one apart.
126
+ * What `writeLater` and `pullRun` have queued, for one set of columns, as runs of rows: each
127
+ * run's first row and how many follow, and its first lane slot where its slots follow one apart
128
+ * too, else -1, when each row's slot is read from `queueFrom` by row.
120
129
  */
121
130
  queueSlots = new Int32Array(0);
122
131
  queueRows = new Int32Array(0);
123
132
  queueCounts = new Int32Array(0);
124
133
  queued = 0;
125
134
  queuedFor = null;
135
+ queueFrom = null;
126
136
  keeps = false;
127
137
  now = Number.NaN;
128
138
  filledAt = Number.NaN;
@@ -233,10 +243,9 @@ export class Lanes {
233
243
  lane.remove(slot);
234
244
  return;
235
245
  }
236
- const c = this.crowdOf.get(id);
237
- const p = c?.rowOf.get(id);
238
- if (c !== undefined && p !== undefined && c.list[p] === slot)
239
- unplace(this, c, p);
246
+ const s = this.seats.get(id);
247
+ if (s !== undefined && s.crowd.list[s.row] === slot)
248
+ unplace(this, s.crowd, s.row);
240
249
  }
241
250
  /** A voice brought a subject back: its next probe meets every lane again, as it met them first. */
242
251
  rejoin(slot) {
@@ -254,9 +263,8 @@ export class Lanes {
254
263
  const lane = this.byId.get(id);
255
264
  if (lane !== undefined)
256
265
  return this.reported(lane, slot);
257
- const c = this.crowdOf.get(id);
258
- const p = c?.rowOf.get(id);
259
- return c === undefined || p === undefined ? undefined : reportedRow(this, c, p, slot);
266
+ const s = this.seats.get(id);
267
+ return s === undefined ? undefined : reportedRow(this, s.crowd, s.row, slot);
260
268
  }
261
269
  reported(lane, slot) {
262
270
  const o = slot * 6 /* Per.SLOT */;
@@ -439,10 +447,9 @@ export class Lanes {
439
447
  lane.remove(slot);
440
448
  const placed = this.placed;
441
449
  for (let id = placed.any(slot); id !== 0; id = placed.any(slot)) {
442
- const c = this.crowdOf.get(id);
443
- const p = c?.rowOf.get(id);
444
- if (c !== undefined && p !== undefined && c.list[p] === slot)
445
- unplace(this, c, p);
450
+ const s = this.seats.get(id);
451
+ if (s !== undefined && s.crowd.list[s.row] === slot)
452
+ unplace(this, s.crowd, s.row);
446
453
  else
447
454
  placed.remove(slot, id);
448
455
  }
@@ -474,10 +481,10 @@ export class Lanes {
474
481
  * next fill, and a lane keeps the sample the old one gave.
475
482
  */
476
483
  stretchChanged(id, s) {
477
- const c = this.crowdOf.get(id);
478
- const p = c?.rowOf.get(id);
479
- if (c !== undefined && p !== undefined) {
480
- c.restale(p, c.hot[p * c.stride + 0 /* Hot.FLAGS */]);
484
+ const seat = this.seats.get(id);
485
+ if (seat !== undefined) {
486
+ const c = seat.crowd;
487
+ c.restale(seat.row, c.hot[seat.row * c.stride + 0 /* Hot.FLAGS */]);
481
488
  return;
482
489
  }
483
490
  const lane = this.byId.get(id);
@@ -502,12 +509,11 @@ export class Lanes {
502
509
  }
503
510
  /** A voice's clock moved the `shown` of records it had already placed: copies them again. */
504
511
  reshown(id) {
505
- const c = this.crowdOf.get(id);
506
- const p = c?.rowOf.get(id);
507
- if (c !== undefined && p !== undefined) {
508
- const held = c.records[p];
512
+ const s = this.seats.get(id);
513
+ if (s !== undefined) {
514
+ const held = s.crowd.records[s.row];
509
515
  if (held !== undefined)
510
- c.data[p * 8 /* Row.STRIDE */ + 1 /* Row.SINCE */] = held.shown;
516
+ s.crowd.data[s.row * 8 /* Row.STRIDE */ + 1 /* Row.SINCE */] = held.shown;
511
517
  }
512
518
  const lane = this.byId.get(id);
513
519
  if (lane === undefined)
@@ -519,12 +525,12 @@ export class Lanes {
519
525
  }
520
526
  }
521
527
  /** Something on a voice changed: a crowd copies it again at the next fill. */
522
- voiceChanged(id) {
523
- const c = this.crowdOf.get(id);
524
- const p = c?.rowOf.get(id);
525
- if (c === undefined || p === undefined)
528
+ voiceChanged(voice) {
529
+ const s = seatOf(this, voice);
530
+ if (s === null)
526
531
  return;
527
- const f = p * c.stride + 0 /* Hot.FLAGS */;
532
+ const c = s.crowd;
533
+ const f = s.row * c.stride + 0 /* Hot.FLAGS */;
528
534
  c.hot[f] = c.hot[f] | 64 /* Flag.VOICE */;
529
535
  }
530
536
  }
package/dist/lch.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { Curve } from './easing.js';
2
+ /** Below this chroma a color is gray and its hue is noise, so it takes the other end's. */
3
+ export declare const GRAY = 0.0001;
4
+ /** Where `shortWay` leaves the hue a way starts from and how far it turns. */
5
+ export declare const HUES: number[];
6
+ /** The way from hue `ah` to `bh` the short way round into `HUES`, a gray end taking the other's. */
7
+ export declare function shortWay(ah: number, ac: number, bh: number, bc: number): void;
8
+ /**
9
+ * Two premultiplied OKLab colors, `[w·L, w·a, w·b, w]` read from `x` at `i` and `y` at `j`, into
10
+ * `out` as `[L, C, h, w]` each, `y`'s hue the short way round from `x`'s. A color with no coverage
11
+ * has none of its own to keep, so it takes the other end's.
12
+ */
13
+ export declare function lchPair(x: ArrayLike<number>, i: number, y: ArrayLike<number>, j: number, out: Float64Array): void;
14
+ /**
15
+ * `closed` for a stretch of a color traveling round the hue, as `ink` does: its ends and velocity,
16
+ * premultiplied OKLab as the patch keeps them, moved into `[L, C, h, w]`, solved there axis by
17
+ * axis, and the position and
18
+ * velocity moved back into `xo` and `vo`. A spring heads round the hue the short way from where
19
+ * the stretch starts, so a retarget bends the path rather than restarting it, and one that
20
+ * overshoots its target overshoots in hue too. A glide comes to rest `v·τ` along each of the four,
21
+ * so it coasts round the hue rather than off along a tangent.
22
+ */
23
+ export declare function closedLch(law: Float64Array, ease: Curve | undefined, at: number, ms: number, x0: ArrayLike<number>, x: number, v0: ArrayLike<number>, v: number, to: ArrayLike<number>, g: number, t: number, xo: Float64Array, vo: Float64Array): boolean;
package/dist/lch.js ADDED
@@ -0,0 +1,126 @@
1
+ import { COAST, closed } from './closed.js';
2
+ /** Below this chroma a color is gray and its hue is noise, so it takes the other end's. */
3
+ export const GRAY = 1e-4;
4
+ /** Where `shortWay` leaves the hue a way starts from and how far it turns. */
5
+ export const HUES = [0, 0];
6
+ /** The way from hue `ah` to `bh` the short way round into `HUES`, a gray end taking the other's. */
7
+ export function shortWay(ah, ac, bh, bc) {
8
+ const from = ac < GRAY ? bh : ah;
9
+ const to = bc < GRAY ? ah : bh;
10
+ let turn = to - from;
11
+ if (turn > Math.PI)
12
+ turn -= 2 * Math.PI;
13
+ else if (turn < -Math.PI)
14
+ turn += 2 * Math.PI;
15
+ HUES[0] = from;
16
+ HUES[1] = turn;
17
+ }
18
+ /**
19
+ * Two premultiplied OKLab colors, `[w·L, w·a, w·b, w]` read from `x` at `i` and `y` at `j`, into
20
+ * `out` as `[L, C, h, w]` each, `y`'s hue the short way round from `x`'s. A color with no coverage
21
+ * has none of its own to keep, so it takes the other end's.
22
+ */
23
+ export function lchPair(x, i, y, j, out) {
24
+ const xw = x[i + 3] ?? 0;
25
+ const yw = y[j + 3] ?? 0;
26
+ const ys = yw > 0 ? 1 / yw : 0;
27
+ const yL = (y[j] ?? 0) * ys;
28
+ const yA = (y[j + 1] ?? 0) * ys;
29
+ const yB = (y[j + 2] ?? 0) * ys;
30
+ const xs = xw > 0 ? 1 / xw : 0;
31
+ const xL = xw > 0 ? (x[i] ?? 0) * xs : yL;
32
+ const xA = xw > 0 ? (x[i + 1] ?? 0) * xs : yA;
33
+ const xB = xw > 0 ? (x[i + 2] ?? 0) * xs : yB;
34
+ const toL = yw > 0 ? yL : xL;
35
+ const toA = yw > 0 ? yA : xA;
36
+ const toB = yw > 0 ? yB : xB;
37
+ const xc = Math.sqrt(xA * xA + xB * xB);
38
+ const yc = Math.sqrt(toA * toA + toB * toB);
39
+ shortWay(Math.atan2(xB, xA), xc, Math.atan2(toB, toA), yc);
40
+ out[0] = xL;
41
+ out[1] = xc;
42
+ out[2] = HUES[0];
43
+ out[3] = xw;
44
+ out[4] = toL;
45
+ out[5] = yc;
46
+ out[6] = HUES[0] + HUES[1];
47
+ out[7] = yw;
48
+ }
49
+ const PAIR = new Float64Array(8);
50
+ const RATES = new Float64Array(4);
51
+ const REST = new Float64Array(4);
52
+ const XS = new Float64Array(4);
53
+ const VS = new Float64Array(4);
54
+ /**
55
+ * `closed` for a stretch of a color traveling round the hue, as `ink` does: its ends and velocity,
56
+ * premultiplied OKLab as the patch keeps them, moved into `[L, C, h, w]`, solved there axis by
57
+ * axis, and the position and
58
+ * velocity moved back into `xo` and `vo`. A spring heads round the hue the short way from where
59
+ * the stretch starts, so a retarget bends the path rather than restarting it, and one that
60
+ * overshoots its target overshoots in hue too. A glide comes to rest `v·τ` along each of the four,
61
+ * so it coasts round the hue rather than off along a tangent.
62
+ */
63
+ export function closedLch(law, ease, at, ms, x0, x, v0, v, to, g, t, xo, vo) {
64
+ const p = PAIR;
65
+ lchPair(x0, x, to, g, p);
66
+ const L = p[0];
67
+ const C = p[1];
68
+ const h = p[2];
69
+ const w = p[3];
70
+ // OKLab's rates of change at the start, its coverage's share divided out, turned onto L, C and h.
71
+ const dw = v0[v + 3];
72
+ let dL = 0;
73
+ let dC = 0;
74
+ let dh = 0;
75
+ if (w > 0) {
76
+ const cos = Math.cos(h);
77
+ const sin = Math.sin(h);
78
+ dL = (v0[v] - dw * L) / w;
79
+ const da = (v0[v + 1] - dw * C * cos) / w;
80
+ const db = (v0[v + 2] - dw * C * sin) / w;
81
+ dC = da * cos + db * sin;
82
+ dh = C < GRAY ? 0 : (db * cos - da * sin) / C;
83
+ }
84
+ RATES[0] = dL;
85
+ RATES[1] = dC;
86
+ RATES[2] = dh;
87
+ RATES[3] = dw;
88
+ let goal = p;
89
+ let o = 4;
90
+ if (law[0] === COAST) {
91
+ const tau = law[2];
92
+ for (let i = 0; i < 4; i++)
93
+ REST[i] = p[i] + RATES[i] * tau;
94
+ goal = REST;
95
+ o = 0;
96
+ }
97
+ const still = closed(law, ease, 4, at, ms, p, 0, RATES, 0, goal, o, t, XS, VS);
98
+ if (still && o === 4) {
99
+ for (let i = 0; i < 4; i++) {
100
+ xo[i] = to[g + i];
101
+ vo[i] = 0;
102
+ }
103
+ return true;
104
+ }
105
+ const nL = XS[0];
106
+ const nC = XS[1];
107
+ const nh = XS[2];
108
+ const nw = XS[3];
109
+ const cos = Math.cos(nh);
110
+ const sin = Math.sin(nh);
111
+ const a = nC * cos;
112
+ const b = nC * sin;
113
+ const rL = VS[0];
114
+ const rC = VS[1];
115
+ const rh = VS[2];
116
+ const rw = VS[3];
117
+ xo[0] = nL * nw;
118
+ xo[1] = a * nw;
119
+ xo[2] = b * nw;
120
+ xo[3] = nw;
121
+ vo[0] = rL * nw + nL * rw;
122
+ vo[1] = (rC * cos - nC * rh * sin) * nw + a * rw;
123
+ vo[2] = (rC * sin + nC * rh * cos) * nw + b * rw;
124
+ vo[3] = rw;
125
+ return still;
126
+ }
package/dist/meet.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { open, shut } from './everyone.js';
2
+ import { seatOf } from './seat.js';
2
3
  /**
3
4
  * Whether the lanes a probe of the subject at `slot` just met (`met`) can be folded on the general
4
5
  * path after the lanes' values: none is in a role or a crowd, the subject owes none already this
@@ -183,10 +184,11 @@ function foldNow(lanes, slot, subject, again) {
183
184
  }
184
185
  /** Places a crowd voice's row on the subject a probe met, as `Lane.add` adds a position. */
185
186
  function place(lanes, voice, slot, subject, from, head) {
186
- const c = lanes.crowdOf.get(voice.id);
187
- const p = c?.rowOf.get(voice.id);
188
- if (c === undefined || p === undefined)
187
+ const s = seatOf(lanes, voice);
188
+ if (s === null)
189
189
  return false;
190
+ const c = s.crowd;
191
+ const p = s.row;
190
192
  const h = p * c.stride;
191
193
  const flags = c.hot[h + 0 /* Hot.FLAGS */];
192
194
  if (c.hot[h + 6 /* Hot.EPOCH */] <= from || (flags & 16 /* Flag.PLACED */) !== 0)
package/dist/mixer.d.ts CHANGED
@@ -4,11 +4,11 @@ import { blended, laneRead, shareKept } from './blend.js';
4
4
  import type { Book } from './book.js';
5
5
  import { aims, chain, linkable, relink } from './chain.js';
6
6
  import { type LerpInto } from './channels.js';
7
- import type { Column } from './columns.js';
8
7
  import { baseFor, contribution, isRest, keyed, read, slopeFor, tick } from './contribution.js';
9
8
  import { DepartedIndex } from './departed.js';
10
9
  import { drop, forgetIn, recordDrop, releaseIn } from './drop.js';
11
10
  import { DueQueue } from './due.js';
11
+ import type { Share } from './everyone.js';
12
12
  import { parting } from './fade.js';
13
13
  import { apply, clamp, fold, foldDetour, folded, foldSharers, foldWith, type Key, linked, owedBy, passes, shared } from './fold.js';
14
14
  import { GoneIndex } from './gone.js';
@@ -20,6 +20,7 @@ import type { Pace } from './pace.js';
20
20
  import { Keys } from './paging.js';
21
21
  import type { Places } from './places.js';
22
22
  import { moved, place, plot, put } from './plot.js';
23
+ import type { Column } from './pull.js';
23
24
  import { keep } from './pull.js';
24
25
  import { Steps } from './relink.js';
25
26
  import { atRest, restful, rests, restsKey, restsLaned, still } from './rests.js';
@@ -32,7 +33,7 @@ import { Store } from './store.js';
32
33
  import type { MotionOwner } from './stretch.js';
33
34
  import type { Subject } from './subject.js';
34
35
  import { type Announced, Transport } from './transport.js';
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';
36
+ import type { ArcHandle, ArcSpec, BlendMember, Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, Trace, TraceOptions, VoiceSpec } from './types.js';
36
37
  import { type Voice } from './voice.js';
37
38
  import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, primeLaned, weigh, weighOwned } from './weigh.js';
38
39
  /**
@@ -209,6 +210,8 @@ export declare class Mixer<I, O> implements Mix<I, O> {
209
210
  roleDepth: number;
210
211
  readonly slotOf: Map<string, number>;
211
212
  lanes: Lanes<I, O> | null;
213
+ /** The sharing hooks of the lanes this entry was built with, which a projection's copy keeps. */
214
+ share: Share | null;
212
215
  handles: HandleHost<I, O> | null;
213
216
  /** What `book` made, still booking; null while there is none. */
214
217
  bookers: Book<I, O>[] | null;
@@ -266,6 +269,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
266
269
  project(at: number | {
267
270
  readonly frame: number;
268
271
  }): Projection<I, O>;
272
+ trace(from: number, to: number, opts?: TraceOptions): Trace<I, O>;
269
273
  announce(name: string, opts?: {
270
274
  at?: number;
271
275
  score?: string;