bireactive 0.3.1 → 0.3.3

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 (95) hide show
  1. package/README.md +14 -7
  2. package/dist/automerge/doc-cell.d.ts +24 -11
  3. package/dist/automerge/doc-cell.js +19 -13
  4. package/dist/automerge/index.d.ts +3 -2
  5. package/dist/automerge/index.js +6 -5
  6. package/dist/automerge/reconcile.d.ts +5 -2
  7. package/dist/automerge/reconcile.js +73 -15
  8. package/dist/core/_counts.js +5 -12
  9. package/dist/core/cell.d.ts +3 -3
  10. package/dist/core/cell.js +6 -7
  11. package/dist/core/derived-geometry.js +4 -7
  12. package/dist/core/index.d.ts +3 -1
  13. package/dist/core/index.js +3 -1
  14. package/dist/core/lenses/aggregates.d.ts +42 -52
  15. package/dist/core/lenses/aggregates.js +225 -116
  16. package/dist/core/lenses/geometry.d.ts +22 -4
  17. package/dist/core/lenses/geometry.js +59 -27
  18. package/dist/core/lenses/index.d.ts +5 -6
  19. package/dist/core/lenses/index.js +5 -6
  20. package/dist/core/lenses/memory.js +4 -17
  21. package/dist/core/lenses/numerical.d.ts +100 -0
  22. package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
  23. package/dist/core/lenses/point-cloud.d.ts +67 -0
  24. package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +218 -81
  25. package/dist/core/lenses/snap.d.ts +1 -1
  26. package/dist/core/lenses/snap.js +3 -10
  27. package/dist/core/lenses/text.d.ts +40 -0
  28. package/dist/core/lenses/text.js +202 -0
  29. package/dist/core/lifecycle.js +3 -6
  30. package/dist/core/linalg.js +5 -11
  31. package/dist/core/optic.js +10 -15
  32. package/dist/core/optics.js +4 -8
  33. package/dist/core/store.d.ts +1 -2
  34. package/dist/core/store.js +7 -15
  35. package/dist/core/traits.d.ts +4 -7
  36. package/dist/core/traits.js +8 -12
  37. package/dist/core/values/anchor.js +0 -4
  38. package/dist/core/values/arr.d.ts +110 -0
  39. package/dist/core/values/arr.js +336 -0
  40. package/dist/core/values/audio.d.ts +8 -9
  41. package/dist/core/values/audio.js +7 -23
  42. package/dist/core/values/bool.d.ts +11 -11
  43. package/dist/core/values/bool.js +12 -22
  44. package/dist/core/values/box.d.ts +15 -20
  45. package/dist/core/values/box.js +20 -33
  46. package/dist/core/values/canvas.d.ts +18 -25
  47. package/dist/core/values/canvas.js +17 -48
  48. package/dist/core/values/color.d.ts +5 -7
  49. package/dist/core/values/color.js +5 -11
  50. package/dist/core/values/field.d.ts +6 -7
  51. package/dist/core/values/field.js +10 -35
  52. package/dist/core/values/flags.d.ts +1 -2
  53. package/dist/core/values/flags.js +1 -17
  54. package/dist/core/values/gpu.d.ts +6 -10
  55. package/dist/core/values/gpu.js +8 -22
  56. package/dist/core/values/matrix.d.ts +2 -4
  57. package/dist/core/values/matrix.js +2 -12
  58. package/dist/core/values/num.d.ts +19 -28
  59. package/dist/core/values/num.js +23 -41
  60. package/dist/core/values/pose.d.ts +2 -4
  61. package/dist/core/values/pose.js +3 -12
  62. package/dist/core/values/range.d.ts +18 -26
  63. package/dist/core/values/range.js +22 -39
  64. package/dist/core/values/reg/ambiguity.d.ts +8 -0
  65. package/dist/core/values/reg/ambiguity.js +131 -0
  66. package/dist/core/values/reg/engine.d.ts +91 -0
  67. package/dist/core/values/reg/engine.js +373 -0
  68. package/dist/core/values/reg/nfa.d.ts +42 -0
  69. package/dist/core/values/reg/nfa.js +391 -0
  70. package/dist/core/values/reg/regex.d.ts +7 -0
  71. package/dist/core/values/reg/regex.js +318 -0
  72. package/dist/core/values/reg/types.d.ts +60 -0
  73. package/dist/core/values/reg/types.js +3 -0
  74. package/dist/core/values/reg.d.ts +250 -0
  75. package/dist/core/values/reg.js +649 -0
  76. package/dist/core/values/str.d.ts +16 -60
  77. package/dist/core/values/str.js +133 -315
  78. package/dist/core/values/template.js +1 -24
  79. package/dist/core/values/transform.d.ts +3 -5
  80. package/dist/core/values/transform.js +3 -12
  81. package/dist/core/values/tri.d.ts +9 -10
  82. package/dist/core/values/tri.js +9 -15
  83. package/dist/core/values/vec.d.ts +9 -24
  84. package/dist/core/values/vec.js +9 -64
  85. package/dist/index.d.ts +0 -11
  86. package/dist/index.js +1 -11
  87. package/package.json +17 -10
  88. package/dist/coll.d.ts +0 -74
  89. package/dist/coll.js +0 -210
  90. package/dist/core/lenses/closed-form-policies.d.ts +0 -57
  91. package/dist/core/lenses/decompositions.d.ts +0 -14
  92. package/dist/core/lenses/decompositions.js +0 -224
  93. package/dist/core/lenses/domain-aggregates.d.ts +0 -42
  94. package/dist/core/lenses/domain-aggregates.js +0 -245
  95. package/dist/core/lenses/typed-factor.d.ts +0 -40
@@ -1,7 +1,3 @@
1
- // color.ts — reactive RGBA color.
2
- //
3
- // Invertibles (`add`, `sub`, `scale`) return `: this` and ride on
4
- // `Cell#lens(fwd, bwd)`. Chained calls compose into a lens chain.
5
1
  import { tween } from "../../animation/index.js";
6
2
  import { Cell, cachedDerive, derive, fieldLens, lazy, reader, readNow, } from "../cell.js";
7
3
  import { Num, num } from "./num.js";
@@ -15,7 +11,7 @@ export const lerp = (a, b, t) => ({
15
11
  a: a.a + (b.a - a.a) * t,
16
12
  });
17
13
  export const equals = (a, b) => a === b || (a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a);
18
- /** L2 distance in RGBA-space. */
14
+ /** Euclidean distance in RGBA-space. */
19
15
  export const metric = (a, b) => Math.hypot(a.r - b.r, a.g - b.g, a.b - b.b, a.a - b.a);
20
16
  const linearImpl = { add, sub, scale };
21
17
  const packImpl = {
@@ -78,22 +74,20 @@ export class Color extends Cell {
78
74
  return `rgba(${r}, ${g}, ${b}, ${c.a})`;
79
75
  }));
80
76
  }
81
- /** Tween-builder, implied by the lerp trait. */
77
+ /** Tween-builder. */
82
78
  to(target, dur, ease) {
83
79
  return tween(this, target, dur, ease);
84
80
  }
85
81
  }
86
- /** Writable `Color` from RGB channels (alpha = 1). Each channel is a
87
- * literal `number` (lifted to a fresh seed) or an existing `Writable<Num>`
88
- * (identity passthrough). All-literal inputs take a fast path; mixed
89
- * inputs build a lens so channel-writes round-trip to source. */
82
+ /** Writable `Color` from RGB channels (alpha = 1). Each channel is a literal
83
+ * or a `Writable<Num>`. */
90
84
  export function rgb(r, g, b) {
91
85
  if (typeof r === "number" && typeof g === "number" && typeof b === "number") {
92
86
  return new Color({ r, g, b, a: 1 });
93
87
  }
94
88
  return Color.lens([num(r), num(g), num(b)], ([r, g, b]) => ({ r, g, b, a: 1 }), target => [target.r, target.g, target.b]);
95
89
  }
96
- /** Writable `Color` from RGBA channels. Same lift rule as `rgb`. */
90
+ /** Writable `Color` from RGBA channels. */
97
91
  export function rgba(r, g, b, a) {
98
92
  if (typeof r === "number" &&
99
93
  typeof g === "number" &&
@@ -46,22 +46,21 @@ export declare class Field<T> extends Cell<FieldVal> {
46
46
  * field as `u_src`; `u_texel` = 1/size is provided). One value is committed
47
47
  * after the substeps, so the reactive graph sees a single new epoch/frame. */
48
48
  evolve(frag: string, uniforms?: Record<string, number | readonly number[]>, steps?: number): void;
49
- /** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`. The
50
- * raster interaction primitive — seed a sim, inject, paint density. */
49
+ /** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`. */
51
50
  splat(x: number, y: number, r: number, value: T, strength?: number): void;
52
- /** Whole-field mean as a read-only `T` cell. Reactive: recomputes on every
53
- * new epoch, propagates only when the value moves. One 1×1 GPU readback. */
51
+ /** Whole-field mean as a read-only `T` cell. Recomputes per epoch; one 1×1
52
+ * GPU readback. */
54
53
  mean(): Read<T>;
55
54
  /** Mean over a sub-rectangle (data pixels, reactive `box`) as a read-only
56
- * `T` cell — the field's "density of this region" observation. */
55
+ * `T` cell. */
57
56
  regionMean(box: Val<{
58
57
  x: number;
59
58
  y: number;
60
59
  w: number;
61
60
  h: number;
62
61
  }>): Read<T>;
63
- /** Render `channel` through a colormap to a read-only `Canvas`. Reactive:
64
- * re-renders on every new epoch, so rendering is itself a reactive edge. */
62
+ /** Render `channel` through a colormap to a read-only `Canvas`; re-renders
63
+ * per epoch. */
65
64
  colormap(channel: number, stops: readonly ColorStop[]): Canvas;
66
65
  }
67
66
  /** Writable `Field<T>` of size `w×h`. `painter` fills each texel (returns a
@@ -1,26 +1,3 @@
1
- // field.ts — dense, GPU-resident reactive field: a grid of `T` as ONE cell.
2
- //
3
- // A Field is a single reactive node, not one-per-texel: the value is a header
4
- // {tex, w, h, epoch} (the Canvas handle trick, see gpu.ts), the graph compares
5
- // `epoch`, and no pixel crosses the bus. So a 1000×1000 field costs the engine
6
- // exactly what a 4×4 does — graph size is the lens-chain length, independent of
7
- // resolution. Per-frame work is allocation-free: `evolve`/`splat` ping-pong
8
- // through reused scratch textures and mint only the small header.
9
- //
10
- // Generic over the texel encoding via `Kind<T>` (channel `dim` + the `Pack`
11
- // codec + the boundary cell class). The dense interior stays a flat RGBA32F
12
- // texture forever; `T` (Num/Vec/Color) is instantiated ONLY at the boundary —
13
- // `mean`, `regionMean` — never per-texel. `dim` (1/2/4) selects the channel
14
- // mask: scalar (heatmap / SDF / density), vector (flow / gradient), colour
15
- // (i.e. a Canvas).
16
- //
17
- // The point of interest is the GPU↔reactivity bridge. `evolve` runs a sim on
18
- // its own clock (a raf/Anim loop), committing one new value per frame; the
19
- // reductions (`mean`, `regionMean`) are ordinary read-only derived cells, so
20
- // they recompute when the field's epoch changes and only *propagate* when their
21
- // scalar actually moves. Reactive logic downstream (`density > 0.5` → effect)
22
- // is thereby loosely coupled to time: it observes a continuously-running GPU
23
- // simulation without knowing a frame exists.
24
1
  import { Cell, reader } from "../cell.js";
25
2
  import { Canvas, stamp as canvasStamp } from "./canvas.js";
26
3
  import { Color } from "./color.js";
@@ -70,8 +47,7 @@ void main() {
70
47
  /** GLSL float literal (always carries a decimal point). */
71
48
  const glf = (n) => (Number.isInteger(n) ? `${n}.0` : String(n));
72
49
  const glv3 = (c) => `${glf(c[0])}, ${glf(c[1])}, ${glf(c[2])}`;
73
- /** Build a piecewise-linear ramp shader over `channel` (baked per stop set,
74
- * cached by source string in gpu.ts's program cache). */
50
+ /** Build a piecewise-linear ramp shader over `channel`. */
75
51
  function rampFrag(channel, stops) {
76
52
  const ch = "rgba"[channel] ?? "r";
77
53
  let body = ` vec3 c = vec3(${glv3(stops[0][1])});\n`;
@@ -87,9 +63,9 @@ void main() {
87
63
  ${body} o = vec4(c, 1.0);
88
64
  }`;
89
65
  }
90
- /** Read-only typed view via the kind's boundary class (`Num`/`Vec`/`Color`).
91
- * Cast through the concrete `derive` signature the static's polymorphic
92
- * `this` rejects the abstract `Ctor<T>`, but at runtime `k.cls` is concrete. */
66
+ /** Read-only typed view via the kind's boundary class. The cast bypasses the
67
+ * polymorphic `this` rejecting the abstract `Ctor<T>`; `k.cls` is concrete at
68
+ * runtime. */
93
69
  function deriveT(k, parent, fn) {
94
70
  const d = k.cls;
95
71
  return d.derive(parent, fn);
@@ -152,8 +128,7 @@ export class Field extends Cell {
152
128
  if (last)
153
129
  this.value = stamp(last.tex, w, h);
154
130
  }
155
- /** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`. The
156
- * raster interaction primitive — seed a sim, inject, paint density. */
131
+ /** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`. */
157
132
  splat(x, y, r, value, strength = 1) {
158
133
  const ping = this.pingTex();
159
134
  const v = this.peek();
@@ -169,14 +144,14 @@ export class Field extends Cell {
169
144
  });
170
145
  this.value = stamp(dst.tex, v.w, v.h);
171
146
  }
172
- /** Whole-field mean as a read-only `T` cell. Reactive: recomputes on every
173
- * new epoch, propagates only when the value moves. One 1×1 GPU readback. */
147
+ /** Whole-field mean as a read-only `T` cell. Recomputes per epoch; one 1×1
148
+ * GPU readback. */
174
149
  mean() {
175
150
  const k = this.kind;
176
151
  return deriveT(k, this, v => packT(k, reduceMean({ tex: v.tex, w: v.w, h: v.h })));
177
152
  }
178
153
  /** Mean over a sub-rectangle (data pixels, reactive `box`) as a read-only
179
- * `T` cell — the field's "density of this region" observation. */
154
+ * `T` cell. */
180
155
  regionMean(box) {
181
156
  const k = this.kind;
182
157
  const bf = reader(box);
@@ -197,8 +172,8 @@ export class Field extends Cell {
197
172
  return packT(k, [m[0] / cov, m[1] / cov, m[2] / cov, 1]);
198
173
  });
199
174
  }
200
- /** Render `channel` through a colormap to a read-only `Canvas`. Reactive:
201
- * re-renders on every new epoch, so rendering is itself a reactive edge. */
175
+ /** Render `channel` through a colormap to a read-only `Canvas`; re-renders
176
+ * per epoch. */
202
177
  colormap(channel, stops) {
203
178
  const frag = rampFrag(channel, stops);
204
179
  const sc = scratch();
@@ -8,8 +8,7 @@ export declare class Flags<K extends string> extends Cell<number> {
8
8
  };
9
9
  readonly _t: typeof Flags.traits;
10
10
  constructor(names: readonly K[], v?: number);
11
- /** Bit lens for `name`; set/clear round-trips through the packed mask.
12
- * Cached per name so repeated calls return the same lens. */
11
+ /** Cached writable bit lens for `name`. */
13
12
  flag<F extends K>(name: F): Writable<Bool>;
14
13
  }
15
14
  /** Writable `Flags` from variadic bit names (bit `i` = the i-th name), or
@@ -1,18 +1,3 @@
1
- // flags.ts — named bit-flags over a packed integer.
2
- //
3
- // A `number` bitmask whose bits are named at construction. `flag(name)` is a
4
- // `Writable<Bool>` mask lens onto one bit — set/clear round-trips through the
5
- // packed value, so the integer and its named booleans are one source seen
6
- // two ways. Sparse-trait (`equals` only): bits are discrete, so there's no
7
- // linear/pack/factor surface. Bit `i` carries value `2^i`, so a flag list
8
- // ordered low→high makes the packed int equal the conventional mask (e.g.
9
- // chmod octal). Two front-ends: variadic names, or an object of defaults.
10
- //
11
- // `flag()` is the collision-free accessor (a single generic method, names
12
- // checked as a union) rather than dynamic `.name` members — no risk of a
13
- // flag named "value"/"flag" shadowing the class. Since each flag is a
14
- // writable Bool, `Tri.allOf([f.flag(a), f.flag(b)])` gives all/none/mixed
15
- // group toggles for free.
16
1
  import { Cell } from "../cell.js";
17
2
  import { Bool } from "./bool.js";
18
3
  const equals = (a, b) => a === b;
@@ -24,8 +9,7 @@ export class Flags extends Cell {
24
9
  super(v, { equals });
25
10
  this.names = names;
26
11
  }
27
- /** Bit lens for `name`; set/clear round-trips through the packed mask.
28
- * Cached per name so repeated calls return the same lens. */
12
+ /** Cached writable bit lens for `name`. */
29
13
  flag(name) {
30
14
  let lens = this.#bits.get(name);
31
15
  if (lens === undefined) {
@@ -11,13 +11,10 @@ export interface Tex {
11
11
  * downgraded to NEAREST where `OES_texture_float_linear` is unavailable. */
12
12
  export declare function newTex(w: number, h: number, data?: Float32Array | null, linear?: boolean): WebGLTexture;
13
13
  export declare function disposeTex(t: WebGLTexture): void;
14
- /** A reusable owned texture, reallocated on size change — the GPU analog of
15
- * the CPU scratch buffer. */
14
+ /** A reusable owned texture, reallocated on size change. */
16
15
  export declare function scratch(): (w: number, h: number) => Tex;
17
16
  /** A feedback-safe scratch pair: returns an owned texture guaranteed not to
18
- * equal `avoid` (the input being read), so a pass never reads and writes the
19
- * same texture. Needed for backward passes that produce a root cell's next
20
- * value (whose current value may be this same scratch). */
17
+ * equal `avoid`, so a pass never reads and writes the same texture. */
21
18
  export declare function scratch2(): (w: number, h: number, avoid: WebGLTexture | null) => Tex;
22
19
  /** Copy `srcTex` into `dst` (distinct textures). */
23
20
  export declare function copy(srcTex: WebGLTexture, dst: Tex): void;
@@ -35,7 +32,7 @@ export declare function pass(frag: string, target: Tex, setup: (s: Setup) => voi
35
32
  /** Mean of all texels (RGBA, 0–1) via GPU pyramid reduction; one 1×1
36
33
  * readback. */
37
34
  export declare function reduceMean(src: Tex): [number, number, number, number];
38
- /** Mean of squared texels — the spring settle metric. */
35
+ /** Mean of squared texels (spring settle metric). */
39
36
  export declare function reduceMeanSquare(src: Tex): number;
40
37
  /** Render `src` to a 2D canvas (GPU draw + drawImage; no CPU readback). */
41
38
  export declare function blit(src: Tex, ctx: CanvasRenderingContext2D): void;
@@ -61,10 +58,9 @@ export declare class Spring {
61
58
  private springFbo;
62
59
  constructor(w: number, h: number, opts?: SpringOpts);
63
60
  private clearTex;
64
- /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
65
- * alias one of our own position textures (the host feeds `current()` back
66
- * as the root value), so skip any copy that would read and write the same
67
- * texture — illegal GL feedback, and a no-op anyway. */
61
+ /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may alias
62
+ * one of our own position textures, so skip a copy that would read and write
63
+ * the same texture (illegal GL feedback). */
68
64
  seed(srcTex: WebGLTexture): void;
69
65
  /** Retarget (keeps current position + velocity). */
70
66
  setTarget(srcTex: WebGLTexture): void;
@@ -1,15 +1,5 @@
1
- // gpu.ts shared WebGL2 core for GPU-resident Canvas values.
2
- //
3
- // There is ONE compute context (a hidden canvas). That is a hard WebGL
4
- // constraint, not a shared buffer: a lens pass samples its parent's texture
5
- // and renders into its child's, and textures can't cross contexts (browsers
6
- // also cap contexts at ~16). Every Canvas value still owns its own float
7
- // texture(s); display blits each value into its own DOM canvas. The only
8
- // CPU readbacks are 1×1 reductions (mean colour, settle energy).
9
- //
10
- // Coordinate convention: data row 0 lives at texture v=0, so every compute
11
- // pass works in a single consistent data space (no flips); only `blit`
12
- // flips Y so the image reads upright on screen.
1
+ // Coordinate convention: data row 0 lives at texture v=0, so every compute pass
2
+ // works in one consistent data space; only `blit` flips Y for the screen.
13
3
  let _gl = null;
14
4
  /** Lazily create the shared WebGL2 context (browser-only). */
15
5
  export function gl() {
@@ -103,8 +93,7 @@ export function newTex(w, h, data = null, linear = false) {
103
93
  export function disposeTex(t) {
104
94
  gl().deleteTexture(t);
105
95
  }
106
- /** A reusable owned texture, reallocated on size change — the GPU analog of
107
- * the CPU scratch buffer. */
96
+ /** A reusable owned texture, reallocated on size change. */
108
97
  export function scratch() {
109
98
  let t = null;
110
99
  let cw = 0;
@@ -121,9 +110,7 @@ export function scratch() {
121
110
  };
122
111
  }
123
112
  /** A feedback-safe scratch pair: returns an owned texture guaranteed not to
124
- * equal `avoid` (the input being read), so a pass never reads and writes the
125
- * same texture. Needed for backward passes that produce a root cell's next
126
- * value (whose current value may be this same scratch). */
113
+ * equal `avoid`, so a pass never reads and writes the same texture. */
127
114
  export function scratch2() {
128
115
  let a = null;
129
116
  let b = null;
@@ -246,7 +233,7 @@ export function reduceMean(src) {
246
233
  return [buf[0] / n, buf[1] / n, buf[2] / n, buf[3] / n];
247
234
  }
248
235
  let _sq = null;
249
- /** Mean of squared texels — the spring settle metric. */
236
+ /** Mean of squared texels (spring settle metric). */
250
237
  export function reduceMeanSquare(src) {
251
238
  if (!_sq || _sq.w !== src.w || _sq.h !== src.h) {
252
239
  if (_sq)
@@ -373,10 +360,9 @@ export class Spring {
373
360
  g.clearColor(0, 0, 0, 0);
374
361
  g.clear(g.COLOR_BUFFER_BIT);
375
362
  }
376
- /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
377
- * alias one of our own position textures (the host feeds `current()` back
378
- * as the root value), so skip any copy that would read and write the same
379
- * texture — illegal GL feedback, and a no-op anyway. */
363
+ /** Snap position + target to `srcTex`, zero the velocity. `srcTex` may alias
364
+ * one of our own position textures, so skip a copy that would read and write
365
+ * the same texture (illegal GL feedback). */
380
366
  seed(srcTex) {
381
367
  for (const tex of [this.pos[0], this.pos[1], this.targetTex])
382
368
  if (srcTex !== tex)
@@ -45,9 +45,7 @@ export declare class Matrix extends Cell<V> {
45
45
  get determinant(): Num;
46
46
  }
47
47
  /** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
48
- * Each entry is a literal `number` (lifted to a fresh seed) or an existing
49
- * `Writable<Num>` (identity passthrough). RO sources are rejected at the
50
- * type level — use `Matrix.derive(...)` for reactive RO tracking, or
51
- * `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
48
+ * Each entry is a literal (new cell) or existing writable (passed through);
49
+ * for read-only sources use `Matrix.derive`. Lock an entry with `Num.pin`. */
52
50
  export declare function matrix(a?: Init<Num>, b?: Init<Num>, c?: Init<Num>, d?: Init<Num>, e?: Init<Num>, f?: Init<Num>): Writable<Matrix>;
53
51
  export {};
@@ -1,10 +1,3 @@
1
- // matrix.ts — reactive 2D affine matrix (SVG/Canvas convention).
2
- //
3
- // Sparse-trait: only `equals`. Element-wise linear combine / lerp don't
4
- // decompose for matrices, so `spring`/`tween`/`mean` reject Matrix at
5
- // compile time. Two invertibles, both `: this` via `Cell#lens`:
6
- // - `multiply(b)` — inverse multiplies by `invert(b)`
7
- // - `invert()` — its own inverse
8
1
  import { Cell, cachedDerive, fieldLens, reader, } from "../cell.js";
9
2
  import { Num, num } from "./num.js";
10
3
  export const identity = () => ({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 });
@@ -115,10 +108,8 @@ export class Matrix extends Cell {
115
108
  }
116
109
  }
117
110
  /** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
118
- * Each entry is a literal `number` (lifted to a fresh seed) or an existing
119
- * `Writable<Num>` (identity passthrough). RO sources are rejected at the
120
- * type level — use `Matrix.derive(...)` for reactive RO tracking, or
121
- * `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
111
+ * Each entry is a literal (new cell) or existing writable (passed through);
112
+ * for read-only sources use `Matrix.derive`. Lock an entry with `Num.pin`. */
122
113
  export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
123
114
  if (typeof a === "number" &&
124
115
  typeof b === "number" &&
@@ -134,6 +125,5 @@ export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
134
125
  const dN = num(d);
135
126
  const eN = num(e);
136
127
  const fN = num(f);
137
- // The view fully reconstructs all 6 cells (1-arg bwd ⇒ no source read).
138
128
  return Matrix.lens([aN, bN, cN, dN, eN, fN], ([a, b, c, d, e, f]) => ({ a, b, c, d, e, f }), v => [v.a, v.b, v.c, v.d, v.e, v.f]);
139
129
  }
@@ -22,46 +22,37 @@ export declare class Num extends Cell<V> {
22
22
  add(b: Val<V>): this;
23
23
  sub(b: Val<V>): this;
24
24
  scale(k: Val<number>): this;
25
- /** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
26
- * for `.scale(k).add(off)`. */
25
+ /** Affine map `v ↦ k·v + off`. Invertible when k ≠ 0. */
27
26
  affine(k: Val<number>, off: Val<number>): this;
28
- /** `sin(this)` (radians). Forward lands in [−1, 1]; the inverse is
29
- * multi-valued, so a write clamps to that domain and returns the
30
- * pre-image nearest the current source — the drag stays on its branch. */
27
+ /** Sine of `this` (radians). The inverse is multi-valued; a write picks the
28
+ * angle nearest the current value, so a drag stays on its branch. */
31
29
  sin(): this;
32
- /** `exp(this)` — bijection on the reals; inverse is the natural log. */
30
+ /** Natural exponential; inverts via log. */
33
31
  exp(): this;
34
- /** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
35
- * the range reads back clamped, not as written). */
32
+ /** Clamp to `[lo, hi]`. Lossy: a write outside the range reads back clamped. */
36
33
  clamp(lo: Val<V>, hi: Val<V>): this;
37
- /** Lossy lens snapping reads/writes to the nearest multiple of `step`. */
34
+ /** Snap reads and writes to the nearest multiple of `step` (lossy). */
38
35
  quantize(step: Val<number>): this;
39
- /** Cyclic-coordinate lens. Reads pass through; writes pick the
40
- * representative closest to current modulo `period`, so dragging an
41
- * angle never jumps a full revolution. The 2-arg bwd is arity-detected
42
- * as stateful, threading the accumulated value through `s`. */
36
+ /** Reads pass through; a write picks the value closest to the current one
37
+ * modulo `period`, so dragging an angle never jumps a full turn. */
43
38
  cyclic(period: Val<number>): this;
44
- /** `this > t` as a Bool. Flipping the view bumps the source across
45
- * the threshold by `eps`. */
39
+ /** `this > t` as a Bool. Flipping it bumps the source across the
40
+ * threshold by `eps`. */
46
41
  greaterThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
47
- /** `this < t`. Dual of `greaterThan`. */
42
+ /** `this < t` as a Bool. */
48
43
  lessThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
49
- /** `round(this) 0 (mod d)` as a Bool; pair with `quantize(1)` for
50
- * integer sliders. Bwd: to make divisible, snap to the nearer
51
- * multiple of `d`; to make non-divisible, bump by `+1`; no-op when
52
- * the class already matches. */
44
+ /** True when `round(this)` is divisible by `d`. A write snaps to the nearest
45
+ * multiple of `d` to make it divisible, or bumps by 1 to make it not. */
53
46
  divisibleBy<T extends Num>(this: T, d: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
54
- /** `divisibleBy(2)` lazy getter for the common case. */
47
+ /** True when even. */
55
48
  get isEven(): this extends WritableBrand ? Writable<Bool> : Bool;
56
- /** `not(divisibleBy(2))` lazy getter. */
49
+ /** True when odd. */
57
50
  get isOdd(): this extends WritableBrand ? Writable<Bool> : Bool;
58
- /** Tween-builder; `this: Writable<Num>` gates the call to writable
59
- * receivers. */
51
+ /** Tween-builder. */
60
52
  to(this: Writable<Num>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
61
53
  }
62
- /** Writable `Num`. Literal seeds a fresh cell; existing `Writable<Num>`
63
- * passes through by identity. RO sources are rejected at the type level —
64
- * use `Num.derive(...)` for reactive RO tracking, or `Num.coerce(...)` for
65
- * the permissive lift over any `Val<number>`. */
54
+ /** Writable `Num` from a literal (new cell) or existing writable (passed
55
+ * through). For read-only sources use `Num.derive`, or `Num.coerce` to lift
56
+ * any `Val<number>`. */
66
57
  export declare function num(v?: Init<Num>): Writable<Num>;
67
58
  export {};
@@ -1,7 +1,3 @@
1
- // num.ts — reactive scalar.
2
- //
3
- // Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
4
- // chained calls compose into a lens chain.
5
1
  import { tween } from "../../animation/index.js";
6
2
  import { Cell, lazy, reader, } from "../cell.js";
7
3
  import { Bool } from "./bool.js";
@@ -47,16 +43,14 @@ export class Num extends Cell {
47
43
  const kf = reader(k);
48
44
  return this.lens(v => v * kf(), n => n / kf());
49
45
  }
50
- /** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
51
- * for `.scale(k).add(off)`. */
46
+ /** Affine map `v ↦ k·v + off`. Invertible when k ≠ 0. */
52
47
  affine(k, off) {
53
48
  const kf = reader(k);
54
49
  const of = reader(off);
55
50
  return this.lens(v => v * kf() + of(), n => (n - of()) / kf());
56
51
  }
57
- /** `sin(this)` (radians). Forward lands in [−1, 1]; the inverse is
58
- * multi-valued, so a write clamps to that domain and returns the
59
- * pre-image nearest the current source — the drag stays on its branch. */
52
+ /** Sine of `this` (radians). The inverse is multi-valued; a write picks the
53
+ * angle nearest the current value, so a drag stays on its branch. */
60
54
  sin() {
61
55
  return this.lens(v => Math.sin(v), (target, s) => {
62
56
  const p = Math.asin(unit(target));
@@ -65,12 +59,11 @@ export class Num extends Cell {
65
59
  return Math.abs(a - s) <= Math.abs(b - s) ? a : b;
66
60
  });
67
61
  }
68
- /** `exp(this)` — bijection on the reals; inverse is the natural log. */
62
+ /** Natural exponential; inverts via log. */
69
63
  exp() {
70
64
  return this.lens(v => Math.exp(v), n => Math.log(n));
71
65
  }
72
- /** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
73
- * the range reads back clamped, not as written). */
66
+ /** Clamp to `[lo, hi]`. Lossy: a write outside the range reads back clamped. */
74
67
  clamp(lo, hi) {
75
68
  const lf = reader(lo);
76
69
  const hf = reader(hi);
@@ -78,31 +71,29 @@ export class Num extends Cell {
78
71
  const l = lf(), h = hf();
79
72
  return v < l ? l : v > h ? h : v;
80
73
  };
81
- // A write whose clamped projection matches the current view leaves
82
- // the source untouched (off-range source preserved).
74
+ // If the clamped write matches the current view, keep the source
75
+ // (preserves an off-range source value).
83
76
  return this.lens(c, (v, s) => {
84
77
  const cv = c(v);
85
78
  return cv === c(s) ? s : cv;
86
79
  });
87
80
  }
88
- /** Lossy lens snapping reads/writes to the nearest multiple of `step`. */
81
+ /** Snap reads and writes to the nearest multiple of `step` (lossy). */
89
82
  quantize(step) {
90
83
  const sf = reader(step);
91
84
  const q = (v) => {
92
85
  const s = sf();
93
86
  return Math.round(v / s) * s;
94
87
  };
95
- // A write that snaps to the current bucket leaves the source
96
- // untouched (off-grid remainder preserved).
88
+ // If the write snaps to the current bucket, keep the source
89
+ // (preserves an off-grid remainder).
97
90
  return this.lens(q, (v, src) => {
98
91
  const qv = q(v);
99
92
  return qv === q(src) ? src : qv;
100
93
  });
101
94
  }
102
- /** Cyclic-coordinate lens. Reads pass through; writes pick the
103
- * representative closest to current modulo `period`, so dragging an
104
- * angle never jumps a full revolution. The 2-arg bwd is arity-detected
105
- * as stateful, threading the accumulated value through `s`. */
95
+ /** Reads pass through; a write picks the value closest to the current one
96
+ * modulo `period`, so dragging an angle never jumps a full turn. */
106
97
  cyclic(period) {
107
98
  const pf = reader(period);
108
99
  return this.lens(v => v, (v, s) => {
@@ -111,13 +102,8 @@ export class Num extends Cell {
111
102
  return s + delta - p * Math.round(delta / p);
112
103
  });
113
104
  }
114
- // Predicate bridges to Bool.
115
- //
116
- // Cross-type quotient lenses projecting Num through a boolean
117
- // predicate. Conditional return type: writable receiver yields
118
- // `Writable<Bool>`, RO receiver yields RO `Bool`.
119
- /** `this > t` as a Bool. Flipping the view bumps the source across
120
- * the threshold by `eps`. */
105
+ /** `this > t` as a Bool. Flipping it bumps the source across the
106
+ * threshold by `eps`. */
121
107
  greaterThan(t, eps = 1e-6) {
122
108
  const tf = reader(t);
123
109
  const ef = reader(eps);
@@ -128,7 +114,7 @@ export class Num extends Cell {
128
114
  return target ? th + ef() : th - ef();
129
115
  });
130
116
  }
131
- /** `this < t`. Dual of `greaterThan`. */
117
+ /** `this < t` as a Bool. */
132
118
  lessThan(t, eps = 1e-6) {
133
119
  const tf = reader(t);
134
120
  const ef = reader(eps);
@@ -139,10 +125,8 @@ export class Num extends Cell {
139
125
  return target ? th - ef() : th + ef();
140
126
  });
141
127
  }
142
- /** `round(this) 0 (mod d)` as a Bool; pair with `quantize(1)` for
143
- * integer sliders. Bwd: to make divisible, snap to the nearer
144
- * multiple of `d`; to make non-divisible, bump by `+1`; no-op when
145
- * the class already matches. */
128
+ /** True when `round(this)` is divisible by `d`. A write snaps to the nearest
129
+ * multiple of `d` to make it divisible, or bumps by 1 to make it not. */
146
130
  divisibleBy(d) {
147
131
  const df = reader(d);
148
132
  return Bool.lens(this, v => Math.round(v) % df() === 0, (target, current) => {
@@ -161,24 +145,22 @@ export class Num extends Cell {
161
145
  return r + 1;
162
146
  });
163
147
  }
164
- /** `divisibleBy(2)` lazy getter for the common case. */
148
+ /** True when even. */
165
149
  get isEven() {
166
150
  return lazy(this, "isEven", () => this.divisibleBy(2));
167
151
  }
168
- /** `not(divisibleBy(2))` lazy getter. */
152
+ /** True when odd. */
169
153
  get isOdd() {
170
154
  return lazy(this, "isOdd", () => this.divisibleBy(2).not());
171
155
  }
172
- /** Tween-builder; `this: Writable<Num>` gates the call to writable
173
- * receivers. */
156
+ /** Tween-builder. */
174
157
  to(target, dur, ease) {
175
158
  return tween(this, target, dur, ease);
176
159
  }
177
160
  }
178
- /** Writable `Num`. Literal seeds a fresh cell; existing `Writable<Num>`
179
- * passes through by identity. RO sources are rejected at the type level —
180
- * use `Num.derive(...)` for reactive RO tracking, or `Num.coerce(...)` for
181
- * the permissive lift over any `Val<number>`. */
161
+ /** Writable `Num` from a literal (new cell) or existing writable (passed
162
+ * through). For read-only sources use `Num.derive`, or `Num.coerce` to lift
163
+ * any `Val<number>`. */
182
164
  export function num(v = 0) {
183
165
  if (v instanceof Num)
184
166
  return v;
@@ -27,9 +27,7 @@ export declare class Pose extends Cell<V> {
27
27
  get y(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
28
28
  get theta(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
29
29
  }
30
- /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
31
- * through by identity. RO sources are rejected at the type level — use
32
- * `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
33
- * snapshot. */
30
+ /** Writable `Pose` from a literal (new cell) or existing writable (passed
31
+ * through). For read-only sources use `Pose.derive`. */
34
32
  export declare function pose(v?: Init<Pose>): Writable<Pose>;
35
33
  export {};
@@ -1,9 +1,3 @@
1
- // pose.ts — reactive 2D rigid-body pose: { x, y, theta }.
2
- //
3
- // Single source of truth for a rigid body. The solver binds the cell as
4
- // a 3-DOF block and writes back through it, so renderers, drag handlers,
5
- // IK, and physics all observe the same value. Vec / Num lenses compose
6
- // for consumers that care only about translation or rotation.
7
1
  import { Cell, fieldLens } from "../cell.js";
8
2
  import { Num } from "./num.js";
9
3
  export const add = (a, b) => ({
@@ -38,8 +32,7 @@ const packImpl = {
38
32
  },
39
33
  write: (a, o) => ({ x: a[o], y: a[o + 1], theta: a[o + 2] }),
40
34
  };
41
- /** Rotate-about-pivot moves the position and adds dθ to orientation;
42
- * scale-about-pivot scales position, orientation untouched. */
35
+ // rotateAbout moves position and adds dθ; scaleAbout scales position, leaves θ.
43
36
  const pivotalImpl = {
44
37
  rotateAbout: (v, p, dθ) => {
45
38
  const cos = Math.cos(dθ);
@@ -80,10 +73,8 @@ export class Pose extends Cell {
80
73
  return fieldLens(this, "theta", Num);
81
74
  }
82
75
  }
83
- /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
84
- * through by identity. RO sources are rejected at the type level — use
85
- * `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
86
- * snapshot. */
76
+ /** Writable `Pose` from a literal (new cell) or existing writable (passed
77
+ * through). For read-only sources use `Pose.derive`. */
87
78
  export function pose(v = { x: 0, y: 0, theta: 0 }) {
88
79
  if (v instanceof Pose)
89
80
  return v;