@msb235/blits 0.13.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 +183 -0
- package/README.md +20 -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/closed.d.ts +1 -0
- package/dist/closed.js +45 -1
- 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 +47 -14
- 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/fling.d.ts +66 -0
- package/dist/fling.js +162 -0
- package/dist/fold.d.ts +4 -37
- package/dist/fold.js +5 -512
- 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 +3 -0
- package/dist/handle.js +19 -8
- package/dist/held.d.ts +2 -1
- package/dist/history.d.ts +6 -5
- package/dist/history.js +9 -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 +61 -35
- package/dist/mixer.js +64 -169
- package/dist/motion.d.ts +19 -106
- package/dist/motion.js +60 -13
- package/dist/motions.d.ts +62 -150
- package/dist/motions.js +57 -369
- package/dist/move.js +24 -1
- 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 +3 -1
- package/dist/pack.js +4 -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 -211
- 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 +4 -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 +13 -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 +21 -14
- 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 -161
- package/dist/types/motion.d.ts +151 -0
- package/dist/types/motion.js +1 -0
- package/dist/types/patch.d.ts +69 -34
- package/dist/types/place.d.ts +51 -36
- package/dist/types/transport.d.ts +20 -18
- package/dist/types/voice.d.ts +145 -72
- package/dist/types/watch.d.ts +88 -40
- package/dist/types.d.ts +1 -0
- package/dist/voice.d.ts +9 -141
- package/dist/voice.js +30 -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 +223 -37
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,189 @@ This package follows [semver](https://semver.org). Below 1.0.0, a breaking chang
|
|
|
4
4
|
version and everything else the patch. Each release lists its changes as **Breaking**, **Added** and
|
|
5
5
|
**Fixed**, and the release workflow refuses a tag with no section here.
|
|
6
6
|
|
|
7
|
+
## 0.15.0
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- `Setting`'s fields are `readonly`, so a patch or signal that assigns to
|
|
12
|
+
`setting.weight`, `dt` or any other field no longer type-checks. The mix reuses one setting per
|
|
13
|
+
voice across subjects, so such a write already reached whatever it called next. A patch's
|
|
14
|
+
`state` is still its own to change in place.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- A locus is now a **role**, `VoiceSpec.role`: voices in one role are alternatives, so whichever
|
|
19
|
+
plays it, the subject shows one performance, not two stacked. `locus` is still taken as `role`
|
|
20
|
+
for one release, deprecated, and the mix warns once outside production; a voice given both with
|
|
21
|
+
different values is refused. The mix option for tags that act as roles is `roles`, and `loci` is
|
|
22
|
+
taken the same way. Errors and warnings say role.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `within` on a cue: a circle `{ x, y, r }` (the type `Circle`) the host moves, outside which the
|
|
27
|
+
voice's weight signal is 0, so the mix gives those subjects nothing without calling the signal for
|
|
28
|
+
them. The host gives the subjects' positions with `mix.plot(subjects, xy)`, two numbers a subject
|
|
29
|
+
read in place, and calls `mix.moved()` after writing new ones. Positions hold an object subject
|
|
30
|
+
weakly, and `drop` takes a subject's away. Outside production the mix warns once if the signal is
|
|
31
|
+
above 0 just outside the circle, which would cut it off there. A voice naming its `subjects` and
|
|
32
|
+
a role member, an owner's child, a motion, a patch keeping state and a `from: 'current'` voice
|
|
33
|
+
take it; a patch that steps still steps outside the circle, and a `from: 'current'` voice still
|
|
34
|
+
takes its base there, as each does at weight 0. Under history a bound reads as its signal does:
|
|
35
|
+
live on a read back, or with `inputs`, as the signal's recorded reads, a 0 recorded for each
|
|
36
|
+
subject read outside.
|
|
37
|
+
- `within` also takes a finder of the host's own, `{ find(out) }` (the type `Finder`), over any axes
|
|
38
|
+
or none: once a frame `find` writes into `out`, an `Int32Array` the mix lends it, the indices of
|
|
39
|
+
the subjects the signal can be above 0 for, and returns how many. A circle and a finder share one
|
|
40
|
+
path through the mix.
|
|
41
|
+
- `mix.place(subject)` gives a subject its index, which a finder names it by: the one it has, or a
|
|
42
|
+
new one. It keeps it until `drop`, or for an object until it is collected, and the index then goes
|
|
43
|
+
to the next subject placed. `plot` hands out indices too, in its list's order. With no `plot`,
|
|
44
|
+
`put` writes into positions the mix keeps itself, and a placed subject has none until then.
|
|
45
|
+
- `mix.around(circle, { ease })`, a weight signal over the plotted positions: 1 at the circle's
|
|
46
|
+
center, falling to 0 at `r` along an easing, linear by default. A voice it weighs is bounded to
|
|
47
|
+
the circle as if cued `within` it, with no check for a clipped signal, from the first `plot` if it
|
|
48
|
+
was cued before one.
|
|
49
|
+
- `mix.put(subject, x, y)` moves one subject, for a host where few move at once. It writes into the
|
|
50
|
+
array `plot` was given, so it mixes freely with writing that array and calling `moved`, or with no
|
|
51
|
+
`plot`, into the mix's own, and keeps the grid as built until a sixteenth of the subjects have left
|
|
52
|
+
their cells. It throws for a subject with no index, or one past the array `plot` was given.
|
|
53
|
+
- A watched mix's `voice(handle)` gives `within`, the circle a voice is bounded to by `within` or
|
|
54
|
+
an `around` weight, or its finder, the host's own object, or undefined for none. blits-panel draws
|
|
55
|
+
circles only.
|
|
56
|
+
- `color({ lerp: 'oklch' })` and `color(last(), { lerp: 'oklch' })` run on lanes, where they took
|
|
57
|
+
the general path. The pose is the same to the bit.
|
|
58
|
+
- `mix.pose()` makes a new empty pose for `probe`'s `out`, holding each channel with a rest in kit
|
|
59
|
+
order. Made once and probed into every frame, its numbers are written without allocating, where
|
|
60
|
+
an `out` made as `{}` can box each one: about 310 kB a frame less on a 10,000-subject scene.
|
|
61
|
+
- Named, exported types for option bags and return values that were written inline, so each can
|
|
62
|
+
be read on its own: `LastOptions`, `NumberTuple`, `Layout`, `OverrunOptions`, `MotionOptions`,
|
|
63
|
+
`SpringOptions`, `GlideOptions`, `TweenOptions`, `Spring`, `Glide`, `Tween`, `SpringSpec`,
|
|
64
|
+
`GlideSpec`, `TweenSpec`, `Input`, `Level`, `SlewOptions`, `LagOptions`, `Band`,
|
|
65
|
+
`AnnounceOptions`, `TapeOptions`, `Taken`, `VoiceFade`, `SubjectFade`, `RegistryChange`,
|
|
66
|
+
`WatchedHistory`, `WatchedVoice`, `Probed`, and `Pulled`. `Moving` and `Signal` are interfaces now,
|
|
67
|
+
of the same shape.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- A host writing into a pose `probe` handed back without `out` no longer changes where a later
|
|
72
|
+
`from: 'current'` voice starts, or the speed it starts at: the mix keeps a copy of that pose, not
|
|
73
|
+
the object itself.
|
|
74
|
+
- Probes of subjects on lanes cost 4 to 10% less a frame: the copy of laned values into the pose
|
|
75
|
+
stores each channel under its own name, written out once per set of laned channels, where one
|
|
76
|
+
store by a varying name went through V8's generic path. A voice over 10,000 subjects replaced
|
|
77
|
+
every frame (`swap`) takes 1.632 ms a frame against 1.739, its keys form 1.165 against 1.259, and
|
|
78
|
+
a probe then `atRest` of each subject (`probed`) 1.333 against 1.487 (teitou, medians of nine
|
|
79
|
+
runs, each in its own process). A pose the mix makes itself, for a probe without `out`, `atRest`
|
|
80
|
+
or `pull`, is now a plain object of the mix's own shape holding each channel with a rest in kit
|
|
81
|
+
order, so its numbers are stored unboxed. Under a content security policy that refuses
|
|
82
|
+
`Function`, the copy is the loop it was, and the mix asks only once, so a page sees one
|
|
83
|
+
violation at most: each refused `Function` fires one, caught or not.
|
|
84
|
+
- A weight signal on lanes is handed, after a stretch of frames at weight 0, the `dt` since its
|
|
85
|
+
last read, as it is off lanes. It was handed the gap since the subject last had a delta.
|
|
86
|
+
- Tweens read by `pull` cost about a twentieth less a frame: a probe holds its subject for the
|
|
87
|
+
next fill only while fills look subjects up, which bare tweens never do, and a motion voice's
|
|
88
|
+
fill reads each subject's flags once and weighs a shared fade once. 10,000 one-subject tween
|
|
89
|
+
voices (`tweens^`) take 0.285 ms a frame against 0.300, and one tween voice over 10,000 subjects
|
|
90
|
+
(`weasel^`) 0.369 against 0.391 (keiei, medians of seven runs of 5,000 frames, each in its own
|
|
91
|
+
process); other rows are unchanged.
|
|
92
|
+
- 10,000 one-subject tween voices read by `pull` (`tweens^`) cost about 7% less a frame, 0.163 ms
|
|
93
|
+
against 0.175: a crowd row's fill checks once, not every frame, that its stretch is numbered and
|
|
94
|
+
its subject met (teitou, medians of nine runs of 5,000 frames, each in its own process).
|
|
95
|
+
- Starting voices allocates about 400 B less each: a voice's `subjects`, given as an array of
|
|
96
|
+
distinct subjects, is copied without being iterated, and voices of a patch writing one channel
|
|
97
|
+
share their channel slots. 10,000 one-subject voices' first frame takes 20.7 ms against 23.5 for
|
|
98
|
+
tweens and 8.9 against 10.8 for fns (teitou, medians of nine rounds; `bench/start.mjs`).
|
|
99
|
+
- A mix with no tape no longer makes the closures that record the host's calls only to drop them:
|
|
100
|
+
a cue, a `drop`, a motion change and every handle write each made one. A voice cued and replaced
|
|
101
|
+
leaves 11.7 KB of garbage against 12.4 (keiei, medians of three alternated passes of
|
|
102
|
+
`bench/churnwho.mjs 5000`); frame cost is level.
|
|
103
|
+
- A stateless fn voice over every subject cued between frames costs about a quarter less on the
|
|
104
|
+
frame its subjects first meet it: their probe makes its first call on its lane and folds it into
|
|
105
|
+
the lanes' values, where it was folded on the general path. A voice over 10,000 subjects
|
|
106
|
+
replaced every frame among a tween voice per subject (`swap`) takes 2.67 ms a frame against
|
|
107
|
+
3.67, and allocates 2.0 MB a frame against 2.8 (msb-uai, medians of seven runs of 3,000 frames,
|
|
108
|
+
each in its own process).
|
|
109
|
+
- That frame costs about a sixth less again where every subject was probed the frame before: the
|
|
110
|
+
voice's lane takes every subject when it opens, and the fill makes each one's first call, where
|
|
111
|
+
each probe met the lane and made it. `swap` takes 2.19 ms a frame against 2.64 (msb-uai, medians
|
|
112
|
+
of seven runs of 3,000 frames, each in its own process); other rows are unchanged.
|
|
113
|
+
- A `keys` voice over every subject cued between frames gets both: a probe meeting its lane reads
|
|
114
|
+
it there, and where every subject was probed the frame before, its lane takes them all when it
|
|
115
|
+
opens. A keys voice over 10,000 subjects replaced every frame among a tween voice per subject
|
|
116
|
+
(`swapkeys`) takes 1.21 ms a frame against 2.26 (teitou, medians of seven runs of 3,000 frames,
|
|
117
|
+
each in its own process); other rows are unchanged.
|
|
118
|
+
- A `keys` voice per subject read by `pull` costs about a sixth less a frame: rows whose stops
|
|
119
|
+
share their phases and easings, at the same phase, find their segment and ease it once rather
|
|
120
|
+
than once a row. 10,000 of them (`keyses^`) take 0.270 ms a frame against 0.327, and allocate
|
|
121
|
+
half as much (teitou, medians of nine runs of 5,000 frames, each in its own process). A lane fill
|
|
122
|
+
also reads a subject's last-read stamp only where the weight it keeps for `weightOf` could
|
|
123
|
+
change, which takes `weasel^` from 0.204 to 0.198 ms; other rows are unchanged.
|
|
124
|
+
- A voice weighted by a signal costs a little less a frame on lanes: its setting's `timestamp`,
|
|
125
|
+
`elapsed` and `pass` are filled once for the subjects that share them, not once a subject. A
|
|
126
|
+
pointer glow over 10,000 subjects read by `pull` (`glow^`) takes 1.657 ms a frame against 1.708,
|
|
127
|
+
and three signal-weighted voices over 10,000 (`signal`) 4.511 against 4.641 (keiei, medians of
|
|
128
|
+
seven runs of 5,000 frames, each in its own process); other rows are within their spread.
|
|
129
|
+
- A role, and so a `mix.blend`, costs a further tenth less a frame on lanes where no other voice
|
|
130
|
+
falls between its members in voice order: each subject's members are gathered and folded
|
|
131
|
+
together, at the first member's turn, rather than gathered for every subject and folded in a
|
|
132
|
+
second pass. Three voices in a role over 10,000 subjects (`role`) take 2.555 ms a frame against
|
|
133
|
+
2.839, and a blend of three (`blend`) 2.841 against 3.055 (teitou, medians of nine runs of 5,000
|
|
134
|
+
frames, each in its own process); three plain voices (`fn`) and three signal-weighted ones
|
|
135
|
+
(`signal`) are unchanged.
|
|
136
|
+
- A role, and so a `mix.blend`, costs about a tenth less a frame on lanes, and allocates no more
|
|
137
|
+
than its members would apart: a member's numbers reach the role without being boxed, 16 bytes
|
|
138
|
+
each a channel a subject. Three voices in a role over 10,000 subjects (`role`) take 4.349 ms a
|
|
139
|
+
frame against 4.810 and allocate 2.4 MB against 4.0, and a blend of three (`blend`) 4.633 against
|
|
140
|
+
5.128 (keiei, medians of seven runs of 5,000 frames, each in its own process).
|
|
141
|
+
- `project` back under `history` reads a motion voice's subject as it showed. A subject retargeted
|
|
142
|
+
and then faded out of the voice or dropped read back to before it left as though met afresh from
|
|
143
|
+
`from`, and a retarget, push or `set` given a time earlier than the frame it was made in read back
|
|
144
|
+
as in force before that frame. A seek already read both right. A read back no longer numbers a
|
|
145
|
+
subject in the live patch either.
|
|
146
|
+
- A read ahead across many known events of a stateful mix costs a quarter to a third less a frame
|
|
147
|
+
and allocates half as much: each stop hands a subject's record on to the next instead of copying
|
|
148
|
+
it, and a voice starting many stops on asks only the copies that may hold a record for it.
|
|
149
|
+
Reading 5 s ahead past the starts, fade-ins and ends of seven voices over 1,000 subjects (`far`)
|
|
150
|
+
takes 15.1 ms a frame against 20.5 and allocates 25 MB against 53, and over 10,000 subjects 201
|
|
151
|
+
ms against 305; seeking back over the calls that cued them and reading ahead to where the mix
|
|
152
|
+
was (`redo`), 15.5 ms against 21.0 (teitou, medians of five alternated runs of 100 frames against
|
|
153
|
+
`be0aaa4`). With 31 voices starting 150 ms apart, the record handed on reads 154 ms against 182,
|
|
154
|
+
and looking past the copies 109 against 151 (against `03af146`).
|
|
155
|
+
- A voice stopped before its end is let go of when it stops. The mix's schedule kept an entry for
|
|
156
|
+
the time each voice would next change, and one stopped early stayed in it, holding the voice,
|
|
157
|
+
until that time came: 20,000 `patch` voices of 10⁹ ms replaced over 1,000 subjects held 47 MB of
|
|
158
|
+
heap, and now 2.4 (keiei, one run each, `bench/leak.mjs`). A motion patch was never held, as it
|
|
159
|
+
schedules no end.
|
|
160
|
+
|
|
161
|
+
## 0.14.0
|
|
162
|
+
|
|
163
|
+
### Added
|
|
164
|
+
|
|
165
|
+
- `set(subject, value, at?)` on `spring`, `glide`, `tween`, and a voice's handle puts a subject at a
|
|
166
|
+
value and keeps its velocity: a spring or tween heads for its target from there, a glide coasts
|
|
167
|
+
on. A seek plays it again, as it does a push.
|
|
168
|
+
- `@msb235/blits/fling`: `fling(element, motion, subject, opts)` drags a subject by pointer and
|
|
169
|
+
flings it on release, and `track` reads a pointer's velocity from its samples with no listeners
|
|
170
|
+
of its own. The package still builds without the DOM's types.
|
|
171
|
+
- `glide` takes `bounds: [min, max]`, per axis and optionally per subject: a coast that would stop
|
|
172
|
+
past an edge springs back to it from where it crosses, on a critically damped spring of
|
|
173
|
+
`stiffness` (default 170). A `set` past an edge stays there until the next push. `g.bounds(subject)`
|
|
174
|
+
reads them, and `Patch.motion` reports the `stiffness` of a glide that has them.
|
|
175
|
+
- `fling` resists a drag past a glide's bounds, carrying the subject at most `give` (default 120)
|
|
176
|
+
past an edge, and a press holds a subject springing back past one where it is.
|
|
177
|
+
|
|
178
|
+
### Fixed
|
|
179
|
+
|
|
180
|
+
- A voice cued with no `start` into a mix whose frame loop sleeps on `inert`, as `ticker` does,
|
|
181
|
+
starts at the next sync. It started at the last one, so an effect cued after a pause had already
|
|
182
|
+
played that far, and a short one was over before its first frame.
|
|
183
|
+
- A patch that names no host type can be cued and blended in a mix that declares one, so a patch
|
|
184
|
+
library's patches type-check in a host-typed mix. `VoiceSpec.patch`, `BlendMember.patch`, and
|
|
185
|
+
`blend`'s members take a patch keeping any state.
|
|
186
|
+
- A seek back keeps out a subject that had already left a voice at rest by then. After a fade with
|
|
187
|
+
`at: 'rest'`, a seek to a moment where that subject was moving again put it back in the voice,
|
|
188
|
+
playing.
|
|
189
|
+
|
|
7
190
|
## 0.13.0
|
|
8
191
|
|
|
9
192
|
### Breaking
|
package/README.md
CHANGED
|
@@ -102,6 +102,23 @@ import { kit, mix, patch, sum } from '@msb235/blits/lean';
|
|
|
102
102
|
|
|
103
103
|
Everything else is the same and so are the poses. `lanes: true` is refused there.
|
|
104
104
|
|
|
105
|
+
## Dragging and flinging
|
|
106
|
+
|
|
107
|
+
`@msb235/blits/fling` drags a motion patch's subject by pointer and flings it on release:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { fling } from '@msb235/blits/fling';
|
|
111
|
+
|
|
112
|
+
const scroll = glide<Page, Pose>('y', { from: 0 });
|
|
113
|
+
mix.cue({ patch: scroll });
|
|
114
|
+
const off = fling(element, scroll, page, { scale: -1 }); // content moves against the drag
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A press stops the glide where it is, each move puts it under the pointer, and letting go pushes it at
|
|
118
|
+
the pointer's velocity. `track` reads that velocity from any `(t, x)` samples, with no listeners of
|
|
119
|
+
its own. Anything else in the mix can read the glide's position and velocity, so parallax, a header
|
|
120
|
+
that shrinks or a blur that grows with speed are voices driven by a signal over `scroll.read(page)`.
|
|
121
|
+
|
|
105
122
|
## Writing patches for blits in another library
|
|
106
123
|
|
|
107
124
|
A library that ships patches exports them with the channels they write, as a `Kit` of its own
|
|
@@ -134,7 +151,7 @@ do yet.
|
|
|
134
151
|
|
|
135
152
|
## Status
|
|
136
153
|
|
|
137
|
-
v0.
|
|
154
|
+
v0.15.0, with five consumers. magicsmoke runs every fault on it, wod its wheel, tricks and
|
|
138
155
|
transitions, weasel its animator's tweens, sherpa its seams, and
|
|
139
156
|
[klieg](https://github.com/orochi235/klieg) composes all three of its systems — letter motion,
|
|
140
157
|
part effects, and the environment a sign is lit by — on a mix each.
|
|
@@ -143,8 +160,8 @@ here is the arithmetic a working renderer already had: klieg's suite of 1974 cas
|
|
|
143
160
|
screenshot baselines came through it unchanged.
|
|
144
161
|
|
|
145
162
|
One thing the port sent back. klieg's three motion phases layer, so they cue as separate voices; a
|
|
146
|
-
`
|
|
147
|
-
alternatives wants and is not the same number on a multiplicative channel. Reach for a
|
|
163
|
+
`role` folds its members through each channel's `lerp` instead, which is what a crossfade between
|
|
164
|
+
alternatives wants and is not the same number on a multiplicative channel. Reach for a role when
|
|
148
165
|
one voice should replace another, not when both should be heard.
|
|
149
166
|
|
|
150
167
|
Zero runtime dependencies, ESM only, types included.
|
package/dist/ahead.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Mixer } from './mixer.js';
|
|
2
|
+
import type { Subject } from './subject.js';
|
|
3
|
+
import { Transport } from './transport.js';
|
|
4
|
+
import type { Voice } from './voice.js';
|
|
5
|
+
/** A live mix's copies at each known event a read ahead stopped at, and the subjects read there. */
|
|
6
|
+
export interface Stages {
|
|
7
|
+
at: Mixer<unknown, unknown>[];
|
|
8
|
+
met: Set<unknown>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Moves a read ahead's copies to mix time `t`, making the calls the tape holds on the way as a seek
|
|
12
|
+
* there makes them on the mixes. While a voice could carry state across it, it stops at each known
|
|
13
|
+
* event: a call, and every mark of a voice or announced. At each stop the copies are kept for
|
|
14
|
+
* subjects to be read there and copied on, so `copies` ends holding those at `t`. Answers the
|
|
15
|
+
* transport they stand on, and puts the tape back where it stood.
|
|
16
|
+
*/
|
|
17
|
+
export declare function playAhead(transport: Transport, c: Transport, copies: Map<Mixer<unknown, unknown>, Mixer<unknown, unknown>>, t: number, earlier: Map<Mixer<unknown, unknown>, Stages>): Transport;
|
|
18
|
+
/**
|
|
19
|
+
* A mix's copy for a read ahead: its voices as they stand, each subject carried from the live one,
|
|
20
|
+
* or from the copy at the known event before.
|
|
21
|
+
*/
|
|
22
|
+
export declare function copyAhead<I, O>(mix: Mixer<I, O>, c: Mixer<I, O>): void;
|
|
23
|
+
/** Gives a projection's copy what it reads of the live mix as it is, before its voices are copied. */
|
|
24
|
+
export declare function mirror<I, O>(mix: Mixer<I, O>, c: Mixer<I, O>): void;
|
|
25
|
+
/** A voice's record of a subject, the one it shares among those it does not reach included. */
|
|
26
|
+
export declare function recordOf<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I): Subject<unknown> | undefined;
|
package/dist/ahead.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { ownBlends } from './blend.js';
|
|
2
|
+
import { headOf } from './chain.js';
|
|
3
|
+
import { open, sighted, weightAt } from './everyone.js';
|
|
4
|
+
import { copyHeld, unread } from './history.js';
|
|
5
|
+
import { markOf, voiceMarks } from './marks.js';
|
|
6
|
+
import { Mixer } from './mixer.js';
|
|
7
|
+
import { move } from './move.js';
|
|
8
|
+
import { relink } from './owner.js';
|
|
9
|
+
import { pin, place } from './place.js';
|
|
10
|
+
import { recount } from './roster.js';
|
|
11
|
+
import { hostAhead, replay } from './tape.js';
|
|
12
|
+
import { Transport } from './transport.js';
|
|
13
|
+
import { unreached } from './unreached.js';
|
|
14
|
+
/**
|
|
15
|
+
* Moves a read ahead's copies to mix time `t`, making the calls the tape holds on the way as a seek
|
|
16
|
+
* there makes them on the mixes. While a voice could carry state across it, it stops at each known
|
|
17
|
+
* event: a call, and every mark of a voice or announced. At each stop the copies are kept for
|
|
18
|
+
* subjects to be read there and copied on, so `copies` ends holding those at `t`. Answers the
|
|
19
|
+
* transport they stand on, and puts the tape back where it stood.
|
|
20
|
+
*/
|
|
21
|
+
export function playAhead(transport, c, copies, t, earlier) {
|
|
22
|
+
const tape = transport.tape;
|
|
23
|
+
const depth = tape?.undoDepth() ?? 0;
|
|
24
|
+
let played = false;
|
|
25
|
+
try {
|
|
26
|
+
for (;;) {
|
|
27
|
+
const e = nextEvent(tape, c, t);
|
|
28
|
+
if (!(e < t))
|
|
29
|
+
break;
|
|
30
|
+
played = playTo(transport, c, copies, e) || played;
|
|
31
|
+
if (!carries(c))
|
|
32
|
+
continue;
|
|
33
|
+
// Placed now, as the next move would place them, while a target that has just left is here.
|
|
34
|
+
for (const m of c.members)
|
|
35
|
+
if (m.anchored > 0)
|
|
36
|
+
place(m);
|
|
37
|
+
const next = restage(c);
|
|
38
|
+
for (const [mix, copy] of copies) {
|
|
39
|
+
const on = new Mixer(copy.kit, { ...copy.opts, transport: next }, null);
|
|
40
|
+
on.slot = copy.slot;
|
|
41
|
+
on.projecting = true;
|
|
42
|
+
copyAhead(copy, on);
|
|
43
|
+
const stages = earlier.get(mix);
|
|
44
|
+
if (stages === undefined)
|
|
45
|
+
earlier.set(mix, { at: [copy], met: new Set() });
|
|
46
|
+
else
|
|
47
|
+
stages.at.push(copy);
|
|
48
|
+
copies.set(mix, on);
|
|
49
|
+
}
|
|
50
|
+
c = next;
|
|
51
|
+
}
|
|
52
|
+
played = playTo(transport, c, copies, t) || played;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
if (played)
|
|
56
|
+
tape?.goto(depth);
|
|
57
|
+
}
|
|
58
|
+
return c;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Whether a voice on a read ahead's copies could carry state from one known event to the next: a
|
|
62
|
+
* patch that steps, or a weight given by a signal, which may keep state. Where none could, a read
|
|
63
|
+
* crosses the events in one go, as fast as one with none.
|
|
64
|
+
*/
|
|
65
|
+
function carries(c) {
|
|
66
|
+
for (const m of c.members)
|
|
67
|
+
for (const v of m.cued)
|
|
68
|
+
if (v.state !== 'done' && (v.patch.step !== undefined || typeof v.spec.weight === 'function'))
|
|
69
|
+
return true;
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The earliest known event a read ahead's copies have yet to reach before `t`, mix time: the next
|
|
74
|
+
* call the tape holds, and while a voice could carry state across one, a mark of a voice or one
|
|
75
|
+
* announced. Infinity where there is none.
|
|
76
|
+
*/
|
|
77
|
+
function nextEvent(tape, c, t) {
|
|
78
|
+
const now = c.now;
|
|
79
|
+
let e = Number.POSITIVE_INFINITY;
|
|
80
|
+
const call = tape?.timestampAt(tape.undoDepth());
|
|
81
|
+
if (call !== undefined)
|
|
82
|
+
e = c.pace === null ? call : c.pace.reading(call);
|
|
83
|
+
if (!carries(c))
|
|
84
|
+
return e < t ? e : Number.POSITIVE_INFINITY;
|
|
85
|
+
for (const m of c.members)
|
|
86
|
+
for (const v of m.cued) {
|
|
87
|
+
if (v.state === 'done')
|
|
88
|
+
continue;
|
|
89
|
+
for (const mark of voiceMarks) {
|
|
90
|
+
const at = markOf(m, v, mark);
|
|
91
|
+
if (at !== undefined && at > now && at < e)
|
|
92
|
+
e = at;
|
|
93
|
+
}
|
|
94
|
+
const inner = v.spec.marks;
|
|
95
|
+
if (inner !== undefined)
|
|
96
|
+
for (const mark of Object.keys(inner)) {
|
|
97
|
+
const at = markOf(m, v, mark);
|
|
98
|
+
if (at !== undefined && at > now && at < e)
|
|
99
|
+
e = at;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const a of c.announced) {
|
|
103
|
+
const at = c.pace === null ? a.at : c.pace.at(a.at, c.u);
|
|
104
|
+
if (at > now && at < e)
|
|
105
|
+
e = at;
|
|
106
|
+
}
|
|
107
|
+
return e < t ? e : Number.POSITIVE_INFINITY;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Makes the calls the tape holds up to mix time `to` on a read ahead's copies and moves them
|
|
111
|
+
* there. Answers whether it moved the tape.
|
|
112
|
+
*/
|
|
113
|
+
function playTo(transport, c, copies, to) {
|
|
114
|
+
const tape = transport.tape;
|
|
115
|
+
const due = (at) => (c.pace === null ? at : c.pace.reading(at)) <= to;
|
|
116
|
+
const next = tape?.timestampAt(tape.undoDepth());
|
|
117
|
+
c.u = hostAhead(c, to);
|
|
118
|
+
let played = false;
|
|
119
|
+
if (next !== undefined && due(next)) {
|
|
120
|
+
// A call may write what a copy shares with the voice it was copied from.
|
|
121
|
+
for (const copy of c.members)
|
|
122
|
+
for (const v of copy.cued) {
|
|
123
|
+
if (v.parts !== null)
|
|
124
|
+
v.parts = new Map(v.parts);
|
|
125
|
+
if (v.parted !== null)
|
|
126
|
+
v.parted = new Map(v.parted);
|
|
127
|
+
}
|
|
128
|
+
played = true;
|
|
129
|
+
const last = replay(transport, due, c, {
|
|
130
|
+
transport: () => c,
|
|
131
|
+
mix: (of) => copies.get(of),
|
|
132
|
+
});
|
|
133
|
+
c.u = Math.max(last, hostAhead(c, to));
|
|
134
|
+
}
|
|
135
|
+
// A call played at `to` itself moved the copies there before it, as a sync does.
|
|
136
|
+
if (!(c.now >= to))
|
|
137
|
+
for (const copy of c.members)
|
|
138
|
+
move(copy, to);
|
|
139
|
+
return played;
|
|
140
|
+
}
|
|
141
|
+
/** A transport for a read ahead's copies to go on from where those on `c` stand. */
|
|
142
|
+
function restage(c) {
|
|
143
|
+
const next = new Transport(undefined, true);
|
|
144
|
+
next.offset = c.offset;
|
|
145
|
+
next.u = c.u;
|
|
146
|
+
next.nextId = c.nextId;
|
|
147
|
+
next.now = c.now;
|
|
148
|
+
next.pace = c.pace === null ? null : c.pace.until(Number.POSITIVE_INFINITY);
|
|
149
|
+
next.announced = c.announced.map((a) => ({ ...a }));
|
|
150
|
+
// The retargets and pushes the tape made carry their times, so every stop shares one list.
|
|
151
|
+
next.ahead = c.ahead;
|
|
152
|
+
return next;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* A mix's copy for a read ahead: its voices as they stand, each subject carried from the live one,
|
|
156
|
+
* or from the copy at the known event before.
|
|
157
|
+
*/
|
|
158
|
+
export function copyAhead(mix, c) {
|
|
159
|
+
mirror(mix, c);
|
|
160
|
+
c.cued = mix.cued
|
|
161
|
+
.filter((v) => v.state !== 'done')
|
|
162
|
+
.map((v) => {
|
|
163
|
+
const link = {
|
|
164
|
+
mix,
|
|
165
|
+
voice: v,
|
|
166
|
+
up: links.get(v),
|
|
167
|
+
seen: 0,
|
|
168
|
+
shared: false,
|
|
169
|
+
filled: false,
|
|
170
|
+
};
|
|
171
|
+
const copy = v.copy((subject) => carry(link, subject, c, copy), undefined, true);
|
|
172
|
+
link.seen = copy.seen;
|
|
173
|
+
link.shared = copy.sharing;
|
|
174
|
+
links.set(copy, link);
|
|
175
|
+
const at = mix.pins?.get(v);
|
|
176
|
+
if (at !== undefined)
|
|
177
|
+
pin(c, copy, at);
|
|
178
|
+
return copy;
|
|
179
|
+
});
|
|
180
|
+
c.waking = mix.waking;
|
|
181
|
+
ownBlends(c.cued);
|
|
182
|
+
// A copy keeps no list of owners, so the one before says only by its voices.
|
|
183
|
+
if (mix.owners !== null || (mix.projecting && c.cued.some((v) => v.holding !== null)))
|
|
184
|
+
relink(c.cued);
|
|
185
|
+
recount(c);
|
|
186
|
+
}
|
|
187
|
+
/** Gives a projection's copy what it reads of the live mix as it is, before its voices are copied. */
|
|
188
|
+
export function mirror(mix, c) {
|
|
189
|
+
c.lastPoses = mix.lastPoses;
|
|
190
|
+
c.wantsPose = mix.wantsPose;
|
|
191
|
+
c.reducedNow = mix.reducedNow;
|
|
192
|
+
}
|
|
193
|
+
/** A voice's record of a subject, the one it shares among those it does not reach included. */
|
|
194
|
+
export function recordOf(mix, voice, subject) {
|
|
195
|
+
// A voice sharing one record has that for every subject a chain has asked it about, pointed at
|
|
196
|
+
// the subject for the caller to copy from.
|
|
197
|
+
if (voice.sharing) {
|
|
198
|
+
const slot = headOf(mix, subject)?.slot ?? -1;
|
|
199
|
+
if (!sighted(voice, slot))
|
|
200
|
+
return undefined;
|
|
201
|
+
const all = voice.everyone;
|
|
202
|
+
open(voice, all, slot);
|
|
203
|
+
all.weight = weightAt(voice, slot);
|
|
204
|
+
return all;
|
|
205
|
+
}
|
|
206
|
+
const held = voice.subjects.get(subject);
|
|
207
|
+
if (held !== undefined || voice.unreachedBits === null)
|
|
208
|
+
return held;
|
|
209
|
+
return unreached(voice, headOf(mix, subject)?.slot ?? -1)
|
|
210
|
+
? voice.unreached
|
|
211
|
+
: undefined;
|
|
212
|
+
}
|
|
213
|
+
/** Each read ahead copy's link, by the copy. */
|
|
214
|
+
const links = new WeakMap();
|
|
215
|
+
/**
|
|
216
|
+
* Whether a read ahead's copy of a voice holds a record only where the copy it was made from does:
|
|
217
|
+
* it has met no subject, taken no record, shares none, and reaches none it was asked about.
|
|
218
|
+
*/
|
|
219
|
+
function empty(voice, link) {
|
|
220
|
+
return (voice.seen === link.seen &&
|
|
221
|
+
!link.filled &&
|
|
222
|
+
!link.shared &&
|
|
223
|
+
voice.unreached === null &&
|
|
224
|
+
voice.unreachedBits === null);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Whether a subject's record on a read ahead's copy at a stop can go on to the copy at the next
|
|
228
|
+
* stop as it is: each stop reads a subject once, before the next carries it on, and nothing reads
|
|
229
|
+
* the record there again. Not one a voice shares among subjects, or one the live mix holds.
|
|
230
|
+
*/
|
|
231
|
+
function handable(mix, voice, h) {
|
|
232
|
+
return mix.projecting && h.reaches && !voice.sharing && h !== voice.unreached;
|
|
233
|
+
}
|
|
234
|
+
/** A record handed on as `copyHeld` would copy it, its place in the stop's chain let go. */
|
|
235
|
+
function handOn(h) {
|
|
236
|
+
h.restored = null;
|
|
237
|
+
return unread(h);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A projection ahead starts each subject from where the live mix holds it, its `since` worked out
|
|
241
|
+
* again where it was still ahead, since the copy's clock may have moved it.
|
|
242
|
+
*/
|
|
243
|
+
function carry(link, subject, c, copy) {
|
|
244
|
+
const mix = link.mix;
|
|
245
|
+
const voice = link.voice;
|
|
246
|
+
// A voice starting many stops on would otherwise ask every copy before it, one by one. Where
|
|
247
|
+
// the first copy that may hold one has none, each copy after it has none, dropped or not.
|
|
248
|
+
let from = link;
|
|
249
|
+
while (from.up !== undefined && empty(from.voice, from.up))
|
|
250
|
+
from = from.up;
|
|
251
|
+
if (from !== link && recordOf(from.mix, from.voice, subject) === undefined)
|
|
252
|
+
return undefined;
|
|
253
|
+
const live = recordOf(mix, voice, subject);
|
|
254
|
+
if (live === undefined)
|
|
255
|
+
return undefined;
|
|
256
|
+
const h = handable(mix, voice, live) ? handOn(live) : copyHeld(voice, live);
|
|
257
|
+
if (h.reaches && !(h.since <= mix.now)) {
|
|
258
|
+
h.since = c.sinceOf(copy, h.delay);
|
|
259
|
+
h.shown = c.shownOf(copy, h.since);
|
|
260
|
+
}
|
|
261
|
+
h.from = h.stepped;
|
|
262
|
+
link.filled = true;
|
|
263
|
+
return h;
|
|
264
|
+
}
|
package/dist/around.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Circle, Easing, Signal } from './types.js';
|
|
3
|
+
/** The circle a weight carries: an `around` signal's own, or none. */
|
|
4
|
+
export declare const carried: (weight: unknown) => Circle | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* `Mix.around`: 1 at the circle's center, falling with distance over the positions `plot` gave to 0
|
|
7
|
+
* at `r` and beyond, along `ease` read from the edge (0) to the center (1).
|
|
8
|
+
*/
|
|
9
|
+
export declare function around<I, O>(this: Mixer<I, O>, circle: Circle, opts?: {
|
|
10
|
+
ease?: Easing;
|
|
11
|
+
}): Signal<I>;
|
package/dist/around.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { curve } from './easing.js';
|
|
2
|
+
/** Each `around` signal's circle, which a voice it weighs is bounded to as if cued `within` it. */
|
|
3
|
+
const circles = new WeakMap();
|
|
4
|
+
/** The circle a weight carries: an `around` signal's own, or none. */
|
|
5
|
+
export const carried = (weight) => typeof weight === 'function' ? circles.get(weight) : undefined;
|
|
6
|
+
/**
|
|
7
|
+
* `Mix.around`: 1 at the circle's center, falling with distance over the positions `plot` gave to 0
|
|
8
|
+
* at `r` and beyond, along `ease` read from the edge (0) to the center (1).
|
|
9
|
+
*/
|
|
10
|
+
export function around(circle, opts = {}) {
|
|
11
|
+
if (typeof circle !== 'object' || circle === null)
|
|
12
|
+
throw new TypeError('blits: around takes a circle, { x, y, r }');
|
|
13
|
+
const shape = curve(opts.ease ?? 'linear');
|
|
14
|
+
const read = (subject) => {
|
|
15
|
+
const places = this.places;
|
|
16
|
+
if (places === null)
|
|
17
|
+
return 0;
|
|
18
|
+
const i = places.cur >= 0 ? places.cur : (places.at.get(subject) ?? -1);
|
|
19
|
+
const xy = places.xy;
|
|
20
|
+
if (i < 0 || 2 * i + 1 >= xy.length)
|
|
21
|
+
return 0;
|
|
22
|
+
const r = circle.r;
|
|
23
|
+
const dx = xy[2 * i] - circle.x;
|
|
24
|
+
const dy = xy[2 * i + 1] - circle.y;
|
|
25
|
+
const d2 = dx * dx + dy * dy;
|
|
26
|
+
if (!(r > 0) || !(d2 <= r * r))
|
|
27
|
+
return 0;
|
|
28
|
+
return shape(1 - Math.sqrt(d2) / r);
|
|
29
|
+
};
|
|
30
|
+
circles.set(read, circle);
|
|
31
|
+
return read;
|
|
32
|
+
}
|
package/dist/bare.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { foldNumber } from './channels.js';
|
|
2
2
|
import { bare } from './crowd.js';
|
|
3
|
+
import { keepProbed } from './lane.js';
|
|
3
4
|
import { between, left, progress } from './tweened.js';
|
|
4
5
|
/** A `Flag.FAST` row's voice time at `now`, before its delay. */
|
|
5
6
|
export function clock(hot, h, now) {
|
|
@@ -26,12 +27,16 @@ export function bareRows(lanes, c, p, id) {
|
|
|
26
27
|
const lately = lanes.lately;
|
|
27
28
|
const now = lanes.now;
|
|
28
29
|
const op = ch.op;
|
|
30
|
+
// Ids are finite, so a fill running to the end need not read each row's.
|
|
31
|
+
const bounded = id !== Number.POSITIVE_INFINITY;
|
|
29
32
|
for (; p < list.length; p++) {
|
|
30
33
|
const h = p * H;
|
|
31
|
-
|
|
34
|
+
const f = hot[h + 0 /* Hot.FLAGS */];
|
|
35
|
+
if ((bounded && hot[h + 7 /* Hot.ID */] >= id) || !bare(f))
|
|
32
36
|
break;
|
|
33
37
|
const o = p * 8 /* Row.STRIDE */;
|
|
34
|
-
|
|
38
|
+
const ready = (f & 2048 /* Flag.READY */) !== 0;
|
|
39
|
+
if (!ready && (data[o + 4 /* Row.MSLOT */] < 0 || data[o + 7 /* Row.MET */] === 0))
|
|
35
40
|
break;
|
|
36
41
|
const slot = list[p];
|
|
37
42
|
const q = slot * 6 /* Per.SLOT */;
|
|
@@ -46,13 +51,15 @@ export function bareRows(lanes, c, p, id) {
|
|
|
46
51
|
const elapsed = clock(hot, h, now) - data[o + 0 /* Row.DELAY */];
|
|
47
52
|
if (!(elapsed >= 0))
|
|
48
53
|
break;
|
|
49
|
-
|
|
50
|
-
data[o + 3 /* Row.PROBED */] = data[o + 2 /* Row.WEIGHT */];
|
|
54
|
+
keepProbed(data, o, per, q, fills);
|
|
51
55
|
const w = hot[h + 4 /* Hot.WEIGHT */];
|
|
52
56
|
data[o + 2 /* Row.WEIGHT */] = w;
|
|
53
|
-
if (
|
|
54
|
-
data[o + 5 /* Row.SAMPLED */]
|
|
55
|
-
|
|
57
|
+
if (!ready) {
|
|
58
|
+
if (data[o + 5 /* Row.SAMPLED */] !== -2 /* Sampled.BARE */) {
|
|
59
|
+
data[o + 5 /* Row.SAMPLED */] = -2 /* Sampled.BARE */;
|
|
60
|
+
c.deltas[p] = null;
|
|
61
|
+
}
|
|
62
|
+
hot[h + 0 /* Hot.FLAGS */] = f | 2048 /* Flag.READY */;
|
|
56
63
|
}
|
|
57
64
|
if (w <= 0)
|
|
58
65
|
continue;
|
|
@@ -77,6 +84,7 @@ export function unbare(c, p) {
|
|
|
77
84
|
return;
|
|
78
85
|
const hot = c.hot;
|
|
79
86
|
const h = p * c.stride;
|
|
87
|
+
hot[h + 0 /* Hot.FLAGS */] = hot[h + 0 /* Hot.FLAGS */] & ~2048 /* Flag.READY */;
|
|
80
88
|
const n = c.axes;
|
|
81
89
|
const elapsed = clock(hot, h, c.bareNow) - data[o + 0 /* Row.DELAY */];
|
|
82
90
|
const share = left(c.eases[p], progress(elapsed, hot[h + 8 /* Hot.AT */], hot[h + 9 /* Hot.MS */]));
|
package/dist/blank.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Channel } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A maker of empty poses for one kit: plain objects holding each channel with a rest, in kit order,
|
|
4
|
+
* a number rest at its value and any other as `undefined` until the fold writes it.
|
|
5
|
+
*
|
|
6
|
+
* `{}` shares V8's chain of shapes with every small object literal, so a host's
|
|
7
|
+
* `kit({ gain: mul() })` leaves `gain` a field that boxes each number stored in it. A constructor of
|
|
8
|
+
* the mix's own starts a chain of its own, where a number field stays a double.
|
|
9
|
+
*/
|
|
10
|
+
export declare function blanks<O>(names: readonly string[], channels: readonly Channel<unknown>[]): () => O;
|
package/dist/blank.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A maker of empty poses for one kit: plain objects holding each channel with a rest, in kit order,
|
|
3
|
+
* a number rest at its value and any other as `undefined` until the fold writes it.
|
|
4
|
+
*
|
|
5
|
+
* `{}` shares V8's chain of shapes with every small object literal, so a host's
|
|
6
|
+
* `kit({ gain: mul() })` leaves `gain` a field that boxes each number stored in it. A constructor of
|
|
7
|
+
* the mix's own starts a chain of its own, where a number field stays a double.
|
|
8
|
+
*/
|
|
9
|
+
export function blanks(names, channels) {
|
|
10
|
+
const keys = [];
|
|
11
|
+
const starts = [];
|
|
12
|
+
for (let i = 0; i < names.length; i++) {
|
|
13
|
+
const rest = channels[i].rest;
|
|
14
|
+
if (rest === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
keys.push(names[i]);
|
|
17
|
+
starts.push(typeof rest === 'number' ? rest : undefined);
|
|
18
|
+
}
|
|
19
|
+
function Pose() {
|
|
20
|
+
for (let i = 0; i < keys.length; i++)
|
|
21
|
+
this[keys[i]] = starts[i];
|
|
22
|
+
}
|
|
23
|
+
Pose.prototype = Object.prototype;
|
|
24
|
+
const P = Pose;
|
|
25
|
+
return () => new P();
|
|
26
|
+
}
|