@msb235/blits 0.14.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.
- package/CHANGELOG.md +154 -0
- package/README.md +3 -3
- package/dist/ahead.d.ts +26 -0
- package/dist/ahead.js +264 -0
- package/dist/around.d.ts +11 -0
- package/dist/around.js +32 -0
- package/dist/bare.js +15 -7
- package/dist/blank.d.ts +10 -0
- package/dist/blank.js +26 -0
- package/dist/blend.d.ts +2 -1
- package/dist/blend.js +1 -6
- package/dist/bound.d.ts +70 -0
- package/dist/bound.js +351 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.js +7 -5
- package/dist/changes.d.ts +24 -0
- package/dist/changes.js +189 -0
- package/dist/channels.d.ts +39 -13
- package/dist/channels.js +14 -5
- package/dist/color.d.ts +12 -10
- package/dist/color.js +25 -25
- package/dist/columns.js +7 -3
- package/dist/contribution.d.ts +30 -0
- package/dist/contribution.js +374 -0
- package/dist/copyout.d.ts +10 -0
- package/dist/copyout.js +69 -24
- package/dist/crowd.d.ts +8 -2
- package/dist/crowd.js +4 -5
- package/dist/cue.d.ts +8 -1
- package/dist/cue.js +39 -13
- package/dist/cut.d.ts +8 -0
- package/dist/cut.js +4 -0
- package/dist/drop.d.ts +9 -0
- package/dist/drop.js +84 -0
- package/dist/due.d.ts +24 -5
- package/dist/due.js +113 -33
- package/dist/easing.d.ts +4 -4
- package/dist/easing.js +3 -3
- package/dist/everyone.d.ts +3 -2
- package/dist/everyone.js +3 -3
- package/dist/fade.d.ts +2 -1
- package/dist/fade.js +2 -1
- package/dist/fill.d.ts +3 -2
- package/dist/fill.js +51 -19
- package/dist/fit.d.ts +44 -26
- package/dist/fit.js +10 -9
- package/dist/fold.d.ts +4 -37
- package/dist/fold.js +5 -513
- package/dist/gather.d.ts +12 -7
- package/dist/gather.js +179 -44
- package/dist/gone.js +2 -2
- package/dist/grid.d.ts +47 -0
- package/dist/grid.js +247 -0
- package/dist/handle.d.ts +2 -0
- package/dist/handle.js +14 -8
- package/dist/held.d.ts +2 -1
- package/dist/history.d.ts +6 -5
- package/dist/history.js +7 -9
- package/dist/hosts.js +9 -15
- package/dist/index.d.ts +5 -4
- package/dist/keyfill.d.ts +1 -1
- package/dist/keyfill.js +5 -3
- package/dist/keyrows.d.ts +12 -0
- package/dist/keyrows.js +99 -12
- package/dist/lane.d.ts +36 -6
- package/dist/lane.js +32 -1
- package/dist/lanes.d.ts +23 -8
- package/dist/lanes.js +29 -8
- package/dist/leavings.d.ts +2 -2
- package/dist/leavings.js +1 -1
- package/dist/lives.d.ts +47 -0
- package/dist/lives.js +180 -0
- package/dist/meet.d.ts +2 -2
- package/dist/meet.js +90 -4
- package/dist/mixer.d.ts +54 -36
- package/dist/mixer.js +53 -171
- package/dist/motion.d.ts +14 -118
- package/dist/motion.js +16 -8
- package/dist/motions.d.ts +62 -155
- package/dist/motions.js +56 -370
- package/dist/moved.d.ts +1 -1
- package/dist/moved.js +16 -7
- package/dist/named.d.ts +0 -1
- package/dist/named.js +20 -2
- package/dist/numbers.d.ts +2 -0
- package/dist/numbers.js +4 -0
- package/dist/owed.d.ts +2 -0
- package/dist/owed.js +4 -0
- package/dist/pack.d.ts +2 -1
- package/dist/pack.js +2 -3
- package/dist/patch.d.ts +7 -3
- package/dist/patch.js +6 -2
- package/dist/places.d.ts +53 -0
- package/dist/places.js +171 -0
- package/dist/plot.d.ts +12 -0
- package/dist/plot.js +52 -0
- package/dist/project.d.ts +4 -4
- package/dist/project.js +9 -212
- package/dist/pull.js +11 -8
- package/dist/qualify.js +84 -21
- package/dist/reading.d.ts +6 -0
- package/dist/reading.js +5 -0
- package/dist/record.d.ts +5 -1
- package/dist/record.js +3 -2
- package/dist/relink.d.ts +1 -1
- package/dist/restored.d.ts +2 -2
- package/dist/restored.js +1 -1
- package/dist/rests.d.ts +9 -0
- package/dist/rests.js +76 -0
- package/dist/revive.d.ts +3 -2
- package/dist/revive.js +2 -2
- package/dist/{locus.d.ts → role.d.ts} +10 -9
- package/dist/{locus.js → role.js} +3 -3
- package/dist/roles.d.ts +10 -0
- package/dist/roles.js +158 -0
- package/dist/roster.js +3 -3
- package/dist/rotation.d.ts +1 -1
- package/dist/rotation.js +1 -1
- package/dist/rows.js +1 -1
- package/dist/sample.d.ts +2 -2
- package/dist/sample.js +33 -7
- package/dist/seek.js +5 -5
- package/dist/setting.d.ts +27 -0
- package/dist/setting.js +55 -0
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/signals.d.ts +64 -32
- package/dist/signals.js +20 -18
- package/dist/stack.d.ts +5 -5
- package/dist/stack.js +5 -5
- package/dist/standing.d.ts +2 -1
- package/dist/standing.js +3 -2
- package/dist/store.d.ts +9 -0
- package/dist/store.js +24 -0
- package/dist/strays.d.ts +2 -1
- package/dist/strays.js +20 -13
- package/dist/stretch.d.ts +112 -0
- package/dist/stretch.js +9 -0
- package/dist/subject.d.ts +142 -0
- package/dist/subject.js +1 -0
- package/dist/tape.d.ts +9 -5
- package/dist/tape.js +5 -1
- package/dist/then.d.ts +9 -0
- package/dist/then.js +57 -0
- package/dist/ticker.d.ts +4 -4
- package/dist/transport.d.ts +0 -8
- package/dist/transport.js +0 -4
- package/dist/turns.d.ts +4 -3
- package/dist/turns.js +4 -3
- package/dist/types/arc.d.ts +12 -11
- package/dist/types/channel.d.ts +12 -8
- package/dist/types/history.d.ts +35 -19
- package/dist/types/mix.d.ts +222 -163
- package/dist/types/motion.d.ts +151 -0
- package/dist/types/motion.js +1 -0
- package/dist/types/patch.d.ts +67 -34
- package/dist/types/place.d.ts +51 -36
- package/dist/types/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +136 -70
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +1 -0
- package/dist/voice.d.ts +7 -143
- package/dist/voice.js +28 -79
- package/dist/warn.d.ts +2 -0
- package/dist/warn.js +1 -1
- package/dist/watch.d.ts +2 -2
- package/dist/watch.js +5 -3
- package/dist/wave.d.ts +4 -3
- package/dist/wave.js +3 -2
- package/dist/weigh.d.ts +7 -1
- package/dist/weigh.js +27 -0
- package/docs/schema.html +212 -34
- package/package.json +2 -2
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
*/
|
package/dist/places.d.ts
ADDED
|
@@ -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
|
|
3
|
+
import type { Subject } from './subject.js';
|
|
4
|
+
import { Transport } from './transport.js';
|
|
3
5
|
import type { TransportProjection } from './types.js';
|
|
4
|
-
import type {
|
|
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.
|
package/dist/project.js
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
+
import { copyAhead, mirror, playAhead, recordOf } from './ahead.js';
|
|
1
2
|
import { ownBlends } from './blend.js';
|
|
2
|
-
import {
|
|
3
|
+
import { heldAt } from './chain.js';
|
|
3
4
|
import { stepOf } from './clock.js';
|
|
4
|
-
import {
|
|
5
|
+
import { within } from './cut.js';
|
|
5
6
|
import { fadesAt } from './fade.js';
|
|
6
7
|
import { meet, origin } from './held.js';
|
|
7
8
|
import { copyHeld, last, lastWithin } from './history.js';
|
|
8
|
-
import { markOf, voiceMarks } from './marks.js';
|
|
9
9
|
import { Mixer } from './mixer.js';
|
|
10
|
-
import { move } from './move.js';
|
|
11
10
|
import { heldByInput, ownerReading, relink } from './owner.js';
|
|
12
|
-
import { pin, place } from './place.js';
|
|
13
11
|
import { reading } from './reading.js';
|
|
14
12
|
import { record } from './record.js';
|
|
15
13
|
import { recount } from './roster.js';
|
|
16
14
|
import { copyStanding, stands } from './standing.js';
|
|
17
|
-
import { hostAhead, hostAt
|
|
18
|
-
import { Transport
|
|
15
|
+
import { hostAhead, hostAt } from './tape.js';
|
|
16
|
+
import { Transport } from './transport.js';
|
|
19
17
|
import { cover, coverFrame } from './unpage.js';
|
|
20
|
-
import { unreached } from './unreached.js';
|
|
21
18
|
/**
|
|
22
19
|
* Every mix on a transport read at mix time `t`, through copies of them on a transport of their
|
|
23
20
|
* own, moved together so an anchor across mixes answers as it would live. Ahead, they stop at each
|
|
@@ -75,6 +72,7 @@ export function projectAll(transport, at) {
|
|
|
75
72
|
}, null);
|
|
76
73
|
copy.slot = mix.slot;
|
|
77
74
|
copy.projecting = true;
|
|
75
|
+
copy.places = mix.places;
|
|
78
76
|
copies.set(mix, copy);
|
|
79
77
|
if (ahead)
|
|
80
78
|
copyAhead(mix, copy);
|
|
@@ -91,15 +89,18 @@ export function projectAll(transport, at) {
|
|
|
91
89
|
earlier = new Map();
|
|
92
90
|
stood = playAhead(transport, c, copies, t, earlier);
|
|
93
91
|
}
|
|
92
|
+
const back = !ahead && kept ? cut : null;
|
|
94
93
|
const read = (f) => {
|
|
95
94
|
reading.live = false;
|
|
96
95
|
reading.ahead = stood.ahead.size > 0 ? stood.ahead : null;
|
|
96
|
+
reading.back = back;
|
|
97
97
|
try {
|
|
98
98
|
return f();
|
|
99
99
|
}
|
|
100
100
|
finally {
|
|
101
101
|
reading.live = true;
|
|
102
102
|
reading.ahead = null;
|
|
103
|
+
reading.back = null;
|
|
103
104
|
}
|
|
104
105
|
};
|
|
105
106
|
return {
|
|
@@ -136,168 +137,6 @@ export function projectAll(transport, at) {
|
|
|
136
137
|
},
|
|
137
138
|
};
|
|
138
139
|
}
|
|
139
|
-
/**
|
|
140
|
-
* Moves a read ahead's copies to mix time `t`, making the calls the tape holds on the way as a seek
|
|
141
|
-
* there makes them on the mixes. While a voice could carry state across it, it stops at each known
|
|
142
|
-
* event: a call, and every mark of a voice or announced. At each stop the copies are kept for
|
|
143
|
-
* subjects to be read there and copied on, so `copies` ends holding those at `t`. Answers the
|
|
144
|
-
* transport they stand on, and puts the tape back where it stood.
|
|
145
|
-
*/
|
|
146
|
-
function playAhead(transport, c, copies, t, earlier) {
|
|
147
|
-
const tape = transport.tape;
|
|
148
|
-
const depth = tape?.undoDepth() ?? 0;
|
|
149
|
-
let played = false;
|
|
150
|
-
try {
|
|
151
|
-
for (;;) {
|
|
152
|
-
const e = nextEvent(tape, c, t);
|
|
153
|
-
if (!(e < t))
|
|
154
|
-
break;
|
|
155
|
-
played = playTo(transport, c, copies, e) || played;
|
|
156
|
-
if (!carries(c))
|
|
157
|
-
continue;
|
|
158
|
-
// Placed now, as the next move would place them, while a target that has just left is here.
|
|
159
|
-
for (const m of c.members)
|
|
160
|
-
if (m.anchored > 0)
|
|
161
|
-
place(m);
|
|
162
|
-
const next = restage(c);
|
|
163
|
-
for (const [mix, copy] of copies) {
|
|
164
|
-
const on = new Mixer(copy.kit, { ...copy.opts, transport: next }, null);
|
|
165
|
-
on.slot = copy.slot;
|
|
166
|
-
on.projecting = true;
|
|
167
|
-
copyAhead(copy, on);
|
|
168
|
-
const stages = earlier.get(mix);
|
|
169
|
-
if (stages === undefined)
|
|
170
|
-
earlier.set(mix, { at: [copy], met: new Set() });
|
|
171
|
-
else
|
|
172
|
-
stages.at.push(copy);
|
|
173
|
-
copies.set(mix, on);
|
|
174
|
-
}
|
|
175
|
-
c = next;
|
|
176
|
-
}
|
|
177
|
-
played = playTo(transport, c, copies, t) || played;
|
|
178
|
-
}
|
|
179
|
-
finally {
|
|
180
|
-
if (played)
|
|
181
|
-
tape?.goto(depth);
|
|
182
|
-
}
|
|
183
|
-
return c;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Whether a voice on a read ahead's copies could carry state from one known event to the next: a
|
|
187
|
-
* patch that steps, or a weight given by a signal, which may keep state. Where none could, a read
|
|
188
|
-
* crosses the events in one go, as fast as one with none.
|
|
189
|
-
*/
|
|
190
|
-
function carries(c) {
|
|
191
|
-
for (const m of c.members)
|
|
192
|
-
for (const v of m.cued)
|
|
193
|
-
if (v.state !== 'done' && (v.patch.step !== undefined || typeof v.spec.weight === 'function'))
|
|
194
|
-
return true;
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* The earliest known event a read ahead's copies have yet to reach before `t`, mix time: the next
|
|
199
|
-
* call the tape holds, and while a voice could carry state across one, a mark of a voice or one
|
|
200
|
-
* announced. Infinity where there is none.
|
|
201
|
-
*/
|
|
202
|
-
function nextEvent(tape, c, t) {
|
|
203
|
-
const now = c.now;
|
|
204
|
-
let e = Number.POSITIVE_INFINITY;
|
|
205
|
-
const call = tape?.timestampAt(tape.undoDepth());
|
|
206
|
-
if (call !== undefined)
|
|
207
|
-
e = c.pace === null ? call : c.pace.reading(call);
|
|
208
|
-
if (!carries(c))
|
|
209
|
-
return e < t ? e : Number.POSITIVE_INFINITY;
|
|
210
|
-
for (const m of c.members)
|
|
211
|
-
for (const v of m.cued) {
|
|
212
|
-
if (v.state === 'done')
|
|
213
|
-
continue;
|
|
214
|
-
for (const mark of voiceMarks) {
|
|
215
|
-
const at = markOf(m, v, mark);
|
|
216
|
-
if (at !== undefined && at > now && at < e)
|
|
217
|
-
e = at;
|
|
218
|
-
}
|
|
219
|
-
const inner = v.spec.marks;
|
|
220
|
-
if (inner !== undefined)
|
|
221
|
-
for (const mark of Object.keys(inner)) {
|
|
222
|
-
const at = markOf(m, v, mark);
|
|
223
|
-
if (at !== undefined && at > now && at < e)
|
|
224
|
-
e = at;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
for (const a of c.announced) {
|
|
228
|
-
const at = c.pace === null ? a.at : c.pace.at(a.at, c.u);
|
|
229
|
-
if (at > now && at < e)
|
|
230
|
-
e = at;
|
|
231
|
-
}
|
|
232
|
-
return e < t ? e : Number.POSITIVE_INFINITY;
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Makes the calls the tape holds up to mix time `to` on a read ahead's copies and moves them
|
|
236
|
-
* there. Answers whether it moved the tape.
|
|
237
|
-
*/
|
|
238
|
-
function playTo(transport, c, copies, to) {
|
|
239
|
-
const tape = transport.tape;
|
|
240
|
-
const due = (at) => (c.pace === null ? at : c.pace.reading(at)) <= to;
|
|
241
|
-
const next = tape?.timestampAt(tape.undoDepth());
|
|
242
|
-
c.u = hostAhead(c, to);
|
|
243
|
-
let played = false;
|
|
244
|
-
if (next !== undefined && due(next)) {
|
|
245
|
-
// A call may write what a copy shares with the voice it was copied from.
|
|
246
|
-
for (const copy of c.members)
|
|
247
|
-
for (const v of copy.cued) {
|
|
248
|
-
if (v.parts !== null)
|
|
249
|
-
v.parts = new Map(v.parts);
|
|
250
|
-
if (v.parted !== null)
|
|
251
|
-
v.parted = new Map(v.parted);
|
|
252
|
-
}
|
|
253
|
-
played = true;
|
|
254
|
-
const last = replay(transport, due, c, {
|
|
255
|
-
transport: () => c,
|
|
256
|
-
mix: (of) => copies.get(of),
|
|
257
|
-
});
|
|
258
|
-
c.u = Math.max(last, hostAhead(c, to));
|
|
259
|
-
}
|
|
260
|
-
// A call played at `to` itself moved the copies there before it, as a sync does.
|
|
261
|
-
if (!(c.now >= to))
|
|
262
|
-
for (const copy of c.members)
|
|
263
|
-
move(copy, to);
|
|
264
|
-
return played;
|
|
265
|
-
}
|
|
266
|
-
/** A transport for a read ahead's copies to go on from where those on `c` stand. */
|
|
267
|
-
function restage(c) {
|
|
268
|
-
const next = new Transport(undefined, true);
|
|
269
|
-
next.offset = c.offset;
|
|
270
|
-
next.u = c.u;
|
|
271
|
-
next.nextId = c.nextId;
|
|
272
|
-
next.now = c.now;
|
|
273
|
-
next.pace = c.pace === null ? null : c.pace.until(Number.POSITIVE_INFINITY);
|
|
274
|
-
next.announced = c.announced.map((a) => ({ ...a }));
|
|
275
|
-
// The retargets and pushes the tape made carry their times, so every stop shares one list.
|
|
276
|
-
next.ahead = c.ahead;
|
|
277
|
-
return next;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* A mix's copy for a read ahead: its voices as they stand, each subject carried from the live one,
|
|
281
|
-
* or from the copy at the known event before.
|
|
282
|
-
*/
|
|
283
|
-
function copyAhead(mix, c) {
|
|
284
|
-
mirror(mix, c);
|
|
285
|
-
c.cued = mix.cued
|
|
286
|
-
.filter((v) => v.state !== 'done')
|
|
287
|
-
.map((v) => {
|
|
288
|
-
const copy = v.copy((subject) => carry(mix, v, subject, c, copy), undefined, true);
|
|
289
|
-
const at = mix.pins?.get(v);
|
|
290
|
-
if (at !== undefined)
|
|
291
|
-
pin(c, copy, at);
|
|
292
|
-
return copy;
|
|
293
|
-
});
|
|
294
|
-
c.waking = mix.waking;
|
|
295
|
-
ownBlends(c.cued);
|
|
296
|
-
// A copy keeps no list of owners, so the one before says only by its voices.
|
|
297
|
-
if (mix.owners !== null || (mix.projecting && c.cued.some((v) => v.holding !== null)))
|
|
298
|
-
relink(c.cued);
|
|
299
|
-
recount(c);
|
|
300
|
-
}
|
|
301
140
|
/** A mix's copy for a read back to `t`, cut at `cut`: its voices then, from the controls and copies kept. */
|
|
302
141
|
function copyBack(mix, c, t, cut) {
|
|
303
142
|
mirror(mix, c);
|
|
@@ -352,48 +191,6 @@ export function stateAt(c, t) {
|
|
|
352
191
|
? 'frozen'
|
|
353
192
|
: 'live';
|
|
354
193
|
}
|
|
355
|
-
/** Gives a projection's copy what it reads of the live mix as it is, before its voices are copied. */
|
|
356
|
-
export function mirror(mix, c) {
|
|
357
|
-
c.pose = mix.pose;
|
|
358
|
-
c.wantsPose = mix.wantsPose;
|
|
359
|
-
c.reducedNow = mix.reducedNow;
|
|
360
|
-
}
|
|
361
|
-
/** A voice's record of a subject, the one it shares among those it does not reach included. */
|
|
362
|
-
function recordOf(mix, voice, subject) {
|
|
363
|
-
// A voice sharing one record has that for every subject a chain has asked it about, pointed at
|
|
364
|
-
// the subject for the caller to copy from.
|
|
365
|
-
if (voice.sharing) {
|
|
366
|
-
const slot = headOf(mix, subject)?.slot ?? -1;
|
|
367
|
-
if (!sighted(voice, slot))
|
|
368
|
-
return undefined;
|
|
369
|
-
const all = voice.everyone;
|
|
370
|
-
open(voice, all, slot);
|
|
371
|
-
all.weight = weightAt(voice, slot);
|
|
372
|
-
return all;
|
|
373
|
-
}
|
|
374
|
-
const held = voice.subjects.get(subject);
|
|
375
|
-
if (held !== undefined || voice.unreachedBits === null)
|
|
376
|
-
return held;
|
|
377
|
-
return unreached(voice, headOf(mix, subject)?.slot ?? -1)
|
|
378
|
-
? voice.unreached
|
|
379
|
-
: undefined;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* A projection ahead starts each subject from where the live mix holds it, its `since` worked out
|
|
383
|
-
* again where it was still ahead, since the copy's clock may have moved it.
|
|
384
|
-
*/
|
|
385
|
-
function carry(mix, voice, subject, c, copy) {
|
|
386
|
-
const live = recordOf(mix, voice, subject);
|
|
387
|
-
if (live === undefined)
|
|
388
|
-
return undefined;
|
|
389
|
-
const h = copyHeld(voice, live);
|
|
390
|
-
if (h.reaches && !(h.since <= mix.now)) {
|
|
391
|
-
h.since = c.sinceOf(copy, h.delay);
|
|
392
|
-
h.shown = c.shownOf(copy, h.since);
|
|
393
|
-
}
|
|
394
|
-
h.from = h.stepped;
|
|
395
|
-
return h;
|
|
396
|
-
}
|
|
397
194
|
/**
|
|
398
195
|
* A projection back starts each subject from the latest copy kept at or before `t`, else from its
|
|
399
196
|
* voice's start with fresh state, which is exact for a voice stepped at a fixed interval.
|
package/dist/pull.js
CHANGED
|
@@ -18,12 +18,10 @@ function writeValue(c, v, n) {
|
|
|
18
18
|
}
|
|
19
19
|
/** After a probe, keeps the pose a retarget from `'current'` reads as the subject's last. */
|
|
20
20
|
export function keep(subject, pose, out) {
|
|
21
|
-
//
|
|
22
|
-
// object only pays for the copy once something
|
|
21
|
+
// A pose handed back without `out` is the host's to edit, so it is copied too. A host sampling
|
|
22
|
+
// into its own object only pays for the copy once something has asked to retarget from it.
|
|
23
23
|
let kept;
|
|
24
|
-
if (out === undefined)
|
|
25
|
-
kept = pose;
|
|
26
|
-
else if (this.wantsPose) {
|
|
24
|
+
if (out === undefined || this.wantsPose) {
|
|
27
25
|
kept = {};
|
|
28
26
|
for (const key of this.names) {
|
|
29
27
|
const v = pose[key];
|
|
@@ -32,9 +30,14 @@ export function keep(subject, pose, out) {
|
|
|
32
30
|
}
|
|
33
31
|
}
|
|
34
32
|
if (kept !== undefined) {
|
|
35
|
-
const rec = this.
|
|
33
|
+
const rec = this.lastPoses.get(subject);
|
|
36
34
|
if (rec === undefined)
|
|
37
|
-
this.
|
|
35
|
+
this.lastPoses.set(subject, {
|
|
36
|
+
pose: kept,
|
|
37
|
+
at: this.now,
|
|
38
|
+
prev: undefined,
|
|
39
|
+
prevAt: Number.NaN,
|
|
40
|
+
});
|
|
38
41
|
else if (rec.at === this.now)
|
|
39
42
|
rec.pose = kept;
|
|
40
43
|
else {
|
|
@@ -50,7 +53,7 @@ export function pull(mix, subjects, into) {
|
|
|
50
53
|
const lanes = mix.lanes;
|
|
51
54
|
const now = mix.now;
|
|
52
55
|
if (mix.scratch === undefined)
|
|
53
|
-
mix.scratch =
|
|
56
|
+
mix.scratch = mix.pose();
|
|
54
57
|
let room = Number.POSITIVE_INFINITY;
|
|
55
58
|
let tightest = '';
|
|
56
59
|
for (const c of columns) {
|