bireactive 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +4 -4
  2. package/dist/{core/anim.d.ts → animation/animators.d.ts} +5 -3
  3. package/dist/{core/anim.js → animation/animators.js} +8 -5
  4. package/dist/animation/index.d.ts +1 -0
  5. package/dist/animation/index.js +1 -0
  6. package/dist/constraints/cluster.js +1 -1
  7. package/dist/constraints/expose.d.ts +17 -0
  8. package/dist/constraints/expose.js +61 -0
  9. package/dist/constraints/index.d.ts +1 -0
  10. package/dist/constraints/index.js +1 -0
  11. package/dist/constraints/linalg.d.ts +1 -8
  12. package/dist/constraints/linalg.js +5 -108
  13. package/dist/core/{signal.d.ts → cell.d.ts} +29 -6
  14. package/dist/core/{signal.js → cell.js} +134 -53
  15. package/dist/core/derived-geometry.d.ts +11 -0
  16. package/dist/core/derived-geometry.js +32 -0
  17. package/dist/core/index.d.ts +5 -10
  18. package/dist/core/index.js +5 -10
  19. package/dist/core/{aggregates.d.ts → lenses/aggregates.d.ts} +3 -14
  20. package/dist/core/{aggregates.js → lenses/aggregates.js} +5 -78
  21. package/dist/core/lenses/closed-form-policies.d.ts +6 -13
  22. package/dist/core/lenses/closed-form-policies.js +14 -24
  23. package/dist/core/lenses/decompositions.d.ts +14 -0
  24. package/dist/core/lenses/decompositions.js +224 -0
  25. package/dist/core/lenses/domain-aggregates.d.ts +10 -22
  26. package/dist/core/lenses/domain-aggregates.js +25 -39
  27. package/dist/core/{new-primitives.d.ts → lenses/geometry.d.ts} +11 -14
  28. package/dist/core/{new-primitives.js → lenses/geometry.js} +23 -37
  29. package/dist/core/lenses/index.d.ts +6 -4
  30. package/dist/core/lenses/index.js +6 -15
  31. package/dist/core/lenses/typed-factor.d.ts +1 -6
  32. package/dist/core/lenses/typed-factor.js +12 -114
  33. package/dist/core/{network-utils.d.ts → lifecycle.d.ts} +1 -1
  34. package/dist/core/{network-utils.js → lifecycle.js} +2 -2
  35. package/dist/core/linalg.d.ts +10 -0
  36. package/dist/core/linalg.js +109 -0
  37. package/dist/core/values/anchor.d.ts +1 -1
  38. package/dist/core/values/audio.d.ts +1 -1
  39. package/dist/core/values/audio.js +1 -1
  40. package/dist/core/values/bool.d.ts +1 -1
  41. package/dist/core/values/bool.js +1 -1
  42. package/dist/core/values/box.d.ts +2 -3
  43. package/dist/core/values/box.js +5 -6
  44. package/dist/core/values/canvas.d.ts +1 -1
  45. package/dist/core/values/canvas.js +2 -3
  46. package/dist/core/values/color.d.ts +2 -3
  47. package/dist/core/values/color.js +3 -4
  48. package/dist/core/values/flags.d.ts +1 -1
  49. package/dist/core/values/flags.js +1 -1
  50. package/dist/core/values/matrix.d.ts +1 -1
  51. package/dist/core/values/matrix.js +2 -3
  52. package/dist/core/values/num.d.ts +2 -3
  53. package/dist/core/values/num.js +2 -2
  54. package/dist/core/values/pose.d.ts +5 -1
  55. package/dist/core/values/pose.js +11 -1
  56. package/dist/core/values/range.d.ts +6 -4
  57. package/dist/core/values/range.js +16 -11
  58. package/dist/core/values/str.d.ts +1 -1
  59. package/dist/core/values/str.js +1 -1
  60. package/dist/core/values/template.d.ts +1 -1
  61. package/dist/core/values/transform.d.ts +2 -3
  62. package/dist/core/values/transform.js +2 -3
  63. package/dist/core/values/tri.d.ts +1 -1
  64. package/dist/core/values/tri.js +1 -1
  65. package/dist/core/values/vec.d.ts +2 -3
  66. package/dist/core/values/vec.js +4 -5
  67. package/dist/ext/timeline.js +3 -3
  68. package/dist/index.d.ts +1 -0
  69. package/dist/index.js +1 -0
  70. package/dist/propagators/layout.js +1 -1
  71. package/dist/shapes/handle.js +4 -4
  72. package/dist/shapes/shape.js +7 -7
  73. package/dist/shapes/transitions.js +2 -2
  74. package/package.json +7 -2
  75. package/dist/core/introspect.d.ts +0 -5
  76. package/dist/core/introspect.js +0 -31
  77. package/dist/core/lenses/factor-lens.d.ts +0 -42
  78. package/dist/core/lenses/factor-lens.js +0 -419
  79. package/dist/core/writable.d.ts +0 -15
  80. package/dist/core/writable.js +0 -29
  81. /package/dist/{core/tree.d.ts → tree.d.ts} +0 -0
  82. /package/dist/{core/tree.js → tree.js} +0 -0
@@ -1,5 +1,7 @@
1
- export { bestFitCircleLens, bestFitLineLens, pcaLens, procrustesViaBuildingBlocks, rigidTranslate, rotateAbout, scaleAbout, scaleAboutXY, totalLens, } from "./closed-form-policies.js";
2
- export { bezierGestaltLens, crossfade, meanColor, meanOf, mix, paletteLens, rigidTranslateOf, select, spreadOf, timeSeriesLens, } from "./domain-aggregates.js";
3
- export { bboxLens, bundleLens, type FactorLensOpts, factorLens, meanDiffLens, procrustesJacobianLens, procrustesLens, } from "./factor-lens.js";
1
+ export { type ArgminOpts, type ArgminVecOpts, argminNum, argminVec, clampToDisc, } from "./aggregates.js";
2
+ export { bestFitCircle, bestFitLine, pca, rigidTranslate, rotateAbout, scaleAbout, scaleAboutXY, total, } from "./closed-form-policies.js";
3
+ export { bbox, meanDiff, procrustes } from "./decompositions.js";
4
+ export { bezierGestalt, crossfade, mean, meanSpread, mix, select, spread, timeSeries, } from "./domain-aggregates.js";
5
+ export { angle, clampedMean, diff, distance, pulleySum, reflection, vecLerp } from "./geometry.js";
4
6
  export { type ContinuousOpts, continuous, type RememberOpts, remember, } from "./memory.js";
5
- export { bundle, type FactorOpts, type FactorResult, factor, factorTuple, type OutputSpec, type PackedInput, procrustesTyped, } from "./typed-factor.js";
7
+ export { bundle, type FactorOpts, type FactorResult, factor, factorTuple, type OutputSpec, type PackedInput, } from "./typed-factor.js";
@@ -1,16 +1,7 @@
1
- // core/lenses/index.ts N→M and 1→M bidirectional lens primitives.
2
- //
3
- // Three layers:
4
- // 1. NUMERICAL `factor`, `factorTuple`, `bundle`: generic
5
- // Jacobian-LSQ, the escape hatch when no closed-form policy fits.
6
- // 2. CLOSED-FORM POLICIES — `rigidTranslate`, `rotateAbout`,
7
- // `scaleAbout`, `scaleAboutXY`: exact group-action primitives.
8
- // 3. DECOMPOSITIONS — `procrustesLens`, `bboxLens`, `bestFitLine`,
9
- // etc.: composed M-output views over the policies and aggregates.
10
- //
11
- // See BIDIRECTIONAL-LENSES.md for the engine substrate.
12
- export { bestFitCircleLens, bestFitLineLens, pcaLens, procrustesViaBuildingBlocks, rigidTranslate, rotateAbout, scaleAbout, scaleAboutXY, totalLens, } from "./closed-form-policies.js";
13
- export { bezierGestaltLens, crossfade, meanColor, meanOf, mix, paletteLens, rigidTranslateOf, select, spreadOf, timeSeriesLens, } from "./domain-aggregates.js";
14
- export { bboxLens, bundleLens, factorLens, meanDiffLens, procrustesJacobianLens, procrustesLens, } from "./factor-lens.js";
1
+ export { argminNum, argminVec, clampToDisc, } from "./aggregates.js";
2
+ export { bestFitCircle, bestFitLine, pca, rigidTranslate, rotateAbout, scaleAbout, scaleAboutXY, total, } from "./closed-form-policies.js";
3
+ export { bbox, meanDiff, procrustes } from "./decompositions.js";
4
+ export { bezierGestalt, crossfade, mean, meanSpread, mix, select, spread, timeSeries, } from "./domain-aggregates.js";
5
+ export { angle, clampedMean, diff, distance, pulleySum, reflection, vecLerp } from "./geometry.js";
15
6
  export { continuous, remember, } from "./memory.js";
16
- export { bundle, factor, factorTuple, procrustesTyped, } from "./typed-factor.js";
7
+ export { bundle, factor, factorTuple, } from "./typed-factor.js";
@@ -1,4 +1,4 @@
1
- import { type Cell, type Inner, Num, type Read, type Traits, Vec, type Writable } from "../index.js";
1
+ import type { Cell, Inner, Read, Traits, Writable } from "../index.js";
2
2
  /** Input cell: writable cell whose value class declares the `pack`
3
3
  * trait. Vec, Num, Pose, Box, Color, Range all satisfy this. */
4
4
  export type PackedInput<T = any> = Writable<Read<T> & Traits<T, "pack">>;
@@ -38,8 +38,3 @@ export declare function factorTuple<T extends readonly OutputSpec<any>[]>(inputs
38
38
  [K in keyof T]: Writable<InstanceType<T[K]["Cls"]>>;
39
39
  };
40
40
  export declare function bundle<T, O extends Record<string, OutputSpec<any>>>(source: Writable<Read<T> & Traits<T, "pack">>, views: O, opts?: FactorOpts): FactorResult<O>;
41
- export declare function procrustesTyped(points: readonly PackedInput<Inner<Vec>>[]): {
42
- centroid: Writable<Vec>;
43
- rotation: Writable<Num>;
44
- scale: Writable<Num>;
45
- };
@@ -1,10 +1,10 @@
1
- // typed-factor.ts — heterogeneous-output factor lens.
1
+ // typed-factor.ts — generic heterogeneous-output factor lens.
2
2
  //
3
- // Generalises `factorLens` (scalar-only) to typed inputs/outputs via the
4
- // `Pack` trait. Inputs and outputs are flat-packed; the Jacobian is the
5
- // full M×N matrix; writing one channel sends a sparse δy through the LSQ
3
+ // The numerical N→M escape hatch: typed inputs/outputs via the `Pack`
4
+ // trait. Inputs and outputs are flat-packed; the Jacobian is the full
5
+ // M×N matrix; writing one channel sends a sparse δy through the LSQ
6
6
  // pseudoinverse. Invariance is approximate (the Jacobian path) — use
7
- // closed-form lenses like `procrustesLens` for exactness.
7
+ // closed-form lenses like `procrustes` when an exact one exists.
8
8
  //
9
9
  // const { centroid, rotation, scale } = factor([v1, v2, v3] as const, {
10
10
  // centroid: { Cls: Vec, fwd: pts => … },
@@ -14,7 +14,7 @@
14
14
  // centroid.value = { x: 100, y: 50 }; // typed
15
15
  //
16
16
  // `bundle()` is the 1→M case: `factor()` over a single typed source.
17
- import { Num, Vec, } from "../index.js";
17
+ import { solveSPD } from "../linalg.js";
18
18
  function getPack(cell) {
19
19
  const ctor = cell.constructor;
20
20
  const p = ctor.traits?.pack;
@@ -87,9 +87,9 @@ export function factor(inputs, outputs, opts = {}) {
87
87
  const flatOutPerturbed = new Float64Array(M);
88
88
  const J = new Float64Array(M * N);
89
89
  const A = new Float64Array(M * M);
90
- const Ainv = new Float64Array(M * M);
90
+ // `dy` carries the sparse residual in, then the solve overwrites it
91
+ // with `k` in place (SPD LDLᵀ destroys A and writes the solution here).
91
92
  const dy = new Float64Array(M);
92
- const kvec = new Float64Array(M);
93
93
  // Per-write driver, shared by all M cells.
94
94
  const computeBwd = (channelIdx, // which named output is being written
95
95
  target, vals) => {
@@ -151,7 +151,7 @@ export function factor(inputs, outputs, opts = {}) {
151
151
  typedScratch[k] = inputPacks[k].write(flatIn, inputOffsets[k]);
152
152
  }
153
153
  }
154
- // 5. A = J W J^T + λI
154
+ // 5. A = J W J^T + λI (SPD by construction: W ≥ 0, λ > 0).
155
155
  for (let r = 0; r < M; r++) {
156
156
  for (let c = 0; c < M; c++) {
157
157
  let s = 0;
@@ -161,16 +161,10 @@ export function factor(inputs, outputs, opts = {}) {
161
161
  A[r * M + c] = s + (r === c ? lambda : 0);
162
162
  }
163
163
  }
164
- // 6. Solve A · k = δy
165
- if (!invertMatrix(A, M, Ainv)) {
164
+ // 6. Solve A · k = δy in place (LDLᵀ; `dy` is overwritten with k).
165
+ if (!solveSPD(A, dy, M)) {
166
166
  return vals.map(() => undefined);
167
167
  }
168
- for (let r = 0; r < M; r++) {
169
- let s = 0;
170
- for (let c = 0; c < M; c++)
171
- s += Ainv[r * M + c] * dy[c];
172
- kvec[r] = s;
173
- }
174
168
  // 7. δx = W J^T k, applied to flatIn → produces new flat input vector.
175
169
  // Then unpack per-input to typed updates.
176
170
  const updates = new Array(inputCount);
@@ -185,7 +179,7 @@ export function factor(inputs, outputs, opts = {}) {
185
179
  continue;
186
180
  let dxi = 0;
187
181
  for (let r = 0; r < M; r++)
188
- dxi += J[r * N + flatIdx] * kvec[r];
182
+ dxi += J[r * N + flatIdx] * dy[r];
189
183
  const newVal = flatIn[flatIdx] + w * dxi;
190
184
  if (newVal !== flatIn[flatIdx])
191
185
  anyChange = true;
@@ -278,99 +272,3 @@ export function bundle(source, views, opts = {}) {
278
272
  }
279
273
  // For field-style bundles, `field()` already covers the independent case;
280
274
  // use `bundle()` when you want coupled writes through the Jacobian solve.
281
- // Matrix inverse (Gauss-Jordan with partial pivoting).
282
- function invertMatrix(A, M, out) {
283
- const W = 2 * M;
284
- const aug = new Float64Array(M * W);
285
- for (let r = 0; r < M; r++) {
286
- for (let c = 0; c < M; c++)
287
- aug[r * W + c] = A[r * M + c];
288
- for (let c = 0; c < M; c++)
289
- aug[r * W + M + c] = r === c ? 1 : 0;
290
- }
291
- for (let i = 0; i < M; i++) {
292
- let p = i;
293
- let pv = Math.abs(aug[i * W + i]);
294
- for (let r = i + 1; r < M; r++) {
295
- const v = Math.abs(aug[r * W + i]);
296
- if (v > pv) {
297
- pv = v;
298
- p = r;
299
- }
300
- }
301
- if (pv < 1e-14)
302
- return false;
303
- if (p !== i) {
304
- for (let c = 0; c < W; c++) {
305
- const t = aug[i * W + c];
306
- aug[i * W + c] = aug[p * W + c];
307
- aug[p * W + c] = t;
308
- }
309
- }
310
- const inv = 1 / aug[i * W + i];
311
- for (let c = 0; c < W; c++)
312
- aug[i * W + c] *= inv;
313
- for (let r = 0; r < M; r++) {
314
- if (r === i)
315
- continue;
316
- const f = aug[r * W + i];
317
- if (f === 0)
318
- continue;
319
- for (let c = 0; c < W; c++)
320
- aug[r * W + c] -= f * aug[i * W + c];
321
- }
322
- }
323
- for (let r = 0; r < M; r++) {
324
- for (let c = 0; c < M; c++)
325
- out[r * M + c] = aug[r * W + M + c];
326
- }
327
- return true;
328
- }
329
- // Sugar: closed-form Procrustes via factor() typed API.
330
- //
331
- // Procrustes with typed outputs (centroid is a real Vec) but a
332
- // Jacobian-LSQ bwd — a showcase for the typed ergonomics. The
333
- // closed-form `procrustesLens` is faster and exact.
334
- export function procrustesTyped(points) {
335
- const K = points.length;
336
- return factor(points, {
337
- centroid: {
338
- Cls: Vec,
339
- fwd: (pts) => {
340
- let sx = 0;
341
- let sy = 0;
342
- for (let i = 0; i < K; i++) {
343
- sx += pts[i].x;
344
- sy += pts[i].y;
345
- }
346
- return { x: sx / K, y: sy / K };
347
- },
348
- },
349
- rotation: {
350
- Cls: Num,
351
- fwd: (pts) => {
352
- let sx = 0;
353
- let sy = 0;
354
- for (let i = 0; i < K; i++) {
355
- sx += pts[i].x;
356
- sy += pts[i].y;
357
- }
358
- return Math.atan2(pts[0].y - sy / K, pts[0].x - sx / K);
359
- },
360
- },
361
- scale: {
362
- Cls: Num,
363
- fwd: (pts) => {
364
- let sx = 0;
365
- let sy = 0;
366
- for (let i = 0; i < K; i++) {
367
- sx += pts[i].x;
368
- sy += pts[i].y;
369
- }
370
- return Math.hypot(pts[0].x - sx / K, pts[0].y - sy / K);
371
- },
372
- },
373
- // Damping bumped up — atan2/hypot are non-linear; without damping the
374
- // first-Newton-step error compounds at the boundary of well-conditioned.
375
- }, { damping: 1e-3 });
376
- }
@@ -1,4 +1,4 @@
1
- import { type Read } from "./signal.js";
1
+ import { type Read } from "./cell.js";
2
2
  /** Disposable handle. */
3
3
  export interface Lifecycle {
4
4
  dispose(): void;
@@ -1,10 +1,10 @@
1
- // network-utils.ts — reactive-collection lifecycle helpers over the
1
+ // lifecycle.ts — reactive-collection lifecycle helpers over the
2
2
  // `network` model. Implemented purely via `effect`:
3
3
  //
4
4
  // `each(source, body)` — body runs per element (keyed by reference
5
5
  // identity); cleanup on removal.
6
6
  // `when(source, body)` — body runs while truthy; cleanup on falsy.
7
- import { effect } from "./signal.js";
7
+ import { effect } from "./cell.js";
8
8
  /** Run `body(item)` per element on first sight (storing its cleanup) and
9
9
  * run that cleanup when the element leaves. Identity is element
10
10
  * reference — keep stable refs across mutations, don't re-create
@@ -0,0 +1,10 @@
1
+ /** Dense vector/matrix backing: a typed buffer or a plain array. */
2
+ export type Floats = Float64Array | number[];
3
+ /** Solve an SPD (or semi-definite) system `A·x = b` in place via LDLᵀ.
4
+ * `A` is destroyed, the solution is written into `b`. Returns `false`
5
+ * if too singular to solve safely (callers typically leave state put). */
6
+ export declare function solveSPD(A: Floats, b: Floats, n: number): boolean;
7
+ /** 2×2 SPD direct solve (uses symmetry: `A[1] === A[2]`). */
8
+ export declare function solve2(A: Floats, b: Floats): boolean;
9
+ /** 3×3 SPD direct solve via cofactor expansion. */
10
+ export declare function solve3(A: Floats, b: Floats): boolean;
@@ -0,0 +1,109 @@
1
+ // linalg.ts — small dense linear algebra for the numerical backward
2
+ // directions. Two callers, one kernel:
3
+ // - lenses: the factor/argmin pseudoinverse solves `A k = δy` where
4
+ // `A = J W Jᵀ + λI` is SPD by construction.
5
+ // - constraints: AVBD's per-cell local Newton solve, `n = cell DOF`.
6
+ //
7
+ // Systems are tiny (n ≤ ~12), so unrolled direct solves (n = 2, 3) and
8
+ // in-place LDLᵀ beat anything general-purpose AND allocate nothing — the
9
+ // matrix is destroyed, the solution is written into `b`. This replaces the
10
+ // old full-inverse-then-multiply path (1.9–5.3× slower; allocated an
11
+ // `n×2n` augmented buffer per write).
12
+ //
13
+ // Convention: matrices are row-major, length `n*n`.
14
+ const TINY = 1e-14;
15
+ /** Solve an SPD (or semi-definite) system `A·x = b` in place via LDLᵀ.
16
+ * `A` is destroyed, the solution is written into `b`. Returns `false`
17
+ * if too singular to solve safely (callers typically leave state put). */
18
+ export function solveSPD(A, b, n) {
19
+ if (n === 1) {
20
+ const a = A[0];
21
+ if (Math.abs(a) < TINY)
22
+ return false;
23
+ b[0] = b[0] / a;
24
+ return true;
25
+ }
26
+ if (n === 2)
27
+ return solve2(A, b);
28
+ if (n === 3)
29
+ return solve3(A, b);
30
+ return ldltGeneric(A, b, n);
31
+ }
32
+ /** 2×2 SPD direct solve (uses symmetry: `A[1] === A[2]`). */
33
+ export function solve2(A, b) {
34
+ const a = A[0];
35
+ const c = A[1];
36
+ const d = A[3];
37
+ const det = a * d - c * c;
38
+ if (Math.abs(det) < TINY)
39
+ return false;
40
+ const inv = 1 / det;
41
+ const x0 = (d * b[0] - c * b[1]) * inv;
42
+ const x1 = (-c * b[0] + a * b[1]) * inv;
43
+ b[0] = x0;
44
+ b[1] = x1;
45
+ return true;
46
+ }
47
+ /** 3×3 SPD direct solve via cofactor expansion. */
48
+ export function solve3(A, b) {
49
+ const a00 = A[0], a01 = A[1], a02 = A[2];
50
+ const a10 = A[3], a11 = A[4], a12 = A[5];
51
+ const a20 = A[6], a21 = A[7], a22 = A[8];
52
+ const c00 = a11 * a22 - a12 * a21;
53
+ const c01 = -(a10 * a22 - a12 * a20);
54
+ const c02 = a10 * a21 - a11 * a20;
55
+ const det = a00 * c00 + a01 * c01 + a02 * c02;
56
+ if (Math.abs(det) < TINY)
57
+ return false;
58
+ const c10 = -(a01 * a22 - a02 * a21);
59
+ const c11 = a00 * a22 - a02 * a20;
60
+ const c12 = -(a00 * a21 - a01 * a20);
61
+ const c20 = a01 * a12 - a02 * a11;
62
+ const c21 = -(a00 * a12 - a02 * a10);
63
+ const c22 = a00 * a11 - a01 * a10;
64
+ const inv = 1 / det;
65
+ const b0 = b[0], b1 = b[1], b2 = b[2];
66
+ b[0] = (c00 * b0 + c10 * b1 + c20 * b2) * inv;
67
+ b[1] = (c01 * b0 + c11 * b1 + c21 * b2) * inv;
68
+ b[2] = (c02 * b0 + c12 * b1 + c22 * b2) * inv;
69
+ return true;
70
+ }
71
+ /** General LDLᵀ for `n ≥ 4`. In-place: `A` is overwritten with the
72
+ * factor, `b` with the solution. */
73
+ function ldltGeneric(A, b, n) {
74
+ for (let j = 0; j < n; j++) {
75
+ let djj = A[j * n + j];
76
+ for (let k = 0; k < j; k++) {
77
+ const ljk = A[j * n + k];
78
+ djj -= ljk * ljk * A[k * n + k];
79
+ }
80
+ if (Math.abs(djj) < TINY)
81
+ return false;
82
+ A[j * n + j] = djj;
83
+ for (let i = j + 1; i < n; i++) {
84
+ let lij = A[i * n + j];
85
+ for (let k = 0; k < j; k++) {
86
+ lij -= A[i * n + k] * A[j * n + k] * A[k * n + k];
87
+ }
88
+ A[i * n + j] = lij / djj;
89
+ }
90
+ }
91
+ // Forward solve L y = b → y in b.
92
+ for (let i = 0; i < n; i++) {
93
+ let yi = b[i];
94
+ for (let k = 0; k < i; k++)
95
+ yi -= A[i * n + k] * b[k];
96
+ b[i] = yi;
97
+ }
98
+ // D solve.
99
+ for (let i = 0; i < n; i++)
100
+ b[i] = b[i] / A[i * n + i];
101
+ // Backward solve Lᵀ x = y → x in b.
102
+ for (let i = n - 1; i >= 0; i--) {
103
+ let xi = b[i];
104
+ for (let k = i + 1; k < n; k++)
105
+ xi -= A[k * n + i] * b[k];
106
+ b[i] = xi;
107
+ }
108
+ return true;
109
+ }
@@ -1,4 +1,4 @@
1
- import type { Inner } from "../signal.js";
1
+ import type { Inner } from "../cell.js";
2
2
  import type { Vec } from "./vec.js";
3
3
  type V = Inner<Vec>;
4
4
  /** Anchor points on the unit box (`Center = {0.5, 0.5}`). */
@@ -1,4 +1,4 @@
1
- import { Cell, type Init, type Val, type Writable } from "../signal.js";
1
+ import { Cell, type Init, type Val, type Writable } from "../cell.js";
2
2
  import { Num } from "./num.js";
3
3
  /** Clip header. The graph compares `epoch`; `pcm` is one channel buffer each. */
4
4
  export interface AudioClip {
@@ -13,7 +13,7 @@
13
13
  // complement (the original peak) recovered on write-back, the audio analog of
14
14
  // str.lowercase()/canvas.grayscale().
15
15
  // - cross-type lens (`rms` → Num).
16
- import { Cell, reader } from "../signal.js";
16
+ import { Cell, reader } from "../cell.js";
17
17
  import { Num } from "./num.js";
18
18
  let EPOCH = 0;
19
19
  /** Stamp channel buffers with a fresh epoch — the only way to mint a value. */
@@ -1,4 +1,4 @@
1
- import { Cell, type Init, type Val, type Writable } from "../signal.js";
1
+ import { Cell, type Init, type Val, type Writable } from "../cell.js";
2
2
  import type { Linear } from "../traits.js";
3
3
  type V = boolean;
4
4
  export declare const not: (a: V) => V;
@@ -7,7 +7,7 @@
7
7
  // `and` / `or` / `implies` / `eq` / `nand` / `nor` return bare RO `Bool`
8
8
  // — lossy fan-ins whose write-back is ambiguous. Lift to writable via
9
9
  // `Bool.lens([a, b], fwd, bwd)` with an explicit policy.
10
- import { Cell, reader } from "../signal.js";
10
+ import { Cell, reader } from "../cell.js";
11
11
  export const not = (a) => !a;
12
12
  export const and = (a, b) => a && b;
13
13
  export const or = (a, b) => a || b;
@@ -1,6 +1,5 @@
1
- import type { Easing } from "../../animation/index.js";
2
- import { type Tween } from "../anim.js";
3
- import { Cell, type Init, type Inner, type Val, type Writable, type WritableBrand } from "../signal.js";
1
+ import { type Easing, type Tween } from "../../animation/index.js";
2
+ import { Cell, type Init, type Inner, type Val, type Writable, type WritableBrand } from "../cell.js";
4
3
  import type { Linear, Pack } from "../traits.js";
5
4
  import { Bool } from "./bool.js";
6
5
  import { Num } from "./num.js";
@@ -2,9 +2,8 @@
2
2
  //
3
3
  // Invertibles (`add`, `sub`, `scale`, `expand`) return `: this` and ride
4
4
  // on `Cell#lens(fwd, bwd)`. Chained calls compose into a lens chain.
5
- import { tween } from "../anim.js";
6
- import { Cell, isComputed, lazy, reader, readNow, } from "../signal.js";
7
- import { derived, field } from "../writable.js";
5
+ import { tween } from "../../animation/index.js";
6
+ import { Cell, cachedDerive, field, isReadonly, lazy, reader, readNow, } from "../cell.js";
8
7
  import { Bool } from "./bool.js";
9
8
  import { Num, num } from "./num.js";
10
9
  import { Vec } from "./vec.js";
@@ -129,9 +128,9 @@ export class Box extends Cell {
129
128
  * nearest edge by `eps`. Literal / RO inputs yield a bare RO `Bool`. */
130
129
  contains(p) {
131
130
  if (p instanceof Vec) {
132
- // A computed Vec has no backward path → RO branch; sources and
131
+ // A read-only Vec has no backward path → RO branch; sources and
133
132
  // writable lenses accept write-back.
134
- if (!isComputed(p)) {
133
+ if (!isReadonly(p)) {
135
134
  // `.bind(Bool)` + cast steps past the generic overloads, whose
136
135
  // mapped-tuple inference over the full class types otherwise blows
137
136
  // the instantiation depth.
@@ -159,7 +158,7 @@ export class Box extends Cell {
159
158
  return field(this, "h", Num);
160
159
  }
161
160
  get area() {
162
- return derived(this, "area", Num, b => b.w * b.h);
161
+ return cachedDerive(this, "area", Num, b => b.w * b.h);
163
162
  }
164
163
  /** Vec at parametric (u, v) within `[0,1]²`. Not memoised (arbitrary
165
164
  * pairs would leak a cache entry each) — use the named edge getters
@@ -1,4 +1,4 @@
1
- import { Cell, type Val, type Writable } from "../signal.js";
1
+ import { Cell, type Val, type Writable } from "../cell.js";
2
2
  import { Bool } from "./bool.js";
3
3
  import { Color } from "./color.js";
4
4
  import { Spring, type SpringOpts } from "./gpu.js";
@@ -20,8 +20,7 @@
20
20
  // A backward pass that produces a ROOT cell's next value reads that root and
21
21
  // writes a result that becomes its value, so it uses `scratch2` (a feedback-
22
22
  // safe pair) to avoid reading and writing the same texture.
23
- import { Cell, reader } from "../signal.js";
24
- import { derived } from "../writable.js";
23
+ import { Cell, cachedDerive, reader } from "../cell.js";
25
24
  import { Bool } from "./bool.js";
26
25
  import { Color } from "./color.js";
27
26
  import { copy, newTex, pass, reduceMean, Spring, scratch, scratch2, } from "./gpu.js";
@@ -464,7 +463,7 @@ export class Canvas extends Cell {
464
463
  }
465
464
  /** Dimensions `(w, h)` as a read-only `Vec`. */
466
465
  get dimensions() {
467
- return derived(this, "dimensions", Vec, v => ({ x: v.w, y: v.h }));
466
+ return cachedDerive(this, "dimensions", Vec, v => ({ x: v.w, y: v.h }));
468
467
  }
469
468
  /** A GPU per-pixel spring driver seeded from this value's texture. The
470
469
  * host steps it and writes `current()` back into a root cell each frame;
@@ -1,6 +1,5 @@
1
- import type { Easing } from "../../animation/index.js";
2
- import { type Tween } from "../anim.js";
3
- import { Cell, type Init, type Val, type Writable } from "../signal.js";
1
+ import { type Easing, type Tween } from "../../animation/index.js";
2
+ import { Cell, type Init, type Val, type Writable } from "../cell.js";
4
3
  import type { Linear, Pack } from "../traits.js";
5
4
  import { Num } from "./num.js";
6
5
  type V = {
@@ -2,9 +2,8 @@
2
2
  //
3
3
  // Invertibles (`add`, `sub`, `scale`) return `: this` and ride on
4
4
  // `Cell#lens(fwd, bwd)`. Chained calls compose into a lens chain.
5
- import { tween } from "../anim.js";
6
- import { Cell, derive, lazy, reader, readNow } from "../signal.js";
7
- import { derived, field } from "../writable.js";
5
+ import { tween } from "../../animation/index.js";
6
+ import { Cell, cachedDerive, derive, field, lazy, reader, readNow, } from "../cell.js";
8
7
  import { Num, num } from "./num.js";
9
8
  export const add = (a, b) => ({ r: a.r + b.r, g: a.g + b.g, b: a.b + b.b, a: a.a + b.a });
10
9
  export const sub = (a, b) => ({ r: a.r - b.r, g: a.g - b.g, b: a.b - b.b, a: a.a - b.a });
@@ -68,7 +67,7 @@ export class Color extends Cell {
68
67
  return field(this, "a", Num);
69
68
  }
70
69
  get luminance() {
71
- return derived(this, "luminance", Num, c => 0.299 * c.r + 0.587 * c.g + 0.114 * c.b);
70
+ return cachedDerive(this, "luminance", Num, c => 0.299 * c.r + 0.587 * c.g + 0.114 * c.b);
72
71
  }
73
72
  get css() {
74
73
  return lazy(this, "css", () => derive(() => {
@@ -1,4 +1,4 @@
1
- import { Cell, type Writable } from "../signal.js";
1
+ import { Cell, type Writable } from "../cell.js";
2
2
  import { Bool } from "./bool.js";
3
3
  export declare class Flags<K extends string> extends Cell<number> {
4
4
  #private;
@@ -13,7 +13,7 @@
13
13
  // flag named "value"/"flag" shadowing the class. Since each flag is a
14
14
  // writable Bool, `Tri.allOf([f.flag(a), f.flag(b)])` gives all/none/mixed
15
15
  // group toggles for free.
16
- import { Cell } from "../signal.js";
16
+ import { Cell } from "../cell.js";
17
17
  import { Bool } from "./bool.js";
18
18
  const equals = (a, b) => a === b;
19
19
  export class Flags extends Cell {
@@ -1,4 +1,4 @@
1
- import { Cell, type Init, type Inner, type Val, type Writable } from "../signal.js";
1
+ import { Cell, type Init, type Inner, type Val, type Writable } from "../cell.js";
2
2
  import { Num } from "./num.js";
3
3
  import type { Vec } from "./vec.js";
4
4
  type V = {
@@ -5,8 +5,7 @@
5
5
  // compile time. Two invertibles, both `: this` via `Cell#lens`:
6
6
  // - `multiply(b)` — inverse multiplies by `invert(b)`
7
7
  // - `invert()` — its own inverse
8
- import { Cell, reader } from "../signal.js";
9
- import { derived, field } from "../writable.js";
8
+ import { Cell, cachedDerive, field, reader, } from "../cell.js";
10
9
  import { Num, num } from "./num.js";
11
10
  export const identity = () => ({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 });
12
11
  export const fromTranslate = (x, y) => ({ a: 1, b: 0, c: 0, d: 1, e: x, f: y });
@@ -112,7 +111,7 @@ export class Matrix extends Cell {
112
111
  return field(this, "f", Num);
113
112
  }
114
113
  get determinant() {
115
- return derived(this, "determinant", Num, determinant);
114
+ return cachedDerive(this, "determinant", Num, determinant);
116
115
  }
117
116
  }
118
117
  /** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
@@ -1,6 +1,5 @@
1
- import type { Easing } from "../../animation/index.js";
2
- import { type Tween } from "../anim.js";
3
- import { Cell, type Init, type Val, type Writable, type WritableBrand } from "../signal.js";
1
+ import { type Easing, type Tween } from "../../animation/index.js";
2
+ import { Cell, type Init, type Val, type Writable, type WritableBrand } from "../cell.js";
4
3
  import type { Linear, Pack } from "../traits.js";
5
4
  import { Bool } from "./bool.js";
6
5
  type V = number;
@@ -2,8 +2,8 @@
2
2
  //
3
3
  // Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
4
4
  // chained calls compose into a lens chain.
5
- import { tween } from "../anim.js";
6
- import { Cell, lazy, reader, } from "../signal.js";
5
+ import { tween } from "../../animation/index.js";
6
+ import { Cell, lazy, reader, } from "../cell.js";
7
7
  import { Bool } from "./bool.js";
8
8
  export const add = (a, b) => a + b;
9
9
  export const sub = (a, b) => a - b;
@@ -1,5 +1,6 @@
1
- import { Cell, type Init, type Writable } from "../signal.js";
1
+ import { Cell, type Init, type Writable } from "../cell.js";
2
2
  import type { Linear, Pack, Pivotal } from "../traits.js";
3
+ import { Num } from "./num.js";
3
4
  type V = {
4
5
  x: number;
5
6
  y: number;
@@ -22,6 +23,9 @@ export declare class Pose extends Cell<V> {
22
23
  };
23
24
  readonly _t: typeof Pose.traits;
24
25
  constructor(v?: V);
26
+ get x(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
27
+ get y(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
28
+ get theta(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
25
29
  }
26
30
  /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
27
31
  * through by identity. RO sources are rejected at the type level — use
@@ -4,7 +4,8 @@
4
4
  // a 3-DOF block and writes back through it, so renderers, drag handlers,
5
5
  // IK, and physics all observe the same value. Vec / Num lenses compose
6
6
  // for consumers that care only about translation or rotation.
7
- import { Cell } from "../signal.js";
7
+ import { Cell, field } from "../cell.js";
8
+ import { Num } from "./num.js";
8
9
  export const add = (a, b) => ({
9
10
  x: a.x + b.x,
10
11
  y: a.y + b.y,
@@ -69,6 +70,15 @@ export class Pose extends Cell {
69
70
  constructor(v = { x: 0, y: 0, theta: 0 }) {
70
71
  super(v, { equals });
71
72
  }
73
+ get x() {
74
+ return field(this, "x", Num);
75
+ }
76
+ get y() {
77
+ return field(this, "y", Num);
78
+ }
79
+ get theta() {
80
+ return field(this, "theta", Num);
81
+ }
72
82
  }
73
83
  /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
74
84
  * through by identity. RO sources are rejected at the type level — use