@msb235/blits 0.15.0 → 0.15.1

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 (51) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +1 -1
  3. package/dist/bare.js +22 -7
  4. package/dist/columns.d.ts +2 -1
  5. package/dist/columns.js +55 -35
  6. package/dist/crowd.d.ts +8 -4
  7. package/dist/crowd.js +17 -7
  8. package/dist/delay.d.ts +3 -0
  9. package/dist/delay.js +7 -0
  10. package/dist/due.js +1 -1
  11. package/dist/easing.d.ts +2 -0
  12. package/dist/easing.js +4 -0
  13. package/dist/everyone.d.ts +5 -0
  14. package/dist/everyone.js +30 -0
  15. package/dist/fill.js +1 -1
  16. package/dist/held.js +2 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/lane.d.ts +2 -5
  19. package/dist/lane.js +14 -10
  20. package/dist/lanes.d.ts +7 -4
  21. package/dist/lanes.js +27 -28
  22. package/dist/meet.js +5 -3
  23. package/dist/mixer.d.ts +2 -1
  24. package/dist/mixer.js +4 -0
  25. package/dist/named.d.ts +6 -1
  26. package/dist/named.js +29 -1
  27. package/dist/numbers.d.ts +5 -0
  28. package/dist/numbers.js +29 -0
  29. package/dist/pull.js +11 -1
  30. package/dist/qualify.js +9 -10
  31. package/dist/renumber.d.ts +10 -0
  32. package/dist/renumber.js +75 -0
  33. package/dist/rows.js +27 -19
  34. package/dist/seat.d.ts +17 -0
  35. package/dist/seat.js +25 -0
  36. package/dist/shared.d.ts +1 -1
  37. package/dist/shared.js +1 -1
  38. package/dist/strays.js +3 -2
  39. package/dist/trace.d.ts +4 -0
  40. package/dist/trace.js +255 -0
  41. package/dist/types/mix.d.ts +15 -0
  42. package/dist/types/trace.d.ts +51 -0
  43. package/dist/types/trace.js +1 -0
  44. package/dist/types/voice.d.ts +10 -5
  45. package/dist/types.d.ts +1 -0
  46. package/dist/unreached.d.ts +2 -0
  47. package/dist/unreached.js +7 -0
  48. package/dist/voice.d.ts +3 -0
  49. package/dist/voice.js +9 -3
  50. package/docs/schema.html +11 -0
  51. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,50 @@ This package follows [semver](https://semver.org). Below 1.0.0, a breaking chang
4
4
  version and everything else the patch. Each release lists its changes as **Breaking**, **Added** and
5
5
  **Fixed**, and the release workflow refuses a tag with no section here.
6
6
 
7
+ ## 0.15.1
8
+
9
+ ### Added
10
+
11
+ - `mix.trace(from, to, { grid })` reads the mix over a window of mix time without moving it, and
12
+ `trace.of(subject, channel)` gives a channel of one number as a `Run`: points joined by straight
13
+ lines, two points at one time for a jump, and `exact` where the lines are the mix's own values
14
+ throughout. Where every voice feeding the channel is a `keys` voice of known shape (plain number
15
+ stops eased `linear`, a number weight, a steady rate, no fade under way), the points are its own
16
+ breakpoints; elsewhere the run is read every `grid` ms, default 5. One trace shares its reads
17
+ across subjects. `Run`, `Trace`, `TraceOptions` and `NumberChannel` are exported.
18
+
19
+ ### Changed
20
+
21
+ - A voice cued with `subjects` asks `stagger` for each of them at `cue`, not on first sight, and a
22
+ finite loop ends once its latest-staggered subject is done whether or not the host has read that
23
+ subject yet. A read ahead now sees the same end as the live mix. A voice reaching its subjects by
24
+ `target` still ends by the subjects it has met.
25
+
26
+ ### Fixed
27
+
28
+ - One-subject tween voices cost about a fifth less a frame once they have landed: a fill folds a
29
+ landed tween's end as it stands, without reading its clock or easing it, until its voice, its
30
+ stretch or its subject's delay changes or the clock reads earlier than when it landed. 10,000 of
31
+ them read by `pull` (`tweens^`) take 0.235 ms a frame against 0.296, `churn^` 0.301 against
32
+ 0.356 and `turnover^` 0.419 against 0.458 (studio, medians of seven runs of 5,000 frames, each in
33
+ its own process); one tween voice over 10,000 subjects (`weasel^`) is unchanged.
34
+ - A crowd of one-subject `keys` or `fn` voices applies its voices' freezes in the fill's own loop
35
+ rather than through a call a row. 10,000 `keys` voices read by `pull` (`keyses^`) take 0.449 ms
36
+ a frame against 0.479, and allocate 6 kB a frame against 162 (studio, medians of seven runs of
37
+ 1,500 frames, each in its own process); the same voices freezing at both ends (`frozen`,
38
+ `frozen^`) are unchanged.
39
+ - `pull` over a list whose order has drifted from the order the mix first saw it in, as a list
40
+ kept dense by swap-remove does, writes rests in a few runs, not one per short stretch of
41
+ subjects. With one subject leaving and one arriving every frame (`turnover^`), a frame takes
42
+ 0.253 ms against 0.262. Teitou, medians of seven runs of 5,000 frames, each in its own process.
43
+ Other rows are unchanged.
44
+ - A list kept dense by swap-remove stays in the mix's own order for `pull`: a subject moved into
45
+ the place a dropped one left takes the number the drop freed, so `pull` reads the list in long
46
+ runs. `turnover^` takes 0.223 ms a frame against 0.263, close to the 0.210 of `churn^`, which
47
+ replaces voices without reordering anything (teitou with the node to itself, medians of seven
48
+ runs). The other rows are unchanged. A mix that keeps history, or has roles or a bound on its
49
+ lanes, keeps the old order.
50
+
7
51
  ## 0.15.0
8
52
 
9
53
  ### Breaking
package/README.md CHANGED
@@ -151,7 +151,7 @@ do yet.
151
151
 
152
152
  ## Status
153
153
 
154
- v0.15.0, with five consumers. magicsmoke runs every fault on it, wod its wheel, tricks and
154
+ v0.15.1, with five consumers. magicsmoke runs every fault on it, wod its wheel, tricks and
155
155
  transitions, weasel its animator's tweens, sherpa its seams, and
156
156
  [klieg](https://github.com/orochi235/klieg) composes all three of its systems — letter motion,
157
157
  part effects, and the environment a sign is lit by — on a mix each.
package/dist/bare.js CHANGED
@@ -48,9 +48,15 @@ export function bareRows(lanes, c, p, id) {
48
48
  !(per[q + 3 /* Per.LANE_PROBE */] > lately ||
49
49
  per[q + 4 /* Per.GENERAL_PROBE */] > lately))
50
50
  break;
51
- const elapsed = clock(hot, h, now) - data[o + 0 /* Row.DELAY */];
52
- if (!(elapsed >= 0))
53
- break;
51
+ // Every step from `now` to a sample's progress rounds monotonically, so a row seen landed at a
52
+ // positive rate is landed at any later `now` while it stays ready, and folds its `to` as it is.
53
+ const landed = ready && now >= hot[h + 8 /* Hot.LANDED */];
54
+ let elapsed = 0;
55
+ if (!landed) {
56
+ elapsed = clock(hot, h, now) - data[o + 0 /* Row.DELAY */];
57
+ if (!(elapsed >= 0))
58
+ break;
59
+ }
54
60
  keepProbed(data, o, per, q, fills);
55
61
  const w = hot[h + 4 /* Hot.WEIGHT */];
56
62
  data[o + 2 /* Row.WEIGHT */] = w;
@@ -60,14 +66,23 @@ export function bareRows(lanes, c, p, id) {
60
66
  c.deltas[p] = null;
61
67
  }
62
68
  hot[h + 0 /* Hot.FLAGS */] = f | 2048 /* Flag.READY */;
69
+ hot[h + 8 /* Hot.LANDED */] = Number.POSITIVE_INFINITY;
63
70
  }
64
71
  if (w <= 0)
65
72
  continue;
66
- const share = left(c.eases[p], progress(elapsed, hot[h + 8 /* Hot.AT */], hot[h + 9 /* Hot.MS */]));
67
73
  const values = ch.values;
68
74
  const base = slot * n;
69
- const x = h + 10 /* Hot.X0 */;
75
+ const x = h + 11 /* Hot.X0 */;
70
76
  const g = x + 2 * n;
77
+ if (landed) {
78
+ for (let a = 0; a < n; a++)
79
+ values[base + a] = foldNumber(op, values[base + a], hot[g + a], w);
80
+ continue;
81
+ }
82
+ const u = progress(elapsed, hot[h + 9 /* Hot.AT */], hot[h + 10 /* Hot.MS */]);
83
+ const share = left(c.eases[p], u);
84
+ if (u >= 1 && hot[h + 3 /* Hot.RATE */] > 0)
85
+ hot[h + 8 /* Hot.LANDED */] = now;
71
86
  for (let a = 0; a < n; a++)
72
87
  values[base + a] = foldNumber(op, values[base + a], between(hot[x + a], hot[g + a], share), w);
73
88
  }
@@ -87,8 +102,8 @@ export function unbare(c, p) {
87
102
  hot[h + 0 /* Hot.FLAGS */] = hot[h + 0 /* Hot.FLAGS */] & ~2048 /* Flag.READY */;
88
103
  const n = c.axes;
89
104
  const elapsed = clock(hot, h, c.bareNow) - data[o + 0 /* Row.DELAY */];
90
- const share = left(c.eases[p], progress(elapsed, hot[h + 8 /* Hot.AT */], hot[h + 9 /* Hot.MS */]));
91
- const x = h + 10 /* Hot.X0 */;
105
+ const share = left(c.eases[p], progress(elapsed, hot[h + 9 /* Hot.AT */], hot[h + 10 /* Hot.MS */]));
106
+ const x = h + 11 /* Hot.X0 */;
92
107
  const g = x + 2 * n;
93
108
  for (let a = 0; a < n; a++)
94
109
  c.samples[p * n + a] = between(hot[x + a], hot[g + a], share);
package/dist/columns.d.ts CHANGED
@@ -22,7 +22,8 @@ export declare function pullRun<I, O>(lanes: Lanes<I, O>, slots: Int32Array, lis
22
22
  * Queues a subject's values for each column at subject `n`'s places, for a `pull` that reads
23
23
  * from the lanes while every voice is on one; `flush` writes what is queued, column by column. A
24
24
  * channel no lane holds is at rest. Anything queued is written before the lanes fill again.
25
+ * `slots` holds each row's lane slot, `slot` among them.
25
26
  */
26
- export declare function writeLater<I, O>(lanes: Lanes<I, O>, slot: number, columns: readonly Column[], n: number): void;
27
+ export declare function writeLater<I, O>(lanes: Lanes<I, O>, slot: number, columns: readonly Column[], n: number, slots: Int32Array): void;
27
28
  /** Writes every queued run into its columns: a channel's values as a block, a rest as a fill. */
28
29
  export declare function flush<I, O>(lanes: Lanes<I, O>): void;
package/dist/columns.js CHANGED
@@ -26,15 +26,8 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
26
26
  // Nothing in `pullRun` makes a subject owe a voice, so one with nobody owing need not ask.
27
27
  const owing = lanes.owed.any();
28
28
  const current = since === Number.POSITIVE_INFINITY;
29
- if (columns !== lanes.queuedFor) {
30
- flush(lanes);
31
- lanes.queuedFor = columns;
32
- }
29
+ queueFor(lanes, columns, slots);
33
30
  room(lanes, lanes.queued + list.length - from);
34
- const qSlots = lanes.queueSlots;
35
- const qRows = lanes.queueRows;
36
- const qCounts = lanes.queueCounts;
37
- let k = lanes.queued;
38
31
  let n = from;
39
32
  // The run being gathered: its first slot and row, and how far it has reached.
40
33
  let first = -1;
@@ -60,23 +53,14 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
60
53
  if (holds)
61
54
  subjects[slot] = subject;
62
55
  if (slot !== first + (n - start)) {
63
- if (first >= 0) {
64
- qSlots[k] = first;
65
- qRows[k] = start;
66
- qCounts[k] = n - start;
67
- k++;
68
- }
56
+ if (first >= 0)
57
+ add(lanes, first, start, n - start);
69
58
  first = slot;
70
59
  start = n;
71
60
  }
72
61
  }
73
- if (first >= 0) {
74
- qSlots[k] = first;
75
- qRows[k] = start;
76
- qCounts[k] = n - start;
77
- k++;
78
- }
79
- lanes.queued = k;
62
+ if (first >= 0)
63
+ add(lanes, first, start, n - start);
80
64
  if (n > from && holds)
81
65
  lanes.holding = true;
82
66
  return n;
@@ -85,24 +69,43 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
85
69
  * Queues a subject's values for each column at subject `n`'s places, for a `pull` that reads
86
70
  * from the lanes while every voice is on one; `flush` writes what is queued, column by column. A
87
71
  * channel no lane holds is at rest. Anything queued is written before the lanes fill again.
72
+ * `slots` holds each row's lane slot, `slot` among them.
88
73
  */
89
- export function writeLater(lanes, slot, columns, n) {
90
- queue(lanes, columns, slot, n, 1);
74
+ export function writeLater(lanes, slot, columns, n, slots) {
75
+ queueFor(lanes, columns, slots);
76
+ add(lanes, slot, n, 1);
91
77
  }
92
- /** Queues `count` subjects from lane slot `slot` for rows from `row`, one apart in both. */
93
- function queue(lanes, columns, slot, row, count) {
94
- if (columns !== lanes.queuedFor) {
95
- flush(lanes);
96
- lanes.queuedFor = columns;
97
- }
78
+ /** Writes what is queued for other columns or another array of slots before queuing for these. */
79
+ function queueFor(lanes, columns, slots) {
80
+ if (columns === lanes.queuedFor && slots === lanes.queueFrom)
81
+ return;
82
+ flush(lanes);
83
+ lanes.queuedFor = columns;
84
+ lanes.queueFrom = slots;
85
+ }
86
+ /**
87
+ * The shortest run `flush` copies as a block from consecutive slots. A shorter one, as a list kept
88
+ * dense by swap-remove leaves its slots, joins the rows before it in one run whose slots `flush`
89
+ * reads row by row, so a run's cost is paid once a stretch of rows rather than once a few.
90
+ */
91
+ const BLOCK = 16;
92
+ /** Queues `count` rows from `row`, whose slots follow one apart from `slot`. */
93
+ function add(lanes, slot, row, count) {
98
94
  const k = lanes.queued;
99
95
  if (k > 0) {
100
96
  const last = k - 1;
101
97
  const had = lanes.queueCounts[last];
102
- if (slot === lanes.queueSlots[last] + had &&
103
- row === lanes.queueRows[last] + had) {
104
- lanes.queueCounts[last] = had + count;
105
- return;
98
+ if (row === lanes.queueRows[last] + had) {
99
+ const first = lanes.queueSlots[last];
100
+ if (first >= 0 && slot === first + had) {
101
+ lanes.queueCounts[last] = had + count;
102
+ return;
103
+ }
104
+ if (count < BLOCK && (first < 0 || had < BLOCK)) {
105
+ lanes.queueSlots[last] = -1;
106
+ lanes.queueCounts[last] = had + count;
107
+ return;
108
+ }
106
109
  }
107
110
  }
108
111
  room(lanes, k + 1);
@@ -173,16 +176,33 @@ function rests(lanes, c, runs) {
173
176
  }
174
177
  /** The longest run of values `copies` copies itself: `set` takes a view, an object a run. */
175
178
  const VIEWED = 256;
176
- /** Copies a laned channel's values for every queued run into its rows. */
179
+ /** Copies a laned channel's values for every queued run into its rows, a block or row by row. */
177
180
  function copies(lanes, c, values, runs) {
178
181
  const slots = lanes.queueSlots;
179
182
  const rows = lanes.queueRows;
180
183
  const counts = lanes.queueCounts;
181
184
  const out = c.out;
182
185
  const axes = c.axes;
186
+ const from = lanes.queueFrom;
183
187
  for (let r = 0; r < runs; r++) {
188
+ const first = slots[r];
189
+ if (first < 0) {
190
+ const row = rows[r];
191
+ const end = row + counts[r];
192
+ if (axes === 1)
193
+ for (let i = row; i < end; i++)
194
+ out[i] = values[from[i]];
195
+ else
196
+ for (let i = row; i < end; i++) {
197
+ const at = i * axes;
198
+ const base = from[i] * axes;
199
+ for (let a = 0; a < axes; a++)
200
+ out[at + a] = values[base + a];
201
+ }
202
+ continue;
203
+ }
184
204
  const at = rows[r] * axes;
185
- const base = slots[r] * axes;
205
+ const base = first * axes;
186
206
  const span = counts[r] * axes;
187
207
  if (span > VIEWED)
188
208
  out.set(values.subarray(base, base + span), at);
package/dist/crowd.d.ts CHANGED
@@ -25,10 +25,15 @@ export declare const enum Hot {
25
25
  SEEKS = 5,
26
26
  EPOCH = 6,
27
27
  ID = 7,
28
- AT = 8,
29
- MS = 9,
28
+ /**
29
+ * A `Flag.READY` row's mix time from which its tween has landed, so a fill folds its `to` as it
30
+ * stands; Infinity until `bareRows` sees it land at a positive rate. Unread on any other row.
31
+ */
32
+ LANDED = 8,
33
+ AT = 9,
34
+ MS = 10,
30
35
  /** `x0`, `v0` and `to`, `axes` numbers each. */
31
- X0 = 10
36
+ X0 = 11
32
37
  }
33
38
  /** A crowd row's flags, at `Hot.FLAGS`. */
34
39
  export declare const enum Flag {
@@ -86,7 +91,6 @@ export declare class Crowd<I, O> implements Positions<I, O> {
86
91
  eases: (Curve | undefined)[];
87
92
  /** Each row's patch law, one array shared by every row whose law is the same. */
88
93
  laws: Float64Array[];
89
- readonly rowOf: Map<number, number>;
90
94
  idle: boolean;
91
95
  readonly line: {
92
96
  stop: number;
package/dist/crowd.js CHANGED
@@ -2,7 +2,7 @@ import { bareRows, clock, unbare } from './bare.js';
2
2
  import { clampWeight, passAt, phaseAt, weighed } from './clock.js';
3
3
  import { closed } from './closed.js';
4
4
  import { KeyRows } from './keyrows.js';
5
- import { frozenAt, keepProbed, SPARSE } from './lane.js';
5
+ import { keepProbed, SPARSE } from './lane.js';
6
6
  import { move } from './sample.js';
7
7
  /**
8
8
  * Whether a laned voice belongs in a crowd: one naming a single subject, which shares nothing
@@ -45,7 +45,6 @@ export class Crowd {
45
45
  eases = [];
46
46
  /** Each row's patch law, one array shared by every row whose law is the same. */
47
47
  laws = [];
48
- rowOf = new Map();
49
48
  idle = false;
50
49
  line = SPARSE.other;
51
50
  /** Where the fill now under way has reached. */
@@ -69,7 +68,7 @@ export class Crowd {
69
68
  constructor(chans, axes) {
70
69
  this.chans = chans;
71
70
  this.axes = axes;
72
- this.stride = 10 /* Hot.X0 */ + 3 * axes;
71
+ this.stride = 11 /* Hot.X0 */ + 3 * axes;
73
72
  this.xs = new Float64Array(axes);
74
73
  this.vs = new Float64Array(axes);
75
74
  }
@@ -218,8 +217,19 @@ function rows(lanes, c, first, id) {
218
217
  const voice = c.voices[p];
219
218
  const elapsedNow = fast ? clock(hot, h, now) : voice.elapsedAt(now);
220
219
  let elapsed = elapsedNow - delay;
220
+ // Its freezes applied as `one` applies them, written out: a function for it stays out of line.
221
221
  if ((f & 256 /* Flag.FREEZES */) !== 0)
222
- elapsed = frozenAt(voice, elapsed);
222
+ elapsed = !(elapsed >= 0)
223
+ ? voice.freezesBefore
224
+ ? 0
225
+ : Number.NaN
226
+ : elapsed > voice.span
227
+ ? voice.hidesPast
228
+ ? Number.NaN
229
+ : voice.freezesAfter
230
+ ? voice.span
231
+ : elapsed
232
+ : elapsed;
223
233
  if (!(elapsed >= 0)) {
224
234
  data[o + 2 /* Row.WEIGHT */] = 0;
225
235
  continue;
@@ -244,7 +254,7 @@ function rows(lanes, c, first, id) {
244
254
  data[o + 2 /* Row.WEIGHT */] = w;
245
255
  if ((f & 512 /* Flag.FLAT */) !== 0) {
246
256
  if (w > 0)
247
- c.keys.fold(hot[h + 8 /* Hot.AT */], phaseAt(elapsed, hot[h + 10 /* Hot.X0 */], hot[h + 9 /* Hot.MS */]), c.chans, slot, w);
257
+ c.keys.fold(hot[h + 9 /* Hot.AT */], phaseAt(elapsed, hot[h + 11 /* Hot.X0 */], hot[h + 10 /* Hot.MS */]), c.chans, slot, w);
248
258
  continue;
249
259
  }
250
260
  arg[1 /* Arg.ELAPSED */] = elapsed;
@@ -274,7 +284,7 @@ function rows(lanes, c, first, id) {
274
284
  c.restale(p, hot[h + 0 /* Hot.FLAGS */]);
275
285
  continue;
276
286
  }
277
- closed(c.laws[p], c.eases[p], n, hot[h + 8 /* Hot.AT */], hot[h + 9 /* Hot.MS */], hot, h + 10 /* Hot.X0 */, hot, h + 10 /* Hot.X0 */ + n, hot, h + 10 /* Hot.X0 */ + 2 * n, elapsed, xs, vs);
287
+ closed(c.laws[p], c.eases[p], n, hot[h + 9 /* Hot.AT */], hot[h + 10 /* Hot.MS */], hot, h + 11 /* Hot.X0 */, hot, h + 11 /* Hot.X0 */ + n, hot, h + 11 /* Hot.X0 */ + 2 * n, elapsed, xs, vs);
278
288
  for (let a = 0; a < n; a++)
279
289
  samples[p * n + a] = xs[a];
280
290
  if (c.deltas[p] !== null)
@@ -371,7 +381,7 @@ function copyStretch(c, p, ms) {
371
381
  const h = p * c.stride;
372
382
  let f = c.hot[h + 0 /* Hot.FLAGS */] & ~(4 /* Flag.STALE */ | 8 /* Flag.BARE */ | 32 /* Flag.FOLDS */);
373
383
  const ch = c.chans[0];
374
- if (run.n === c.axes && run.stretchInto(ms, c.hot, h + 8 /* Hot.AT */)) {
384
+ if (run.n === c.axes && run.stretchInto(ms, c.hot, h + 9 /* Hot.AT */)) {
375
385
  f |= 8 /* Flag.BARE */;
376
386
  if (run.scalar(ms) === ch.scalar && ch.plain)
377
387
  f |= 32 /* Flag.FOLDS */;
@@ -0,0 +1,3 @@
1
+ import type { Voice } from './voice.js';
2
+ /** A subject's `stagger` answer: as asked at cue for a voice naming its subjects, else asked now. */
3
+ export declare function delayOf<I, O>(voice: Voice<I, O>, subject: I): number;
package/dist/delay.js ADDED
@@ -0,0 +1,7 @@
1
+ /** A subject's `stagger` answer: as asked at cue for a voice naming its subjects, else asked now. */
2
+ export function delayOf(voice, subject) {
3
+ const known = voice.named?.delays?.get(subject);
4
+ if (known !== undefined)
5
+ return known;
6
+ return voice.spec.stagger ? voice.spec.stagger(subject) : 0;
7
+ }
package/dist/due.js CHANGED
@@ -159,7 +159,7 @@ function dueOf(mix, voice) {
159
159
  export function schedule(mix, voice) {
160
160
  if (mix.projecting)
161
161
  return;
162
- mix.lanes?.voiceChanged(voice.id);
162
+ mix.lanes?.voiceChanged(voice);
163
163
  const token = ++voice.dueToken;
164
164
  const due = dueOf(mix, voice);
165
165
  if (due === Number.POSITIVE_INFINITY)
package/dist/easing.d.ts CHANGED
@@ -5,6 +5,8 @@ import type { Easing } from './types.js';
5
5
  * @category patch
6
6
  */
7
7
  export type Curve = (u: number) => number;
8
+ /** Whether a segment eased by `c` runs in a straight line: no easing, or `linear`. */
9
+ export declare function straight(c: Curve | undefined): boolean;
8
10
  /**
9
11
  * How fast a curve leaves 0, for bending a retarget to the speed the subject had: Infinity where
10
12
  * the curve jumps or leaves vertically, so no finite bend can match it.
package/dist/easing.js CHANGED
@@ -1,4 +1,8 @@
1
1
  const linear = (u) => u;
2
+ /** Whether a segment eased by `c` runs in a straight line: no easing, or `linear`. */
3
+ export function straight(c) {
4
+ return c === undefined || c === linear;
5
+ }
2
6
  /** A CSS `cubic-bezier`: solves x(t) = u for t, then reads y(t). */
3
7
  function bezier(x1, y1, x2, y2) {
4
8
  const cx = 3 * x1;
@@ -51,6 +51,11 @@ export declare function open<I, O>(voice: Voice<I, O>, rec: Subject<unknown>, sl
51
51
  export declare function shut<I, O>(voice: Voice<I, O>, rec: Subject<unknown>, slot: number, cap: number): void;
52
52
  /** Makes stale what a sharing voice last read for the subject numbered `slot`, as `touch` asks. */
53
53
  export declare function restamp<I, O>(voice: Voice<I, O>, slot: number): void;
54
+ /**
55
+ * The subject numbered `from` is numbered `to` now, a number let go of: what the voice keeps for it
56
+ * moves along, and `from` is left as `unsight` leaves it.
57
+ */
58
+ export declare function resight<I, O>(voice: Voice<I, O>, from: number, to: number, cap: number): void;
54
59
  /** A subject's number was let go of: the next subject to take it is one the voice has not seen. */
55
60
  export declare function unsight<I, O>(voice: Voice<I, O>, slot: number): void;
56
61
  /**
package/dist/everyone.js CHANGED
@@ -185,6 +185,36 @@ export function restamp(voice, slot) {
185
185
  if (voice.built !== null && voice.everyone !== null)
186
186
  voice.everyone.seeks = -1;
187
187
  }
188
+ /**
189
+ * The subject numbered `from` is numbered `to` now, a number let go of: what the voice keeps for it
190
+ * moves along, and `from` is left as `unsight` leaves it.
191
+ */
192
+ export function resight(voice, from, to, cap) {
193
+ if (sighted(voice, from)) {
194
+ const seen = voice.seen;
195
+ sight(voice, to, cap);
196
+ voice.seen = seen;
197
+ }
198
+ const w = weightAt(voice, from);
199
+ if (w !== 0 || (voice.weights !== null && to < voice.weights.length))
200
+ weighAt(voice, to, w, cap);
201
+ let stamps = voice.stamps;
202
+ const o = from * 2 /* Stamp.SIZE */;
203
+ if (stamps !== null && o < stamps.length) {
204
+ const t = to * 2 /* Stamp.SIZE */;
205
+ if (t >= stamps.length) {
206
+ const grown = new Float64Array(Math.max(to + 1, cap) * 2 /* Stamp.SIZE */).fill(Number.NaN);
207
+ grown.set(stamps);
208
+ voice.stamps = grown;
209
+ stamps = grown;
210
+ }
211
+ stamps[t + 0 /* Stamp.PROBED */] = stamps[o + 0 /* Stamp.PROBED */];
212
+ stamps[t + 1 /* Stamp.SEEKS */] = stamps[o + 1 /* Stamp.SEEKS */];
213
+ const deltas = voice.deltas;
214
+ deltas[to] = deltas[from] ?? null;
215
+ }
216
+ unsight(voice, from);
217
+ }
188
218
  /** A subject's number was let go of: the next subject to take it is one the voice has not seen. */
189
219
  export function unsight(voice, slot) {
190
220
  const bits = voice.sighted;
package/dist/fill.js CHANGED
@@ -313,7 +313,7 @@ export function one(lane, p, slot) {
313
313
  return false;
314
314
  const delay = data[o + 0 /* Row.DELAY */];
315
315
  const raw = elapsedNow - delay;
316
- // `frozenAt` written out: V8 leaves it out of line here, a call a subject.
316
+ // Its freezes applied, written out: V8 left a function for it out of line here, a call a subject.
317
317
  const elapsed = !(raw >= 0)
318
318
  ? voice.freezesBefore
319
319
  ? 0
package/dist/held.js CHANGED
@@ -1,5 +1,6 @@
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
5
  import { everyoneOf, inherit, sighted } from './everyone.js';
5
6
  import { originOf } from './origin.js';
@@ -50,7 +51,7 @@ export function held(voice, subject, slot, guess = null) {
50
51
  voice.keyed ??= new Set();
51
52
  voice.keyed.add(this.keys.key(subject));
52
53
  }
53
- const delay = voice.spec.stagger ? voice.spec.stagger(subject) : 0;
54
+ const delay = delayOf(voice, subject);
54
55
  const since = this.sinceOf(voice, delay);
55
56
  const shown = this.shownOf(voice, since);
56
57
  held = record(voice, true, voice.patch.state ? voice.patch.state(subject) : undefined);
package/dist/index.d.ts CHANGED
@@ -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/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
@@ -7,6 +7,7 @@ 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 Seat } from './seat.js';
10
11
  import type { Watcher } from './stretch.js';
11
12
  import type { Subject } from './subject.js';
12
13
  import type { Channel } from './types.js';
@@ -70,7 +71,7 @@ export declare class Lanes<I, O> implements Watcher {
70
71
  readonly byId: Map<number, Lane<I, O>>;
71
72
  /** The crowds of single-subject voices, one per list of channels, and which holds each voice. */
72
73
  crowds: Crowd<I, O>[];
73
- readonly crowdOf: Map<number, Crowd<I, O>>;
74
+ readonly seats: Map<number, Seat<I, O>>;
74
75
  /** Which crowd voices' rows are placed on each subject. */
75
76
  readonly placed: Placed;
76
77
  /**
@@ -161,14 +162,16 @@ export declare class Lanes<I, O> implements Watcher {
161
162
  */
162
163
  readonly late: number[];
163
164
  /**
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.
165
+ * What `writeLater` and `pullRun` have queued, for one set of columns, as runs of rows: each
166
+ * run's first row and how many follow, and its first lane slot where its slots follow one apart
167
+ * too, else -1, when each row's slot is read from `queueFrom` by row.
166
168
  */
167
169
  queueSlots: Int32Array<ArrayBuffer>;
168
170
  queueRows: Int32Array<ArrayBuffer>;
169
171
  queueCounts: Int32Array<ArrayBuffer>;
170
172
  queued: number;
171
173
  queuedFor: readonly Column[] | null;
174
+ queueFrom: Int32Array | null;
172
175
  keeps: boolean;
173
176
  now: number;
174
177
  filledAt: number;
@@ -285,7 +288,7 @@ export declare class Lanes<I, O> implements Watcher {
285
288
  /** A voice's clock moved the `shown` of records it had already placed: copies them again. */
286
289
  reshown(id: number): void;
287
290
  /** Something on a voice changed: a crowd copies it again at the next fill. */
288
- voiceChanged(id: number): void;
291
+ voiceChanged(voice: Voice<I, O>): void;
289
292
  }
290
293
  declare const methods: {
291
294
  copy: typeof copy;