@msb235/blits 0.3.0 → 0.5.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 (102) hide show
  1. package/CHANGELOG.md +314 -0
  2. package/README.md +3 -2
  3. package/dist/bare.d.ts +29 -0
  4. package/dist/bare.js +105 -0
  5. package/dist/blend.d.ts +34 -0
  6. package/dist/blend.js +102 -0
  7. package/dist/book.d.ts +53 -0
  8. package/dist/book.js +218 -0
  9. package/dist/chain.d.ts +22 -0
  10. package/dist/chain.js +150 -0
  11. package/dist/channels.d.ts +2 -0
  12. package/dist/channels.js +3 -1
  13. package/dist/clock.d.ts +50 -3
  14. package/dist/clock.js +102 -9
  15. package/dist/clone.d.ts +7 -0
  16. package/dist/clone.js +57 -0
  17. package/dist/columns.d.ts +29 -0
  18. package/dist/columns.js +183 -0
  19. package/dist/crowd.d.ts +128 -0
  20. package/dist/crowd.js +354 -0
  21. package/dist/cue.d.ts +3 -0
  22. package/dist/cue.js +115 -0
  23. package/dist/due.d.ts +12 -0
  24. package/dist/due.js +93 -0
  25. package/dist/fade.d.ts +19 -0
  26. package/dist/fade.js +133 -0
  27. package/dist/fill.d.ts +42 -0
  28. package/dist/fill.js +381 -0
  29. package/dist/fold.d.ts +51 -0
  30. package/dist/fold.js +475 -0
  31. package/dist/gather.d.ts +24 -0
  32. package/dist/gather.js +186 -0
  33. package/dist/handle.d.ts +31 -0
  34. package/dist/handle.js +56 -0
  35. package/dist/held.d.ts +14 -0
  36. package/dist/held.js +117 -0
  37. package/dist/history.d.ts +22 -0
  38. package/dist/history.js +149 -0
  39. package/dist/hosts.d.ts +16 -0
  40. package/dist/hosts.js +178 -0
  41. package/dist/index.d.ts +1 -1
  42. package/dist/keyrows.d.ts +36 -0
  43. package/dist/keyrows.js +138 -0
  44. package/dist/lane.d.ts +210 -0
  45. package/dist/lane.js +187 -0
  46. package/dist/lanes.d.ts +143 -122
  47. package/dist/lanes.js +236 -742
  48. package/dist/locus.d.ts +40 -0
  49. package/dist/locus.js +89 -0
  50. package/dist/marks.d.ts +12 -0
  51. package/dist/marks.js +80 -0
  52. package/dist/meet.d.ts +16 -0
  53. package/dist/meet.js +113 -0
  54. package/dist/mixer.d.ts +244 -169
  55. package/dist/mixer.js +321 -1793
  56. package/dist/motion.d.ts +104 -19
  57. package/dist/motion.js +448 -180
  58. package/dist/move.d.ts +9 -0
  59. package/dist/move.js +190 -0
  60. package/dist/named.d.ts +12 -0
  61. package/dist/named.js +39 -0
  62. package/dist/numbers.d.ts +18 -6
  63. package/dist/numbers.js +59 -31
  64. package/dist/owed.d.ts +24 -0
  65. package/dist/owed.js +51 -0
  66. package/dist/owner.d.ts +43 -0
  67. package/dist/owner.js +117 -0
  68. package/dist/pace.d.ts +40 -0
  69. package/dist/pace.js +113 -0
  70. package/dist/patch.d.ts +51 -10
  71. package/dist/patch.js +140 -45
  72. package/dist/place.d.ts +20 -0
  73. package/dist/place.js +196 -0
  74. package/dist/project.d.ts +4 -0
  75. package/dist/project.js +233 -0
  76. package/dist/pull.d.ts +5 -0
  77. package/dist/pull.js +175 -0
  78. package/dist/qualify.d.ts +31 -0
  79. package/dist/qualify.js +345 -0
  80. package/dist/relink.d.ts +25 -0
  81. package/dist/relink.js +59 -0
  82. package/dist/rows.d.ts +33 -0
  83. package/dist/rows.js +282 -0
  84. package/dist/sample.d.ts +21 -0
  85. package/dist/sample.js +232 -0
  86. package/dist/signals.d.ts +12 -9
  87. package/dist/signals.js +8 -5
  88. package/dist/store.d.ts +10 -3
  89. package/dist/store.js +44 -9
  90. package/dist/strays.d.ts +21 -0
  91. package/dist/strays.js +84 -0
  92. package/dist/tweened.d.ts +11 -0
  93. package/dist/tweened.js +48 -0
  94. package/dist/types.d.ts +261 -42
  95. package/dist/unreached.d.ts +5 -0
  96. package/dist/unreached.js +24 -0
  97. package/dist/voice.d.ts +240 -0
  98. package/dist/voice.js +322 -0
  99. package/dist/weigh.d.ts +26 -0
  100. package/dist/weigh.js +89 -0
  101. package/docs/schema.html +151 -46
  102. package/package.json +5 -2
package/dist/store.js CHANGED
@@ -1,22 +1,47 @@
1
+ /** What every store reads before its first write: never written, so always empty. */
2
+ const noWeak = new WeakMap();
3
+ const noStrong = new Map();
1
4
  /**
2
5
  * Per-subject storage keyed by identity. Objects go in a WeakMap and are forgotten when the host
3
6
  * drops them; anything else — a string id, the unit subject of a mix with no subject dimension —
4
7
  * goes in a Map that only `drop` and `clear` empty, which is why a host keying by value should
5
- * call `drop`.
8
+ * call `drop`. Each map is made when first written, since a mix holds a store per voice and patch;
9
+ * until then it is a shared empty one, so the field always holds a map.
6
10
  */
7
11
  export class Store {
8
- weak = new WeakMap();
9
- strong = new Map();
12
+ weak = noWeak;
13
+ strong = noStrong;
14
+ /**
15
+ * The first key that is not an object, and its value, held here until a second comes: a voice of
16
+ * one subject keyed by id would otherwise spend a Map's smallest table on it.
17
+ */
18
+ oneKey = none;
19
+ oneValue = undefined;
10
20
  get(key) {
11
- return typeof key === 'object' && key !== null
12
- ? this.weak.get(key)
13
- : this.strong.get(key);
21
+ if (typeof key === 'object' && key !== null)
22
+ return this.weak.get(key);
23
+ return same(this.oneKey, key) ? this.oneValue : this.strong.get(key);
14
24
  }
15
25
  set(key, value) {
16
- if (typeof key === 'object' && key !== null)
26
+ if (typeof key === 'object' && key !== null) {
27
+ if (this.weak === noWeak)
28
+ this.weak = new WeakMap();
17
29
  this.weak.set(key, value);
18
- else
19
- this.strong.set(key, value);
30
+ return;
31
+ }
32
+ if (this.strong === noStrong && (this.oneKey === none || same(this.oneKey, key))) {
33
+ this.oneKey = key;
34
+ this.oneValue = value;
35
+ return;
36
+ }
37
+ if (this.strong === noStrong)
38
+ this.strong = new Map();
39
+ if (this.oneKey !== none) {
40
+ this.strong.set(this.oneKey, this.oneValue);
41
+ this.oneKey = none;
42
+ this.oneValue = undefined;
43
+ }
44
+ this.strong.set(key, value);
20
45
  }
21
46
  has(key) {
22
47
  return this.get(key) !== undefined;
@@ -24,10 +49,20 @@ export class Store {
24
49
  delete(key) {
25
50
  if (typeof key === 'object' && key !== null)
26
51
  this.weak.delete(key);
52
+ else if (same(this.oneKey, key)) {
53
+ this.oneKey = none;
54
+ this.oneValue = undefined;
55
+ }
27
56
  else
28
57
  this.strong.delete(key);
29
58
  }
30
59
  clear() {
60
+ this.oneKey = none;
61
+ this.oneValue = undefined;
31
62
  this.strong.clear();
32
63
  }
33
64
  }
65
+ /** No key held inline. */
66
+ const none = Symbol('none');
67
+ /** A Map's key equality, SameValueZero: NaN finds NaN. */
68
+ const same = (a, b) => a === b || Object.is(a, b);
@@ -0,0 +1,21 @@
1
+ import type { Mixer } from './mixer.js';
2
+ import type { MotionOwner, Motions } from './motion.js';
3
+ import type { Voice } from './voice.js';
4
+ /** Notes a motion patch that may hold state for a subject no voice playing it reaches. */
5
+ export declare function stray<I, O>(mix: Mixer<I, O>, motion: Motions<I>, subject: I): void;
6
+ /** A motion voice left the list: if its patch plays on, its subjects' state there may stray. */
7
+ export declare function unplay<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>): void;
8
+ /**
9
+ * What a motion patch asks of the mix playing it, by its voice's id. It holds the mix weakly, so a
10
+ * patch the host keeps does not keep a mix it has let go of alive; a retired voice's patch no
11
+ * longer asks.
12
+ */
13
+ export declare function motionOwner<I, O>(mix: WeakRef<Mixer<I, O>>): MotionOwner;
14
+ /** The voice in the list with this id, which is in id order. */
15
+ export declare function cuedById<I, O>(mix: Mixer<I, O>, id: number): Voice<I, O> | undefined;
16
+ /**
17
+ * A subject's voice time at the latest frame, where a motion patch places an untimed change and a
18
+ * `read` with no time; NaN until the voice has started and met the subject, and while the
19
+ * subject's own time is still short of its stagger. A retired voice's patch no longer asks.
20
+ */
21
+ export declare function frameOf<I, O>(mix: Mixer<I, O>, voice: Voice<I, O>, subject: I): number;
package/dist/strays.js ADDED
@@ -0,0 +1,84 @@
1
+ import { unpart } from './fade.js';
2
+ /** Notes a motion patch that may hold state for a subject no voice playing it reaches. */
3
+ export function stray(mix, motion, subject) {
4
+ const list = mix.strays.get(subject);
5
+ if (list === undefined)
6
+ mix.strays.set(subject, [motion]);
7
+ else if (!list.includes(motion))
8
+ list.push(motion);
9
+ }
10
+ /** A motion voice left the list: if its patch plays on, its subjects' state there may stray. */
11
+ export function unplay(mix, voice) {
12
+ const motion = voice.motion;
13
+ if (motion === undefined)
14
+ return;
15
+ const left = (mix.playing.get(motion) ?? 1) - 1;
16
+ if (left <= 0) {
17
+ mix.playing.delete(motion);
18
+ mix.strayAll.delete(motion);
19
+ return;
20
+ }
21
+ mix.playing.set(motion, left);
22
+ if (voice.named === null)
23
+ mix.strayAll.add(motion);
24
+ else
25
+ for (const subject of voice.named)
26
+ stray(mix, motion, subject);
27
+ }
28
+ /**
29
+ * What a motion patch asks of the mix playing it, by its voice's id. It holds the mix weakly, so a
30
+ * patch the host keeps does not keep a mix it has let go of alive; a retired voice's patch no
31
+ * longer asks.
32
+ */
33
+ export function motionOwner(mix) {
34
+ return {
35
+ frame(id, subject) {
36
+ const m = mix.deref();
37
+ const v = m === undefined ? undefined : cuedById(m, id);
38
+ return m === undefined || v === undefined ? Number.NaN : frameOf(m, v, subject);
39
+ },
40
+ revive(id, subject) {
41
+ const m = mix.deref();
42
+ if (m === undefined)
43
+ return;
44
+ m.stir();
45
+ const v = cuedById(m, id);
46
+ if (v === undefined)
47
+ return;
48
+ if (v.motion !== undefined && v.named !== null && !v.named.has(subject))
49
+ stray(m, v.motion, subject);
50
+ unpart(m, v, subject);
51
+ },
52
+ };
53
+ }
54
+ /** The voice in the list with this id, which is in id order. */
55
+ export function cuedById(mix, id) {
56
+ const cued = mix.cued;
57
+ let lo = 0;
58
+ let hi = cued.length - 1;
59
+ while (lo <= hi) {
60
+ const mid = (lo + hi) >> 1;
61
+ const v = cued[mid];
62
+ if (v.id === id)
63
+ return v;
64
+ if (v.id < id)
65
+ lo = mid + 1;
66
+ else
67
+ hi = mid - 1;
68
+ }
69
+ return undefined;
70
+ }
71
+ /**
72
+ * A subject's voice time at the latest frame, where a motion patch places an untimed change and a
73
+ * `read` with no time; NaN until the voice has started and met the subject, and while the
74
+ * subject's own time is still short of its stagger. A retired voice's patch no longer asks.
75
+ */
76
+ export function frameOf(mix, voice, subject) {
77
+ if (Number.isNaN(mix.now) || voice.state === 'pending')
78
+ return Number.NaN;
79
+ const held = voice.subjects.get(subject);
80
+ if (held === undefined || !held.reaches)
81
+ return Number.NaN;
82
+ const t = voice.elapsedAt(mix.now) - held.delay;
83
+ return t < 0 ? Number.NaN : t;
84
+ }
@@ -0,0 +1,11 @@
1
+ import type { Curve } from './easing.js';
2
+ /** Turns the velocity `eased` works out on or off. */
3
+ export declare function sloped(on: boolean): void;
4
+ /** How far through a stretch released at `at` and lasting `ms` voice time `t` is, from 0. */
5
+ export declare function progress(t: number, at: number, ms: number): number;
6
+ /** The share of a tween's way left to go at `u`, 0 once it has arrived. */
7
+ export declare function left(ease: Curve, u: number): number;
8
+ /** An axis of a tween from `x0` to `goal` with `share` of the way left. */
9
+ export declare function between(x0: number, goal: number, share: number): number;
10
+ /** A tween's stretch at `u` into `xo` and `vo`: true once it has arrived. */
11
+ export declare function eased(ease: Curve, n: number, u: number, ms: number, x0: ArrayLike<number>, x: number, to: ArrayLike<number>, g: number, xo: Float64Array, vo: Float64Array): boolean;
@@ -0,0 +1,48 @@
1
+ /** How far a tween's slope is read either side of `u`, for an easing that is only a function. */
2
+ const SPAN = 1e-4;
3
+ /**
4
+ * Whether an evaluation wants a tween's velocity, which costs two more reads of its easing. Only
5
+ * `read` asks for it; a mix and a retarget never use it.
6
+ */
7
+ let slope = false;
8
+ /** The last tween sample's time, curve and length, and the share of the way it had left to go. */
9
+ const memo = { u: Number.NaN, ease: undefined, left: 0 };
10
+ /** Turns the velocity `eased` works out on or off. */
11
+ export function sloped(on) {
12
+ slope = on;
13
+ }
14
+ /** How far through a stretch released at `at` and lasting `ms` voice time `t` is, from 0. */
15
+ export function progress(t, at, ms) {
16
+ return Math.max(0, t - at) / ms;
17
+ }
18
+ /** The share of a tween's way left to go at `u`, 0 once it has arrived. */
19
+ export function left(ease, u) {
20
+ // Subjects released together share `u`, so a frame reads a bezier once, not once each.
21
+ if (u !== memo.u || ease !== memo.ease) {
22
+ memo.u = u;
23
+ memo.ease = ease;
24
+ memo.left = u >= 1 ? 0 : 1 - ease(u);
25
+ }
26
+ return memo.left;
27
+ }
28
+ /** An axis of a tween from `x0` to `goal` with `share` of the way left. */
29
+ export function between(x0, goal, share) {
30
+ return share === 0 ? goal : goal + (x0 - goal) * share;
31
+ }
32
+ /** A tween's stretch at `u` into `xo` and `vo`: true once it has arrived. */
33
+ export function eased(ease, n, u, ms, x0, x, to, g, xo, vo) {
34
+ const share = left(ease, u);
35
+ let rate = 0;
36
+ if (slope && u < 1) {
37
+ const lo = Math.max(0, u - SPAN);
38
+ const hi = Math.min(1, u + SPAN);
39
+ rate = ((ease(hi) - ease(lo)) / (hi - lo)) * (1000 / ms);
40
+ }
41
+ for (let i = 0; i < n; i++) {
42
+ const a = x0[x + i];
43
+ const goal = to[g + i];
44
+ xo[i] = between(a, goal, share);
45
+ vo[i] = -(a - goal) * rate;
46
+ }
47
+ return share === 0;
48
+ }