@msb235/blits 0.10.0 → 0.12.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 (126) hide show
  1. package/CHANGELOG.md +248 -0
  2. package/README.md +15 -4
  3. package/dist/arcs.d.ts +40 -0
  4. package/dist/arcs.js +239 -0
  5. package/dist/blend.js +1 -5
  6. package/dist/book.d.ts +2 -0
  7. package/dist/book.js +27 -3
  8. package/dist/chain.d.ts +17 -1
  9. package/dist/chain.js +57 -15
  10. package/dist/channels.d.ts +2 -0
  11. package/dist/channels.js +12 -1
  12. package/dist/clock.d.ts +14 -0
  13. package/dist/clock.js +9 -1
  14. package/dist/clone.js +16 -4
  15. package/dist/columns.d.ts +4 -5
  16. package/dist/columns.js +11 -5
  17. package/dist/crowd.d.ts +1 -1
  18. package/dist/crowd.js +4 -1
  19. package/dist/cue.js +63 -7
  20. package/dist/departed.d.ts +8 -3
  21. package/dist/departed.js +76 -29
  22. package/dist/due.d.ts +4 -0
  23. package/dist/due.js +16 -3
  24. package/dist/easing.d.ts +12 -1
  25. package/dist/easing.js +7 -1
  26. package/dist/everyone.d.ts +14 -1
  27. package/dist/everyone.js +27 -2
  28. package/dist/fade.d.ts +12 -5
  29. package/dist/fade.js +57 -19
  30. package/dist/fill.js +16 -9
  31. package/dist/fit.d.ts +8 -1
  32. package/dist/fit.js +7 -3
  33. package/dist/fold.d.ts +1 -1
  34. package/dist/fold.js +112 -9
  35. package/dist/frames.d.ts +2 -0
  36. package/dist/frames.js +17 -0
  37. package/dist/gather.js +38 -7
  38. package/dist/handle.d.ts +7 -2
  39. package/dist/handle.js +28 -12
  40. package/dist/held.d.ts +13 -2
  41. package/dist/held.js +43 -9
  42. package/dist/history.d.ts +25 -2
  43. package/dist/history.js +163 -35
  44. package/dist/hosts.js +45 -17
  45. package/dist/index.d.ts +5 -2
  46. package/dist/index.js +3 -1
  47. package/dist/keyfill.js +3 -1
  48. package/dist/lane.d.ts +1 -1
  49. package/dist/lane.js +3 -1
  50. package/dist/lanes.d.ts +31 -7
  51. package/dist/lanes.js +57 -6
  52. package/dist/lean.d.ts +15 -0
  53. package/dist/lean.js +23 -0
  54. package/dist/leavings.d.ts +6 -1
  55. package/dist/leavings.js +16 -2
  56. package/dist/marks.d.ts +7 -3
  57. package/dist/marks.js +105 -27
  58. package/dist/meet.js +1 -0
  59. package/dist/mixer.d.ts +35 -6
  60. package/dist/mixer.js +113 -45
  61. package/dist/motion.d.ts +4 -1
  62. package/dist/motion.js +16 -2
  63. package/dist/motions.d.ts +4 -0
  64. package/dist/motions.js +45 -11
  65. package/dist/move.js +35 -12
  66. package/dist/numbers.d.ts +5 -1
  67. package/dist/numbers.js +4 -0
  68. package/dist/owner.d.ts +18 -2
  69. package/dist/owner.js +44 -3
  70. package/dist/pack.d.ts +1 -0
  71. package/dist/pack.js +3 -0
  72. package/dist/place.d.ts +8 -0
  73. package/dist/place.js +16 -8
  74. package/dist/placed.d.ts +19 -0
  75. package/dist/placed.js +54 -0
  76. package/dist/project.d.ts +5 -2
  77. package/dist/project.js +238 -34
  78. package/dist/pull.js +37 -8
  79. package/dist/qualify.d.ts +10 -0
  80. package/dist/qualify.js +60 -3
  81. package/dist/reading.d.ts +8 -1
  82. package/dist/reading.js +8 -1
  83. package/dist/record.d.ts +6 -0
  84. package/dist/record.js +2 -0
  85. package/dist/restored.d.ts +69 -17
  86. package/dist/restored.js +163 -50
  87. package/dist/revive.d.ts +6 -3
  88. package/dist/revive.js +11 -3
  89. package/dist/roster.js +2 -1
  90. package/dist/rows.js +15 -5
  91. package/dist/seek.d.ts +7 -2
  92. package/dist/seek.js +152 -43
  93. package/dist/shared.d.ts +5 -0
  94. package/dist/shared.js +5 -0
  95. package/dist/signals.d.ts +10 -0
  96. package/dist/signals.js +17 -0
  97. package/dist/spans.d.ts +5 -0
  98. package/dist/spans.js +75 -5
  99. package/dist/store.d.ts +13 -0
  100. package/dist/store.js +23 -0
  101. package/dist/strays.js +50 -2
  102. package/dist/tape.d.ts +29 -4
  103. package/dist/tape.js +49 -11
  104. package/dist/touch.js +3 -2
  105. package/dist/transport.d.ts +11 -2
  106. package/dist/transport.js +26 -10
  107. package/dist/types/arc.d.ts +55 -0
  108. package/dist/types/arc.js +1 -0
  109. package/dist/types/mix.d.ts +57 -14
  110. package/dist/types/patch.d.ts +5 -1
  111. package/dist/types/place.d.ts +11 -4
  112. package/dist/types/transport.d.ts +12 -3
  113. package/dist/types/voice.d.ts +72 -5
  114. package/dist/types/watch.d.ts +126 -0
  115. package/dist/types/watch.js +1 -0
  116. package/dist/types.d.ts +2 -0
  117. package/dist/unpage.d.ts +6 -1
  118. package/dist/unpage.js +34 -2
  119. package/dist/voice.d.ts +93 -16
  120. package/dist/voice.js +60 -3
  121. package/dist/watch.d.ts +26 -0
  122. package/dist/watch.js +441 -0
  123. package/dist/weigh.d.ts +13 -2
  124. package/dist/weigh.js +10 -3
  125. package/docs/schema.html +106 -38
  126. package/package.json +10 -4
package/dist/owner.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { arcShare } from './arcs.js';
1
2
  import { backWith, clampWeight, elapsedWith, envelope, timeWith } from './clock.js';
2
3
  /** What an owner is cued with in place of a patch: it writes nothing, so no fold ever meets it. */
3
4
  export const ownerPatch = Object.freeze({
@@ -13,6 +14,35 @@ export class Holding {
13
14
  /** How many it has held, and how many of those finished their passes. */
14
15
  kids = 0;
15
16
  played = 0;
17
+ /**
18
+ * When the children that left coast, as pairs of the frame one left in and the latest coast so
19
+ * far, mix time: a pair only where the latest grew, so a seek back cuts it to a prefix.
20
+ */
21
+ coasts = [];
22
+ /** The frame the first child to leave before its passes ended left in; Infinity for none. */
23
+ cutSeq = Number.POSITIVE_INFINITY;
24
+ /** The latest coast of a child that left; -Infinity for none. */
25
+ get coast() {
26
+ const n = this.coasts.length;
27
+ return n === 0 ? Number.NEGATIVE_INFINITY : this.coasts[n - 1];
28
+ }
29
+ /** A child left in frame `seq`, coasting at `coast`, or before its passes ended for undefined. */
30
+ left(coast, seq) {
31
+ if (coast === undefined) {
32
+ if (seq < this.cutSeq)
33
+ this.cutSeq = seq;
34
+ }
35
+ else if (coast > this.coast)
36
+ this.coasts.push(seq, coast);
37
+ }
38
+ /** Forgets the children that left after frame `seq`, for a seek or read back to it. */
39
+ back(seq) {
40
+ const c = this.coasts;
41
+ while (c.length > 0 && c[c.length - 2] > seq)
42
+ c.length -= 2;
43
+ if (this.cutSeq > seq)
44
+ this.cutSeq = Number.POSITIVE_INFINITY;
45
+ }
16
46
  }
17
47
  /**
18
48
  * What an owner's clock reads at mix time `now`, through every owner above it: -Infinity before it
@@ -99,12 +129,19 @@ export function ownWeight(owner, now, reduced, base) {
99
129
  /** What every owner above a voice multiplies into its weight. */
100
130
  export function ownersWeight(voice, now, reduced, base) {
101
131
  let w = 1;
102
- for (let o = voice.owner; o !== null; o = o.owner)
132
+ let child = voice;
133
+ for (let o = voice.owner; o !== null; child = o, o = o.owner) {
103
134
  w *= ownWeight(o, now, reduced, base);
135
+ if (o.arc !== null)
136
+ w *= arcShare(o, child, now, reduced);
137
+ }
104
138
  return w;
105
139
  }
106
- /** Points a projection's copies at each other's owners, each owner holding only copies. */
107
- export function relink(copies) {
140
+ /**
141
+ * Points a projection's copies at each other's owners, each owner holding only copies; for a read
142
+ * back to frame `seq`, as its children that had left by then left it.
143
+ */
144
+ export function relink(copies, seq) {
108
145
  const byId = new Map();
109
146
  for (const v of copies) {
110
147
  byId.set(v.id, v);
@@ -112,6 +149,10 @@ export function relink(copies) {
112
149
  const h = new Holding();
113
150
  h.kids = v.holding.kids;
114
151
  h.played = v.holding.played;
152
+ h.coasts = v.holding.coasts.slice();
153
+ h.cutSeq = v.holding.cutSeq;
154
+ if (seq !== undefined)
155
+ h.back(seq);
115
156
  v.holding = h;
116
157
  }
117
158
  }
package/dist/pack.d.ts CHANGED
@@ -12,6 +12,7 @@ export interface PackedRecord {
12
12
  stepped: number;
13
13
  ticks: number;
14
14
  rebuilt: number;
15
+ copied?: true;
15
16
  /**
16
17
  * What `setting.keep` held, in the order its owners first kept it. An owner is code, which no
17
18
  * store keeps, and a voice revived from a descriptor has new ones; the same code keeps in the same
package/dist/pack.js CHANGED
@@ -18,6 +18,8 @@ export function packHeld(voice, h) {
18
18
  rebuilt: h.rebuilt,
19
19
  kept: kept.length === 0 ? null : kept.map(clone),
20
20
  };
21
+ if (h.copied)
22
+ p.copied = true;
21
23
  if (h.base !== undefined)
22
24
  p.base = clone(h.base);
23
25
  if (h.slope !== undefined)
@@ -42,6 +44,7 @@ export function unpackHeld(voice, p) {
42
44
  h.stepped = p.stepped;
43
45
  h.ticks = p.ticks;
44
46
  h.rebuilt = p.rebuilt;
47
+ h.copied = p.copied === true;
45
48
  if (p.kept !== null)
46
49
  h.unkept = p.kept;
47
50
  if (p.base !== undefined)
package/dist/place.d.ts CHANGED
@@ -10,6 +10,8 @@ export declare function mixAt<I, O>(mix: Mixer<I, O>, u: number): number;
10
10
  export declare function pin<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, u: number): void;
11
11
  /** Puts each pending voice's start, given in host time, where the mix's rate now puts it. */
12
12
  export declare function repin<I, O>(mix: Mixer<I, O>): void;
13
+ /** Moves a pending voice's start, its clock with it; false where it is already there. */
14
+ export declare function startAt<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, start: number): boolean;
13
15
  /** Refuses a placement that names a mark twice on one side, or that waits on itself. */
14
16
  export declare function checkPlacement<I, O>(mix: Mixer<I, O>, spec: VoiceSpec<I, O>, anchor: Placement, owner: Voice<I, O> | null): void;
15
17
  /**
@@ -18,3 +20,9 @@ export declare function checkPlacement<I, O>(mix: Mixer<I, O>, spec: VoiceSpec<I
18
20
  * last gave. Repeated so a chain of anchors settles in one sync.
19
21
  */
20
22
  export declare function place<I, O>(mix: Mixer<I, O>): void;
23
+ /**
24
+ * The time an anchor answers on its voice's owner's clock, or undefined while its target has none.
25
+ * A time before the owner starts answers `before`: undefined, so a start waits, or -Infinity for a
26
+ * floor, which is no bound at all.
27
+ */
28
+ export declare function resolve<I, O>(mix: Mixer<I, O>, a: number | NonNullable<Placement['start']>, self: Voice<I, O>, before?: number): number | undefined;
package/dist/place.js CHANGED
@@ -5,6 +5,7 @@ import { noted } from './history.js';
5
5
  import { markOf } from './marks.js';
6
6
  import { mixTime, ownerReading } from './owner.js';
7
7
  import { holders, scored, scoredGone, scoreVersion, unhold } from './scored.js';
8
+ import { refloor } from './spans.js';
8
9
  import { none } from './voice.js';
9
10
  import { warnOnce } from './warn.js';
10
11
  /** What a voice's own owner's clock reads now: the mix clock for a voice no owner holds. */
@@ -35,7 +36,7 @@ export function repin(mix) {
35
36
  }
36
37
  }
37
38
  /** Moves a pending voice's start, its clock with it; false where it is already there. */
38
- function startAt(mix, voice, start) {
39
+ export function startAt(mix, voice, start) {
39
40
  if (start === voice.start)
40
41
  return false;
41
42
  voice.start = start;
@@ -145,18 +146,23 @@ export function place(mix) {
145
146
  }
146
147
  }
147
148
  if (!moved)
148
- return;
149
+ break;
149
150
  }
151
+ refloor(mix);
150
152
  }
151
- /** The time an anchor answers on its voice's owner's clock, or undefined while its target has none. */
152
- function resolve(mix, a, self) {
153
+ /**
154
+ * The time an anchor answers on its voice's owner's clock, or undefined while its target has none.
155
+ * A time before the owner starts answers `before`: undefined, so a start waits, or -Infinity for a
156
+ * floor, which is no bound at all.
157
+ */
158
+ export function resolve(mix, a, self, before) {
153
159
  const o = self.owner;
154
160
  if (typeof a === 'number')
155
161
  return o === null ? mixAt(mix, a - mix.offset) : a;
156
162
  if ('all' in a) {
157
163
  let latest = Number.NEGATIVE_INFINITY;
158
164
  for (const m of a.all) {
159
- const t = member(mix, m, self);
165
+ const t = member(mix, m, self, before);
160
166
  if (t === undefined)
161
167
  return undefined;
162
168
  latest = Math.max(latest, t);
@@ -169,7 +175,7 @@ function resolve(mix, a, self) {
169
175
  let earliest = Number.POSITIVE_INFINITY;
170
176
  let unknown = false;
171
177
  for (const m of a.any) {
172
- const t = member(mix, m, self);
178
+ const t = member(mix, m, self, before);
173
179
  if (t === undefined)
174
180
  unknown = true;
175
181
  else
@@ -205,14 +211,16 @@ function resolve(mix, a, self) {
205
211
  if (o === null)
206
212
  return t + by;
207
213
  const local = ownerReading(o, t);
214
+ if (local === Number.NEGATIVE_INFINITY)
215
+ return before;
208
216
  return Number.isFinite(local) ? local + by : undefined;
209
217
  }
210
218
  /**
211
219
  * What one member of a join answers, or the time it last gave once its target has left, as a lone
212
220
  * anchor keeps its voice where its target last put it.
213
221
  */
214
- function member(mix, a, self) {
215
- const t = resolve(mix, a, self);
222
+ function member(mix, a, self, before) {
223
+ const t = resolve(mix, a, self, before);
216
224
  if (t === undefined)
217
225
  return self.answers?.get(a);
218
226
  if (!mix.projecting) {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * By subject number, the crowd voices whose rows are placed on it, so letting a number go reaches
3
+ * its rows without walking every crowd's. Kept by voice id, which a row keeps as a crowd closes up
4
+ * or is rebuilt around it.
5
+ */
6
+ export declare class Placed {
7
+ /** By subject number, one voice placed on it; 0, which is no voice's id, for none. */
8
+ private one;
9
+ /** The others, for a subject with several; null until some subject has two. */
10
+ private rest;
11
+ /** Makes room for subject numbers below `cap`. */
12
+ grow(cap: number): void;
13
+ /** Some voice placed on the subject at `slot`, 0 for none. */
14
+ any(slot: number): number;
15
+ /** Every voice placed on the subject at `slot`, in no order: for a test to read. */
16
+ all(slot: number): number[];
17
+ add(slot: number, id: number): void;
18
+ remove(slot: number, id: number): void;
19
+ }
package/dist/placed.js ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * By subject number, the crowd voices whose rows are placed on it, so letting a number go reaches
3
+ * its rows without walking every crowd's. Kept by voice id, which a row keeps as a crowd closes up
4
+ * or is rebuilt around it.
5
+ */
6
+ export class Placed {
7
+ /** By subject number, one voice placed on it; 0, which is no voice's id, for none. */
8
+ one = new Int32Array(0);
9
+ /** The others, for a subject with several; null until some subject has two. */
10
+ rest = null;
11
+ /** Makes room for subject numbers below `cap`. */
12
+ grow(cap) {
13
+ const one = new Int32Array(cap);
14
+ one.set(this.one);
15
+ this.one = one;
16
+ }
17
+ /** Some voice placed on the subject at `slot`, 0 for none. */
18
+ any(slot) {
19
+ return this.one[slot] | 0;
20
+ }
21
+ /** Every voice placed on the subject at `slot`, in no order: for a test to read. */
22
+ all(slot) {
23
+ const one = this.any(slot);
24
+ return one === 0 ? [] : [one, ...(this.rest?.get(slot) ?? [])];
25
+ }
26
+ add(slot, id) {
27
+ if (this.one[slot] === 0) {
28
+ this.one[slot] = id;
29
+ return;
30
+ }
31
+ this.rest ??= new Map();
32
+ const more = this.rest.get(slot);
33
+ if (more === undefined)
34
+ this.rest.set(slot, [id]);
35
+ else
36
+ more.push(id);
37
+ }
38
+ remove(slot, id) {
39
+ const more = this.rest?.get(slot);
40
+ if (this.one[slot] === id)
41
+ this.one[slot] = more === undefined ? 0 : more.pop();
42
+ else if (more !== undefined) {
43
+ const i = more.indexOf(id);
44
+ if (i < 0)
45
+ return;
46
+ more[i] = more[more.length - 1];
47
+ more.pop();
48
+ }
49
+ else
50
+ return;
51
+ if (more !== undefined && more.length === 0)
52
+ this.rest.delete(slot);
53
+ }
54
+ }
package/dist/project.d.ts CHANGED
@@ -4,9 +4,12 @@ import type { TransportProjection } from './types.js';
4
4
  import type { Subject, Voice } from './voice.js';
5
5
  /**
6
6
  * Every mix on a transport read at mix time `t`, through copies of them on a transport of their
7
- * own, moved together so an anchor across mixes answers as it would live.
7
+ * own, moved together so an anchor across mixes answers as it would live. Ahead, they stop at each
8
+ * known event on the way, and a subject asked about is read at each before `t`.
8
9
  */
9
- export declare function projectAll(transport: Transport, t: number): TransportProjection;
10
+ export declare function projectAll(transport: Transport, at: number | {
11
+ readonly frame: number;
12
+ }): TransportProjection;
10
13
  /** Sets each copied voice's state to what its clock and fade make it at `t`. */
11
14
  export declare function stateAt<I, O>(c: Mixer<I, O>, t: number): void;
12
15
  /** Gives a projection's copy what it reads of the live mix as it is, before its voices are copied. */