@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.
- package/CHANGELOG.md +198 -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 +37 -14
- 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.d.ts +2 -1
- package/dist/columns.js +62 -38
- 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 +16 -6
- package/dist/crowd.js +20 -11
- 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/delay.d.ts +3 -0
- package/dist/delay.js +7 -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 +114 -34
- package/dist/easing.d.ts +6 -4
- package/dist/easing.js +7 -3
- package/dist/everyone.d.ts +8 -2
- package/dist/everyone.js +33 -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 +52 -20
- 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/held.js +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 +38 -11
- package/dist/lane.js +46 -11
- package/dist/lanes.d.ts +30 -12
- package/dist/lanes.js +56 -36
- 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 +95 -7
- package/dist/mixer.d.ts +56 -37
- package/dist/mixer.js +57 -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 +6 -2
- package/dist/named.js +49 -3
- package/dist/numbers.d.ts +7 -0
- package/dist/numbers.js +33 -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 +22 -9
- package/dist/qualify.js +93 -31
- 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/renumber.d.ts +10 -0
- package/dist/renumber.js +75 -0
- 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 +28 -20
- package/dist/sample.d.ts +2 -2
- package/dist/sample.js +33 -7
- package/dist/seat.d.ts +17 -0
- package/dist/seat.js +25 -0
- 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 +23 -15
- 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/trace.d.ts +4 -0
- package/dist/trace.js +255 -0
- 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 +237 -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/trace.d.ts +51 -0
- package/dist/types/trace.js +1 -0
- package/dist/types/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +144 -73
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +2 -0
- package/dist/unreached.d.ts +2 -0
- package/dist/unreached.js +7 -0
- package/dist/voice.d.ts +10 -143
- package/dist/voice.js +37 -82
- 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 +223 -34
- package/package.json +2 -2
package/dist/renumber.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { resight } from './everyone.js';
|
|
2
|
+
import { goneIndex } from './gone.js';
|
|
3
|
+
import { reunreach } from './unreached.js';
|
|
4
|
+
/**
|
|
5
|
+
* Gives a subject the free number `to` in place of its own, carrying everything kept under the old
|
|
6
|
+
* one, so `pull` can keep a list's numbers in its order: a list kept dense by swap-remove moves its
|
|
7
|
+
* last subject into the place a dropped one left, whose number the drop just freed. Says whether it
|
|
8
|
+
* did; it does not where some store it would have to carry is in use: while a fill runs, under
|
|
9
|
+
* history, in a read ahead, or with roles, a bound or anything owed on the lanes.
|
|
10
|
+
*/
|
|
11
|
+
export function renumber(mix, subject, head, to) {
|
|
12
|
+
const lanes = mix.lanes;
|
|
13
|
+
const from = head.slot;
|
|
14
|
+
if (from < 0 ||
|
|
15
|
+
lanes.filling ||
|
|
16
|
+
lanes.host.keeps ||
|
|
17
|
+
mix.projecting ||
|
|
18
|
+
lanes.roles.length > 0 ||
|
|
19
|
+
lanes.owed.any() ||
|
|
20
|
+
lanes.lanes.some((lane) => lane.voice.bound !== null) ||
|
|
21
|
+
!lanes.numbers.move(from, to))
|
|
22
|
+
return false;
|
|
23
|
+
for (const lane of lanes.lanes)
|
|
24
|
+
lane.renumber(from, to);
|
|
25
|
+
const placed = lanes.placed;
|
|
26
|
+
for (const id of placed.all(from)) {
|
|
27
|
+
placed.remove(from, id);
|
|
28
|
+
const s = lanes.seats.get(id);
|
|
29
|
+
if (s === undefined || s.crowd.list[s.row] !== from)
|
|
30
|
+
continue;
|
|
31
|
+
s.crowd.list[s.row] = to;
|
|
32
|
+
placed.add(to, id);
|
|
33
|
+
}
|
|
34
|
+
const per = lanes.per;
|
|
35
|
+
per.copyWithin(to * 6 /* Per.SLOT */, from * 6 /* Per.SLOT */, (from + 1) * 6 /* Per.SLOT */);
|
|
36
|
+
per[from * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = lanes.fills;
|
|
37
|
+
lanes.unlinked[to] = lanes.unlinked[from];
|
|
38
|
+
lanes.subjects[to] = lanes.subjects[from];
|
|
39
|
+
lanes.subjects[from] = undefined;
|
|
40
|
+
for (const ch of lanes.laned)
|
|
41
|
+
ch.values.copyWithin(to * ch.axes, from * ch.axes, (from + 1) * ch.axes);
|
|
42
|
+
mix.places?.forgot(from);
|
|
43
|
+
mix.places?.number(subject, to);
|
|
44
|
+
const cap = lanes.cap;
|
|
45
|
+
for (const voice of mix.general) {
|
|
46
|
+
reunreach(voice, from, to);
|
|
47
|
+
resight(voice, from, to, cap);
|
|
48
|
+
reblend(mix, voice, from, to);
|
|
49
|
+
}
|
|
50
|
+
for (const voice of goneIndex(mix).general)
|
|
51
|
+
reunreach(voice, from, to);
|
|
52
|
+
for (let held = head; held !== null; held = held.next)
|
|
53
|
+
if (held.voice !== null)
|
|
54
|
+
reblend(mix, held.voice, from, to);
|
|
55
|
+
head.slot = to;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
/** Moves a blend's read for the subject this frame, if it has made one, to its new number. */
|
|
59
|
+
function reblend(mix, voice, from, to) {
|
|
60
|
+
const of = voice.blend?.of;
|
|
61
|
+
if (of === undefined || from >= of.frames.length || of.frames[from] !== mix.frame)
|
|
62
|
+
return;
|
|
63
|
+
if (to >= of.frames.length) {
|
|
64
|
+
const size = Math.max(to + 1, of.frames.length * 2, 64);
|
|
65
|
+
const frames = new Float64Array(size);
|
|
66
|
+
const values = new Float64Array(size);
|
|
67
|
+
frames.set(of.frames);
|
|
68
|
+
values.set(of.values);
|
|
69
|
+
of.frames = frames;
|
|
70
|
+
of.values = values;
|
|
71
|
+
}
|
|
72
|
+
of.frames[to] = mix.frame;
|
|
73
|
+
of.values[to] = of.values[from];
|
|
74
|
+
of.frames[from] = Number.NaN;
|
|
75
|
+
}
|
package/dist/restored.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { Cut } from './cut.js';
|
|
1
2
|
import type { Leavings } from './leavings.js';
|
|
2
3
|
import { Store } from './store.js';
|
|
3
|
-
import type {
|
|
4
|
-
import type { Subject } from './voice.js';
|
|
4
|
+
import type { Subject } from './subject.js';
|
|
5
5
|
/**
|
|
6
6
|
* The seeks back after the one a record was last put back, written or deleted at, which every
|
|
7
7
|
* record stamped then shares. The next seek fills it; a later seek to as early a moment joins it
|
package/dist/restored.js
CHANGED
|
@@ -138,7 +138,7 @@ export class Restored extends Store {
|
|
|
138
138
|
}
|
|
139
139
|
/** `get`, given what `own` holds for `key`. */
|
|
140
140
|
from(key, own) {
|
|
141
|
-
const mark = own
|
|
141
|
+
const mark = own?.reaches;
|
|
142
142
|
const at = (mark ? own.restored : this.at.get(key)) ?? this.first;
|
|
143
143
|
const now = this.stamp;
|
|
144
144
|
if (at === now)
|
package/dist/rests.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Voice } from './voice.js';
|
|
3
|
+
export declare function atRest<I, O>(this: Mixer<I, O>, subject: I): boolean;
|
|
4
|
+
export declare function rests<I, O>(this: Mixer<I, O>, pose: O): boolean;
|
|
5
|
+
export declare function restful<I, O>(this: Mixer<I, O>): boolean;
|
|
6
|
+
/** Whether a voice will change no pose from `now` on, short of a change made to it. */
|
|
7
|
+
export declare function still<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, now: number): boolean;
|
|
8
|
+
export declare function restsLaned<I, O>(this: Mixer<I, O>): boolean;
|
|
9
|
+
export declare function restsKey<I, O>(this: Mixer<I, O>): number;
|
package/dist/rests.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { near } from './fold.js';
|
|
2
|
+
import { Store } from './store.js';
|
|
3
|
+
export function atRest(subject) {
|
|
4
|
+
this.restStamps ??= new Store();
|
|
5
|
+
const head = this.linked(subject);
|
|
6
|
+
const laned = this.linkedLaned;
|
|
7
|
+
const lanes = this.lanes;
|
|
8
|
+
// Every voice on lanes and nothing to clamp: the pose a fold would make is the lanes' values.
|
|
9
|
+
if (laned && this.restsLaned()) {
|
|
10
|
+
const slot = head.slot;
|
|
11
|
+
if (!lanes.owes(slot))
|
|
12
|
+
return lanes.rests(slot);
|
|
13
|
+
}
|
|
14
|
+
// A probe this frame answers for the pose it gave the host.
|
|
15
|
+
const since = (this.restStamps.get(subject) ?? Number.NaN) - 2 * this.restsKey();
|
|
16
|
+
if (since >= 0)
|
|
17
|
+
return since === 1;
|
|
18
|
+
this.restScratch ??= this.pose();
|
|
19
|
+
const pose = this.foldWith(subject, this.restScratch, head, laned, true);
|
|
20
|
+
return this.rests((this.bounded.length === 0 ? pose : this.clamp(pose)));
|
|
21
|
+
}
|
|
22
|
+
export function rests(pose) {
|
|
23
|
+
for (let i = 0; i < this.names.length; i++) {
|
|
24
|
+
const channel = this.channels[i];
|
|
25
|
+
const value = pose[this.names[i]];
|
|
26
|
+
if (channel.rest === undefined) {
|
|
27
|
+
if (value !== undefined)
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
else if (!near(value, channel.rest))
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
export function restful() {
|
|
36
|
+
// Standing still, a voice changes no pose but by its weight signal, or by an anchor or start
|
|
37
|
+
// that host time may yet reach.
|
|
38
|
+
if (this.pace?.stopped(this.u))
|
|
39
|
+
return this.cued.every((v) => v.state === 'done' ||
|
|
40
|
+
(v.state !== 'pending' &&
|
|
41
|
+
typeof v.spec.weight !== 'function' &&
|
|
42
|
+
v.spec.anchor === undefined));
|
|
43
|
+
return this.cued.every((v) => this.still(v, this.now));
|
|
44
|
+
}
|
|
45
|
+
/** Whether a voice will change no pose from `now` on, short of a change made to it. */
|
|
46
|
+
export function still(voice, now) {
|
|
47
|
+
if (voice.state === 'done')
|
|
48
|
+
return true;
|
|
49
|
+
if (voice.state !== 'frozen' && voice.state !== 'live')
|
|
50
|
+
return false;
|
|
51
|
+
if (typeof voice.spec.weight === 'function' || voice.parts !== null)
|
|
52
|
+
return false;
|
|
53
|
+
const fadeIn = this.reducedNow ? 0 : (voice.fade.in ?? 0);
|
|
54
|
+
if (fadeIn > 0 && now - this.sinceOf(voice, voice.latest) < fadeIn)
|
|
55
|
+
return false;
|
|
56
|
+
if (voice.state === 'frozen' || voice.holding !== null)
|
|
57
|
+
return true;
|
|
58
|
+
const motion = voice.motion;
|
|
59
|
+
return motion !== undefined && voice.elapsedAt(now) >= voice.latest && motion.landed();
|
|
60
|
+
}
|
|
61
|
+
export function restsLaned() {
|
|
62
|
+
return this.lanes.whole && this.bounded.length === 0;
|
|
63
|
+
}
|
|
64
|
+
export function restsKey() {
|
|
65
|
+
if (this.frame !== this.restsFrame ||
|
|
66
|
+
this.version !== this.restsVersion ||
|
|
67
|
+
this.relinks !== this.restsRelinks ||
|
|
68
|
+
this.stirs !== this.restsStirs) {
|
|
69
|
+
this.restsEpoch++;
|
|
70
|
+
this.restsFrame = this.frame;
|
|
71
|
+
this.restsVersion = this.version;
|
|
72
|
+
this.restsRelinks = this.relinks;
|
|
73
|
+
this.restsStirs = this.stirs;
|
|
74
|
+
}
|
|
75
|
+
return this.restsEpoch;
|
|
76
|
+
}
|
package/dist/revive.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Mixer } from './mixer.js';
|
|
2
|
-
import type { Run } from './motions.js';
|
|
3
2
|
import type { Past } from './origin.js';
|
|
4
3
|
import { type PackedRecord } from './pack.js';
|
|
5
4
|
import { type Key } from './paging.js';
|
|
5
|
+
import type { Run } from './stretch.js';
|
|
6
|
+
import type { Parted, Parting } from './subject.js';
|
|
6
7
|
import type { Described } from './types.js';
|
|
7
|
-
import { type Controls, type
|
|
8
|
+
import { type Controls, type Ramp, type Rise, Voice } from './voice.js';
|
|
8
9
|
/** A voice that has left, as plain data: the descriptor it was cued with, its controls and its records. */
|
|
9
10
|
interface PackedVoice {
|
|
10
11
|
as: Described;
|
package/dist/revive.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { playable } from './cue.js';
|
|
1
|
+
import { playable, roleOf } from './cue.js';
|
|
2
2
|
import { VoiceHandle } from './handle.js';
|
|
3
3
|
import { handle } from './hosts.js';
|
|
4
4
|
import { Leavings } from './leavings.js';
|
|
@@ -120,7 +120,7 @@ export function reviveVoice(mix, id, d) {
|
|
|
120
120
|
const revive = mix.opts.history?.revive;
|
|
121
121
|
if (revive === undefined)
|
|
122
122
|
throw new Error('blits: a paged voice needs history.revive');
|
|
123
|
-
const spec = { ...revive(d.as), as: d.as };
|
|
123
|
+
const spec = roleOf(mix, { ...revive(d.as), as: d.as });
|
|
124
124
|
playable(mix, spec);
|
|
125
125
|
const voice = new Voice(id, spec, spec.patch, spec.fade ?? {}, Number.NEGATIVE_INFINITY, d.start, mix.slotOf, mix.channels, mix.opts.host, mix.send, null);
|
|
126
126
|
voice.clocks = d.clocks;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Mixer } from './mixer.js';
|
|
2
|
-
import type { Subject
|
|
2
|
+
import type { Subject } from './subject.js';
|
|
3
|
+
import type { Voice } from './voice.js';
|
|
3
4
|
/**
|
|
4
|
-
* What a fold with a
|
|
5
|
-
* voice order, each a voice's own or a
|
|
6
|
-
* slot, a
|
|
5
|
+
* What a fold with a role in play gathers, kept from one fold to the next: the contributions in
|
|
6
|
+
* voice order, each a voice's own or a role's first member; every role member; and per channel
|
|
7
|
+
* slot, a role's folding value, the weight taken into it and the array it owns.
|
|
7
8
|
*/
|
|
8
|
-
export interface
|
|
9
|
+
export interface RoleScratch<I, O> {
|
|
9
10
|
n: number;
|
|
10
11
|
voices: (Voice<I, O> | null)[];
|
|
11
12
|
helds: (Subject<unknown> | null)[];
|
|
@@ -27,17 +28,17 @@ export interface LocusScratch<I, O> {
|
|
|
27
28
|
touched: number[];
|
|
28
29
|
owned: (number[] | undefined)[];
|
|
29
30
|
}
|
|
30
|
-
export declare function
|
|
31
|
+
export declare function roleScratch<I, O>(channels: number): RoleScratch<I, O>;
|
|
31
32
|
/**
|
|
32
33
|
* `value` copied into the array `k` owns for the slot, as long as the channel's rest, where a
|
|
33
34
|
* shorter value leaves the rest's own fill: what the channel's fold and lerp read past its end.
|
|
34
35
|
*/
|
|
35
|
-
export declare function own<I, O>(k:
|
|
36
|
+
export declare function own<I, O>(k: RoleScratch<I, O>, slot: number, value: number[], rest: number[]): number[];
|
|
36
37
|
/**
|
|
37
|
-
* Folds one
|
|
38
|
+
* Folds one role's members into one contribution through each channel's own `lerp`: its value per
|
|
38
39
|
* channel goes in `k.values`, the channels in `k.touched` in the order first met, and its weight
|
|
39
40
|
* is returned. A stock `last` channel has nothing to lerp, so it takes its heaviest member's value,
|
|
40
41
|
* the later cued of two as heavy. A channel folding by scale takes its value in an array of `k`'s own, lerped in
|
|
41
42
|
* place, since the fold only reads it.
|
|
42
43
|
*/
|
|
43
|
-
export declare function
|
|
44
|
+
export declare function foldRole<I, O>(this: Mixer<I, O>, k: RoleScratch<I, O>, group: number): number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { first } from './clone.js';
|
|
2
|
-
export function
|
|
2
|
+
export function roleScratch(channels) {
|
|
3
3
|
return {
|
|
4
4
|
n: 0,
|
|
5
5
|
voices: [],
|
|
@@ -36,13 +36,13 @@ export function own(k, slot, value, rest) {
|
|
|
36
36
|
return out;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Folds one
|
|
39
|
+
* Folds one role's members into one contribution through each channel's own `lerp`: its value per
|
|
40
40
|
* channel goes in `k.values`, the channels in `k.touched` in the order first met, and its weight
|
|
41
41
|
* is returned. A stock `last` channel has nothing to lerp, so it takes its heaviest member's value,
|
|
42
42
|
* the later cued of two as heavy. A channel folding by scale takes its value in an array of `k`'s own, lerped in
|
|
43
43
|
* place, since the fold only reads it.
|
|
44
44
|
*/
|
|
45
|
-
export function
|
|
45
|
+
export function foldRole(k, group) {
|
|
46
46
|
k.touched.length = 0;
|
|
47
47
|
let sum = 0;
|
|
48
48
|
for (let m = 0; m < k.m; m++)
|
package/dist/roles.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Subject } from './subject.js';
|
|
3
|
+
import type { Voice } from './voice.js';
|
|
4
|
+
/**
|
|
5
|
+
* Whether a fold's next voice is its chain's next record, `held`, and not the next voice sharing
|
|
6
|
+
* one record, `sharers[q]`: the two lists are each in voice order, and a fold takes them as one.
|
|
7
|
+
*/
|
|
8
|
+
export declare function chainFirst<I, O>(held: Subject<unknown> | null, sharers: readonly Voice<I, O>[], q: number): boolean;
|
|
9
|
+
/** `foldWith`'s voices with a role in play: each role folds at its first member's place. */
|
|
10
|
+
export declare function foldRoles<I, O>(this: Mixer<I, O>, subject: I, pose: Record<string, unknown>, head: Subject<unknown>, laned: boolean, dry: boolean, except: number | undefined, owed: number): O;
|
package/dist/roles.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { first } from './clone.js';
|
|
2
|
+
import { open as openRecord } from './everyone.js';
|
|
3
|
+
import { merged, unband } from './fold.js';
|
|
4
|
+
import { roleScratch } from './role.js';
|
|
5
|
+
// V8 reads a local const as a constant, where it reads an import from its module on every call.
|
|
6
|
+
const open = openRecord;
|
|
7
|
+
/**
|
|
8
|
+
* Whether a fold's next voice is its chain's next record, `held`, and not the next voice sharing
|
|
9
|
+
* one record, `sharers[q]`: the two lists are each in voice order, and a fold takes them as one.
|
|
10
|
+
*/
|
|
11
|
+
export function chainFirst(held, sharers, q) {
|
|
12
|
+
if (held === null)
|
|
13
|
+
return false;
|
|
14
|
+
if (q >= sharers.length || held.voice === null)
|
|
15
|
+
return true;
|
|
16
|
+
const next = sharers[q];
|
|
17
|
+
return next.id > held.voice.id;
|
|
18
|
+
}
|
|
19
|
+
/** `foldWith`'s voices with a role in play: each role folds at its first member's place. */
|
|
20
|
+
export function foldRoles(subject, pose, head, laned, dry, except, owed) {
|
|
21
|
+
const now = this.now;
|
|
22
|
+
// A patch reading the pose can fold again from inside this one, so each depth has its own.
|
|
23
|
+
let k = this.roleScratch[this.roleDepth];
|
|
24
|
+
if (k === undefined) {
|
|
25
|
+
k = roleScratch(this.channels.length);
|
|
26
|
+
this.roleScratch[this.roleDepth] = k;
|
|
27
|
+
}
|
|
28
|
+
this.roleDepth++;
|
|
29
|
+
try {
|
|
30
|
+
k.n = 0;
|
|
31
|
+
k.m = 0;
|
|
32
|
+
k.names.length = 0;
|
|
33
|
+
const sharers = this.sharers;
|
|
34
|
+
let q = 0;
|
|
35
|
+
let at = head;
|
|
36
|
+
while (at !== null || q < sharers.length) {
|
|
37
|
+
const mine = chainFirst(at, sharers, q);
|
|
38
|
+
let voice;
|
|
39
|
+
let rec;
|
|
40
|
+
if (mine) {
|
|
41
|
+
rec = at;
|
|
42
|
+
voice = rec.voice;
|
|
43
|
+
at = rec.next;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
voice = sharers[q++];
|
|
47
|
+
rec = voice.state === 'pending' ? null : voice.everyone;
|
|
48
|
+
}
|
|
49
|
+
if (voice === null || rec === null)
|
|
50
|
+
continue;
|
|
51
|
+
if (laned && voice.laned && !this.owedBy(owed, voice))
|
|
52
|
+
continue;
|
|
53
|
+
if (voice.id === except || voice.state === 'done')
|
|
54
|
+
continue;
|
|
55
|
+
const held = rec;
|
|
56
|
+
if (!mine)
|
|
57
|
+
open(voice, held, head.slot);
|
|
58
|
+
const delta = this.read(voice, subject, now, dry, held);
|
|
59
|
+
if (owed >= 0 && voice.laned)
|
|
60
|
+
this.lanes.paid(voice.id, owed, held.weight);
|
|
61
|
+
if (!mine)
|
|
62
|
+
q = this.shared(voice, head.slot, held, q);
|
|
63
|
+
if (delta === null)
|
|
64
|
+
continue;
|
|
65
|
+
const role = voice.spec.role;
|
|
66
|
+
let group = -1;
|
|
67
|
+
if (role !== undefined) {
|
|
68
|
+
group = k.names.indexOf(role);
|
|
69
|
+
const m = k.m++;
|
|
70
|
+
k.mVoices[m] = voice;
|
|
71
|
+
k.mDeltas[m] = delta;
|
|
72
|
+
k.mWeights[m] = this.w;
|
|
73
|
+
if (group >= 0) {
|
|
74
|
+
k.mGroups[m] = group;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
group = k.names.length;
|
|
78
|
+
k.names.push(role);
|
|
79
|
+
k.mGroups[m] = group;
|
|
80
|
+
}
|
|
81
|
+
const n = k.n++;
|
|
82
|
+
k.voices[n] = voice;
|
|
83
|
+
k.helds[n] = held;
|
|
84
|
+
k.deltas[n] = delta;
|
|
85
|
+
k.weights[n] = role === undefined ? this.w : 0;
|
|
86
|
+
k.groups[n] = group;
|
|
87
|
+
}
|
|
88
|
+
let bands = head.roles;
|
|
89
|
+
if (bands === null) {
|
|
90
|
+
bands = new Map();
|
|
91
|
+
head.roles = bands;
|
|
92
|
+
}
|
|
93
|
+
for (let n = 0; n < k.n; n++) {
|
|
94
|
+
const group = k.groups[n];
|
|
95
|
+
if (group < 0) {
|
|
96
|
+
const weight = k.weights[n];
|
|
97
|
+
if (weight > 0)
|
|
98
|
+
this.apply(pose, k.voices[n], k.helds[n], k.deltas[n], weight);
|
|
99
|
+
else
|
|
100
|
+
unband(k.helds[n]);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const weight = this.foldRole(k, group);
|
|
104
|
+
const name = k.names[group];
|
|
105
|
+
if (!(weight > 0)) {
|
|
106
|
+
bands.get(name)?.fill(2);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
let on = bands.get(name);
|
|
110
|
+
if (on === undefined) {
|
|
111
|
+
on = new Uint8Array(this.channels.length);
|
|
112
|
+
bands.set(name, on);
|
|
113
|
+
}
|
|
114
|
+
for (let t = 0; t < k.touched.length; t++) {
|
|
115
|
+
const slot = k.touched[t];
|
|
116
|
+
const value = k.values[slot];
|
|
117
|
+
k.values[slot] = undefined;
|
|
118
|
+
const key = this.names[slot];
|
|
119
|
+
const channel = this.channels[slot];
|
|
120
|
+
// A channel folds at the weight of the members that wrote it, not the whole role.
|
|
121
|
+
const taken = k.taken[slot];
|
|
122
|
+
const w = taken > 1 ? 1 : taken;
|
|
123
|
+
if (channel.rest !== undefined && channel.scale) {
|
|
124
|
+
// pose[key] is the copy of rest this fold made, so it is ours to write into.
|
|
125
|
+
pose[key] = channel.fold
|
|
126
|
+
? channel.fold(pose[key], value, w)
|
|
127
|
+
: merged(channel, pose[key], channel.scale(value, w));
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const band = on[slot];
|
|
131
|
+
const passes = this.passes(band === 0 ? undefined : band === 1, w);
|
|
132
|
+
on[slot] = passes ? 1 : 2;
|
|
133
|
+
if (passes)
|
|
134
|
+
pose[key] = pose[key] === undefined ? first(value) : merged(channel, pose[key], value);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return pose;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
for (let n = 0; n < k.n; n++) {
|
|
141
|
+
k.voices[n] = null;
|
|
142
|
+
k.helds[n] = null;
|
|
143
|
+
k.deltas[n] = null;
|
|
144
|
+
}
|
|
145
|
+
for (let m = 0; m < k.m; m++) {
|
|
146
|
+
k.mVoices[m] = null;
|
|
147
|
+
k.mDeltas[m] = null;
|
|
148
|
+
}
|
|
149
|
+
// Left set only where a channel's lerp threw partway, which the next fold must not inherit.
|
|
150
|
+
for (let t = 0; t < k.touched.length; t++) {
|
|
151
|
+
const slot = k.touched[t];
|
|
152
|
+
k.met[slot] = 0;
|
|
153
|
+
k.values[slot] = undefined;
|
|
154
|
+
}
|
|
155
|
+
k.touched.length = 0;
|
|
156
|
+
this.roleDepth--;
|
|
157
|
+
}
|
|
158
|
+
}
|
package/dist/roster.js
CHANGED
|
@@ -2,8 +2,8 @@ import { index, unindex } from './chain.js';
|
|
|
2
2
|
import { detour } from './everyone.js';
|
|
3
3
|
import { Store } from './store.js';
|
|
4
4
|
function tally(mix, voice, by) {
|
|
5
|
-
if (voice.spec.
|
|
6
|
-
mix.
|
|
5
|
+
if (voice.spec.role !== undefined)
|
|
6
|
+
mix.roleVoices += by;
|
|
7
7
|
const e = voice.spec.earliest;
|
|
8
8
|
if (voice.spec.anchor !== undefined || (e !== undefined && typeof e !== 'number'))
|
|
9
9
|
mix.anchored += by;
|
|
@@ -36,7 +36,7 @@ export function recount(mix) {
|
|
|
36
36
|
mix.naming = 0;
|
|
37
37
|
mix.general = [];
|
|
38
38
|
mix.sharers = [];
|
|
39
|
-
mix.
|
|
39
|
+
mix.roleVoices = 0;
|
|
40
40
|
mix.anchored = 0;
|
|
41
41
|
for (const voice of mix.cued) {
|
|
42
42
|
index(mix, voice);
|
package/dist/rotation.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface AngleOptions {
|
|
|
10
10
|
turn?: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* A rotation about one axis
|
|
13
|
+
* A rotation about one axis that interpolates the short way round. It adds as `sum` does, and from
|
|
14
14
|
* 350 to 10 it passes 0, not 180. A weight below 1 fades it the short way to rest too. Its values
|
|
15
15
|
* are angles, so two a whole number of turns apart are the same value, and a fold may answer with
|
|
16
16
|
* either.
|
package/dist/rotation.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { doubles, vouch } from './channels.js';
|
|
2
2
|
import { shortWay } from './turns.js';
|
|
3
3
|
/**
|
|
4
|
-
* A rotation about one axis
|
|
4
|
+
* A rotation about one axis that interpolates the short way round. It adds as `sum` does, and from
|
|
5
5
|
* 350 to 10 it passes 0, not 180. A weight below 1 fades it the short way to rest too. Its values
|
|
6
6
|
* are angles, so two a whole number of turns apart are the same value, and a fold may answer with
|
|
7
7
|
* either.
|
package/dist/rows.js
CHANGED
|
@@ -4,6 +4,7 @@ import { reach } from './meet.js';
|
|
|
4
4
|
import { motionOf } from './motion.js';
|
|
5
5
|
import { open } from './qualify.js';
|
|
6
6
|
import { settle } from './sample.js';
|
|
7
|
+
import { Seat, seatOf } from './seat.js';
|
|
7
8
|
/** Whether two crowds' channels are the same list. */
|
|
8
9
|
function same(a, b) {
|
|
9
10
|
if (a.length !== b.length)
|
|
@@ -25,7 +26,7 @@ export function unplace(lanes, c, p) {
|
|
|
25
26
|
c.records[p] = undefined;
|
|
26
27
|
c.deltas[p] = null;
|
|
27
28
|
const f = p * c.stride + 0 /* Hot.FLAGS */;
|
|
28
|
-
c.hot[f] = c.hot[f] & ~16 /* Flag.PLACED
|
|
29
|
+
c.hot[f] = c.hot[f] & ~(16 /* Flag.PLACED */ | 2048 /* Flag.READY */);
|
|
29
30
|
const o = p * 8 /* Row.STRIDE */;
|
|
30
31
|
c.data[o + 4 /* Row.MSLOT */] = -1;
|
|
31
32
|
c.data[o + 5 /* Row.SAMPLED */] = -1;
|
|
@@ -38,14 +39,13 @@ export function unplace(lanes, c, p) {
|
|
|
38
39
|
export function sameCrowds(lanes, members) {
|
|
39
40
|
let live = 0;
|
|
40
41
|
for (const v of members) {
|
|
41
|
-
if (!lanes.
|
|
42
|
+
if (!lanes.seats.has(v.id))
|
|
42
43
|
return false;
|
|
43
44
|
live++;
|
|
44
45
|
}
|
|
45
46
|
let rows = 0;
|
|
46
|
-
for (const
|
|
47
|
-
|
|
48
|
-
if (c.voices[p].state !== 'done')
|
|
47
|
+
for (const s of lanes.seats.values()) {
|
|
48
|
+
if (s.crowd.voices[s.row].state !== 'done')
|
|
49
49
|
rows++;
|
|
50
50
|
}
|
|
51
51
|
return rows === live;
|
|
@@ -64,13 +64,15 @@ export function regroup(lanes, members) {
|
|
|
64
64
|
leaveCrowd(lanes, c, p);
|
|
65
65
|
}
|
|
66
66
|
const old = lanes.crowds;
|
|
67
|
-
const was = new Map(lanes.
|
|
67
|
+
const was = new Map(lanes.seats);
|
|
68
|
+
for (const s of was.values())
|
|
69
|
+
s.lanes = null;
|
|
68
70
|
lanes.crowds = [];
|
|
69
|
-
lanes.
|
|
71
|
+
lanes.seats.clear();
|
|
70
72
|
lanes.overlap = false;
|
|
71
73
|
for (const v of members) {
|
|
72
74
|
const from = was.get(v.id);
|
|
73
|
-
join(lanes, v, from, from?.
|
|
75
|
+
join(lanes, v, from?.crowd, from?.row, old);
|
|
74
76
|
}
|
|
75
77
|
if (lanes.crowds.length === 0) {
|
|
76
78
|
lanes.lawsByKey.clear();
|
|
@@ -104,8 +106,9 @@ export function join(lanes, v, from, at, old) {
|
|
|
104
106
|
newRow(lanes, c, p, v);
|
|
105
107
|
if (c.hot[p * c.stride + 6 /* Hot.EPOCH */] === 0 && v.state !== 'pending')
|
|
106
108
|
c.hot[p * c.stride + 6 /* Hot.EPOCH */] = open(lanes, v);
|
|
107
|
-
|
|
108
|
-
lanes.
|
|
109
|
+
const seat = new Seat(lanes, c, p);
|
|
110
|
+
lanes.seats.set(v.id, seat);
|
|
111
|
+
v.seat = seat;
|
|
109
112
|
v.laned = true;
|
|
110
113
|
}
|
|
111
114
|
/** A crowd row takes up its voice as it now stands: opened once it starts, its fields copied again. */
|
|
@@ -122,27 +125,28 @@ export function retouchRow(lanes, c, p, v) {
|
|
|
122
125
|
export function compactSparse(lanes) {
|
|
123
126
|
for (const c of lanes.crowds)
|
|
124
127
|
if (c.dead > 64 && c.dead * 4 >= c.size)
|
|
125
|
-
compact(c);
|
|
128
|
+
compact(lanes, c);
|
|
126
129
|
}
|
|
127
130
|
/**
|
|
128
131
|
* Slides a crowd's rows down over the empty ones its departed voices left, in order, so its rows
|
|
129
132
|
* stay in voice order; a qualify did this by rebuilding every crowd, a dropped frame at 10k rows.
|
|
130
133
|
*/
|
|
131
|
-
function compact(c) {
|
|
134
|
+
function compact(lanes, c) {
|
|
132
135
|
// The rows' stops are copied into a fresh pool, leaving the departed rows' behind.
|
|
133
136
|
const keys = c.keys;
|
|
134
137
|
c.keys = new KeyRows();
|
|
135
138
|
let q = 0;
|
|
136
139
|
for (let p = 0; p < c.size; p++) {
|
|
137
140
|
const v = c.voices[p];
|
|
138
|
-
|
|
141
|
+
const s = seatOf(lanes, v);
|
|
142
|
+
if (s === null || s.crowd !== c || s.row !== p) {
|
|
139
143
|
c.odd?.delete(p);
|
|
140
144
|
continue;
|
|
141
145
|
}
|
|
142
146
|
if (q !== p) {
|
|
143
147
|
copyRow(c, p, c, q, keys);
|
|
144
148
|
c.odd?.delete(p);
|
|
145
|
-
|
|
149
|
+
s.row = q;
|
|
146
150
|
}
|
|
147
151
|
else
|
|
148
152
|
copyKeys(keys, c, q);
|
|
@@ -166,8 +170,12 @@ export function bury(lanes, c, p) {
|
|
|
166
170
|
c.deltas[p] = null;
|
|
167
171
|
c.motions[p] = undefined;
|
|
168
172
|
c.hot[p * c.stride + 0 /* Hot.FLAGS */] = 0;
|
|
169
|
-
|
|
170
|
-
|
|
173
|
+
lanes.seats.delete(v.id);
|
|
174
|
+
const s = v.seat;
|
|
175
|
+
if (s !== null && s.lanes === lanes) {
|
|
176
|
+
s.lanes = null;
|
|
177
|
+
v.seat = null;
|
|
178
|
+
}
|
|
171
179
|
c.dead++;
|
|
172
180
|
}
|
|
173
181
|
/** Copies `n` numbers of a row; a view of them to hand `set` was three objects a row moved. */
|
|
@@ -202,7 +210,7 @@ function copyRow(from, p, to, q, keys = from.keys) {
|
|
|
202
210
|
function copyKeys(keys, c, q) {
|
|
203
211
|
const h = q * c.stride;
|
|
204
212
|
if ((c.hot[h + 0 /* Hot.FLAGS */] & 512 /* Flag.FLAT */) !== 0)
|
|
205
|
-
c.hot[h +
|
|
213
|
+
c.hot[h + 9 /* Hot.AT */] = c.keys.copy(keys, c.hot[h + 9 /* Hot.AT */]);
|
|
206
214
|
}
|
|
207
215
|
function newRow(lanes, c, p, v) {
|
|
208
216
|
const run = motionOf(v.patch);
|
|
@@ -210,9 +218,9 @@ function newRow(lanes, c, p, v) {
|
|
|
210
218
|
c.hot.fill(0, h, h + c.stride);
|
|
211
219
|
const at = v.built === null ? -1 : c.keys.add(v.built, c.chans);
|
|
212
220
|
if (at >= 0) {
|
|
213
|
-
c.hot[h +
|
|
214
|
-
c.hot[h +
|
|
215
|
-
c.hot[h +
|
|
221
|
+
c.hot[h + 9 /* Hot.AT */] = at;
|
|
222
|
+
c.hot[h + 11 /* Hot.X0 */] = v.duration;
|
|
223
|
+
c.hot[h + 10 /* Hot.MS */] = v.passes;
|
|
216
224
|
}
|
|
217
225
|
c.restale(p, 64 /* Flag.VOICE */ |
|
|
218
226
|
(run === undefined ? 0 : 128 /* Flag.MOTION */) |
|
package/dist/sample.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Lane, type Positions } from './lane.js';
|
|
2
2
|
import type { Lanes } from './lanes.js';
|
|
3
|
-
import type
|
|
4
|
-
import type { Subject } from './
|
|
3
|
+
import { type Motions } from './motions.js';
|
|
4
|
+
import type { Subject } from './subject.js';
|
|
5
5
|
/**
|
|
6
6
|
* A motion voice's fill: what `one` and `move` do for each subject, in one loop for the common
|
|
7
7
|
* case, a subject not yet probed this frame with nothing pending, and through `move` for the rest.
|