@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/handle.js CHANGED
@@ -76,9 +76,10 @@ export class VoiceHandle {
76
76
  voice.written = true;
77
77
  voice.weight = w;
78
78
  this.#host.changed(voice);
79
- this.#host.record(this, 'weight', (h) => {
80
- h.weight = w;
81
- });
79
+ if (this.#host.taping())
80
+ this.#host.record(this, 'weight', (h) => {
81
+ h.weight = w;
82
+ });
82
83
  }
83
84
  get rate() {
84
85
  const voice = this.#voice;
@@ -105,7 +106,8 @@ export class VoiceHandle {
105
106
  retime(voice, now, r, over);
106
107
  }
107
108
  this.#host.changed(voice);
108
- this.#host.record(this, 'rate', (h) => h.ramp(r, over));
109
+ if (this.#host.taping())
110
+ this.#host.record(this, 'rate', (h) => h.ramp(r, over));
109
111
  }
110
112
  seek(elapsed, opts) {
111
113
  const voice = this.#cued;
@@ -122,7 +124,8 @@ export class VoiceHandle {
122
124
  voice.seeks++;
123
125
  const doubt = this.#host.sought(voice, opts?.state !== 'keep');
124
126
  this.#host.changed(voice);
125
- this.#host.record(this, 'seek', (h) => h.seek(elapsed, opts));
127
+ if (this.#host.taping())
128
+ this.#host.record(this, 'seek', (h) => h.seek(elapsed, opts));
126
129
  return doubt;
127
130
  }
128
131
  fade(opts) {
@@ -131,7 +134,8 @@ export class VoiceHandle {
131
134
  return;
132
135
  voice.written = true;
133
136
  this.#host.fade(voice, opts);
134
- this.#host.record(this, 'fade', (h) => h.fade(opts));
137
+ if (this.#host.taping())
138
+ this.#host.record(this, 'fade', (h) => h.fade(opts));
135
139
  }
136
140
  rise(opts) {
137
141
  const voice = this.#cued;
@@ -139,7 +143,8 @@ export class VoiceHandle {
139
143
  return;
140
144
  voice.written = true;
141
145
  this.#host.rise(voice, opts);
142
- this.#host.record(this, 'rise', (h) => h.rise(opts));
146
+ if (this.#host.taping())
147
+ this.#host.record(this, 'rise', (h) => h.rise(opts));
143
148
  }
144
149
  release() {
145
150
  const voice = this.#cued;
@@ -147,7 +152,8 @@ export class VoiceHandle {
147
152
  return;
148
153
  voice.written = true;
149
154
  this.#host.release(voice);
150
- this.#host.record(this, 'release', (h) => h.release());
155
+ if (this.#host.taping())
156
+ this.#host.record(this, 'release', (h) => h.release());
151
157
  }
152
158
  weightOf(subject) {
153
159
  const voice = this.#voice;
@@ -164,6 +170,11 @@ export class VoiceHandle {
164
170
  if (voice !== null)
165
171
  moves(voice.patch, 'push', 'a spring or a glide').push(subject, velocity, at);
166
172
  }
173
+ set(subject, value, at) {
174
+ const voice = this.#voice;
175
+ if (voice !== null)
176
+ moves(voice.patch, 'set', 'a motion patch').set(subject, value, at);
177
+ }
167
178
  read(subject, at) {
168
179
  const voice = this.#voice;
169
180
  if (voice === null || motionOf(voice.patch) === undefined)
package/dist/held.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Mixer } from './mixer.js';
2
- import type { Subject, Voice } from './voice.js';
2
+ import type { Subject } from './subject.js';
3
+ import type { Voice } from './voice.js';
3
4
  /** The record a voice gives every subject it does not reach. */
4
5
  export declare function unreachedOf<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, now: number): Subject<unknown>;
5
6
  /**
package/dist/history.d.ts CHANGED
@@ -1,6 +1,7 @@
1
+ import { type Cut } from './cut.js';
1
2
  import type { Mixer } from './mixer.js';
2
- import { type Cut } from './transport.js';
3
- import { type Subject, type Voice } from './voice.js';
3
+ import type { Subject } from './subject.js';
4
+ import { type Voice } from './voice.js';
4
5
  /** The last entry taken before `t`, or at it where `inclusive` says so. */
5
6
  export declare function last<T extends {
6
7
  at: number;
@@ -56,7 +57,7 @@ export declare function remember<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, sub
56
57
  */
57
58
  export declare function leave<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown> | undefined): void;
58
59
  /**
59
- * The mix time a motion patch keeps a released subject from, how far back, and the frame, under
60
- * history.
60
+ * The mix time a motion patch keeps a released subject from, how far back, the frame, and whether
61
+ * the release came before anything read it, under history.
61
62
  */
62
- export declare function releasing<I, O>(mix: Mixer<I, O>): [at?: number, reach?: number, seq?: number];
63
+ export declare function releasing<I, O>(mix: Mixer<I, O>): [at?: number, reach?: number, seq?: number, early?: boolean];
package/dist/history.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { elapsedWith } from './clock.js';
2
2
  import { clone } from './clone.js';
3
+ import { within } from './cut.js';
3
4
  import { schedule } from './due.js';
4
5
  import { Leavings } from './leavings.js';
5
6
  import { packHeld } from './pack.js';
6
7
  import { pageOut } from './paging.js';
7
8
  import { scoreTouched } from './scored.js';
8
- import { within } from './transport.js';
9
9
  import { none } from './voice.js';
10
10
  /** The last entry taken before `t`, or at it where `inclusive` says so. */
11
11
  export function last(list, t, inclusive) {
@@ -121,7 +121,7 @@ export function copyHeld(voice, h, shared = false) {
121
121
  since: h.since,
122
122
  shown: h.shown,
123
123
  weight: h.weight,
124
- rested: h.rested,
124
+ restedAt: h.restedAt,
125
125
  bands: h.bands === null ? null : h.bands.slice(),
126
126
  state: h.state === undefined || shared ? h.state : copyState(voice, h.state),
127
127
  stepped: h.stepped,
@@ -144,7 +144,7 @@ export function copyHeld(voice, h, shared = false) {
144
144
  voice: h.voice,
145
145
  next: h.next,
146
146
  version: h.version,
147
- loci: h.loci,
147
+ roles: h.roles,
148
148
  slot: h.slot,
149
149
  run: h.run,
150
150
  freed: h.freed,
@@ -171,7 +171,7 @@ export function copyHeldInto(voice, h, into, shared = false) {
171
171
  into.since = h.since;
172
172
  into.shown = h.shown;
173
173
  into.weight = h.weight;
174
- into.rested = h.rested;
174
+ into.restedAt = h.restedAt;
175
175
  into.bands = h.bands === null ? null : h.bands.slice();
176
176
  into.state = h.state === undefined || shared ? h.state : copyState(voice, h.state);
177
177
  into.stepped = h.stepped;
@@ -190,7 +190,7 @@ export function copyHeldInto(voice, h, into, shared = false) {
190
190
  into.run = h.run;
191
191
  into.freed = h.freed;
192
192
  into.restored = null;
193
- // `probed`, `delta`, `phase`, `seeks`, `snaps`, `inputs`, `next`, `version` and `loci` as
193
+ // `probed`, `delta`, `phase`, `seeks`, `snaps`, `inputs`, `next`, `version` and `roles` as
194
194
  // `unread` leaves them.
195
195
  return unread(into);
196
196
  }
@@ -207,7 +207,7 @@ export function unread(h) {
207
207
  h.inputs = undefined;
208
208
  h.next = null;
209
209
  h.version = Number.NaN;
210
- h.loci = null;
210
+ h.roles = null;
211
211
  return h;
212
212
  }
213
213
  /** Under `history` with `inputs`, copies the host fields any voice reads, once a frame, when they changed. */
@@ -332,12 +332,12 @@ export function leave(mix, voice, subject, held) {
332
332
  });
333
333
  }
334
334
  /**
335
- * The mix time a motion patch keeps a released subject from, how far back, and the frame, under
336
- * history.
335
+ * The mix time a motion patch keeps a released subject from, how far back, the frame, and whether
336
+ * the release came before anything read it, under history.
337
337
  */
338
338
  export function releasing(mix) {
339
339
  const history = mix.opts.history;
340
340
  if (history === undefined || Number.isNaN(mix.now))
341
341
  return [];
342
- return [mix.now, mix.now - history.ms, mix.transport.seq];
342
+ return [mix.now, mix.now - history.ms, mix.transport.seq, mix.syncing || !mix.looked];
343
343
  }
package/dist/hosts.js CHANGED
@@ -18,7 +18,7 @@ import { localNow } from './place.js';
18
18
  import { reading } from './reading.js';
19
19
  import { heard } from './signals.js';
20
20
  import { cuedById } from './strays.js';
21
- import { record } from './tape.js';
21
+ import { record, taping } from './tape.js';
22
22
  import { unreach } from './unreached.js';
23
23
  export function book(mix, opts) {
24
24
  if (mix.projecting)
@@ -62,6 +62,7 @@ export function laneHost(mix) {
62
62
  return mix.held(voice, subject, slot);
63
63
  },
64
64
  forgot: (slot) => {
65
+ mix.places?.forgot(slot);
65
66
  // Only a voice over every subject keeps bits, and one gone may still be read back.
66
67
  for (const voice of mix.general) {
67
68
  unreach(voice, slot, false);
@@ -80,11 +81,7 @@ export function laneHost(mix) {
80
81
  parting: (voice, subject) => mix.parting(voice, subject, mix.now),
81
82
  ready: (voice, subject, held) => {
82
83
  const arg = mix.lanes.arg;
83
- const primed = mix.primed;
84
- primed[0 /* Primed.NOW */] = mix.now;
85
- primed[1 /* Primed.ELAPSED */] = arg[1 /* Arg.ELAPSED */];
86
- primed[2 /* Primed.PASS */] = arg[3 /* Arg.PASS */];
87
- mix.prime(voice, held);
84
+ mix.primeLaned(voice, held, arg);
88
85
  voice.setting.weight = arg[0 /* Arg.WEIGHT */];
89
86
  mix.sending.voice = voice;
90
87
  mix.sending.subject = subject;
@@ -97,11 +94,7 @@ export function laneHost(mix) {
97
94
  arg[0 /* Arg.WEIGHT */] = read;
98
95
  return;
99
96
  }
100
- const primed = mix.primed;
101
- primed[0 /* Primed.NOW */] = mix.now;
102
- primed[1 /* Primed.ELAPSED */] = arg[1 /* Arg.ELAPSED */];
103
- primed[2 /* Primed.PASS */] = arg[3 /* Arg.PASS */];
104
- mix.prime(voice, held);
97
+ mix.primeLaned(voice, held, arg);
105
98
  const kept = reading.kept;
106
99
  const base = mix.base(voice, subject, mix.now, held, slot);
107
100
  if (reading.kept !== kept && !voice.keeping)
@@ -145,13 +138,13 @@ export function fits(mix, voice) {
145
138
  return false;
146
139
  if (patch.reads !== undefined && patch.reads.length > 0)
147
140
  return false;
148
- // A locus on lanes gathers keys and fn members; a motion member keeps it on the general path.
149
- if (spec.locus !== undefined && voice.motion !== undefined)
141
+ // A role on lanes gathers keys and fn members; a motion member keeps it on the general path.
142
+ if (spec.role !== undefined && voice.motion !== undefined)
150
143
  return false;
151
144
  if (spec.from === 'current')
152
145
  return false;
153
- // A rest-less channel's band is read on the record `one` folds; crowds, motions and loci fold elsewhere.
154
- if ((voice.motion !== undefined || spec.locus !== undefined || crowdable(voice)) &&
146
+ // A rest-less channel's band is read on the record `one` folds; crowds, motions and roles fold elsewhere.
147
+ if ((voice.motion !== undefined || spec.role !== undefined || crowdable(voice)) &&
155
148
  voice.slots.some((s) => numericOf(mix.channels[s])?.op === 'last'))
156
149
  return false;
157
150
  if (voice.out?.rest)
@@ -257,6 +250,7 @@ function handleHost(mix) {
257
250
  // A voice that stopped sharing makes a subject's record at its next read, from what it kept.
258
251
  return held !== undefined ? held.weight : weightAt(voice, slot);
259
252
  },
253
+ taping: () => taping(mix),
260
254
  record: (h, label, again) => record(mix, label, (stage) => {
261
255
  const to = stage.mix(mix);
262
256
  if (to === mix)
package/dist/index.d.ts CHANGED
@@ -1,25 +1,26 @@
1
- export type { NumberOptions, Vec } from './channels.js';
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
4
  export { color, css, mixHex, oklab, toHex } from './color.js';
5
5
  export type { Curve } from './easing.js';
6
6
  export { curve } from './easing.js';
7
- export type { Claim, Fit, FitPlan, Order, SpanClaim, Strength } from './fit.js';
7
+ export type { Claim, Fit, FitPlan, Layout, Order, OverrunOptions, SpanClaim, Strength, } from './fit.js';
8
8
  export { conclude, condense, lax, layout, overrun, pipe, plain, shed, } from './fit.js';
9
9
  export { mix, mixer } from './mixer.js';
10
- export type { Motion, Moving, PerSubject, Value } from './motion.js';
10
+ export type { Glide, GlideOptions, Motion, MotionOptions, Moving, PerSubject, Spring, SpringOptions, Tween, TweenOptions, Value, } from './motion.js';
11
11
  export { glide, spring, tween } from './motion.js';
12
12
  export { HistoryMiss } from './paging.js';
13
13
  export type { KeysOptions, PatchOptions } from './patch.js';
14
14
  export { keys, patch } from './patch.js';
15
15
  export type { AngleOptions } from './rotation.js';
16
16
  export { angle, quat } from './rotation.js';
17
+ export type { Input, LagOptions, Level, SlewOptions } from './signals.js';
17
18
  export { gate, input, lag, level, peak, share, slew } from './signals.js';
18
19
  export { fold } from './stack.js';
19
20
  export type { Ticked, Ticker, TickerOptions } from './ticker.js';
20
21
  export { ticker } from './ticker.js';
21
22
  export { transport } from './transport.js';
22
- export type { Anchor, ArcHandle, ArcPart, ArcSpec, ArcVoice, BlendMember, BookedHit, Booker, BookOptions, Channel, Columns, Described, Doubt, Easing, Engine, FadeOptions, FadeSpec, FitResult, Handle, HistoryOptions, HistoryStore, Hit, Hold, Keyframe, Kit, Mark, Marked, MarkName, Mix, MixOptions, MotionSpec, OwnerSpec, Paged, PagedStream, Patch, Placement, Projection, Query, Registry, SeekOptions, Sent, Setting, Signal, SpanHandle, SpanHints, SpanSpec, Tape, TapeBranch, TapeMaker, TapeOp, Transport, TransportOptions, TransportProjection, VoiceSpec, Watched, WatchedFrame, } 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, 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
24
  export { watched } from './watch.js';
24
25
  export type { WaveOptions, WaveShape } from './wave.js';
25
26
  export { wave, waveAt } from './wave.js';
package/dist/keyfill.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type Lane } from './lane.js';
2
2
  import type { Lanes } from './lanes.js';
3
3
  /**
4
- * A keys voice's fill outside a locus, its weight a number: what `one` does for each subject, in
4
+ * A keys voice's fill outside a role, its weight a number: what `one` does for each subject, in
5
5
  * one loop, so no weight or time crosses a call as a boxed double per subject.
6
6
  */
7
7
  export declare function runKeys<I, O>(lanes: Lanes<I, O>, lane: Lane<I, O>): void;
package/dist/keyfill.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { foldNumber } from './channels.js';
2
2
  import { clampWeight, frozenTime, phaseAt, same, weighed } from './clock.js';
3
3
  import { unband } from './fold.js';
4
+ import { keepProbed } from './lane.js';
4
5
  import { shortWay } from './turns.js';
5
6
  /**
6
- * A keys voice's fill outside a locus, its weight a number: what `one` does for each subject, in
7
+ * A keys voice's fill outside a role, its weight a number: what `one` does for each subject, in
7
8
  * one loop, so no weight or time crosses a call as a boxed double per subject.
8
9
  */
9
10
  export function runKeys(lanes, lane) {
@@ -18,6 +19,8 @@ export function runKeys(lanes, lane) {
18
19
  const fills = lanes.fills;
19
20
  const lately = lanes.lately;
20
21
  const elapsedNow = voice.elapsedAt(lanes.now);
22
+ // For `one`, where a probe meeting the voice later this fill reads it (`foldNow` in `meet.ts`).
23
+ lane.elapsedNow = elapsedNow;
21
24
  const duration = voice.duration;
22
25
  const passes = voice.passes;
23
26
  const before = voice.freezesBefore;
@@ -39,8 +42,7 @@ export function runKeys(lanes, lane) {
39
42
  for (let p = 0; p < list.length; p++) {
40
43
  const slot = list[p];
41
44
  const o = p * 8 /* Row.STRIDE */;
42
- if (per[slot * 6 /* Per.SLOT */ + 5 /* Per.LANE_FILL */] === fills - 1)
43
- data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
45
+ keepProbed(data, o, per, slot * 6 /* Per.SLOT */, fills);
44
46
  if (lately >= 0 && lanes.unread(slot))
45
47
  continue;
46
48
  const raw = elapsedNow - data[o + 0 /* Row.DELAY */];
package/dist/keyrows.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface KeyChannel {
6
6
  scalar: boolean;
7
7
  op: Numeric['op'];
8
8
  plain: boolean;
9
+ straight: boolean;
9
10
  rest: number;
10
11
  axes: number;
11
12
  values: Float64Array;
@@ -21,6 +22,11 @@ export declare class KeyRows {
21
22
  data: Float64Array<ArrayBuffer>;
22
23
  used: number;
23
24
  eases: (Curve | undefined)[];
25
+ /** The timing and phase `search` last ran for, and what it found per track. */
26
+ private lastTiming;
27
+ private lastPhase;
28
+ private found;
29
+ private easedAt;
24
30
  /**
25
31
  * Copies a voice's stops for channels `chans` and returns the row's offset; -1 where a stop's
26
32
  * value is not one a lane folds as a number per axis, so the row reads its voice's stops instead.
@@ -33,5 +39,11 @@ export declare class KeyRows {
33
39
  * and the stock lerp and fold give for each track, read from this pool.
34
40
  */
35
41
  fold(off: number, phase: number, chans: readonly KeyChannel[], slot: number, w: number): void;
42
+ /**
43
+ * What `segment` finds for each track of the row at `off` at `phase`, kept for every row after it
44
+ * with the same timing at the same phase: the stop, and the eased fraction of its segment.
45
+ */
46
+ private search;
47
+ private grow;
36
48
  private reserve;
37
49
  }
package/dist/keyrows.js CHANGED
@@ -1,7 +1,49 @@
1
1
  import { foldNumber, lerpNumber } from './channels.js';
2
2
  import { fraction, locate, shift } from './patch.js';
3
- /** A row's header: its length in `data`, its offset in `eases`, its duration, its stop count. */
4
- const HEAD = 4;
3
+ /**
4
+ * A row's header: its length in `data`, its offset in `eases`, its duration, its stop count, and
5
+ * its timing's number.
6
+ */
7
+ const HEAD = 5;
8
+ /**
9
+ * Timings by what makes two rows find the same stop and easing at the same phase: the duration,
10
+ * and each track's delay, stop phases and easings. Numbers are never reused, so clearing the map
11
+ * once it is large only stops rows added later sharing with rows added earlier.
12
+ */
13
+ const timings = new Map();
14
+ const easeIds = new WeakMap();
15
+ let timed = 0;
16
+ let eased = 0;
17
+ function easeId(ease) {
18
+ if (ease === undefined)
19
+ return 0;
20
+ let id = easeIds.get(ease);
21
+ if (id === undefined) {
22
+ id = ++eased;
23
+ easeIds.set(ease, id);
24
+ }
25
+ return id;
26
+ }
27
+ /** A phase written so -0 and 0 differ, as a stop search can tell them apart. */
28
+ const exact = (x) => (Object.is(x, -0) ? '-0' : String(x));
29
+ function timingOf(built) {
30
+ let key = exact(built.duration);
31
+ for (const track of built.tracks) {
32
+ if (track === undefined)
33
+ return ++timed;
34
+ key += `|${exact(track.delay)}`;
35
+ for (const pt of track.all)
36
+ key += `,${exact(pt.at)}:${easeId(pt.ease)}`;
37
+ }
38
+ let id = timings.get(key);
39
+ if (id === undefined) {
40
+ if (timings.size >= 4096)
41
+ timings.clear();
42
+ id = ++timed;
43
+ timings.set(key, id);
44
+ }
45
+ return id;
46
+ }
5
47
  /**
6
48
  * The stops of a crowd's `keys` rows, copied into one array as a row joins, so a fill reads each
7
49
  * row's stops in place rather than through its voice, its built tracks and every stop's own array.
@@ -13,6 +55,11 @@ export class KeyRows {
13
55
  data = new Float64Array(64);
14
56
  used = 0;
15
57
  eases = [];
58
+ /** The timing and phase `search` last ran for, and what it found per track. */
59
+ lastTiming = -1;
60
+ lastPhase = Number.NaN;
61
+ found = new Int32Array(4);
62
+ easedAt = new Float64Array(4);
16
63
  /**
17
64
  * Copies a voice's stops for channels `chans` and returns the row's offset; -1 where a stop's
18
65
  * value is not one a lane folds as a number per axis, so the row reads its voice's stops instead.
@@ -23,7 +70,7 @@ export class KeyRows {
23
70
  for (let i = 0; i < built.tracks.length; i++) {
24
71
  const track = built.tracks[i];
25
72
  const ch = chans[i];
26
- if (track === undefined || ch === undefined || !ch.plain)
73
+ if (track === undefined || ch === undefined || !ch.plain || !ch.straight)
27
74
  return -1;
28
75
  for (const pt of track.all)
29
76
  if (!flat(pt.value, ch))
@@ -37,6 +84,7 @@ export class KeyRows {
37
84
  d[off + 1] = this.eases.length;
38
85
  d[off + 2] = built.duration;
39
86
  d[off + 3] = stops;
87
+ d[off + 4] = timingOf(built);
40
88
  let o = off + HEAD;
41
89
  for (let i = 0; i < built.tracks.length; i++) {
42
90
  const track = built.tracks[i];
@@ -75,8 +123,12 @@ export class KeyRows {
75
123
  */
76
124
  fold(off, phase, chans, slot, w) {
77
125
  const d = this.data;
78
- const duration = d[off + 2];
79
- let e = d[off + 1];
126
+ const timing = d[off + 4];
127
+ // A phase of 0 is searched afresh: -0 and 0 compare equal and need not find the same.
128
+ if (timing !== this.lastTiming || phase !== this.lastPhase || phase === 0)
129
+ this.search(off, phase, chans);
130
+ const found = this.found;
131
+ const easedAt = this.easedAt;
80
132
  let o = off + HEAD;
81
133
  const end = off + d[off];
82
134
  for (let i = 0; o < end; i++) {
@@ -84,12 +136,9 @@ export class KeyRows {
84
136
  const axes = ch.axes;
85
137
  const stride = 1 + axes;
86
138
  const n = d[o];
87
- const ph = shift(d[o + 1], phase, duration);
88
139
  const first = o + 2;
89
140
  o = first + n * stride;
90
- const k = locate(d, first, stride, n, ph);
91
- const stops = e;
92
- e += n;
141
+ const k = found[i];
93
142
  if (k === -2)
94
143
  continue;
95
144
  const values = ch.values;
@@ -102,15 +151,53 @@ export class KeyRows {
102
151
  }
103
152
  const b = first + k * stride;
104
153
  const before = b - stride;
105
- const u = fraction(ph, d[before], d[b]);
106
- const ease = this.eases[stops + k];
107
- const eased = ease ? ease(u) : u;
154
+ const eased = easedAt[i];
108
155
  for (let a = 0; a < axes; a++) {
109
156
  const v = lerpNumber(d[before + 1 + a], d[b + 1 + a], eased);
110
157
  values[base + a] = foldNumber(ch.op, values[base + a], v, w);
111
158
  }
112
159
  }
113
160
  }
161
+ /**
162
+ * What `segment` finds for each track of the row at `off` at `phase`, kept for every row after it
163
+ * with the same timing at the same phase: the stop, and the eased fraction of its segment.
164
+ */
165
+ search(off, phase, chans) {
166
+ const d = this.data;
167
+ this.lastTiming = -1;
168
+ const duration = d[off + 2];
169
+ let e = d[off + 1];
170
+ let o = off + HEAD;
171
+ const end = off + d[off];
172
+ for (let i = 0; o < end; i++) {
173
+ if (i >= this.found.length)
174
+ this.grow(i + 1);
175
+ const stride = 1 + chans[i].axes;
176
+ const n = d[o];
177
+ const ph = shift(d[o + 1], phase, duration);
178
+ const first = o + 2;
179
+ o = first + n * stride;
180
+ const k = locate(d, first, stride, n, ph);
181
+ this.found[i] = k;
182
+ if (k >= 0 && k < n) {
183
+ const b = first + k * stride;
184
+ const u = fraction(ph, d[b - stride], d[b]);
185
+ const ease = this.eases[e + k];
186
+ this.easedAt[i] = ease ? ease(u) : u;
187
+ }
188
+ e += n;
189
+ }
190
+ this.lastTiming = d[off + 4];
191
+ this.lastPhase = phase;
192
+ }
193
+ grow(tracks) {
194
+ const found = new Int32Array(tracks * 2);
195
+ found.set(this.found);
196
+ this.found = found;
197
+ const easedAt = new Float64Array(tracks * 2);
198
+ easedAt.set(this.easedAt);
199
+ this.easedAt = easedAt;
200
+ }
114
201
  reserve(len) {
115
202
  const at = this.used;
116
203
  if (at + len > this.data.length) {
package/dist/lane.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { Numeric } from './channels.js';
2
2
  import type { Motions } from './motions.js';
3
3
  import type { Scratch } from './patch.js';
4
+ import type { Subject } from './subject.js';
4
5
  import type { Channel } from './types.js';
5
- import type { Subject, Voice } from './voice.js';
6
+ import type { Voice } from './voice.js';
6
7
  /** One laned channel: its arithmetic, and every numbered subject's folded value, `axes` per subject. */
7
8
  export interface Laned {
8
9
  name: string;
@@ -11,6 +12,8 @@ export interface Laned {
11
12
  op: Numeric['op'];
12
13
  /** Its values fold as its `op` alone: not through a band as `'last'` does, and not a rotation. */
13
14
  plain: boolean;
15
+ /** A plain channel's values lerp straight across each axis, not through the channel's `lerp`. */
16
+ straight: boolean;
14
17
  /** An angle's full turn, and 0 for any other channel. */
15
18
  turn: number;
16
19
  /** The channel, whose own `fold` and `lerp` an `'own'` lane calls. */
@@ -24,11 +27,11 @@ export interface Laned {
24
27
  index: number;
25
28
  }
26
29
  /**
27
- * The laned voices sharing one locus, in voice order, and what a fill gathers from them for each
30
+ * The laned voices sharing one role, in voice order, and what a fill gathers from them for each
28
31
  * subject before folding it in at the subject's first member: their summed weight, and per laned
29
32
  * channel the members' values lerped by share of weight and the weight taken into each.
30
33
  */
31
- export interface Locus<I, O> {
34
+ export interface Role<I, O> {
32
35
  readonly members: Lane<I, O>[];
33
36
  /** By subject number: the fill that last met it, the member it met first then, the summed weight. */
34
37
  met: Float64Array;
@@ -37,6 +40,10 @@ export interface Locus<I, O> {
37
40
  /** By laned channel, then subject number (times axes for `values`). */
38
41
  values: (Float64Array | undefined)[];
39
42
  taken: (Float64Array | undefined)[];
43
+ /** No other laned or crowded voice falls between its members in voice order, as qualify found. */
44
+ contiguous: boolean;
45
+ /** This fill runs it a subject at a time at its first member's turn, folding each as it goes. */
46
+ inline: boolean;
40
47
  }
41
48
  /**
42
49
  * Where each of a lane position's or crowd row's `STRIDE` numbers sits. The layouts are const
@@ -58,6 +65,11 @@ export declare const enum Row {
58
65
  /** 1 once the general path has made the voice's first call for the subject, which never undoes. */
59
66
  MET = 7
60
67
  }
68
+ /**
69
+ * Keeps the weight a probe read from the last fill as `Row.PROBED`, for `weightOf`, before a fill
70
+ * writes the position's new weight; reads `per` only where the copy could change a bit.
71
+ */
72
+ export declare function keepProbed(data: Float64Array, o: number, per: Float64Array, q: number, fills: number): void;
61
73
  /** What `begin` found: the general path, or lanes filled. */
62
74
  export declare const enum Begin {
63
75
  GENERAL = 0,
@@ -79,7 +91,14 @@ export declare const enum Arg {
79
91
  DELAY = 4,
80
92
  /** The voice's own fade, for `signalled`. */
81
93
  FADE = 5,
82
- SIZE = 6
94
+ /**
95
+ * The fill and voice time a lane's subjects share since it last placed one, numbered once per
96
+ * placing; NaN where the caller shares none. A setting already filled under it skips its per-lane fields.
97
+ */
98
+ MARK = 6,
99
+ /** A number a delta gave, for `gatherScalar`. */
100
+ VALUE = 7,
101
+ SIZE = 8
83
102
  }
84
103
  /** Where each of a subject's `SLOT` numbers in `Lanes.per` sits. */
85
104
  export declare const enum Per {
@@ -207,6 +226,8 @@ export declare class Lane<I, O> implements Positions<I, O> {
207
226
  fade: number;
208
227
  /** Whether the voice has no fade in or out this fill, so its envelope is 1 for every subject. */
209
228
  flat: boolean;
229
+ /** The fill that last ran it, so what it remembers within one fill is that fill's. */
230
+ ran: number;
210
231
  /** The voice's clock at this fill, set before `one` runs its subjects. */
211
232
  elapsedNow: number;
212
233
  /** A motion patch's state, which the lane samples in place of calling the patch. */
@@ -217,8 +238,12 @@ export declare class Lane<I, O> implements Positions<I, O> {
217
238
  bareFill: number;
218
239
  bareElapsed: number;
219
240
  bareSeeks: number;
220
- /** The locus its voice shares, gathered before the voices are folded in order. */
221
- group: Locus<I, O> | null;
241
+ /** The role its voice shares, gathered before the voices are folded in order. */
242
+ group: Role<I, O> | null;
243
+ /** The `Arg.MARK` it was last placed under, for a role run a subject at a time to put back. */
244
+ mark: number;
245
+ /** Whether it took every subject when it opened, so a fill makes each one's first call. */
246
+ every: boolean;
222
247
  voiceAt(): Voice<I, O>;
223
248
  fix(p: number): void;
224
249
  numbered(p: number, ms: number): void;
@@ -228,6 +253,11 @@ export declare class Lane<I, O> implements Positions<I, O> {
228
253
  motionAt(): Motions<I> | undefined;
229
254
  constructor(voice: Voice<I, O>);
230
255
  positionOf(slot: number): number;
256
+ /**
257
+ * Room for `n` positions and subject numbers below `cap`, for a voice that reaches every subject:
258
+ * grown as it met them, a voice replaced every frame regrew both from nothing each frame.
259
+ */
260
+ reserve(n: number, cap: number): void;
231
261
  /** Gives a subject the voice reaches a position, from what its record fixed on first sight. */
232
262
  add(slot: number, held: Subject<unknown>): void;
233
263
  /** A subject lost its number: the last position moves into its place. */