bireactive 0.2.3 → 0.3.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 (98) hide show
  1. package/dist/animation/anim.js +4 -0
  2. package/dist/coll.d.ts +7 -7
  3. package/dist/coll.js +3 -1
  4. package/dist/core/cell.d.ts +89 -66
  5. package/dist/core/cell.js +642 -401
  6. package/dist/core/index.d.ts +4 -14
  7. package/dist/core/index.js +4 -14
  8. package/dist/core/lenses/aggregates.d.ts +1 -1
  9. package/dist/core/lenses/aggregates.js +4 -3
  10. package/dist/core/lenses/closed-form-policies.js +6 -6
  11. package/dist/core/lenses/decompositions.js +3 -3
  12. package/dist/core/lenses/domain-aggregates.js +5 -5
  13. package/dist/core/lenses/geometry.d.ts +1 -1
  14. package/dist/core/lenses/geometry.js +6 -7
  15. package/dist/core/lenses/memory.d.ts +2 -2
  16. package/dist/core/lenses/memory.js +3 -3
  17. package/dist/core/lenses/typed-factor.js +4 -3
  18. package/dist/core/traits.d.ts +1 -0
  19. package/dist/core/values/box.js +7 -7
  20. package/dist/core/values/color.js +5 -5
  21. package/dist/core/values/field.d.ts +70 -0
  22. package/dist/core/values/field.js +230 -0
  23. package/dist/core/values/gpu.d.ts +4 -2
  24. package/dist/core/values/gpu.js +11 -4
  25. package/dist/core/values/matrix.js +7 -7
  26. package/dist/core/values/num.d.ts +1 -1
  27. package/dist/core/values/num.js +1 -1
  28. package/dist/core/values/pose.js +4 -4
  29. package/dist/core/values/range.js +6 -6
  30. package/dist/core/values/template.d.ts +1 -1
  31. package/dist/core/values/template.js +2 -1
  32. package/dist/core/values/transform.js +7 -7
  33. package/dist/core/values/tri.js +3 -3
  34. package/dist/core/values/vec.js +8 -12
  35. package/dist/ext/timeline.js +2 -2
  36. package/dist/formats/cst.d.ts +127 -0
  37. package/dist/formats/cst.js +280 -0
  38. package/dist/formats/edn.d.ts +2 -0
  39. package/dist/formats/edn.js +301 -0
  40. package/dist/formats/index.d.ts +6 -0
  41. package/dist/formats/index.js +8 -0
  42. package/dist/formats/json.d.ts +2 -0
  43. package/dist/formats/json.js +332 -0
  44. package/dist/formats/lens.d.ts +8 -0
  45. package/dist/formats/lens.js +54 -0
  46. package/dist/formats/toml.d.ts +2 -0
  47. package/dist/formats/toml.js +526 -0
  48. package/dist/formats/yaml.d.ts +2 -0
  49. package/dist/formats/yaml.js +661 -0
  50. package/dist/index.d.ts +10 -0
  51. package/dist/index.js +10 -0
  52. package/dist/learn/data.d.ts +49 -0
  53. package/dist/learn/data.js +181 -0
  54. package/dist/learn/index.d.ts +3 -0
  55. package/dist/learn/index.js +6 -0
  56. package/dist/learn/lens-net.d.ts +63 -0
  57. package/dist/learn/lens-net.js +219 -0
  58. package/dist/learn/mlp.d.ts +77 -0
  59. package/dist/learn/mlp.js +292 -0
  60. package/dist/propagators/csp.d.ts +13 -0
  61. package/dist/propagators/csp.js +52 -0
  62. package/dist/propagators/flex.d.ts +31 -0
  63. package/dist/propagators/flex.js +189 -0
  64. package/dist/propagators/graph.d.ts +73 -0
  65. package/dist/propagators/graph.js +543 -0
  66. package/dist/propagators/index.d.ts +8 -6
  67. package/dist/propagators/index.js +15 -6
  68. package/dist/propagators/lattice.d.ts +45 -0
  69. package/dist/propagators/lattice.js +113 -0
  70. package/dist/propagators/layout.d.ts +1 -27
  71. package/dist/propagators/layout.js +6 -175
  72. package/dist/propagators/numeric.d.ts +17 -0
  73. package/dist/propagators/numeric.js +93 -0
  74. package/dist/propagators/solver.d.ts +51 -0
  75. package/dist/propagators/solver.js +175 -0
  76. package/dist/schema/index.d.ts +1 -0
  77. package/dist/schema/index.js +3 -0
  78. package/dist/schema/lens.d.ts +121 -0
  79. package/dist/schema/lens.js +429 -0
  80. package/dist/shapes/annular-sector.js +4 -4
  81. package/dist/shapes/button.js +1 -1
  82. package/dist/shapes/circle.js +1 -1
  83. package/dist/shapes/handle.js +2 -2
  84. package/dist/shapes/label.js +1 -1
  85. package/dist/shapes/layout.js +2 -2
  86. package/dist/shapes/rect.js +7 -7
  87. package/dist/shapes/shape.js +8 -8
  88. package/dist/tex/tex.js +9 -2
  89. package/dist/web/diagram.js +2 -2
  90. package/package.json +9 -19
  91. package/dist/propagators/network.d.ts +0 -52
  92. package/dist/propagators/network.js +0 -185
  93. package/dist/propagators/propagator.d.ts +0 -12
  94. package/dist/propagators/propagator.js +0 -16
  95. package/dist/propagators/range.d.ts +0 -45
  96. package/dist/propagators/range.js +0 -147
  97. package/dist/propagators/relations.d.ts +0 -60
  98. package/dist/propagators/relations.js +0 -343
@@ -1,33 +1,23 @@
1
- export { batch, Cell, type CellOptions, cachedDerive, cell, derive, effect, field, type Init, type Inner, isCell, isLens, isReadonly, lazy, lens, type Network, network, type Read, reader, readNow, type StatefulBwd, type StatefulLensSpec, setCellWriteHook, transitiveDeps, untracked, type Val, type Writable, type WritableBrand, } from "./cell.js";
1
+ export { batch, Cell, type CellOptions, cachedDerive, cell, derive, effect, fieldLens, fieldOf, type Init, type Inner, isCell, isLens, isReadonly, lazy, lens, type Network, network, type Read, reader, readNow, SKIP, type Skip, type StatefulBwd, type StatefulLensSpec, setCellWriteHook, settle, transitiveDeps, untracked, type Val, type Writable, type WritableBrand, } from "./cell.js";
2
2
  export { bezier2, bezier3 } from "./derived-geometry.js";
3
3
  export * from "./lenses/index.js";
4
4
  export { each, type Lifecycle } from "./lifecycle.js";
5
5
  export { type Equals, type Lerp, type Linear, type Metric, type Pack, type Pivotal, requireEquals, requireLerp, requireLinear, requireMetric, requirePack, requirePivotal, type TraitDict, type Traits, } from "./traits.js";
6
6
  export { Anchor, Dir } from "./values/anchor.js";
7
7
  export { Audio, type AudioClip, audio, stamp as audioStamp } from "./values/audio.js";
8
- export * as BoolMath from "./values/bool.js";
9
8
  export { Bool, bool } from "./values/bool.js";
10
- export * as BoxMath from "./values/box.js";
11
- export { Box, box } from "./values/box.js";
9
+ export { Box, box, edgeFrom as boxEdgeFrom, expand as boxExpand, union as boxUnion, } from "./values/box.js";
12
10
  export { Canvas, canvas, type Raster, stamp as canvasStamp } from "./values/canvas.js";
13
- export * as ColorMath from "./values/color.js";
14
11
  export { Color, rgb, rgba } from "./values/color.js";
12
+ export { type ColorStop, Colour, Field, type FieldVal, field, type Kind as FieldKind, Scalar, Vector, } from "./values/field.js";
15
13
  export { Flags, flags } from "./values/flags.js";
16
14
  export { blit as gpuBlit, brush as gpuBrush, copy as gpuCopy, newTex as gpuNewTex, Spring, scratch2 as gpuScratch2, type Tex, } from "./values/gpu.js";
17
- export * as MatrixMath from "./values/matrix.js";
18
- export { Matrix, matrix, transformBox, transformPoint } from "./values/matrix.js";
19
- export * as NumMath from "./values/num.js";
15
+ export { compose as matrixCompose, Matrix, matrix, toMatrixString, transformBox, transformPoint, } from "./values/matrix.js";
20
16
  export { Num, num } from "./values/num.js";
21
- export * as PoseMath from "./values/pose.js";
22
17
  export { Pose, pose } from "./values/pose.js";
23
- export * as RangeMath from "./values/range.js";
24
18
  export { Range, range, span } from "./values/range.js";
25
- export * as StrMath from "./values/str.js";
26
19
  export { Str, str } from "./values/str.js";
27
20
  export { type Codec, enumCodec, numCodec, route, type Slot, slot, strCodec, template, tpl, } from "./values/template.js";
28
- export * as TransformMath from "./values/transform.js";
29
21
  export { Transform, type TransformInit, transform } from "./values/transform.js";
30
- export * as TriMath from "./values/tri.js";
31
22
  export { Tri, tri } from "./values/tri.js";
32
- export * as VecMath from "./values/vec.js";
33
23
  export { type PolarPolicy, polar, tangentPoint, Vec, vec } from "./values/vec.js";
@@ -1,33 +1,23 @@
1
- export { batch, Cell, cachedDerive, cell, derive, effect, field, isCell, isLens, isReadonly, lazy, lens, network, reader, readNow, setCellWriteHook, transitiveDeps, untracked, } from "./cell.js";
1
+ export { batch, Cell, cachedDerive, cell, derive, effect, fieldLens, fieldOf, isCell, isLens, isReadonly, lazy, lens, network, reader, readNow, SKIP, setCellWriteHook, settle, transitiveDeps, untracked, } from "./cell.js";
2
2
  export { bezier2, bezier3 } from "./derived-geometry.js";
3
3
  export * from "./lenses/index.js";
4
4
  export { each } from "./lifecycle.js";
5
5
  export { requireEquals, requireLerp, requireLinear, requireMetric, requirePack, requirePivotal, } from "./traits.js";
6
6
  export { Anchor, Dir } from "./values/anchor.js";
7
7
  export { Audio, audio, stamp as audioStamp } from "./values/audio.js";
8
- export * as BoolMath from "./values/bool.js";
9
8
  export { Bool, bool } from "./values/bool.js";
10
- export * as BoxMath from "./values/box.js";
11
- export { Box, box } from "./values/box.js";
9
+ export { Box, box, edgeFrom as boxEdgeFrom, expand as boxExpand, union as boxUnion, } from "./values/box.js";
12
10
  export { Canvas, canvas, stamp as canvasStamp } from "./values/canvas.js";
13
- export * as ColorMath from "./values/color.js";
14
11
  export { Color, rgb, rgba } from "./values/color.js";
12
+ export { Colour, Field, field, Scalar, Vector, } from "./values/field.js";
15
13
  export { Flags, flags } from "./values/flags.js";
16
14
  export { blit as gpuBlit, brush as gpuBrush, copy as gpuCopy, newTex as gpuNewTex, Spring, scratch2 as gpuScratch2, } from "./values/gpu.js";
17
- export * as MatrixMath from "./values/matrix.js";
18
- export { Matrix, matrix, transformBox, transformPoint } from "./values/matrix.js";
19
- export * as NumMath from "./values/num.js";
15
+ export { compose as matrixCompose, Matrix, matrix, toMatrixString, transformBox, transformPoint, } from "./values/matrix.js";
20
16
  export { Num, num } from "./values/num.js";
21
- export * as PoseMath from "./values/pose.js";
22
17
  export { Pose, pose } from "./values/pose.js";
23
- export * as RangeMath from "./values/range.js";
24
18
  export { Range, range, span } from "./values/range.js";
25
- export * as StrMath from "./values/str.js";
26
19
  export { Str, str } from "./values/str.js";
27
20
  export { enumCodec, numCodec, route, slot, strCodec, template, tpl, } from "./values/template.js";
28
- export * as TransformMath from "./values/transform.js";
29
21
  export { Transform, transform } from "./values/transform.js";
30
- export * as TriMath from "./values/tri.js";
31
22
  export { Tri, tri } from "./values/tri.js";
32
- export * as VecMath from "./values/vec.js";
33
23
  export { polar, tangentPoint, Vec, vec } from "./values/vec.js";
@@ -1,4 +1,4 @@
1
- import type { Writable } from "../cell.js";
1
+ import { type Writable } from "../cell.js";
2
2
  import { Num } from "../values/num.js";
3
3
  import { Vec } from "../values/vec.js";
4
4
  export interface ArgminOpts {
@@ -5,6 +5,7 @@
5
5
  // All route through the engine's N-input lens path. Stateless-bwd
6
6
  // (`(target) => updates`) skips the peek loop on the hot path;
7
7
  // stateful-bwd (`(target, vals) => updates`) reads the scratch.
8
+ import { SKIP } from "../cell.js";
8
9
  import { Num } from "../values/num.js";
9
10
  import { Vec } from "../values/vec.js";
10
11
  /** Project `p` into the closed disc of radius `r` centred on `c` (points
@@ -51,7 +52,7 @@ export function argminNum(inputs, forward, weights, opts = {}) {
51
52
  const k = dy / denom;
52
53
  for (let i = 0; i < n; i++) {
53
54
  if (weights[i] === 0) {
54
- out[i] = undefined;
55
+ out[i] = SKIP;
55
56
  }
56
57
  else {
57
58
  out[i] = xs[i] + weights[i] * J[i] * k;
@@ -103,7 +104,7 @@ export function argminVec(inputs, forward, weights, opts = {}) {
103
104
  if (Math.abs(det) < 1e-14) {
104
105
  // Singular; leave inputs unchanged.
105
106
  for (let i = 0; i < n; i++)
106
- out[i] = undefined;
107
+ out[i] = SKIP;
107
108
  return out;
108
109
  }
109
110
  const invA = c / det;
@@ -114,7 +115,7 @@ export function argminVec(inputs, forward, weights, opts = {}) {
114
115
  for (let i = 0; i < n; i++) {
115
116
  const w = weights[i];
116
117
  if (w === 0) {
117
- out[i] = undefined;
118
+ out[i] = SKIP;
118
119
  }
119
120
  else {
120
121
  out[i] = xs[i] + w * (Jx[i] * kx + Jy[i] * ky);
@@ -10,7 +10,7 @@
10
10
  // (bestFitLine, bestFitCircle, pca, total). All exact, idempotent,
11
11
  // cross-channel invariant by construction, on the same `Cls.lens`
12
12
  // machinery — no engine changes.
13
- import { mean, Num, Vec, } from "../index.js";
13
+ import { mean, Num, SKIP, Vec, } from "../index.js";
14
14
  import { continuous, remember } from "./memory.js";
15
15
  // Pivotal trait lookup via the value class's `static traits.pivotal` slot.
16
16
  // biome-ignore lint/suspicious/noExplicitAny: dynamic trait lookup
@@ -50,7 +50,7 @@ export function rotateAbout(points, pivot) {
50
50
  const rx0 = vals[0].x - p.x;
51
51
  const ry0 = vals[0].y - p.y;
52
52
  if (rx0 * rx0 + ry0 * ry0 < 1e-24) {
53
- return vals.map(() => undefined);
53
+ return vals.map(() => SKIP);
54
54
  }
55
55
  const oldθ = Math.atan2(ry0, rx0);
56
56
  const dθ = target - oldθ;
@@ -96,11 +96,11 @@ export function scaleAbout(points, pivot) {
96
96
  // re-projects to the current radius and is absorbed (sources put).
97
97
  const rNow = Math.hypot(vals[0].x - p.x, vals[0].y - p.y);
98
98
  if (Math.abs(target) === rNow)
99
- return { updates: vals.map(() => undefined), complement: c };
99
+ return { updates: vals.map(() => SKIP), complement: c };
100
100
  const d0 = c.devs[0];
101
101
  const r0 = Math.hypot(d0.x, d0.y);
102
102
  if (r0 < 1e-12)
103
- return { updates: vals.map(() => undefined), complement: c };
103
+ return { updates: vals.map(() => SKIP), complement: c };
104
104
  const k = target / r0;
105
105
  const out = vals.map((v, i) => ({
106
106
  ...v,
@@ -299,7 +299,7 @@ export function pca(points) {
299
299
  const rotation = Num.lens(points, (vals) => decompose(vals)?.θ ?? 0, (target, vals) => {
300
300
  const d = decompose(vals);
301
301
  if (!d)
302
- return vals.map(() => undefined);
302
+ return vals.map(() => SKIP);
303
303
  const dθ = target - d.θ;
304
304
  const cos = Math.cos(dθ);
305
305
  const sin = Math.sin(dθ);
@@ -382,7 +382,7 @@ export function pca(points) {
382
382
  // Lossy magnitude view: a same-magnitude target re-projects to
383
383
  // the current axis length and is absorbed (cluster left put).
384
384
  if (Math.abs(target) === c.lenThis)
385
- return { updates: vals.map(() => undefined), complement: c };
385
+ return { updates: vals.map(() => SKIP), complement: c };
386
386
  // Non-degenerate fast path: scale current cluster along axis.
387
387
  const k = target / c.lenThis;
388
388
  return { updates: scaleAlongAxis(vals, d.cx, d.cy, c.uX, c.uY, k), complement: c };
@@ -6,7 +6,7 @@
6
6
  // about the centroid), so cross-channel invariance is EXACT and one
7
7
  // write lands in O(K). For the generic numerical N→M escape hatch (when
8
8
  // no closed form fits) see `factor` in `typed-factor.ts`.
9
- import { Num, Vec } from "../index.js";
9
+ import { Num, SKIP, Vec } from "../index.js";
10
10
  // meanDiff — M=2 isomorphism baseline.
11
11
  //
12
12
  // (a, b) → ((a+b)/2, a−b). Square full-rank linear lens; bwd is the
@@ -84,7 +84,7 @@ export function procrustes(points) {
84
84
  const ry0 = vals[0].y - cy;
85
85
  if (rx0 * rx0 + ry0 * ry0 < 1e-24) {
86
86
  // Collapsed cluster; no angle to rotate from.
87
- return vals.map(() => undefined);
87
+ return vals.map(() => SKIP);
88
88
  }
89
89
  const oldθ = Math.atan2(ry0, rx0);
90
90
  const dθ = target - oldθ;
@@ -130,7 +130,7 @@ export function procrustes(points) {
130
130
  const d0 = c.devs[0];
131
131
  const r0 = Math.hypot(d0.x, d0.y);
132
132
  if (r0 < 1e-12)
133
- return { updates: vals.map(() => undefined), complement: c };
133
+ return { updates: vals.map(() => SKIP), complement: c };
134
134
  const k = target / r0;
135
135
  const out = c.devs.map(d => ({ x: cen.x + k * d.x, y: cen.y + k * d.y }));
136
136
  return { updates: out, complement: c };
@@ -6,7 +6,7 @@
6
6
  // (2) Bezier gestalt handles ({start, end, startTangent, endTangent}).
7
7
  // (3) Time-series ({mean, slope}) over (i, value) samples.
8
8
  // All exact, idempotent, cross-channel invariant by construction.
9
- import { Num, reader, Vec, } from "../index.js";
9
+ import { Num, reader, SKIP, Vec, } from "../index.js";
10
10
  import { remember } from "./memory.js";
11
11
  // Generic Linear-trait aggregates.
12
12
  //
@@ -107,9 +107,9 @@ export function mix(weights, branches) {
107
107
  const { w, sumSq } = readW();
108
108
  const delta = lin.sub(target, combine(vals, w));
109
109
  if (sumSq < 1e-12)
110
- return vals.map(() => undefined);
110
+ return vals.map(() => SKIP);
111
111
  const inv = 1 / sumSq;
112
- return vals.map((v, i) => w[i] === 0 ? undefined : lin.add(v, lin.scale(delta, w[i] * inv)));
112
+ return vals.map((v, i) => w[i] === 0 ? SKIP : lin.add(v, lin.scale(delta, w[i] * inv)));
113
113
  });
114
114
  }
115
115
  /** Two-branch router (mix simplex *vertex*): reads the live branch, writes
@@ -187,8 +187,8 @@ export function bezierGestalt(p0, p1, p2, p3) {
187
187
  const dy = target.y - vals[1].y;
188
188
  return [{ x: vals[0].x + dx, y: vals[0].y + dy }, target];
189
189
  });
190
- const startTangent = Vec.lens([p0, p1], (vals) => ({ x: vals[1].x - vals[0].x, y: vals[1].y - vals[0].y }), (target, vals) => [undefined, { x: vals[0].x + target.x, y: vals[0].y + target.y }]);
191
- const endTangent = Vec.lens([p2, p3], (vals) => ({ x: vals[1].x - vals[0].x, y: vals[1].y - vals[0].y }), (target, vals) => [{ x: vals[1].x - target.x, y: vals[1].y - target.y }, undefined]);
190
+ const startTangent = Vec.lens([p0, p1], (vals) => ({ x: vals[1].x - vals[0].x, y: vals[1].y - vals[0].y }), (target, vals) => [SKIP, { x: vals[0].x + target.x, y: vals[0].y + target.y }]);
191
+ const endTangent = Vec.lens([p2, p3], (vals) => ({ x: vals[1].x - vals[0].x, y: vals[1].y - vals[0].y }), (target, vals) => [{ x: vals[1].x - target.x, y: vals[1].y - target.y }, SKIP]);
192
192
  return { start, end, startTangent, endTangent };
193
193
  }
194
194
  // Time-series aggregates.
@@ -1,4 +1,4 @@
1
- import type { Cell, Read, Writable } from "../cell.js";
1
+ import { type Cell, type Read, type Writable } from "../cell.js";
2
2
  import { Num } from "../values/num.js";
3
3
  import { Vec } from "../values/vec.js";
4
4
  type V = {
@@ -1,5 +1,6 @@
1
1
  // geometry.ts — geometric lens building blocks over the N-input
2
2
  // `Cls.lens` / `Cls.derive` forms. All are a few lines on top of the engine.
3
+ import { SKIP } from "../cell.js";
3
4
  import { Num } from "../values/num.js";
4
5
  import { Vec } from "../values/vec.js";
5
6
  import { rotateAbout } from "./closed-form-policies.js";
@@ -36,7 +37,7 @@ export function reflection(point, axisStart, axisEnd) {
36
37
  const projY = a.y + t * dy;
37
38
  return { x: 2 * projX - p.x, y: 2 * projY - p.y };
38
39
  };
39
- return Vec.lens([point, axisStart, axisEnd], vals => reflect(vals[0], vals[1], vals[2]), (target, vals) => [reflect(target, vals[1], vals[2]), undefined, undefined]);
40
+ return Vec.lens([point, axisStart, axisEnd], vals => reflect(vals[0], vals[1], vals[2]), (target, vals) => [reflect(target, vals[1], vals[2]), SKIP, SKIP]);
40
41
  }
41
42
  /** Lerp between two Vecs at parameter `t`. Writing the interpolated point
42
43
  * shifts both endpoints rigidly (preserving t). */
@@ -48,7 +49,7 @@ export function vecLerp(a, b, t) {
48
49
  const [av, bv, tv] = vals;
49
50
  const dx = target.x - (av.x + (bv.x - av.x) * tv);
50
51
  const dy = target.y - (av.y + (bv.y - av.y) * tv);
51
- return [{ x: av.x + dx, y: av.y + dy }, { x: bv.x + dx, y: bv.y + dy }, undefined];
52
+ return [{ x: av.x + dx, y: av.y + dy }, { x: bv.x + dx, y: bv.y + dy }, SKIP];
52
53
  });
53
54
  }
54
55
  /** Sum of two nums; writing the sum splits the delta equally between
@@ -77,23 +78,21 @@ export function clampedMean(parents, lo, hi) {
77
78
  const n = parents.length;
78
79
  const inv = 1 / n;
79
80
  return Num.lens(parents, vals => {
80
- const arr = vals;
81
81
  let s = 0;
82
82
  for (let i = 0; i < n; i++)
83
- s += arr[i];
83
+ s += vals[i];
84
84
  const m = s * inv;
85
85
  return m < lo ? lo : m > hi ? hi : m;
86
86
  }, (target, vals) => {
87
- const arr = vals;
88
87
  const clamped = target < lo ? lo : target > hi ? hi : target;
89
88
  let s = 0;
90
89
  for (let i = 0; i < n; i++)
91
- s += arr[i];
90
+ s += vals[i];
92
91
  const cur = s * inv;
93
92
  const delta = clamped - cur;
94
93
  const out = new Array(n);
95
94
  for (let i = 0; i < n; i++)
96
- out[i] = arr[i] + delta;
95
+ out[i] = vals[i] + delta;
97
96
  return out;
98
97
  });
99
98
  }
@@ -1,4 +1,4 @@
1
- import { type Cell, Num, type Traits, type Writable } from "../index.js";
1
+ import { type Cell, Num, type Skip, type Traits, type Writable } from "../index.js";
2
2
  /** Options for {@link remember}. `anchor` is the fixed point sources scale
3
3
  * about (a pivot, or the live centroid); `feature` is the writable scalar
4
4
  * (a radius, mean distance, or sum). */
@@ -38,7 +38,7 @@ export interface ContinuousOpts<T> {
38
38
  };
39
39
  /** Realize `target` (already unwrapped, absolute) onto the sources,
40
40
  * given the `current` unwrapped reading (for a delta). */
41
- apply: (target: number, vals: readonly T[], current: number) => readonly (T | undefined)[];
41
+ apply: (target: number, vals: readonly T[], current: number) => readonly (T | Skip)[];
42
42
  }
43
43
  /** Continuous (winding-aware) lens over a cyclic reading. The complement
44
44
  * tracks the last emitted value and unwraps each raw reading to the
@@ -15,7 +15,7 @@
15
15
  // unwraps the raw reading to the nearest sheet, so the
16
16
  // view never tears at a branch cut. Covers the eigenvector
17
17
  // angle of bestFitLine; the primitive behind winding.
18
- import { Num } from "../index.js";
18
+ import { Num, SKIP } from "../index.js";
19
19
  /** Scalar shape-memory lens. Reads `feature(sources)`; writing it scales
20
20
  * the cluster about `anchor` so the new feature matches, reinflating the
21
21
  * remembered shape when the cluster has collapsed onto the anchor. The
@@ -59,7 +59,7 @@ export function remember(sources, opts) {
59
59
  // Magnitude is lossy (|−f| = f): a same-magnitude target re-projects
60
60
  // to the current feature, so the cluster is left put.
61
61
  if (magnitude && Math.abs(target) === f) {
62
- return { updates: vals.map(() => undefined), complement: c };
62
+ return { updates: vals.map(() => SKIP), complement: c };
63
63
  }
64
64
  if (f > eps) {
65
65
  const k = target / f;
@@ -94,7 +94,7 @@ export function continuous(sources, opts) {
94
94
  bwd: (target, vals, c) => {
95
95
  const r = raw(vals);
96
96
  if (!r.defined)
97
- return { updates: vals.map(() => undefined), complement: { prev: target } };
97
+ return { updates: vals.map(() => SKIP), complement: { prev: target } };
98
98
  const current = unwrap(r.value, c.prev);
99
99
  return { updates: apply(target, vals, current), complement: { prev: target } };
100
100
  },
@@ -14,6 +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 { SKIP } from "../index.js";
17
18
  import { solveSPD } from "../linalg.js";
18
19
  function getPack(cell) {
19
20
  const ctor = cell.constructor;
@@ -163,7 +164,7 @@ export function factor(inputs, outputs, opts = {}) {
163
164
  }
164
165
  // 6. Solve A · k = δy in place (LDLᵀ; `dy` is overwritten with k).
165
166
  if (!solveSPD(A, dy, M)) {
166
- return vals.map(() => undefined);
167
+ return vals.map(() => SKIP);
167
168
  }
168
169
  // 7. δx = W J^T k, applied to flatIn → produces new flat input vector.
169
170
  // Then unpack per-input to typed updates.
@@ -187,7 +188,7 @@ export function factor(inputs, outputs, opts = {}) {
187
188
  }
188
189
  updates[k] = anyChange
189
190
  ? inputPacks[k].write(flatIn, baseOff)
190
- : undefined;
191
+ : SKIP;
191
192
  }
192
193
  return updates;
193
194
  };
@@ -211,7 +212,7 @@ export function factor(inputs, outputs, opts = {}) {
211
212
  for (let it = 0; it < maxIters; it++) {
212
213
  const updates = computeBwd(idx, target, cur);
213
214
  for (let i = 0; i < cur.length; i++) {
214
- if (updates[i] !== undefined)
215
+ if (updates[i] !== SKIP)
215
216
  cur[i] = updates[i];
216
217
  }
217
218
  outPack.read(spec.fwd(cur), currentBuf, 0);
@@ -49,6 +49,7 @@ export type TraitKey = keyof TraitDict<unknown>;
49
49
  * function spring<T>(sig: Traits<T, "linear" | "metric">, target: Val<T>)
50
50
  * function tween<T>(sig: Traits<T, "lerp">, target: T, dur: Val<number>) */
51
51
  export type Traits<T, K extends TraitKey = never> = {
52
+ /** @internal Phantom slot; never accessed at runtime. */
52
53
  readonly _t: {
53
54
  [P in K]-?: NonNullable<TraitDict<T>[P]>;
54
55
  } & TraitDict<T>;
@@ -3,7 +3,7 @@
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
5
  import { tween } from "../../animation/index.js";
6
- import { Cell, cachedDerive, field, isReadonly, lazy, reader, readNow, } from "../cell.js";
6
+ import { Cell, cachedDerive, fieldLens, isReadonly, lazy, reader, readNow, SKIP, } from "../cell.js";
7
7
  import { Bool } from "./bool.js";
8
8
  import { Num, num } from "./num.js";
9
9
  import { Vec } from "./vec.js";
@@ -138,24 +138,24 @@ export class Box extends Cell {
138
138
  return mk([this, p], vals => contains(vals[0], vals[1]), (target, vals) => {
139
139
  const [b, v] = vals;
140
140
  if (contains(b, v) === target)
141
- return [undefined, undefined];
142
- return [undefined, target ? clampToBox(v, b) : ejectFromBox(v, b)];
141
+ return [SKIP, SKIP];
142
+ return [SKIP, target ? clampToBox(v, b) : ejectFromBox(v, b)];
143
143
  });
144
144
  }
145
145
  }
146
146
  return Bool.derive(() => contains(this.value, readNow(p)));
147
147
  }
148
148
  get x() {
149
- return field(this, "x", Num);
149
+ return fieldLens(this, "x", Num);
150
150
  }
151
151
  get y() {
152
- return field(this, "y", Num);
152
+ return fieldLens(this, "y", Num);
153
153
  }
154
154
  get w() {
155
- return field(this, "w", Num);
155
+ return fieldLens(this, "w", Num);
156
156
  }
157
157
  get h() {
158
- return field(this, "h", Num);
158
+ return fieldLens(this, "h", Num);
159
159
  }
160
160
  get area() {
161
161
  return cachedDerive(this, "area", Num, b => b.w * b.h);
@@ -3,7 +3,7 @@
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
5
  import { tween } from "../../animation/index.js";
6
- import { Cell, cachedDerive, derive, field, lazy, reader, readNow, } from "../cell.js";
6
+ import { Cell, cachedDerive, derive, fieldLens, lazy, reader, readNow, } from "../cell.js";
7
7
  import { Num, num } from "./num.js";
8
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 });
9
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 });
@@ -55,16 +55,16 @@ export class Color extends Cell {
55
55
  return Color.derive(() => lerp(this.value, readNow(b), readNow(t)));
56
56
  }
57
57
  get r() {
58
- return field(this, "r", Num);
58
+ return fieldLens(this, "r", Num);
59
59
  }
60
60
  get g() {
61
- return field(this, "g", Num);
61
+ return fieldLens(this, "g", Num);
62
62
  }
63
63
  get b() {
64
- return field(this, "b", Num);
64
+ return fieldLens(this, "b", Num);
65
65
  }
66
66
  get a() {
67
- return field(this, "a", Num);
67
+ return fieldLens(this, "a", Num);
68
68
  }
69
69
  get luminance() {
70
70
  return cachedDerive(this, "luminance", Num, c => 0.299 * c.r + 0.587 * c.g + 0.114 * c.b);
@@ -0,0 +1,70 @@
1
+ import { Cell, type Read, type Val } from "../cell.js";
2
+ import type { Pack } from "../traits.js";
3
+ import { Canvas } from "./canvas.js";
4
+ /** Field cell value: a GPU handle. The graph compares `epoch`. */
5
+ export interface FieldVal {
6
+ readonly tex: WebGLTexture;
7
+ readonly w: number;
8
+ readonly h: number;
9
+ readonly epoch: number;
10
+ }
11
+ type Ctor<T> = new (...args: never[]) => Cell<T>;
12
+ /** Texel encoding: channel count, the flat-buffer codec, and the boundary
13
+ * cell class that reductions return. */
14
+ export interface Kind<T> {
15
+ readonly dim: 1 | 2 | 4;
16
+ readonly pack: Pack<T>;
17
+ readonly cls: Ctor<T>;
18
+ }
19
+ /** Scalar field (1 channel) — heatmaps, SDFs, density, height. */
20
+ export declare const Scalar: Kind<number>;
21
+ /** Vector field (2 channels) — flow, gradient, displacement, RD chemicals. */
22
+ export declare const Vector: Kind<{
23
+ x: number;
24
+ y: number;
25
+ }>;
26
+ /** Colour field (4 channels) — i.e. a Canvas. */
27
+ export declare const Colour: Kind<{
28
+ r: number;
29
+ g: number;
30
+ b: number;
31
+ a: number;
32
+ }>;
33
+ /** A colormap stop: a field value mapped to an RGB triple (0–1). */
34
+ export type ColorStop = readonly [number, readonly [number, number, number]];
35
+ export declare class Field<T> extends Cell<FieldVal> {
36
+ readonly kind: Kind<T>;
37
+ static traits: {
38
+ equals: (a: FieldVal, b: FieldVal) => boolean;
39
+ };
40
+ readonly _t: typeof Field.traits;
41
+ /** Feedback-safe ping-pong pair for `evolve`/`splat`, lazily allocated. */
42
+ private io;
43
+ constructor(kind: Kind<T>, v?: FieldVal);
44
+ private pingTex;
45
+ /** Step the field by `steps` GPU passes of `frag` (which samples the current
46
+ * field as `u_src`; `u_texel` = 1/size is provided). One value is committed
47
+ * after the substeps, so the reactive graph sees a single new epoch/frame. */
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. */
51
+ 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. */
54
+ mean(): Read<T>;
55
+ /** Mean over a sub-rectangle (data pixels, reactive `box`) as a read-only
56
+ * `T` cell — the field's "density of this region" observation. */
57
+ regionMean(box: Val<{
58
+ x: number;
59
+ y: number;
60
+ w: number;
61
+ h: number;
62
+ }>): 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. */
65
+ colormap(channel: number, stops: readonly ColorStop[]): Canvas;
66
+ }
67
+ /** Writable `Field<T>` of size `w×h`. `painter` fills each texel (returns a
68
+ * `T`); omit for an all-zero field. Linear-filtered for continuous sampling. */
69
+ export declare function field<T>(kind: Kind<T>, w: number, h: number, painter?: (x: number, y: number) => T): Field<T>;
70
+ export {};