@msb235/blits 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +3 -3
  3. package/dist/ahead.d.ts +26 -0
  4. package/dist/ahead.js +264 -0
  5. package/dist/around.d.ts +11 -0
  6. package/dist/around.js +32 -0
  7. package/dist/bare.js +15 -7
  8. package/dist/blank.d.ts +10 -0
  9. package/dist/blank.js +26 -0
  10. package/dist/blend.d.ts +2 -1
  11. package/dist/blend.js +1 -6
  12. package/dist/bound.d.ts +70 -0
  13. package/dist/bound.js +351 -0
  14. package/dist/chain.d.ts +2 -1
  15. package/dist/chain.js +7 -5
  16. package/dist/changes.d.ts +24 -0
  17. package/dist/changes.js +189 -0
  18. package/dist/channels.d.ts +39 -13
  19. package/dist/channels.js +14 -5
  20. package/dist/color.d.ts +12 -10
  21. package/dist/color.js +25 -25
  22. package/dist/columns.js +7 -3
  23. package/dist/contribution.d.ts +30 -0
  24. package/dist/contribution.js +374 -0
  25. package/dist/copyout.d.ts +10 -0
  26. package/dist/copyout.js +69 -24
  27. package/dist/crowd.d.ts +8 -2
  28. package/dist/crowd.js +4 -5
  29. package/dist/cue.d.ts +8 -1
  30. package/dist/cue.js +39 -13
  31. package/dist/cut.d.ts +8 -0
  32. package/dist/cut.js +4 -0
  33. package/dist/drop.d.ts +9 -0
  34. package/dist/drop.js +84 -0
  35. package/dist/due.d.ts +24 -5
  36. package/dist/due.js +113 -33
  37. package/dist/easing.d.ts +4 -4
  38. package/dist/easing.js +3 -3
  39. package/dist/everyone.d.ts +3 -2
  40. package/dist/everyone.js +3 -3
  41. package/dist/fade.d.ts +2 -1
  42. package/dist/fade.js +2 -1
  43. package/dist/fill.d.ts +3 -2
  44. package/dist/fill.js +51 -19
  45. package/dist/fit.d.ts +44 -26
  46. package/dist/fit.js +10 -9
  47. package/dist/fold.d.ts +4 -37
  48. package/dist/fold.js +5 -513
  49. package/dist/gather.d.ts +12 -7
  50. package/dist/gather.js +179 -44
  51. package/dist/gone.js +2 -2
  52. package/dist/grid.d.ts +47 -0
  53. package/dist/grid.js +247 -0
  54. package/dist/handle.d.ts +2 -0
  55. package/dist/handle.js +14 -8
  56. package/dist/held.d.ts +2 -1
  57. package/dist/history.d.ts +6 -5
  58. package/dist/history.js +7 -9
  59. package/dist/hosts.js +9 -15
  60. package/dist/index.d.ts +5 -4
  61. package/dist/keyfill.d.ts +1 -1
  62. package/dist/keyfill.js +5 -3
  63. package/dist/keyrows.d.ts +12 -0
  64. package/dist/keyrows.js +99 -12
  65. package/dist/lane.d.ts +36 -6
  66. package/dist/lane.js +32 -1
  67. package/dist/lanes.d.ts +23 -8
  68. package/dist/lanes.js +29 -8
  69. package/dist/leavings.d.ts +2 -2
  70. package/dist/leavings.js +1 -1
  71. package/dist/lives.d.ts +47 -0
  72. package/dist/lives.js +180 -0
  73. package/dist/meet.d.ts +2 -2
  74. package/dist/meet.js +90 -4
  75. package/dist/mixer.d.ts +54 -36
  76. package/dist/mixer.js +53 -171
  77. package/dist/motion.d.ts +14 -118
  78. package/dist/motion.js +16 -8
  79. package/dist/motions.d.ts +62 -155
  80. package/dist/motions.js +56 -370
  81. package/dist/moved.d.ts +1 -1
  82. package/dist/moved.js +16 -7
  83. package/dist/named.d.ts +0 -1
  84. package/dist/named.js +20 -2
  85. package/dist/numbers.d.ts +2 -0
  86. package/dist/numbers.js +4 -0
  87. package/dist/owed.d.ts +2 -0
  88. package/dist/owed.js +4 -0
  89. package/dist/pack.d.ts +2 -1
  90. package/dist/pack.js +2 -3
  91. package/dist/patch.d.ts +7 -3
  92. package/dist/patch.js +6 -2
  93. package/dist/places.d.ts +53 -0
  94. package/dist/places.js +171 -0
  95. package/dist/plot.d.ts +12 -0
  96. package/dist/plot.js +52 -0
  97. package/dist/project.d.ts +4 -4
  98. package/dist/project.js +9 -212
  99. package/dist/pull.js +11 -8
  100. package/dist/qualify.js +84 -21
  101. package/dist/reading.d.ts +6 -0
  102. package/dist/reading.js +5 -0
  103. package/dist/record.d.ts +5 -1
  104. package/dist/record.js +3 -2
  105. package/dist/relink.d.ts +1 -1
  106. package/dist/restored.d.ts +2 -2
  107. package/dist/restored.js +1 -1
  108. package/dist/rests.d.ts +9 -0
  109. package/dist/rests.js +76 -0
  110. package/dist/revive.d.ts +3 -2
  111. package/dist/revive.js +2 -2
  112. package/dist/{locus.d.ts → role.d.ts} +10 -9
  113. package/dist/{locus.js → role.js} +3 -3
  114. package/dist/roles.d.ts +10 -0
  115. package/dist/roles.js +158 -0
  116. package/dist/roster.js +3 -3
  117. package/dist/rotation.d.ts +1 -1
  118. package/dist/rotation.js +1 -1
  119. package/dist/rows.js +1 -1
  120. package/dist/sample.d.ts +2 -2
  121. package/dist/sample.js +33 -7
  122. package/dist/seek.js +5 -5
  123. package/dist/setting.d.ts +27 -0
  124. package/dist/setting.js +55 -0
  125. package/dist/shared.d.ts +1 -1
  126. package/dist/shared.js +1 -1
  127. package/dist/signals.d.ts +64 -32
  128. package/dist/signals.js +20 -18
  129. package/dist/stack.d.ts +5 -5
  130. package/dist/stack.js +5 -5
  131. package/dist/standing.d.ts +2 -1
  132. package/dist/standing.js +3 -2
  133. package/dist/store.d.ts +9 -0
  134. package/dist/store.js +24 -0
  135. package/dist/strays.d.ts +2 -1
  136. package/dist/strays.js +20 -13
  137. package/dist/stretch.d.ts +112 -0
  138. package/dist/stretch.js +9 -0
  139. package/dist/subject.d.ts +142 -0
  140. package/dist/subject.js +1 -0
  141. package/dist/tape.d.ts +9 -5
  142. package/dist/tape.js +5 -1
  143. package/dist/then.d.ts +9 -0
  144. package/dist/then.js +57 -0
  145. package/dist/ticker.d.ts +4 -4
  146. package/dist/transport.d.ts +0 -8
  147. package/dist/transport.js +0 -4
  148. package/dist/turns.d.ts +4 -3
  149. package/dist/turns.js +4 -3
  150. package/dist/types/arc.d.ts +12 -11
  151. package/dist/types/channel.d.ts +12 -8
  152. package/dist/types/history.d.ts +35 -19
  153. package/dist/types/mix.d.ts +222 -163
  154. package/dist/types/motion.d.ts +151 -0
  155. package/dist/types/motion.js +1 -0
  156. package/dist/types/patch.d.ts +67 -34
  157. package/dist/types/place.d.ts +51 -36
  158. package/dist/types/transport.d.ts +20 -18
  159. package/dist/types/voice.d.ts +136 -70
  160. package/dist/types/watch.d.ts +88 -40
  161. package/dist/types.d.ts +1 -0
  162. package/dist/voice.d.ts +7 -143
  163. package/dist/voice.js +28 -79
  164. package/dist/warn.d.ts +2 -0
  165. package/dist/warn.js +1 -1
  166. package/dist/watch.d.ts +2 -2
  167. package/dist/watch.js +5 -3
  168. package/dist/wave.d.ts +4 -3
  169. package/dist/wave.js +3 -2
  170. package/dist/weigh.d.ts +7 -1
  171. package/dist/weigh.js +27 -0
  172. package/docs/schema.html +212 -34
  173. package/package.json +2 -2
@@ -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
- * Position axes, rotation, crawl, yaw, pitch, offset.
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
- * Gain, scale, opacity, hold.
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
- * Dark, aperture.
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
- * A channel with no rest: the last contribution to pass wins, and a weight can only gate it. The
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
- * `N` numbers: a tuple where `N` is a literal, as `Vec<3>` is `[number, number, number]`, and
94
- * `number[]` where it is only known to be a number.
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[] : Tuple<N>;
125
+ export type Vec<N extends number> = number extends N ? number[] : NumberTuple<N>;
99
126
  /**
100
- * Vec3 position, premultiplied light: one channel's arithmetic applied down an axis list.
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
- * Position axes, rotation, crawl, yaw, pitch, offset.
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
- * Gain, scale, opacity, hold.
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
- * Dark, aperture.
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: the last contribution to pass wins, and a weight can only gate it. The
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
- * Vec3 position, premultiplied light: one channel's arithmetic applied down an axis list.
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, so a crossfade between hues keeps its lightness and saturation
4
- * instead of dipping through a muddy middle. A mix that leaves sRGB's gamut is clipped per channel.
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: straight across OKLab, or in OKLCH with hue the
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, off lanes.
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 as a `color` value: OKLab `[L, a, b]` at full coverage. Convert keyframe stops once
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: its color divided out of its coverage, the coverage capped at 1
39
- * and laid over `under` in OKLab, then clipped to sRGB per channel. No coverage gives `under`.
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 with its coverage, capped at 1, as the alpha, for a
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, so a crossfade between hues keeps its lightness and saturation
86
- * instead of dipping through a muddy middle. A mix that leaves sRGB's gamut is clipped per channel.
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 from = ac < GRAY ? slotH[sb] : ah;
102
- const to = bc < GRAY ? ah : slotH[sb];
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 fromOklch(mix(al, slotL[sb], u), mix(ac, bc, u), from + turn * u);
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 = from + turn * u;
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
- // A lane interpolates a stock channel straight across, so a lerp of its own takes it off them.
153
- return round ? { ...channel, lerp: lerpLch } : channel;
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
- if (!round)
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 as a `color` value: OKLab `[L, a, b]` at full coverage. Convert keyframe stops once
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: its color divided out of its coverage, the coverage capped at 1
180
- * and laid over `under` in OKLab, then clipped to sRGB per channel. No coverage gives `under`.
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 with its coverage, capped at 1, as the alpha, for a
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.js CHANGED
@@ -22,6 +22,9 @@ 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
29
  if (columns !== lanes.queuedFor) {
27
30
  flush(lanes);
@@ -47,14 +50,15 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
47
50
  per[o + 0 /* Per.FILLED */] >= busy ||
48
51
  per[o + 2 /* Per.IDLE */] !== 0 ||
49
52
  per[o + 1 /* Per.SEEN */] < wide ||
50
- lanes.owes(slot))
53
+ (owing && lanes.owes(slot)))
51
54
  break;
52
55
  if (!(per[o + 3 /* Per.LANE_PROBE */] > frame ||
53
56
  per[o + 4 /* Per.GENERAL_PROBE */] > frame))
54
57
  lanes.distinct++;
55
58
  per[o + 3 /* Per.LANE_PROBE */] = ++lanes.probes;
56
59
  per[o + 5 /* Per.LANE_FILL */] = fills;
57
- subjects[slot] = subject;
60
+ if (holds)
61
+ subjects[slot] = subject;
58
62
  if (slot !== first + (n - start)) {
59
63
  if (first >= 0) {
60
64
  qSlots[k] = first;
@@ -73,7 +77,7 @@ export function pullRun(lanes, slots, list, was, since, from, columns, now, vers
73
77
  k++;
74
78
  }
75
79
  lanes.queued = k;
76
- if (n > from)
80
+ if (n > from && holds)
77
81
  lanes.holding = true;
78
82
  return n;
79
83
  }
@@ -0,0 +1,30 @@
1
+ import type { Mixer } from './mixer.js';
2
+ import type { Subject } from './subject.js';
3
+ import { type Voice } from './voice.js';
4
+ /**
5
+ * One live voice's delta for one subject this frame, through its record for the subject, or null
6
+ * when it does not reach. Its weight is left in `w`.
7
+ */
8
+ export declare function contribution<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number, held: Subject<unknown>): Record<string, unknown> | null;
9
+ /** Reads a keys voice's stops at `held.phase` into the subject's delta, through the voice's scratch. */
10
+ export declare function keyed<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>): Record<string, unknown>;
11
+ /**
12
+ * Runs `step` once per whole interval between where this subject last stopped and now, on a grid
13
+ * counted from when its delay ran out, and leaves the remainder for the next sample. Counting
14
+ * rather than adding keeps the grid where it is however many samples it is reached through.
15
+ */
16
+ export declare function tick<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, held: Subject<unknown>, tick: number, to: number): void;
17
+ /**
18
+ * Stop 0 for a retargeting voice: the subject's pose in the frame before this voice contributes.
19
+ * With no such frame on record — a voice cued before the subject was ever probed, or a host that
20
+ * samples into its own object — it is this frame's pose with every other voice folded in.
21
+ */
22
+ export declare function baseFor<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I): Record<string, unknown>;
23
+ /**
24
+ * How fast each numeric channel this voice writes was moving, from the subject's last two probed
25
+ * poses, in units per ms. Undefined with fewer than two on record, which starts the voice at rest.
26
+ */
27
+ export declare function slopeFor<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I): Record<string, unknown> | undefined;
28
+ export declare function isRest<I, O>(this: Mixer<I, O>, delta: Record<string, unknown>): boolean;
29
+ /** `contribution`, or for a dry fold a subject already probed this frame, without advancing it. */
30
+ export declare function read<I, O>(this: Mixer<I, O>, voice: Voice<I, O>, subject: I, now: number, dry: boolean, held: Subject<unknown>): Record<string, unknown> | null;