@msb235/blits 0.14.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 (187) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +3 -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 +37 -14
  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/color.d.ts +12 -10
  21. package/dist/color.js +25 -25
  22. package/dist/columns.d.ts +2 -1
  23. package/dist/columns.js +62 -38
  24. package/dist/contribution.d.ts +30 -0
  25. package/dist/contribution.js +374 -0
  26. package/dist/copyout.d.ts +10 -0
  27. package/dist/copyout.js +69 -24
  28. package/dist/crowd.d.ts +16 -6
  29. package/dist/crowd.js +20 -11
  30. package/dist/cue.d.ts +8 -1
  31. package/dist/cue.js +39 -13
  32. package/dist/cut.d.ts +8 -0
  33. package/dist/cut.js +4 -0
  34. package/dist/delay.d.ts +3 -0
  35. package/dist/delay.js +7 -0
  36. package/dist/drop.d.ts +9 -0
  37. package/dist/drop.js +84 -0
  38. package/dist/due.d.ts +24 -5
  39. package/dist/due.js +114 -34
  40. package/dist/easing.d.ts +6 -4
  41. package/dist/easing.js +7 -3
  42. package/dist/everyone.d.ts +8 -2
  43. package/dist/everyone.js +33 -3
  44. package/dist/fade.d.ts +2 -1
  45. package/dist/fade.js +2 -1
  46. package/dist/fill.d.ts +3 -2
  47. package/dist/fill.js +52 -20
  48. package/dist/fit.d.ts +44 -26
  49. package/dist/fit.js +10 -9
  50. package/dist/fold.d.ts +4 -37
  51. package/dist/fold.js +5 -513
  52. package/dist/gather.d.ts +12 -7
  53. package/dist/gather.js +179 -44
  54. package/dist/gone.js +2 -2
  55. package/dist/grid.d.ts +47 -0
  56. package/dist/grid.js +247 -0
  57. package/dist/handle.d.ts +2 -0
  58. package/dist/handle.js +14 -8
  59. package/dist/held.d.ts +2 -1
  60. package/dist/held.js +2 -1
  61. package/dist/history.d.ts +6 -5
  62. package/dist/history.js +7 -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 +38 -11
  70. package/dist/lane.js +46 -11
  71. package/dist/lanes.d.ts +30 -12
  72. package/dist/lanes.js +56 -36
  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 +95 -7
  79. package/dist/mixer.d.ts +56 -37
  80. package/dist/mixer.js +57 -171
  81. package/dist/motion.d.ts +14 -118
  82. package/dist/motion.js +16 -8
  83. package/dist/motions.d.ts +62 -155
  84. package/dist/motions.js +56 -370
  85. package/dist/moved.d.ts +1 -1
  86. package/dist/moved.js +16 -7
  87. package/dist/named.d.ts +6 -2
  88. package/dist/named.js +49 -3
  89. package/dist/numbers.d.ts +7 -0
  90. package/dist/numbers.js +33 -0
  91. package/dist/owed.d.ts +2 -0
  92. package/dist/owed.js +4 -0
  93. package/dist/pack.d.ts +2 -1
  94. package/dist/pack.js +2 -3
  95. package/dist/patch.d.ts +7 -3
  96. package/dist/patch.js +6 -2
  97. package/dist/places.d.ts +53 -0
  98. package/dist/places.js +171 -0
  99. package/dist/plot.d.ts +12 -0
  100. package/dist/plot.js +52 -0
  101. package/dist/project.d.ts +4 -4
  102. package/dist/project.js +9 -212
  103. package/dist/pull.js +22 -9
  104. package/dist/qualify.js +93 -31
  105. package/dist/reading.d.ts +6 -0
  106. package/dist/reading.js +5 -0
  107. package/dist/record.d.ts +5 -1
  108. package/dist/record.js +3 -2
  109. package/dist/relink.d.ts +1 -1
  110. package/dist/renumber.d.ts +10 -0
  111. package/dist/renumber.js +75 -0
  112. package/dist/restored.d.ts +2 -2
  113. package/dist/restored.js +1 -1
  114. package/dist/rests.d.ts +9 -0
  115. package/dist/rests.js +76 -0
  116. package/dist/revive.d.ts +3 -2
  117. package/dist/revive.js +2 -2
  118. package/dist/{locus.d.ts → role.d.ts} +10 -9
  119. package/dist/{locus.js → role.js} +3 -3
  120. package/dist/roles.d.ts +10 -0
  121. package/dist/roles.js +158 -0
  122. package/dist/roster.js +3 -3
  123. package/dist/rotation.d.ts +1 -1
  124. package/dist/rotation.js +1 -1
  125. package/dist/rows.js +28 -20
  126. package/dist/sample.d.ts +2 -2
  127. package/dist/sample.js +33 -7
  128. package/dist/seat.d.ts +17 -0
  129. package/dist/seat.js +25 -0
  130. package/dist/seek.js +5 -5
  131. package/dist/setting.d.ts +27 -0
  132. package/dist/setting.js +55 -0
  133. package/dist/shared.d.ts +1 -1
  134. package/dist/shared.js +1 -1
  135. package/dist/signals.d.ts +64 -32
  136. package/dist/signals.js +20 -18
  137. package/dist/stack.d.ts +5 -5
  138. package/dist/stack.js +5 -5
  139. package/dist/standing.d.ts +2 -1
  140. package/dist/standing.js +3 -2
  141. package/dist/store.d.ts +9 -0
  142. package/dist/store.js +24 -0
  143. package/dist/strays.d.ts +2 -1
  144. package/dist/strays.js +23 -15
  145. package/dist/stretch.d.ts +112 -0
  146. package/dist/stretch.js +9 -0
  147. package/dist/subject.d.ts +142 -0
  148. package/dist/subject.js +1 -0
  149. package/dist/tape.d.ts +9 -5
  150. package/dist/tape.js +5 -1
  151. package/dist/then.d.ts +9 -0
  152. package/dist/then.js +57 -0
  153. package/dist/ticker.d.ts +4 -4
  154. package/dist/trace.d.ts +4 -0
  155. package/dist/trace.js +255 -0
  156. package/dist/transport.d.ts +0 -8
  157. package/dist/transport.js +0 -4
  158. package/dist/turns.d.ts +4 -3
  159. package/dist/turns.js +4 -3
  160. package/dist/types/arc.d.ts +12 -11
  161. package/dist/types/channel.d.ts +12 -8
  162. package/dist/types/history.d.ts +35 -19
  163. package/dist/types/mix.d.ts +237 -163
  164. package/dist/types/motion.d.ts +151 -0
  165. package/dist/types/motion.js +1 -0
  166. package/dist/types/patch.d.ts +67 -34
  167. package/dist/types/place.d.ts +51 -36
  168. package/dist/types/trace.d.ts +51 -0
  169. package/dist/types/trace.js +1 -0
  170. package/dist/types/transport.d.ts +20 -18
  171. package/dist/types/voice.d.ts +144 -73
  172. package/dist/types/watch.d.ts +88 -40
  173. package/dist/types.d.ts +2 -0
  174. package/dist/unreached.d.ts +2 -0
  175. package/dist/unreached.js +7 -0
  176. package/dist/voice.d.ts +10 -143
  177. package/dist/voice.js +37 -82
  178. package/dist/warn.d.ts +2 -0
  179. package/dist/warn.js +1 -1
  180. package/dist/watch.d.ts +2 -2
  181. package/dist/watch.js +5 -3
  182. package/dist/wave.d.ts +4 -3
  183. package/dist/wave.js +3 -2
  184. package/dist/weigh.d.ts +7 -1
  185. package/dist/weigh.js +27 -0
  186. package/docs/schema.html +223 -34
  187. package/package.json +2 -2
package/dist/numbers.js CHANGED
@@ -66,6 +66,10 @@ export class Numbers {
66
66
  this.release(dead);
67
67
  return slot;
68
68
  }
69
+ /** Whether `slot` is a number in use, its subject perhaps collected and not yet released. */
70
+ has(slot) {
71
+ return this.refs[slot] !== undefined;
72
+ }
69
73
  subject(slot) {
70
74
  const ref = this.refs[slot];
71
75
  if (ref === undefined)
@@ -78,6 +82,35 @@ export class Numbers {
78
82
  }
79
83
  return ref;
80
84
  }
85
+ /**
86
+ * Hands the subject numbered `from` the free number `to`, and frees `from` without telling the
87
+ * owner, whose stores the caller moves; false, changing nothing, where either does not hold.
88
+ */
89
+ move(from, to) {
90
+ const ref = this.refs[from];
91
+ const free = this.free;
92
+ if (ref === undefined || to >= this.next || this.refs[to] !== undefined || free === null)
93
+ return false;
94
+ const at = free.lastIndexOf(to);
95
+ if (at < 0)
96
+ return false;
97
+ if (ref instanceof WeakRef) {
98
+ const subject = ref.deref();
99
+ if (subject === undefined)
100
+ return false;
101
+ if (ref === this.lone)
102
+ this.loneSlot = to;
103
+ else if (this.registry !== null) {
104
+ this.registry.unregister(ref);
105
+ this.registry.register(subject, to, ref);
106
+ }
107
+ }
108
+ free.splice(at, 1);
109
+ free.push(from);
110
+ this.refs[to] = ref;
111
+ this.refs[from] = undefined;
112
+ return true;
113
+ }
81
114
  release(slot) {
82
115
  const ref = this.refs[slot];
83
116
  if (ref === undefined)
package/dist/owed.d.ts CHANGED
@@ -13,6 +13,8 @@ export declare class Owed {
13
13
  begin(fill: number): void;
14
14
  /** Makes room for subject numbers below `cap`. */
15
15
  grow(cap: number): void;
16
+ /** Whether any subject owes a laned voice this fill. */
17
+ any(): boolean;
16
18
  /** Whether the subject at `slot` owes some laned voice this fill. */
17
19
  owes(slot: number): boolean;
18
20
  /** Whether the subject at `slot` owes voice `id` this fill. */
package/dist/owed.js CHANGED
@@ -23,6 +23,10 @@ export class Owed {
23
23
  at.set(this.at);
24
24
  this.at = at;
25
25
  }
26
+ /** Whether any subject owes a laned voice this fill. */
27
+ any() {
28
+ return this.lists.length > 0;
29
+ }
26
30
  /** Whether the subject at `slot` owes some laned voice this fill. */
27
31
  owes(slot) {
28
32
  return this.lists.length > 0 && this.fill[slot] === this.now;
package/dist/pack.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { Subject, Voice } from './voice.js';
1
+ import type { Subject } from './subject.js';
2
+ import type { Voice } from './voice.js';
2
3
  /** A subject's record as plain data, for a history store. */
3
4
  export interface PackedRecord {
4
5
  reaches: boolean;
package/dist/pack.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { clone } from './clone.js';
2
- import { record } from './record.js';
2
+ import { isRested, record } from './record.js';
3
3
  /** A voice's record of one subject as plain data, its patch's `pack` taking its state. */
4
4
  export function packHeld(voice, h) {
5
5
  const patch = voice.patch;
@@ -10,7 +10,7 @@ export function packHeld(voice, h) {
10
10
  since: h.since,
11
11
  shown: h.shown,
12
12
  weight: h.weight,
13
- rested: h.rested,
13
+ rested: isRested(h),
14
14
  restedAt: h.restedAt,
15
15
  bands: h.bands === null ? null : Array.from(h.bands),
16
16
  state: h.state === undefined ? undefined : patch.pack ? patch.pack(h.state) : clone(h.state),
@@ -40,7 +40,6 @@ export function unpackHeld(voice, p) {
40
40
  h.since = p.since;
41
41
  h.shown = p.shown;
42
42
  h.weight = p.weight;
43
- h.rested = p.rested;
44
43
  h.restedAt = p.restedAt;
45
44
  h.bands = bands;
46
45
  h.stepped = p.stepped;
package/dist/patch.d.ts CHANGED
@@ -15,12 +15,14 @@ export interface PatchOptions<I, O, S, H = unknown> {
15
15
  reads?: readonly string[];
16
16
  state?(subject: I): S;
17
17
  step?(state: S, dt: number, subject: I, setting: Setting<S, H>): void;
18
- /** `state` as plain data and back, for a history store: see `Patch.pack`. */
18
+ /** `state` as plain data and back, for a history store. See `Patch.pack`. */
19
19
  pack?(state: S): unknown;
20
20
  unpack?(data: unknown): S;
21
21
  }
22
22
  /**
23
- * The procedural form. Flicker, roving, a hash walk, a starter strike.
23
+ * A patch whose function computes its output from phase, subject and setting each frame.
24
+ *
25
+ * The procedural form, for flicker, roving, a hash walk, a starter strike.
24
26
  *
25
27
  * @category patch
26
28
  */
@@ -137,7 +139,9 @@ export declare function builtOf<I, O, S>(p: Patch<I, O, S>): Built;
137
139
  /** Evaluates a stop list into a fresh delta. */
138
140
  export declare function evalKeys<O>(stops: readonly Keyframe<O>[], writes: readonly (keyof O)[], phase: number, duration: number, opts: KeysOptions<O>, base?: Partial<O>): Partial<O>;
139
141
  /**
140
- * The declarative form. klieg's transition sugar and wod's keyframes.
142
+ * A patch that interpolates between keyframes over its duration.
143
+ *
144
+ * The declarative form, as klieg's transition sugar and wod's keyframes use.
141
145
  *
142
146
  * @category patch
143
147
  */
package/dist/patch.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { lerpInto, lerpNumber } from './channels.js';
2
2
  import { curve, startSlope } from './easing.js';
3
3
  /**
4
- * The procedural form. Flicker, roving, a hash walk, a starter strike.
4
+ * A patch whose function computes its output from phase, subject and setting each frame.
5
+ *
6
+ * The procedural form, for flicker, roving, a hash walk, a starter strike.
5
7
  *
6
8
  * @category patch
7
9
  */
@@ -326,7 +328,9 @@ export function evalKeys(stops, writes, phase, duration, opts, base) {
326
328
  return readKeys(build(stops, writes, duration, opts), phase, {}, base);
327
329
  }
328
330
  /**
329
- * The declarative form. klieg's transition sugar and wod's keyframes.
331
+ * A patch that interpolates between keyframes over its duration.
332
+ *
333
+ * The declarative form, as klieg's transition sugar and wod's keyframes use.
330
334
  *
331
335
  * @category patch
332
336
  */
@@ -0,0 +1,53 @@
1
+ import { Grid } from './grid.js';
2
+ import { Store } from './store.js';
3
+ /**
4
+ * The index the host gives each subject, by `place` or by its place in the list `plot` was given,
5
+ * and where the host says each one is, over `Grid`. Subjects are held as the rest of the mix holds
6
+ * them: an object weakly, anything else until `drop`. An index a subject leaves, by `drop` or by
7
+ * being collected, is handed out again.
8
+ */
9
+ export declare class Places<I> extends Grid {
10
+ private readonly slotOf;
11
+ private readonly keepRefs;
12
+ /** By subject, its index. */
13
+ at: Store<I, number>;
14
+ /** By index, its subject's lane number, -1 for one with none or a subject let go. */
15
+ slots: Int32Array<ArrayBuffer>;
16
+ /** By lane number, the index it was put in `slots` at, -1 for none. */
17
+ placeOf: Int32Array<ArrayBuffer>;
18
+ /** Outside production, by index, its subject, for the check that a bound does not clip a signal. */
19
+ refs: (WeakRef<object> | I | undefined)[] | null;
20
+ /** While a bounded fill reads the subject at an index, that index, so `around` need not look it up; else -1. */
21
+ cur: number;
22
+ /** Indices a subject left, for `place` to hand out again. */
23
+ free: number[];
24
+ /** Whether `xy` is the mix's own, grown as `place` hands out indices, rather than the array `plot` was given. */
25
+ owned: boolean;
26
+ /**
27
+ * By index, the token its object subject is registered under: a reused index never hears of an
28
+ * old subject's collection.
29
+ */
30
+ private tokens;
31
+ private registry;
32
+ constructor(slotOf: (subject: I) => number, keepRefs: boolean);
33
+ /** Takes the host's subjects and positions; `xy` is read in place, so a move needs only `moved`. */
34
+ set(list: readonly I[], xy: Float64Array, cell: number): void;
35
+ /** `subject`'s index: the one it has, or a new one, a freed index first. */
36
+ place(subject: I): number;
37
+ private hold;
38
+ /** Room for indices below `n`; the mix's own positions start as no position, NaN. */
39
+ private room;
40
+ /** The lanes numbered `subject`: its place, if it has one, reads the number from now on. */
41
+ number(subject: I, slot: number): void;
42
+ private numbered;
43
+ /** The lanes let lane number `slot` go. */
44
+ forgot(slot: number): void;
45
+ /** The host dropped `subject`: its index is freed. */
46
+ drop(subject: I): void;
47
+ /** Frees index `i`, its subject dropped or collected. */
48
+ private release;
49
+ /** The subject at index `i`, where it is still held; only outside production. */
50
+ subjectAt(i: number): I | undefined;
51
+ /** Whether `subject` is within `r` of (`cx`, `cy`), by the same test `query` makes. */
52
+ inside(subject: I, cx: number, cy: number, r2: number): boolean;
53
+ }
package/dist/places.js ADDED
@@ -0,0 +1,171 @@
1
+ import { Grid } from './grid.js';
2
+ import { Store } from './store.js';
3
+ /**
4
+ * The index the host gives each subject, by `place` or by its place in the list `plot` was given,
5
+ * and where the host says each one is, over `Grid`. Subjects are held as the rest of the mix holds
6
+ * them: an object weakly, anything else until `drop`. An index a subject leaves, by `drop` or by
7
+ * being collected, is handed out again.
8
+ */
9
+ export class Places extends Grid {
10
+ slotOf;
11
+ keepRefs;
12
+ /** By subject, its index. */
13
+ at = new Store();
14
+ /** By index, its subject's lane number, -1 for one with none or a subject let go. */
15
+ slots = new Int32Array(0);
16
+ /** By lane number, the index it was put in `slots` at, -1 for none. */
17
+ placeOf = new Int32Array(0);
18
+ /** Outside production, by index, its subject, for the check that a bound does not clip a signal. */
19
+ refs = null;
20
+ /** While a bounded fill reads the subject at an index, that index, so `around` need not look it up; else -1. */
21
+ cur = -1;
22
+ /** Indices a subject left, for `place` to hand out again. */
23
+ free = [];
24
+ /** Whether `xy` is the mix's own, grown as `place` hands out indices, rather than the array `plot` was given. */
25
+ owned = true;
26
+ /**
27
+ * By index, the token its object subject is registered under: a reused index never hears of an
28
+ * old subject's collection.
29
+ */
30
+ tokens = [];
31
+ registry = null;
32
+ constructor(slotOf, keepRefs) {
33
+ super();
34
+ this.slotOf = slotOf;
35
+ this.keepRefs = keepRefs;
36
+ this.refs = keepRefs ? [] : null;
37
+ }
38
+ /** Takes the host's subjects and positions; `xy` is read in place, so a move needs only `moved`. */
39
+ set(list, xy, cell) {
40
+ const n = list.length;
41
+ this.at = new Store();
42
+ for (let i = 0; i < n; i++)
43
+ this.at.set(list[i], i);
44
+ this.slots = new Int32Array(Math.max(n, 16)).fill(-1);
45
+ this.placeOf.fill(-1);
46
+ this.refs = this.keepRefs ? [] : null;
47
+ this.tokens = [];
48
+ this.free = [];
49
+ for (let i = 0; i < n; i++) {
50
+ const subject = list[i];
51
+ // A subject listed twice is at its last index.
52
+ if (this.at.get(subject) !== i)
53
+ continue;
54
+ this.hold(subject, i);
55
+ }
56
+ this.n = n;
57
+ this.xy = xy;
58
+ this.owned = false;
59
+ this.cell = cell;
60
+ this.dirty = true;
61
+ }
62
+ /** `subject`'s index: the one it has, or a new one, a freed index first. */
63
+ place(subject) {
64
+ const had = this.at.get(subject);
65
+ if (had !== undefined)
66
+ return had;
67
+ const i = this.free.pop() ?? this.n++;
68
+ this.room(i + 1);
69
+ this.at.set(subject, i);
70
+ this.hold(subject, i);
71
+ if (this.owned)
72
+ this.put(i, Number.NaN, Number.NaN);
73
+ return i;
74
+ }
75
+ hold(subject, i) {
76
+ const slot = this.slotOf(subject);
77
+ if (slot >= 0)
78
+ this.numbered(slot, i);
79
+ const object = typeof subject === 'object' && subject !== null;
80
+ if (this.refs !== null)
81
+ this.refs[i] = object ? new WeakRef(subject) : subject;
82
+ if (!object)
83
+ return;
84
+ const token = { i };
85
+ this.tokens[i] = token;
86
+ this.registry ??= new FinalizationRegistry((t) => {
87
+ if (this.tokens[t.i] === t)
88
+ this.release(t.i);
89
+ });
90
+ this.registry.register(subject, token, token);
91
+ }
92
+ /** Room for indices below `n`; the mix's own positions start as no position, NaN. */
93
+ room(n) {
94
+ if (this.slots.length < n) {
95
+ const slots = new Int32Array(Math.max(n, this.slots.length * 2)).fill(-1);
96
+ slots.set(this.slots);
97
+ this.slots = slots;
98
+ }
99
+ if (this.owned && this.xy.length < 2 * n) {
100
+ const xy = new Float64Array(Math.max(2 * n, this.xy.length * 2, 32)).fill(Number.NaN);
101
+ xy.set(this.xy);
102
+ this.xy = xy;
103
+ }
104
+ }
105
+ /** The lanes numbered `subject`: its place, if it has one, reads the number from now on. */
106
+ number(subject, slot) {
107
+ const i = this.at.get(subject);
108
+ if (i !== undefined)
109
+ this.numbered(slot, i);
110
+ }
111
+ numbered(slot, i) {
112
+ if (slot >= this.placeOf.length) {
113
+ const grown = new Int32Array(Math.max(slot + 1, this.placeOf.length * 2, 64)).fill(-1);
114
+ grown.set(this.placeOf);
115
+ this.placeOf = grown;
116
+ }
117
+ this.placeOf[slot] = i;
118
+ this.slots[i] = slot;
119
+ }
120
+ /** The lanes let lane number `slot` go. */
121
+ forgot(slot) {
122
+ if (slot >= this.placeOf.length)
123
+ return;
124
+ const i = this.placeOf[slot];
125
+ if (i < 0)
126
+ return;
127
+ this.placeOf[slot] = -1;
128
+ if (this.slots[i] === slot)
129
+ this.slots[i] = -1;
130
+ }
131
+ /** The host dropped `subject`: its index is freed. */
132
+ drop(subject) {
133
+ const i = this.at.get(subject);
134
+ if (i === undefined)
135
+ return;
136
+ this.at.delete(subject);
137
+ const token = this.tokens[i];
138
+ if (token !== undefined)
139
+ this.registry?.unregister(token);
140
+ this.release(i);
141
+ }
142
+ /** Frees index `i`, its subject dropped or collected. */
143
+ release(i) {
144
+ this.tokens[i] = undefined;
145
+ if (this.refs !== null)
146
+ this.refs[i] = undefined;
147
+ const slot = this.slots[i];
148
+ if (slot >= 0)
149
+ this.placeOf[slot] = -1;
150
+ this.slots[i] = -1;
151
+ // The host's own array is the host's: a reused index reads whatever it holds there.
152
+ if (this.owned)
153
+ this.put(i, Number.NaN, Number.NaN);
154
+ this.free.push(i);
155
+ }
156
+ /** The subject at index `i`, where it is still held; only outside production. */
157
+ subjectAt(i) {
158
+ const ref = this.refs?.[i];
159
+ const s = ref instanceof WeakRef ? ref.deref() : ref;
160
+ return s !== undefined && this.at.get(s) === i ? s : undefined;
161
+ }
162
+ /** Whether `subject` is within `r` of (`cx`, `cy`), by the same test `query` makes. */
163
+ inside(subject, cx, cy, r2) {
164
+ const i = this.at.get(subject);
165
+ if (i === undefined || 2 * i + 1 >= this.xy.length)
166
+ return false;
167
+ const dx = this.xy[2 * i] - cx;
168
+ const dy = this.xy[2 * i + 1] - cy;
169
+ return dx * dx + dy * dy <= r2;
170
+ }
171
+ }
package/dist/plot.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { Mixer } from './mixer.js';
2
+ /** `Mix.plot`: the mix's subjects and their positions, `x` then `y` side by side in `xy`, read in place. */
3
+ export declare function plot<I, O>(this: Mixer<I, O>, subjects: readonly I[], xy: Float64Array, cell?: number): void;
4
+ /** `Mix.place`: the subject's index, its own or a new one, which a `within` finder names it by. */
5
+ export declare function place<I, O>(this: Mixer<I, O>, subject: I): number;
6
+ /** `Mix.moved`: the host wrote new positions into the `xy` it gave `plot`. */
7
+ export declare function moved<I, O>(this: Mixer<I, O>): void;
8
+ /**
9
+ * `Mix.put`: moves one subject, writing into the `xy` `plot` gave or the mix's own. The grid is built
10
+ * again only once a sixteenth of the indices have left their cells.
11
+ */
12
+ export declare function put<I, O>(this: Mixer<I, O>, subject: I, x: number, y: number): void;
package/dist/plot.js ADDED
@@ -0,0 +1,52 @@
1
+ import { bind } from './bound.js';
2
+ import { Places } from './places.js';
3
+ import { production } from './warn.js';
4
+ /** `Mix.plot`: the mix's subjects and their positions, `x` then `y` side by side in `xy`, read in place. */
5
+ export function plot(subjects, xy, cell) {
6
+ if (xy.length < 2 * subjects.length)
7
+ throw new RangeError(`blits: plot takes two numbers a subject, x then y: ${subjects.length} subjects need ${2 * subjects.length}, not ${xy.length}`);
8
+ if (cell !== undefined && !(cell > 0 && cell < Number.POSITIVE_INFINITY))
9
+ throw new RangeError(`blits: plot's cell is a size above 0, not ${cell}`);
10
+ placesOf(this).set(subjects, xy, cell ?? Number.NaN);
11
+ this.moved();
12
+ }
13
+ /** `Mix.place`: the subject's index, its own or a new one, which a `within` finder names it by. */
14
+ export function place(subject) {
15
+ return placesOf(this).place(subject);
16
+ }
17
+ /**
18
+ * The mix's indices, made at the first `place` or `plot`. An `around` voice cued before then read 0
19
+ * everywhere, so it takes its circle with nothing to undo.
20
+ */
21
+ function placesOf(mix) {
22
+ if (mix.places !== null)
23
+ return mix.places;
24
+ const places = new Places((s) => mix.chains.get(s)?.slot ?? -1, !production());
25
+ mix.places = places;
26
+ for (const voice of mix.cued)
27
+ if (voice.bound === null)
28
+ bind(mix, voice);
29
+ return places;
30
+ }
31
+ /** `Mix.moved`: the host wrote new positions into the `xy` it gave `plot`. */
32
+ export function moved() {
33
+ if (this.places !== null)
34
+ this.places.dirty = true;
35
+ this.lanes?.refill();
36
+ this.stir();
37
+ }
38
+ /**
39
+ * `Mix.put`: moves one subject, writing into the `xy` `plot` gave or the mix's own. The grid is built
40
+ * again only once a sixteenth of the indices have left their cells.
41
+ */
42
+ export function put(subject, x, y) {
43
+ const places = this.places;
44
+ const i = places === null ? undefined : places.at.get(subject);
45
+ if (places === null || i === undefined)
46
+ throw new Error('blits: put takes a subject with an index: place or plot it first');
47
+ if (2 * i + 1 >= places.xy.length)
48
+ throw new RangeError(`blits: put's subject is at index ${i}, past the positions plot was given: plot again with room for it`);
49
+ places.put(i, x, y);
50
+ this.lanes?.refill();
51
+ this.stir();
52
+ }
package/dist/project.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { type Cut } from './cut.js';
1
2
  import { Mixer } from './mixer.js';
2
- import { type Cut, Transport } from './transport.js';
3
+ import type { Subject } from './subject.js';
4
+ import { Transport } from './transport.js';
3
5
  import type { TransportProjection } from './types.js';
4
- import type { Subject, Voice } from './voice.js';
6
+ import type { Voice } from './voice.js';
5
7
  /**
6
8
  * Every mix on a transport read at mix time `t`, through copies of them on a transport of their
7
9
  * own, moved together so an anchor across mixes answers as it would live. Ahead, they stop at each
@@ -12,8 +14,6 @@ export declare function projectAll(transport: Transport, at: number | {
12
14
  }): TransportProjection;
13
15
  /** Sets each copied voice's state to what its clock and fade make it at `t`. */
14
16
  export declare function stateAt<I, O>(c: Mixer<I, O>, t: number): void;
15
- /** Gives a projection's copy what it reads of the live mix as it is, before its voices are copied. */
16
- export declare function mirror<I, O>(mix: Mixer<I, O>, c: Mixer<I, O>): void;
17
17
  /**
18
18
  * A projection back starts each subject from the latest copy kept at or before `t`, else from its
19
19
  * voice's start with fresh state, which is exact for a voice stepped at a fixed interval.