@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/changes.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { absent } from './numbers.js';
|
|
2
|
+
import { reading } from './reading.js';
|
|
3
|
+
import { numbers } from './stretch.js';
|
|
4
|
+
const PENDING = 2 /* RunFlag.PENDING */;
|
|
5
|
+
const OLDER = 4 /* RunFlag.OLDER */;
|
|
6
|
+
/** Where `applied` reads a stretch at the time a change lands; copied out before it returns. */
|
|
7
|
+
let landing = { x: new Float64Array(4), v: new Float64Array(4) };
|
|
8
|
+
/**
|
|
9
|
+
* Queues a retarget, set or push, untimed at the latest frame, for `subject`'s next read past its time.
|
|
10
|
+
* The queue keeps the changes with a time in time order, ahead of any still waiting for one.
|
|
11
|
+
*/
|
|
12
|
+
export function change(subject, c) {
|
|
13
|
+
const back = this.owner === null ? Number.NaN : this.owner.revive(this.ownerId, subject);
|
|
14
|
+
const s = this.slot(subject);
|
|
15
|
+
this.check(c.to, this.n);
|
|
16
|
+
this.check(c.x, this.n);
|
|
17
|
+
this.check(c.v, this.n);
|
|
18
|
+
c.made = this.owner === null ? Number.NaN : this.owner.now();
|
|
19
|
+
c.seq = this.owner === null ? Number.NaN : this.owner.seq();
|
|
20
|
+
c.early = this.owner?.early() === true;
|
|
21
|
+
if (c.at === undefined) {
|
|
22
|
+
const at = this.frame(subject);
|
|
23
|
+
if (!Number.isNaN(at))
|
|
24
|
+
c.at = at;
|
|
25
|
+
else if (!Number.isNaN(back))
|
|
26
|
+
c.at = back;
|
|
27
|
+
}
|
|
28
|
+
const list = this.pending?.[s];
|
|
29
|
+
if (list === undefined) {
|
|
30
|
+
if (this.pending === null)
|
|
31
|
+
this.pending = [undefined];
|
|
32
|
+
this.pending[s] = [c];
|
|
33
|
+
this.flag(s, PENDING, true);
|
|
34
|
+
}
|
|
35
|
+
else if (c.at === undefined)
|
|
36
|
+
list.push(c);
|
|
37
|
+
else
|
|
38
|
+
this.queue(list, c, c.at);
|
|
39
|
+
reading.moved++;
|
|
40
|
+
const made = { at: c.at, to: c.to, x: c.x, v: c.v };
|
|
41
|
+
this.owner?.changed(this.ownerId, this, subject, made);
|
|
42
|
+
}
|
|
43
|
+
/** Files a change with a time after every one due by then, ahead of those still waiting for one. */
|
|
44
|
+
export function queue(list, c, at) {
|
|
45
|
+
let i = list.length;
|
|
46
|
+
while (i > 0) {
|
|
47
|
+
const before = list[i - 1].at;
|
|
48
|
+
if (before !== undefined && before <= at)
|
|
49
|
+
break;
|
|
50
|
+
i--;
|
|
51
|
+
}
|
|
52
|
+
list.splice(i, 0, c);
|
|
53
|
+
}
|
|
54
|
+
/** Every change due by `t` applied to a copy of the stretch playing then, committing nothing. */
|
|
55
|
+
export function peek(s, t, xo, vo) {
|
|
56
|
+
let seg = this.playing(s, t);
|
|
57
|
+
const list = this.dueTo(s);
|
|
58
|
+
if (list !== undefined)
|
|
59
|
+
for (const change of list) {
|
|
60
|
+
const a = change.at;
|
|
61
|
+
if (a === undefined || !(a <= t))
|
|
62
|
+
break;
|
|
63
|
+
if (a >= seg.at)
|
|
64
|
+
seg = this.applied(this.numbers.subject(s), seg, change, a);
|
|
65
|
+
}
|
|
66
|
+
this.evaluateSegment(seg, t, xo, vo);
|
|
67
|
+
}
|
|
68
|
+
/** Subject `s`'s changes waiting, with those a read ahead's tape made filed among them by time. */
|
|
69
|
+
export function dueTo(s) {
|
|
70
|
+
const own = this.pending?.[s];
|
|
71
|
+
const ahead = reading.ahead?.get(this);
|
|
72
|
+
if (ahead === undefined)
|
|
73
|
+
return own;
|
|
74
|
+
const subject = this.numbers.subject(s);
|
|
75
|
+
const made = subject === absent ? undefined : ahead.get(subject);
|
|
76
|
+
if (made === undefined)
|
|
77
|
+
return own;
|
|
78
|
+
if (own === undefined)
|
|
79
|
+
return made;
|
|
80
|
+
// The patch's own were made first, and a stable sort keeps them first at one time.
|
|
81
|
+
return [...own.filter((c) => c.at !== undefined), ...made].sort((a, b) => a.at - b.at);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Takes back every change made after frame `seq`: each subject plays from its earliest stretch
|
|
85
|
+
* kept, with the changes made by then queued to be applied again.
|
|
86
|
+
*/
|
|
87
|
+
export function rewind(seq) {
|
|
88
|
+
this.unrelease(seq);
|
|
89
|
+
for (let s = 0; s < this.numbers.size; s++)
|
|
90
|
+
if (this.numbers.subject(s) !== absent)
|
|
91
|
+
this.cut(s, seq);
|
|
92
|
+
}
|
|
93
|
+
export function cut(s, seq) {
|
|
94
|
+
const older = this.older?.[s];
|
|
95
|
+
const pending = this.pending?.[s];
|
|
96
|
+
const current = this.latest(s);
|
|
97
|
+
const stretches = older === undefined ? [current] : [...older, current];
|
|
98
|
+
const kept = [];
|
|
99
|
+
let cut = false;
|
|
100
|
+
for (const c of [...stretches.slice(1).map((seg) => seg.change), ...(pending ?? [])])
|
|
101
|
+
if (c === undefined || c.seq === undefined || !(c.seq > seq)) {
|
|
102
|
+
if (c !== undefined)
|
|
103
|
+
kept.push(c);
|
|
104
|
+
}
|
|
105
|
+
else
|
|
106
|
+
cut = true;
|
|
107
|
+
if (!cut)
|
|
108
|
+
return;
|
|
109
|
+
if (older !== undefined) {
|
|
110
|
+
this.older[s] = undefined;
|
|
111
|
+
this.flag(s, OLDER, false);
|
|
112
|
+
}
|
|
113
|
+
this.write(s, stretches[0]);
|
|
114
|
+
// Applied again in the order they take effect; one never stamped waits for the next read.
|
|
115
|
+
const timed = kept
|
|
116
|
+
.filter((c) => c.at !== undefined)
|
|
117
|
+
.sort((a, b) => a.at - b.at);
|
|
118
|
+
const list = [...timed, ...kept.filter((c) => c.at === undefined)];
|
|
119
|
+
if (list.length === 0) {
|
|
120
|
+
if (pending !== undefined) {
|
|
121
|
+
this.pending[s] = undefined;
|
|
122
|
+
this.flag(s, PENDING, false);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (this.pending === null)
|
|
127
|
+
this.pending = [undefined];
|
|
128
|
+
this.pending[s] = list;
|
|
129
|
+
this.flag(s, PENDING, true);
|
|
130
|
+
}
|
|
131
|
+
/** The stretch `change` releases from `seg` at `at`, for `subject` or one no longer numbered. */
|
|
132
|
+
export function applied(subject, seg, change, at) {
|
|
133
|
+
const n = this.n;
|
|
134
|
+
if (landing.x.length < n)
|
|
135
|
+
landing = { x: new Float64Array(n), v: new Float64Array(n) };
|
|
136
|
+
const { x, v } = landing;
|
|
137
|
+
this.evaluateSegment(seg, at, x, v);
|
|
138
|
+
const xs = change.x ?? numbers(x, 0, n);
|
|
139
|
+
const vs = change.v ?? numbers(v, 0, n);
|
|
140
|
+
const known = subject === absent ? undefined : subject;
|
|
141
|
+
const put = change.x !== undefined && change.v === undefined;
|
|
142
|
+
const to = this.shape.aim(xs, vs, change.to ?? seg.to, known, put);
|
|
143
|
+
const ms = subject === absent ? seg.ms : (this.shape.ms?.(known) ?? 0);
|
|
144
|
+
return { at, ms, x0: xs, v0: vs, to, change };
|
|
145
|
+
}
|
|
146
|
+
export function commit(s, t) {
|
|
147
|
+
const list = this.pending[s];
|
|
148
|
+
// The first read of a subject no frame had met: those waiting for a time take this one.
|
|
149
|
+
if (list[list.length - 1].at === undefined) {
|
|
150
|
+
let i = list.length;
|
|
151
|
+
while (i > 0 && list[i - 1].at === undefined)
|
|
152
|
+
i--;
|
|
153
|
+
for (const c of list.splice(i)) {
|
|
154
|
+
c.at = t;
|
|
155
|
+
this.queue(list, c, t);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
while (list.length > 0) {
|
|
159
|
+
const change = list[0];
|
|
160
|
+
const a = change.at;
|
|
161
|
+
if (a > t)
|
|
162
|
+
break;
|
|
163
|
+
list.shift();
|
|
164
|
+
this.insert(s, this.applied(this.numbers.subject(s), this.playing(s, a), change, a));
|
|
165
|
+
}
|
|
166
|
+
if (list.length === 0) {
|
|
167
|
+
this.pending[s] = undefined;
|
|
168
|
+
this.flag(s, PENDING, false);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export function insert(s, seg) {
|
|
172
|
+
let list = this.older?.[s];
|
|
173
|
+
if (list === undefined) {
|
|
174
|
+
list = [];
|
|
175
|
+
if (this.older === null)
|
|
176
|
+
this.older = [undefined];
|
|
177
|
+
this.older[s] = list;
|
|
178
|
+
this.flag(s, OLDER, true);
|
|
179
|
+
}
|
|
180
|
+
if (seg.at >= this.runs[this.base(s)]) {
|
|
181
|
+
list.push(this.latest(s));
|
|
182
|
+
this.write(s, seg);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
let i = list.length;
|
|
186
|
+
while (i > 0 && list[i - 1].at > seg.at)
|
|
187
|
+
i--;
|
|
188
|
+
list.splice(i, 0, seg);
|
|
189
|
+
}
|
package/dist/channels.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export interface Numeric {
|
|
|
26
26
|
axes: number;
|
|
27
27
|
/** An angle's full turn: each value is taken the short way round before it folds. */
|
|
28
28
|
turn?: number;
|
|
29
|
+
/**
|
|
30
|
+
* An `'own'` channel whose fold is this plain arithmetic, its lerp alone its own: a lane folds
|
|
31
|
+
* it as the op and lerps it through the channel. A copy that predates it folds it as `'own'`.
|
|
32
|
+
*/
|
|
33
|
+
folds?: 'sum';
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* The stock arithmetic of a channel `sum`, `mul`, `max` or `vec` made, or `vouch` was given, keyed
|
|
@@ -55,49 +60,71 @@ export interface NumberOptions {
|
|
|
55
60
|
bounds?: readonly [min: number, max: number];
|
|
56
61
|
}
|
|
57
62
|
/**
|
|
58
|
-
*
|
|
63
|
+
* A number whose contributions add, resting at 0.
|
|
64
|
+
*
|
|
65
|
+
* A weight scales a contribution toward 0, so two voices each moving `x` by 10 at half weight move it
|
|
66
|
+
* 10 in all. For position axes, rotation, crawl, yaw, pitch, offset.
|
|
59
67
|
*
|
|
60
68
|
* @category channel
|
|
61
69
|
*/
|
|
62
70
|
export declare function sum(opts?: NumberOptions): Channel<number>;
|
|
63
71
|
/**
|
|
64
|
-
*
|
|
72
|
+
* A number whose contributions multiply, resting at 1.
|
|
73
|
+
*
|
|
74
|
+
* A weight fades a contribution toward 1 rather than 0, so 0.5 at half weight multiplies by 0.75, and
|
|
75
|
+
* at weight 0 it leaves the value alone. For gain, scale, opacity, hold.
|
|
65
76
|
*
|
|
66
77
|
* @category channel
|
|
67
78
|
*/
|
|
68
79
|
export declare function mul(opts?: NumberOptions): Channel<number>;
|
|
69
80
|
/**
|
|
70
|
-
*
|
|
81
|
+
* A number where the largest contribution wins, resting at 0.
|
|
82
|
+
*
|
|
83
|
+
* A weight scales a contribution toward 0 before the comparison, so a fading voice gives way once
|
|
84
|
+
* another's value passes its scaled one. For dark, aperture.
|
|
71
85
|
*
|
|
72
86
|
* @category channel
|
|
73
87
|
*/
|
|
74
88
|
export declare function max(opts?: NumberOptions): Channel<number>;
|
|
75
89
|
/**
|
|
76
|
-
*
|
|
90
|
+
* What `last` takes.
|
|
91
|
+
*
|
|
92
|
+
* @category channel
|
|
93
|
+
*/
|
|
94
|
+
export interface LastOptions<V> {
|
|
95
|
+
/** How to interpolate between two values. Default: step at the midpoint. */
|
|
96
|
+
lerp?: (a: V, b: V, u: number) => V;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A channel with no rest, where the last contribution to pass wins. A weight can only gate it. The
|
|
77
100
|
* default `lerp` steps at the midpoint, which is all a value with no arithmetic can promise; a
|
|
78
101
|
* consumer with a real interpolation passes its own, as `hex` does.
|
|
79
102
|
*
|
|
80
103
|
* @category channel
|
|
81
104
|
*/
|
|
82
|
-
export declare function last<V>(opts?:
|
|
83
|
-
lerp?: (a: V, b: V, u: number) => V;
|
|
84
|
-
}): Channel<V>;
|
|
105
|
+
export declare function last<V>(opts?: LastOptions<V>): Channel<V>;
|
|
85
106
|
/**
|
|
86
107
|
* `n` copies of `v` in an array the engine stores as doubles from the start, so a copy of it, and
|
|
87
108
|
* every fraction written into that copy, keeps one element kind: a pose's arrays then look alike to
|
|
88
109
|
* every fold, whichever path made them.
|
|
89
110
|
*/
|
|
90
111
|
export declare function doubles(n: number, v: number): number[];
|
|
91
|
-
type Tuple<N extends number, T extends number[] = []> = T['length'] extends N ? T : Tuple<N, [...T, number]>;
|
|
92
112
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
113
|
+
* A tuple of `N` numbers, as `NumberTuple<3>` is `[number, number, number]`. `T` is the tuple built
|
|
114
|
+
* so far; leave it out.
|
|
115
|
+
*
|
|
116
|
+
* @category channel
|
|
117
|
+
*/
|
|
118
|
+
export type NumberTuple<N extends number, T extends number[] = []> = T['length'] extends N ? T : NumberTuple<N, [...T, number]>;
|
|
119
|
+
/**
|
|
120
|
+
* `N` numbers, as a tuple where `N` is a literal. `Vec<3>` is `[number, number, number]`, and a
|
|
121
|
+
* `Vec` is `number[]` where `N` is only known to be a number.
|
|
95
122
|
*
|
|
96
123
|
* @category channel
|
|
97
124
|
*/
|
|
98
|
-
export type Vec<N extends number> = number extends N ? number[] :
|
|
125
|
+
export type Vec<N extends number> = number extends N ? number[] : NumberTuple<N>;
|
|
99
126
|
/**
|
|
100
|
-
*
|
|
127
|
+
* One channel's arithmetic applied down an axis list. For a Vec3 position, or premultiplied light.
|
|
101
128
|
*
|
|
102
129
|
* @category channel
|
|
103
130
|
*/
|
|
@@ -108,4 +135,3 @@ export declare function vec<N extends number>(n: N, of: Channel<number>): Channe
|
|
|
108
135
|
* @category channel
|
|
109
136
|
*/
|
|
110
137
|
export declare function kit<O>(channels: Kit<O>): Kit<O>;
|
|
111
|
-
export {};
|
package/dist/channels.js
CHANGED
|
@@ -67,7 +67,10 @@ export function scalarOp(channel) {
|
|
|
67
67
|
/** Bounds change how a value folds at its edges, so they are part of the kind. */
|
|
68
68
|
const kindOf = (name, bounds) => bounds === undefined ? name : `${name}[${bounds[0]}, ${bounds[1]}]`;
|
|
69
69
|
/**
|
|
70
|
-
*
|
|
70
|
+
* A number whose contributions add, resting at 0.
|
|
71
|
+
*
|
|
72
|
+
* A weight scales a contribution toward 0, so two voices each moving `x` by 10 at half weight move it
|
|
73
|
+
* 10 in all. For position axes, rotation, crawl, yaw, pitch, offset.
|
|
71
74
|
*
|
|
72
75
|
* @category channel
|
|
73
76
|
*/
|
|
@@ -84,7 +87,10 @@ export function sum(opts) {
|
|
|
84
87
|
return channel;
|
|
85
88
|
}
|
|
86
89
|
/**
|
|
87
|
-
*
|
|
90
|
+
* A number whose contributions multiply, resting at 1.
|
|
91
|
+
*
|
|
92
|
+
* A weight fades a contribution toward 1 rather than 0, so 0.5 at half weight multiplies by 0.75, and
|
|
93
|
+
* at weight 0 it leaves the value alone. For gain, scale, opacity, hold.
|
|
88
94
|
*
|
|
89
95
|
* @category channel
|
|
90
96
|
*/
|
|
@@ -101,7 +107,10 @@ export function mul(opts) {
|
|
|
101
107
|
return channel;
|
|
102
108
|
}
|
|
103
109
|
/**
|
|
104
|
-
*
|
|
110
|
+
* A number where the largest contribution wins, resting at 0.
|
|
111
|
+
*
|
|
112
|
+
* A weight scales a contribution toward 0 before the comparison, so a fading voice gives way once
|
|
113
|
+
* another's value passes its scaled one. For dark, aperture.
|
|
105
114
|
*
|
|
106
115
|
* @category channel
|
|
107
116
|
*/
|
|
@@ -118,7 +127,7 @@ export function max(opts) {
|
|
|
118
127
|
return channel;
|
|
119
128
|
}
|
|
120
129
|
/**
|
|
121
|
-
* A channel with no rest
|
|
130
|
+
* A channel with no rest, where the last contribution to pass wins. A weight can only gate it. The
|
|
122
131
|
* default `lerp` steps at the midpoint, which is all a value with no arithmetic can promise; a
|
|
123
132
|
* consumer with a real interpolation passes its own, as `hex` does.
|
|
124
133
|
*
|
|
@@ -148,7 +157,7 @@ export function doubles(n, v) {
|
|
|
148
157
|
return a;
|
|
149
158
|
}
|
|
150
159
|
/**
|
|
151
|
-
*
|
|
160
|
+
* One channel's arithmetic applied down an axis list. For a Vec3 position, or premultiplied light.
|
|
152
161
|
*
|
|
153
162
|
* @category channel
|
|
154
163
|
*/
|
package/dist/color.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Channel } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* OKLCH, hue the short way round
|
|
4
|
-
* instead of dipping through a muddy middle. A mix that leaves sRGB's
|
|
3
|
+
* Crossfades two hex colors in OKLCH, hue the short way round. A crossfade between hues then keeps
|
|
4
|
+
* its lightness and saturation instead of dipping through a muddy middle. A mix that leaves sRGB's
|
|
5
|
+
* gamut is clipped per channel.
|
|
5
6
|
*
|
|
6
7
|
* @category channel
|
|
7
8
|
*/
|
|
8
9
|
export declare function mixHex(a: number, b: number, u: number): number;
|
|
9
10
|
/**
|
|
10
|
-
* How a `color` channel interpolates two colors
|
|
11
|
+
* How a `color` channel interpolates two colors. Straight across OKLab, or in OKLCH with hue the
|
|
11
12
|
* short way round, as `mixHex` does.
|
|
12
13
|
*
|
|
13
14
|
* @category channel
|
|
@@ -21,29 +22,30 @@ export interface ColorOptions {
|
|
|
21
22
|
* `toHex` divides it out and lays it over what is under it. `color(last())` replaces, the last
|
|
22
23
|
* voice to pass winning, at the coverage it was authored with. `lerp` picks how two colors
|
|
23
24
|
* interpolate: OKLab, the default, crosses through gray between opposite hues, and `'oklch'`
|
|
24
|
-
* goes round the hue instead
|
|
25
|
+
* goes round the hue instead.
|
|
25
26
|
*
|
|
26
27
|
* @category channel
|
|
27
28
|
*/
|
|
28
29
|
export declare function color(opts?: ColorOptions): Channel<number[]>;
|
|
29
30
|
export declare function color(of: Channel<number> | Channel<unknown>, opts?: ColorOptions): Channel<number[]>;
|
|
30
31
|
/**
|
|
31
|
-
* 0xrrggbb
|
|
32
|
-
* when authoring them, and call it outside the hot path in an `fn` patch.
|
|
32
|
+
* Converts 0xrrggbb to a `color` value. The value is OKLab `[L, a, b]` at full coverage. Convert
|
|
33
|
+
* keyframe stops once when authoring them, and call it outside the hot path in an `fn` patch.
|
|
33
34
|
*
|
|
34
35
|
* @category channel
|
|
35
36
|
*/
|
|
36
37
|
export declare function oklab(rgb: number): number[];
|
|
37
38
|
/**
|
|
38
|
-
* A `color` pose as 0xrrggbb
|
|
39
|
-
* and laid over `under` in OKLab, then clipped to sRGB per channel. No
|
|
39
|
+
* A `color` pose as 0xrrggbb, laid over `under`. Its color is divided out of its coverage, the
|
|
40
|
+
* coverage capped at 1 and laid over `under` in OKLab, then clipped to sRGB per channel. No
|
|
41
|
+
* coverage gives `under`.
|
|
40
42
|
*
|
|
41
43
|
* @category channel
|
|
42
44
|
*/
|
|
43
45
|
export declare function toHex(pose: ArrayLike<number>, under?: number): number;
|
|
44
46
|
/**
|
|
45
|
-
* A `color` pose as a CSS `oklab()` color
|
|
46
|
-
* host that lets the browser lay it over what is under it.
|
|
47
|
+
* A `color` pose as a CSS `oklab()` color, its coverage as the alpha. The coverage is capped at 1.
|
|
48
|
+
* For a host that lets the browser lay it over what is under it.
|
|
47
49
|
*
|
|
48
50
|
* @category channel
|
|
49
51
|
*/
|
package/dist/color.js
CHANGED
|
@@ -82,8 +82,9 @@ function fromOklch(L, C, h) {
|
|
|
82
82
|
return fromLab(L, C * Math.cos(h), C * Math.sin(h));
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
* OKLCH, hue the short way round
|
|
86
|
-
* instead of dipping through a muddy middle. A mix that leaves sRGB's
|
|
85
|
+
* Crossfades two hex colors in OKLCH, hue the short way round. A crossfade between hues then keeps
|
|
86
|
+
* its lightness and saturation instead of dipping through a muddy middle. A mix that leaves sRGB's
|
|
87
|
+
* gamut is clipped per channel.
|
|
87
88
|
*
|
|
88
89
|
* @category channel
|
|
89
90
|
*/
|
|
@@ -98,14 +99,19 @@ export function mixHex(a, b, u) {
|
|
|
98
99
|
const ah = slotH[sa];
|
|
99
100
|
const sb = oklch(b);
|
|
100
101
|
const bc = slotC[sb];
|
|
101
|
-
const
|
|
102
|
-
|
|
102
|
+
const h = hueAt(ah, ac, slotH[sb], bc, u);
|
|
103
|
+
return fromOklch(mix(al, slotL[sb], u), mix(ac, bc, u), h);
|
|
104
|
+
}
|
|
105
|
+
/** The hue `u` of the way from `ah` to `bh` the short way round, a gray end taking the other's. */
|
|
106
|
+
function hueAt(ah, ac, bh, bc, u) {
|
|
107
|
+
const from = ac < GRAY ? bh : ah;
|
|
108
|
+
const to = bc < GRAY ? ah : bh;
|
|
103
109
|
let turn = to - from;
|
|
104
110
|
if (turn > Math.PI)
|
|
105
111
|
turn -= 2 * Math.PI;
|
|
106
112
|
else if (turn < -Math.PI)
|
|
107
113
|
turn += 2 * Math.PI;
|
|
108
|
-
return
|
|
114
|
+
return from + turn * u;
|
|
109
115
|
}
|
|
110
116
|
/** Two OKLab colors with coverage, interpolated in OKLCH: color by `u`, coverage by `u` alongside. */
|
|
111
117
|
function lerpLch(x, y, u) {
|
|
@@ -126,17 +132,8 @@ function lerpLch(x, y, u) {
|
|
|
126
132
|
const toB = yw > 0 ? yB : xB;
|
|
127
133
|
const xc = Math.sqrt(xA * xA + xB * xB);
|
|
128
134
|
const yc = Math.sqrt(toA * toA + toB * toB);
|
|
129
|
-
const xh = Math.atan2(xB, xA);
|
|
130
|
-
const yh = Math.atan2(toB, toA);
|
|
131
|
-
const from = xc < GRAY ? yh : xh;
|
|
132
|
-
const to = yc < GRAY ? xh : yh;
|
|
133
|
-
let turn = to - from;
|
|
134
|
-
if (turn > Math.PI)
|
|
135
|
-
turn -= 2 * Math.PI;
|
|
136
|
-
else if (turn < -Math.PI)
|
|
137
|
-
turn += 2 * Math.PI;
|
|
138
135
|
const C = mix(xc, yc, u);
|
|
139
|
-
const h =
|
|
136
|
+
const h = hueAt(Math.atan2(xB, xA), xc, Math.atan2(toB, toA), yc, u);
|
|
140
137
|
return [mix(xL, toL, u) * w, C * Math.cos(h) * w, C * Math.sin(h) * w, w];
|
|
141
138
|
}
|
|
142
139
|
export function color(ofOrOpts, maybe) {
|
|
@@ -149,8 +146,11 @@ export function color(ofOrOpts, maybe) {
|
|
|
149
146
|
(stock?.op === 'sum' && stock.turn === undefined && of.bounds === undefined)) {
|
|
150
147
|
const channel = vec(4, sum());
|
|
151
148
|
channel.kind = `color(sum${space})`;
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
if (!round)
|
|
150
|
+
return channel;
|
|
151
|
+
const turning = { ...channel, lerp: lerpLch };
|
|
152
|
+
vouch(turning, { op: 'own', axes: 4, folds: 'sum' });
|
|
153
|
+
return turning;
|
|
154
154
|
}
|
|
155
155
|
if (of.kind === 'last') {
|
|
156
156
|
const channel = {
|
|
@@ -159,15 +159,14 @@ export function color(ofOrOpts, maybe) {
|
|
|
159
159
|
lerp: round ? lerpLch : vec(4, sum()).lerp,
|
|
160
160
|
};
|
|
161
161
|
widen(channel, 4);
|
|
162
|
-
|
|
163
|
-
vouch(channel, { op: 'last', axes: 4 });
|
|
162
|
+
vouch(channel, { op: 'last', axes: 4 });
|
|
164
163
|
return channel;
|
|
165
164
|
}
|
|
166
165
|
throw new Error(`color takes sum() to average or last() to replace, not ${of.kind ?? 'a channel of its own'}`);
|
|
167
166
|
}
|
|
168
167
|
/**
|
|
169
|
-
* 0xrrggbb
|
|
170
|
-
* when authoring them, and call it outside the hot path in an `fn` patch.
|
|
168
|
+
* Converts 0xrrggbb to a `color` value. The value is OKLab `[L, a, b]` at full coverage. Convert
|
|
169
|
+
* keyframe stops once when authoring them, and call it outside the hot path in an `fn` patch.
|
|
171
170
|
*
|
|
172
171
|
* @category channel
|
|
173
172
|
*/
|
|
@@ -176,8 +175,9 @@ export function oklab(rgb) {
|
|
|
176
175
|
return [LAB[0], LAB[1], LAB[2], 1];
|
|
177
176
|
}
|
|
178
177
|
/**
|
|
179
|
-
* A `color` pose as 0xrrggbb
|
|
180
|
-
* and laid over `under` in OKLab, then clipped to sRGB per channel. No
|
|
178
|
+
* A `color` pose as 0xrrggbb, laid over `under`. Its color is divided out of its coverage, the
|
|
179
|
+
* coverage capped at 1 and laid over `under` in OKLab, then clipped to sRGB per channel. No
|
|
180
|
+
* coverage gives `under`.
|
|
181
181
|
*
|
|
182
182
|
* @category channel
|
|
183
183
|
*/
|
|
@@ -195,8 +195,8 @@ export function toHex(pose, under = 0x000000) {
|
|
|
195
195
|
return fromLab(mix(LAB[0], L, w), mix(LAB[1], A, w), mix(LAB[2], B, w));
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
198
|
-
* A `color` pose as a CSS `oklab()` color
|
|
199
|
-
* host that lets the browser lay it over what is under it.
|
|
198
|
+
* A `color` pose as a CSS `oklab()` color, its coverage as the alpha. The coverage is capped at 1.
|
|
199
|
+
* For a host that lets the browser lay it over what is under it.
|
|
200
200
|
*
|
|
201
201
|
* @category channel
|
|
202
202
|
*/
|
package/dist/columns.d.ts
CHANGED
|
@@ -22,7 +22,8 @@ export declare function pullRun<I, O>(lanes: Lanes<I, O>, slots: Int32Array, lis
|
|
|
22
22
|
* Queues a subject's values for each column at subject `n`'s places, for a `pull` that reads
|
|
23
23
|
* from the lanes while every voice is on one; `flush` writes what is queued, column by column. A
|
|
24
24
|
* channel no lane holds is at rest. Anything queued is written before the lanes fill again.
|
|
25
|
+
* `slots` holds each row's lane slot, `slot` among them.
|
|
25
26
|
*/
|
|
26
|
-
export declare function writeLater<I, O>(lanes: Lanes<I, O>, slot: number, columns: readonly Column[], n: number): void;
|
|
27
|
+
export declare function writeLater<I, O>(lanes: Lanes<I, O>, slot: number, columns: readonly Column[], n: number, slots: Int32Array): void;
|
|
27
28
|
/** Writes every queued run into its columns: a channel's values as a block, a rest as a fill. */
|
|
28
29
|
export declare function flush<I, O>(lanes: Lanes<I, O>): void;
|
package/dist/columns.js
CHANGED
|
@@ -22,16 +22,12 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
|
|
|
22
22
|
const frame = lanes.frameProbes;
|
|
23
23
|
const subjects = lanes.subjects;
|
|
24
24
|
const unlinked = lanes.unlinked;
|
|
25
|
+
const holds = lanes.holds;
|
|
26
|
+
// Nothing in `pullRun` makes a subject owe a voice, so one with nobody owing need not ask.
|
|
27
|
+
const owing = lanes.owed.any();
|
|
25
28
|
const current = since === Number.POSITIVE_INFINITY;
|
|
26
|
-
|
|
27
|
-
flush(lanes);
|
|
28
|
-
lanes.queuedFor = columns;
|
|
29
|
-
}
|
|
29
|
+
queueFor(lanes, columns, slots);
|
|
30
30
|
room(lanes, lanes.queued + list.length - from);
|
|
31
|
-
const qSlots = lanes.queueSlots;
|
|
32
|
-
const qRows = lanes.queueRows;
|
|
33
|
-
const qCounts = lanes.queueCounts;
|
|
34
|
-
let k = lanes.queued;
|
|
35
31
|
let n = from;
|
|
36
32
|
// The run being gathered: its first slot and row, and how far it has reached.
|
|
37
33
|
let first = -1;
|
|
@@ -47,33 +43,25 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
|
|
|
47
43
|
per[o + 0 /* Per.FILLED */] >= busy ||
|
|
48
44
|
per[o + 2 /* Per.IDLE */] !== 0 ||
|
|
49
45
|
per[o + 1 /* Per.SEEN */] < wide ||
|
|
50
|
-
lanes.owes(slot))
|
|
46
|
+
(owing && lanes.owes(slot)))
|
|
51
47
|
break;
|
|
52
48
|
if (!(per[o + 3 /* Per.LANE_PROBE */] > frame ||
|
|
53
49
|
per[o + 4 /* Per.GENERAL_PROBE */] > frame))
|
|
54
50
|
lanes.distinct++;
|
|
55
51
|
per[o + 3 /* Per.LANE_PROBE */] = ++lanes.probes;
|
|
56
52
|
per[o + 5 /* Per.LANE_FILL */] = fills;
|
|
57
|
-
|
|
53
|
+
if (holds)
|
|
54
|
+
subjects[slot] = subject;
|
|
58
55
|
if (slot !== first + (n - start)) {
|
|
59
|
-
if (first >= 0)
|
|
60
|
-
|
|
61
|
-
qRows[k] = start;
|
|
62
|
-
qCounts[k] = n - start;
|
|
63
|
-
k++;
|
|
64
|
-
}
|
|
56
|
+
if (first >= 0)
|
|
57
|
+
add(lanes, first, start, n - start);
|
|
65
58
|
first = slot;
|
|
66
59
|
start = n;
|
|
67
60
|
}
|
|
68
61
|
}
|
|
69
|
-
if (first >= 0)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
qCounts[k] = n - start;
|
|
73
|
-
k++;
|
|
74
|
-
}
|
|
75
|
-
lanes.queued = k;
|
|
76
|
-
if (n > from)
|
|
62
|
+
if (first >= 0)
|
|
63
|
+
add(lanes, first, start, n - start);
|
|
64
|
+
if (n > from && holds)
|
|
77
65
|
lanes.holding = true;
|
|
78
66
|
return n;
|
|
79
67
|
}
|
|
@@ -81,24 +69,43 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
|
|
|
81
69
|
* Queues a subject's values for each column at subject `n`'s places, for a `pull` that reads
|
|
82
70
|
* from the lanes while every voice is on one; `flush` writes what is queued, column by column. A
|
|
83
71
|
* channel no lane holds is at rest. Anything queued is written before the lanes fill again.
|
|
72
|
+
* `slots` holds each row's lane slot, `slot` among them.
|
|
84
73
|
*/
|
|
85
|
-
export function writeLater(lanes, slot, columns, n) {
|
|
86
|
-
|
|
74
|
+
export function writeLater(lanes, slot, columns, n, slots) {
|
|
75
|
+
queueFor(lanes, columns, slots);
|
|
76
|
+
add(lanes, slot, n, 1);
|
|
87
77
|
}
|
|
88
|
-
/**
|
|
89
|
-
function
|
|
90
|
-
if (columns
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
/** Writes what is queued for other columns or another array of slots before queuing for these. */
|
|
79
|
+
function queueFor(lanes, columns, slots) {
|
|
80
|
+
if (columns === lanes.queuedFor && slots === lanes.queueFrom)
|
|
81
|
+
return;
|
|
82
|
+
flush(lanes);
|
|
83
|
+
lanes.queuedFor = columns;
|
|
84
|
+
lanes.queueFrom = slots;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The shortest run `flush` copies as a block from consecutive slots. A shorter one, as a list kept
|
|
88
|
+
* dense by swap-remove leaves its slots, joins the rows before it in one run whose slots `flush`
|
|
89
|
+
* reads row by row, so a run's cost is paid once a stretch of rows rather than once a few.
|
|
90
|
+
*/
|
|
91
|
+
const BLOCK = 16;
|
|
92
|
+
/** Queues `count` rows from `row`, whose slots follow one apart from `slot`. */
|
|
93
|
+
function add(lanes, slot, row, count) {
|
|
94
94
|
const k = lanes.queued;
|
|
95
95
|
if (k > 0) {
|
|
96
96
|
const last = k - 1;
|
|
97
97
|
const had = lanes.queueCounts[last];
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
if (row === lanes.queueRows[last] + had) {
|
|
99
|
+
const first = lanes.queueSlots[last];
|
|
100
|
+
if (first >= 0 && slot === first + had) {
|
|
101
|
+
lanes.queueCounts[last] = had + count;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (count < BLOCK && (first < 0 || had < BLOCK)) {
|
|
105
|
+
lanes.queueSlots[last] = -1;
|
|
106
|
+
lanes.queueCounts[last] = had + count;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
room(lanes, k + 1);
|
|
@@ -169,16 +176,33 @@ function rests(lanes, c, runs) {
|
|
|
169
176
|
}
|
|
170
177
|
/** The longest run of values `copies` copies itself: `set` takes a view, an object a run. */
|
|
171
178
|
const VIEWED = 256;
|
|
172
|
-
/** Copies a laned channel's values for every queued run into its rows. */
|
|
179
|
+
/** Copies a laned channel's values for every queued run into its rows, a block or row by row. */
|
|
173
180
|
function copies(lanes, c, values, runs) {
|
|
174
181
|
const slots = lanes.queueSlots;
|
|
175
182
|
const rows = lanes.queueRows;
|
|
176
183
|
const counts = lanes.queueCounts;
|
|
177
184
|
const out = c.out;
|
|
178
185
|
const axes = c.axes;
|
|
186
|
+
const from = lanes.queueFrom;
|
|
179
187
|
for (let r = 0; r < runs; r++) {
|
|
188
|
+
const first = slots[r];
|
|
189
|
+
if (first < 0) {
|
|
190
|
+
const row = rows[r];
|
|
191
|
+
const end = row + counts[r];
|
|
192
|
+
if (axes === 1)
|
|
193
|
+
for (let i = row; i < end; i++)
|
|
194
|
+
out[i] = values[from[i]];
|
|
195
|
+
else
|
|
196
|
+
for (let i = row; i < end; i++) {
|
|
197
|
+
const at = i * axes;
|
|
198
|
+
const base = from[i] * axes;
|
|
199
|
+
for (let a = 0; a < axes; a++)
|
|
200
|
+
out[at + a] = values[base + a];
|
|
201
|
+
}
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
180
204
|
const at = rows[r] * axes;
|
|
181
|
-
const base =
|
|
205
|
+
const base = first * axes;
|
|
182
206
|
const span = counts[r] * axes;
|
|
183
207
|
if (span > VIEWED)
|
|
184
208
|
out.set(values.subarray(base, base + span), at);
|