bireactive 0.3.0 → 0.3.2

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 (117) hide show
  1. package/README.md +14 -7
  2. package/dist/automerge/doc-cell.d.ts +20 -0
  3. package/dist/automerge/doc-cell.js +80 -0
  4. package/dist/automerge/index.d.ts +3 -0
  5. package/dist/automerge/index.js +12 -0
  6. package/dist/automerge/reconcile.d.ts +5 -0
  7. package/dist/automerge/reconcile.js +63 -0
  8. package/dist/core/_counts.d.ts +48 -0
  9. package/dist/core/_counts.js +51 -0
  10. package/dist/core/cell.d.ts +148 -112
  11. package/dist/core/cell.js +945 -768
  12. package/dist/core/debug.d.ts +25 -0
  13. package/dist/core/debug.js +121 -0
  14. package/dist/core/derived-geometry.js +4 -7
  15. package/dist/core/index.d.ts +9 -2
  16. package/dist/core/index.js +8 -1
  17. package/dist/core/lenses/aggregates.d.ts +42 -52
  18. package/dist/core/lenses/aggregates.js +225 -116
  19. package/dist/core/lenses/geometry.d.ts +22 -4
  20. package/dist/core/lenses/geometry.js +59 -27
  21. package/dist/core/lenses/index.d.ts +6 -6
  22. package/dist/core/lenses/index.js +6 -6
  23. package/dist/core/lenses/memory.js +4 -17
  24. package/dist/core/lenses/numerical.d.ts +100 -0
  25. package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
  26. package/dist/core/lenses/point-cloud.d.ts +67 -0
  27. package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +226 -84
  28. package/dist/core/lenses/snap.d.ts +18 -0
  29. package/dist/core/lenses/snap.js +138 -0
  30. package/dist/core/lenses/text.d.ts +40 -0
  31. package/dist/core/lenses/text.js +202 -0
  32. package/dist/core/lifecycle.js +3 -6
  33. package/dist/core/linalg.js +5 -11
  34. package/dist/core/optic.d.ts +13 -0
  35. package/dist/core/optic.js +39 -0
  36. package/dist/core/optics.d.ts +10 -0
  37. package/dist/core/optics.js +26 -0
  38. package/dist/core/store.d.ts +9 -0
  39. package/dist/core/store.js +77 -0
  40. package/dist/core/traits.d.ts +4 -7
  41. package/dist/core/traits.js +8 -12
  42. package/dist/core/values/anchor.js +0 -4
  43. package/dist/core/values/arr.d.ts +110 -0
  44. package/dist/core/values/arr.js +336 -0
  45. package/dist/core/values/audio.d.ts +8 -9
  46. package/dist/core/values/audio.js +11 -28
  47. package/dist/core/values/bool.d.ts +11 -11
  48. package/dist/core/values/bool.js +12 -22
  49. package/dist/core/values/box.d.ts +15 -20
  50. package/dist/core/values/box.js +20 -33
  51. package/dist/core/values/canvas.d.ts +18 -25
  52. package/dist/core/values/canvas.js +32 -66
  53. package/dist/core/values/color.d.ts +5 -7
  54. package/dist/core/values/color.js +5 -11
  55. package/dist/core/values/field.d.ts +6 -7
  56. package/dist/core/values/field.js +10 -35
  57. package/dist/core/values/flags.d.ts +1 -2
  58. package/dist/core/values/flags.js +1 -17
  59. package/dist/core/values/gpu.d.ts +6 -10
  60. package/dist/core/values/gpu.js +8 -22
  61. package/dist/core/values/matrix.d.ts +2 -4
  62. package/dist/core/values/matrix.js +2 -12
  63. package/dist/core/values/num.d.ts +19 -28
  64. package/dist/core/values/num.js +23 -41
  65. package/dist/core/values/pose.d.ts +2 -4
  66. package/dist/core/values/pose.js +3 -12
  67. package/dist/core/values/range.d.ts +18 -26
  68. package/dist/core/values/range.js +22 -39
  69. package/dist/core/values/reg/ambiguity.d.ts +8 -0
  70. package/dist/core/values/reg/ambiguity.js +131 -0
  71. package/dist/core/values/reg/engine.d.ts +91 -0
  72. package/dist/core/values/reg/engine.js +373 -0
  73. package/dist/core/values/reg/nfa.d.ts +42 -0
  74. package/dist/core/values/reg/nfa.js +391 -0
  75. package/dist/core/values/reg/regex.d.ts +7 -0
  76. package/dist/core/values/reg/regex.js +318 -0
  77. package/dist/core/values/reg/types.d.ts +60 -0
  78. package/dist/core/values/reg/types.js +3 -0
  79. package/dist/core/values/reg.d.ts +250 -0
  80. package/dist/core/values/reg.js +649 -0
  81. package/dist/core/values/str.d.ts +16 -60
  82. package/dist/core/values/str.js +133 -315
  83. package/dist/core/values/template.js +1 -24
  84. package/dist/core/values/transform.d.ts +3 -5
  85. package/dist/core/values/transform.js +3 -12
  86. package/dist/core/values/tri.d.ts +9 -10
  87. package/dist/core/values/tri.js +9 -15
  88. package/dist/core/values/vec.d.ts +9 -24
  89. package/dist/core/values/vec.js +9 -64
  90. package/dist/formats/lens.js +6 -9
  91. package/dist/index.d.ts +0 -11
  92. package/dist/index.js +1 -11
  93. package/dist/jsx-dev-runtime.d.ts +2 -0
  94. package/dist/jsx-dev-runtime.js +5 -0
  95. package/dist/jsx-runtime.d.ts +54 -0
  96. package/dist/jsx-runtime.js +219 -0
  97. package/dist/schema/lens.js +5 -5
  98. package/dist/shapes/drag-behaviors.d.ts +56 -0
  99. package/dist/shapes/drag-behaviors.js +102 -0
  100. package/dist/shapes/drag-spec.d.ts +52 -0
  101. package/dist/shapes/drag-spec.js +112 -0
  102. package/dist/shapes/index.d.ts +3 -1
  103. package/dist/shapes/index.js +3 -1
  104. package/dist/shapes/interaction.d.ts +2 -3
  105. package/dist/shapes/interaction.js +77 -56
  106. package/dist/shapes/label.js +6 -0
  107. package/dist/shapes/layout.d.ts +47 -1
  108. package/dist/shapes/layout.js +59 -1
  109. package/package.json +22 -1
  110. package/dist/coll.d.ts +0 -74
  111. package/dist/coll.js +0 -210
  112. package/dist/core/lenses/closed-form-policies.d.ts +0 -57
  113. package/dist/core/lenses/decompositions.d.ts +0 -14
  114. package/dist/core/lenses/decompositions.js +0 -224
  115. package/dist/core/lenses/domain-aggregates.d.ts +0 -42
  116. package/dist/core/lenses/domain-aggregates.js +0 -245
  117. package/dist/core/lenses/typed-factor.d.ts +0 -40
@@ -0,0 +1,100 @@
1
+ import { type Cell, type Inner, type Read, type Writable } from "../cell.js";
2
+ import type { Traits } from "../traits.js";
3
+ import { Num } from "../values/num.js";
4
+ import { Vec } from "../values/vec.js";
5
+ export interface ArgminOpts {
6
+ /** Finite-difference epsilon for the Jacobian. Default 1e-4. */
7
+ eps?: number;
8
+ /** Levenberg-Marquardt damping. Default `1e-6` for `argminNum`
9
+ * (Jacobian is always well-conditioned for linear constraints) and
10
+ * `1e-3` for `argminVec` (IK chains hit rank-deficient regimes at
11
+ * full extension). Larger → smaller, more stable updates; smaller
12
+ * → closer to pure pseudoinverse. */
13
+ damping?: number;
14
+ }
15
+ /** Target-shaping for `argminVec`: project a write into the reachable
16
+ * workspace before the Jacobian step, sidestepping the rank-deficient
17
+ * swings at the boundary. For an N-link chain rooted at `R` with reach
18
+ * `L`, pass `clampToDisc(R, L)`. */
19
+ export interface ArgminVecOpts extends ArgminOpts {
20
+ /** Pre-write hook: transform the requested target into one that's
21
+ * guaranteed solvable. Most useful as a workspace clamp. */
22
+ clampTarget?: (target: {
23
+ x: number;
24
+ y: number;
25
+ }, currentInputs: readonly number[]) => {
26
+ x: number;
27
+ y: number;
28
+ };
29
+ }
30
+ /** Project `p` into the closed disc of radius `r` centred on `c` (points
31
+ * inside pass through). Use as `argminVec`'s `clampTarget` to fix IK
32
+ * explosion at maximum reach. */
33
+ export declare function clampToDisc(c: {
34
+ x: number;
35
+ y: number;
36
+ }, r: number): (p: {
37
+ x: number;
38
+ y: number;
39
+ }) => {
40
+ x: number;
41
+ y: number;
42
+ };
43
+ /** Scalar-output argmin lens: write does one Newton step against the FD
44
+ * Jacobian, distributing the residual by `weights`. For typed/multi-
45
+ * output cases use `factor()`; this M=1 path is kept for its hand-rolled
46
+ * inner loop. */
47
+ export declare function argminNum(inputs: readonly Num[], forward: (xs: readonly number[]) => number, weights: readonly number[], opts?: ArgminOpts): Writable<Num>;
48
+ /** 2D-output argmin lens (scalar Num inputs, `{x, y}` forward). For IK
49
+ * arms, draggable points, handle projection. Kept for its hand-rolled
50
+ * 2×2 inverse + `clampTarget` hook; see `factor()` for other M. */
51
+ export declare function argminVec(inputs: readonly Num[], forward: (xs: readonly number[]) => {
52
+ x: number;
53
+ y: number;
54
+ }, weights: readonly number[], opts?: ArgminVecOpts): Writable<Vec>;
55
+ /** Input cell: writable cell whose value class declares the `pack`
56
+ * trait. Vec, Num, Pose, Box, Color, Range all satisfy this. */
57
+ export type PackedInput<T = any> = Writable<Read<T> & Traits<T, "pack">>;
58
+ /** Output specification: a target class + a fwd from typed inputs to
59
+ * the value the class wraps. Optional analytical Jacobian skips FD. */
60
+ export interface OutputSpec<C extends new (...args: never[]) => Cell<any>> {
61
+ Cls: C;
62
+ fwd: (inputs: ReadonlyArray<any>) => Inner<InstanceType<C>>;
63
+ /** Optional analytical Jacobian. Returns dim(Cls) rows, each of
64
+ * length `sum(input pack dims)`. If supplied for ALL outputs, FD
65
+ * is skipped entirely → faster AND exact (no eps drift). */
66
+ jacobian?: (inputs: ReadonlyArray<any>) => readonly (readonly number[])[];
67
+ }
68
+ /** Result type: writable cell per output key, typed by the spec's Cls. */
69
+ export type FactorResult<O extends Record<string, OutputSpec<any>>> = {
70
+ [K in keyof O]: Writable<InstanceType<O[K]["Cls"]>>;
71
+ };
72
+ export interface FactorOpts {
73
+ /** Per-input mobility weights. 0 = pinned. Defaults to 1 for all. */
74
+ inputWeights?: readonly number[];
75
+ /** Levenberg-Marquardt damping. Default 1e-6. */
76
+ damping?: number;
77
+ /** Finite-difference epsilon. Default 1e-5. */
78
+ eps?: number;
79
+ /** Auto-iterate the bwd until the written channel's reading is
80
+ * within `tol` of target (or `maxIters` exhausted). Cheap when
81
+ * forwards are linear (1 iter); needed for non-linear forwards
82
+ * to land exactly without user-side loops. Default `false`. */
83
+ converge?: boolean;
84
+ /** Max iters when `converge: true`. Default 10. */
85
+ maxIters?: number;
86
+ /** Convergence tolerance (per-channel Euclidean). Default 1e-4. */
87
+ tol?: number;
88
+ }
89
+ /** Factor packed inputs into a named record of coupled writable outputs.
90
+ * Each output is `{ Cls, fwd }`; writing one solves the Jacobian LSQ for the
91
+ * input deltas. See `bundle` for the 1→M case, `factorTuple` for positional. */
92
+ export declare function factor<O extends Record<string, OutputSpec<any>>>(inputs: readonly PackedInput[], outputs: O, opts?: FactorOpts): FactorResult<O>;
93
+ /** Positional `factor`: outputs are a tuple of specs, result a tuple of
94
+ * writables. Terser to destructure but order-sensitive. */
95
+ export declare function factorTuple<T extends readonly OutputSpec<any>[]>(inputs: readonly PackedInput[], outputs: readonly [...T], opts?: FactorOpts): {
96
+ [K in keyof T]: Writable<InstanceType<T[K]["Cls"]>>;
97
+ };
98
+ /** A single typed source factored into M coupled views: `factor` with one
99
+ * input. Writing a view solves the (small) Jacobian over the source's pack. */
100
+ export declare function bundle<T, O extends Record<string, OutputSpec<any>>>(source: Writable<Read<T> & Traits<T, "pack">>, views: O, opts?: FactorOpts): FactorResult<O>;
@@ -1,27 +1,134 @@
1
- // typed-factor.ts generic heterogeneous-output factor lens.
2
- //
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
- // pseudoinverse. Invariance is approximate (the Jacobian path) — use
7
- // closed-form lenses like `procrustes` when an exact one exists.
8
- //
9
- // const { centroid, rotation, scale } = factor([v1, v2, v3] as const, {
10
- // centroid: { Cls: Vec, fwd: pts => … },
11
- // rotation: { Cls: Num, fwd: pts => atan2(…) },
12
- // scale: { Cls: Num, fwd: pts => hypot(…) },
13
- // });
14
- // centroid.value = { x: 100, y: 50 }; // typed
15
- //
16
- // `bundle()` is the 1→M case: `factor()` over a single typed source.
17
- import { SKIP } from "../index.js";
1
+ // Numerical lenses: writes solve a finite-differenced Jacobian (one Newton
2
+ // step, Levenberg-Marquardt damped) rather than a closed form, so invariance
3
+ // is approximate prefer the exact lenses (point-cloud.ts, aggregates.ts)
4
+ // when one fits. `argminNum`/`argminVec` are hand-rolled M=1/M=2 forms;
5
+ // `factor` is the typed N→M generalization over the `Pack` trait.
6
+ import { SKIP } from "../cell.js";
18
7
  import { solveSPD } from "../linalg.js";
8
+ import { Num } from "../values/num.js";
9
+ import { Vec } from "../values/vec.js";
10
+ /** Project `p` into the closed disc of radius `r` centred on `c` (points
11
+ * inside pass through). Use as `argminVec`'s `clampTarget` to fix IK
12
+ * explosion at maximum reach. */
13
+ export function clampToDisc(c, r) {
14
+ return p => {
15
+ const dx = p.x - c.x;
16
+ const dy = p.y - c.y;
17
+ const d = Math.hypot(dx, dy);
18
+ if (d <= r)
19
+ return p;
20
+ const k = r / d;
21
+ return { x: c.x + dx * k, y: c.y + dy * k };
22
+ };
23
+ }
24
+ /** Scalar-output argmin lens: write does one Newton step against the FD
25
+ * Jacobian, distributing the residual by `weights`. For typed/multi-
26
+ * output cases use `factor()`; this M=1 path is kept for its hand-rolled
27
+ * inner loop. */
28
+ export function argminNum(inputs, forward, weights, opts = {}) {
29
+ if (weights.length !== inputs.length) {
30
+ throw new Error("argminNum: weights/inputs length mismatch");
31
+ }
32
+ const eps = opts.eps ?? 1e-4;
33
+ const damping = opts.damping ?? 1e-6;
34
+ const n = inputs.length;
35
+ // Pre-allocated to avoid per-write allocations.
36
+ const J = new Array(n);
37
+ const out = new Array(n);
38
+ return Num.lens(inputs, vals => forward(vals), (target, vals) => {
39
+ const xs = vals;
40
+ const y0 = forward(xs);
41
+ const dy = target - y0;
42
+ for (let i = 0; i < n; i++) {
43
+ const saved = xs[i];
44
+ xs[i] = saved + eps;
45
+ J[i] = (forward(xs) - y0) / eps;
46
+ xs[i] = saved;
47
+ }
48
+ let denom = damping;
49
+ for (let i = 0; i < n; i++)
50
+ denom += weights[i] * J[i] * J[i];
51
+ const k = dy / denom;
52
+ for (let i = 0; i < n; i++) {
53
+ if (weights[i] === 0) {
54
+ out[i] = SKIP;
55
+ }
56
+ else {
57
+ out[i] = xs[i] + weights[i] * J[i] * k;
58
+ }
59
+ }
60
+ return out;
61
+ });
62
+ }
63
+ /** 2D-output argmin lens (scalar Num inputs, `{x, y}` forward). For IK
64
+ * arms, draggable points, handle projection. Kept for its hand-rolled
65
+ * 2×2 inverse + `clampTarget` hook; see `factor()` for other M. */
66
+ export function argminVec(inputs, forward, weights, opts = {}) {
67
+ if (weights.length !== inputs.length) {
68
+ throw new Error("argminVec: weights/inputs length mismatch");
69
+ }
70
+ const eps = opts.eps ?? 1e-4;
71
+ const damping = opts.damping ?? 1e-3;
72
+ const clamp = opts.clampTarget;
73
+ const n = inputs.length;
74
+ // Pre-allocated to avoid per-write allocations.
75
+ const Jx = new Array(n);
76
+ const Jy = new Array(n);
77
+ const out = new Array(n);
78
+ return Vec.lens(inputs, vals => forward(vals), (rawTarget, vals) => {
79
+ const xs = vals;
80
+ const target = clamp ? clamp(rawTarget, xs) : rawTarget;
81
+ const y0 = forward(xs);
82
+ const dx = target.x - y0.x;
83
+ const dy = target.y - y0.y;
84
+ for (let i = 0; i < n; i++) {
85
+ const saved = xs[i];
86
+ xs[i] = saved + eps;
87
+ const ye = forward(xs);
88
+ xs[i] = saved;
89
+ Jx[i] = (ye.x - y0.x) / eps;
90
+ Jy[i] = (ye.y - y0.y) / eps;
91
+ }
92
+ // J·W·Jᵀ is the 2×2 [a b; b c]. Add damping to the diagonal, invert.
93
+ let a = damping;
94
+ let b = 0;
95
+ let c = damping;
96
+ for (let i = 0; i < n; i++) {
97
+ const w = weights[i];
98
+ a += w * Jx[i] * Jx[i];
99
+ b += w * Jx[i] * Jy[i];
100
+ c += w * Jy[i] * Jy[i];
101
+ }
102
+ const det = a * c - b * b;
103
+ if (Math.abs(det) < 1e-14) {
104
+ // Singular; leave inputs unchanged.
105
+ for (let i = 0; i < n; i++)
106
+ out[i] = SKIP;
107
+ return out;
108
+ }
109
+ const invA = c / det;
110
+ const invB = -b / det;
111
+ const invC = a / det;
112
+ const kx = invA * dx + invB * dy;
113
+ const ky = invB * dx + invC * dy;
114
+ for (let i = 0; i < n; i++) {
115
+ const w = weights[i];
116
+ if (w === 0) {
117
+ out[i] = SKIP;
118
+ }
119
+ else {
120
+ out[i] = xs[i] + w * (Jx[i] * kx + Jy[i] * ky);
121
+ }
122
+ }
123
+ return out;
124
+ });
125
+ }
19
126
  function getPack(cell) {
20
127
  const ctor = cell.constructor;
21
128
  const p = ctor.traits?.pack;
22
129
  if (!p) {
23
130
  const name = ctor.name ?? "?";
24
- throw new Error(`typed-factor: ${name} has no traits.pack`);
131
+ throw new Error(`numerical: ${name} has no traits.pack`);
25
132
  }
26
133
  return p;
27
134
  }
@@ -29,7 +136,7 @@ function getPackFromCls(Cls) {
29
136
  const p = Cls.traits?.pack;
30
137
  if (!p) {
31
138
  const name = Cls.name ?? "?";
32
- throw new Error(`typed-factor: ${name} has no traits.pack`);
139
+ throw new Error(`numerical: ${name} has no traits.pack`);
33
140
  }
34
141
  return p;
35
142
  }
@@ -42,10 +149,13 @@ function cumOffsets(dims) {
42
149
  }
43
150
  return out;
44
151
  }
152
+ /** Factor packed inputs into a named record of coupled writable outputs.
153
+ * Each output is `{ Cls, fwd }`; writing one solves the Jacobian LSQ for the
154
+ * input deltas. See `bundle` for the 1→M case, `factorTuple` for positional. */
45
155
  export function factor(inputs, outputs, opts = {}) {
46
156
  const inputCount = inputs.length;
47
157
  if (inputCount === 0) {
48
- throw new Error("typed-factor: need ≥ 1 input");
158
+ throw new Error("numerical: need ≥ 1 input");
49
159
  }
50
160
  const inputPacks = inputs.map(s => getPack(s));
51
161
  const inputDims = inputPacks.map(p => p.dim);
@@ -62,7 +172,7 @@ export function factor(inputs, outputs, opts = {}) {
62
172
  const outputKeys = Object.keys(outputs);
63
173
  const outputCount = outputKeys.length;
64
174
  if (outputCount === 0) {
65
- throw new Error("typed-factor: need ≥ 1 output");
175
+ throw new Error("numerical: need ≥ 1 output");
66
176
  }
67
177
  const outputSpecs = outputKeys.map(k => outputs[k]);
68
178
  const outputPacks = outputSpecs.map(s => getPackFromCls(s.Cls));
@@ -71,7 +181,7 @@ export function factor(inputs, outputs, opts = {}) {
71
181
  const M = outputDims.reduce((s, d) => s + d, 0);
72
182
  const weights = opts.inputWeights ?? Array.from({ length: N }, () => 1);
73
183
  if (weights.length !== N) {
74
- throw new Error(`typed-factor: inputWeights length ${weights.length} ≠ flat input dim ${N}`);
184
+ throw new Error(`numerical: inputWeights length ${weights.length} ≠ flat input dim ${N}`);
75
185
  }
76
186
  const eps = opts.eps ?? 1e-5;
77
187
  const lambda = opts.damping ?? 1e-6;
@@ -233,11 +343,8 @@ export function factor(inputs, outputs, opts = {}) {
233
343
  }
234
344
  return result;
235
345
  }
236
- // factorTuple positional API.
237
- //
238
- // Same engine, no names. Outputs are a tuple of specs; the result is a
239
- // tuple of writables. Terser to destructure, but order-sensitive and
240
- // loses self-documenting names.
346
+ /** Positional `factor`: outputs are a tuple of specs, result a tuple of
347
+ * writables. Terser to destructure but order-sensitive. */
241
348
  export function factorTuple(inputs, outputs, opts = {}) {
242
349
  // Wrap to named, call factor, unwrap (one-time setup; hot path identical).
243
350
  // biome-ignore lint/suspicious/noExplicitAny: variance escape
@@ -247,11 +354,8 @@ export function factorTuple(inputs, outputs, opts = {}) {
247
354
  const result = factor(inputs, named, opts);
248
355
  return outputs.map((_, i) => result[String(i)]);
249
356
  }
250
- // bundle 1→M dual, sugar over factor() with one input.
251
- //
252
- // A single typed source factored into M coupled views — `factor()` with
253
- // a length-1 input array. Writing a view sends a sparse δy through the
254
- // Jacobian solve (small, since N = the source's pack dim).
357
+ /** A single typed source factored into M coupled views: `factor` with one
358
+ * input. Writing a view solves the (small) Jacobian over the source's pack. */
255
359
  export function bundle(source, views, opts = {}) {
256
360
  // factor() takes an input array, so pass [source] and wrap each view's
257
361
  // fwd to receive the single-element array form.
@@ -271,5 +375,3 @@ export function bundle(source, views, opts = {}) {
271
375
  }
272
376
  return factor([source], wrapped, opts);
273
377
  }
274
- // For field-style bundles, `field()` already covers the independent case;
275
- // use `bundle()` when you want coupled writes through the Jacobian solve.
@@ -0,0 +1,67 @@
1
+ import { type Cell, Num, type Read, type Traits, Vec, type Writable } from "../index.js";
2
+ type V = {
3
+ x: number;
4
+ y: number;
5
+ };
6
+ /** Writable angle from `pivot` to `points[0]`; write rotates every input
7
+ * about `pivot` by (target − current) via its `Pivotal` trait (Vec rotates
8
+ * position, Pose also rotates orientation). `pivot` is reactive; pass
9
+ * `mean(points)` to rotate about the cluster's own centroid. */
10
+ export declare function rotateAbout<T extends {
11
+ x: number;
12
+ y: number;
13
+ }>(points: readonly Writable<Traits<T, "pivotal"> & Cell<T>>[], pivot: Read<V>): Writable<Num>;
14
+ /** Writable radial distance from pivot to `points[0]`; write scales every
15
+ * input radially about `pivot` (negative target reflects). The complement
16
+ * carries per-point offsets from the pivot, so a collapse onto it (radius
17
+ * ≈ 0) reinflates from the stored shape. Pose `theta` survives. */
18
+ export declare function scaleAbout<T extends {
19
+ x: number;
20
+ y: number;
21
+ }>(points: readonly Writable<Traits<T, "pivotal"> & Cell<T>>[], pivot: Read<V>): Writable<Num>;
22
+ /** Per-axis scale about a pivot (Vec-specific). The complement carries
23
+ * per-point per-axis fractions of point 0's offset, so a per-axis collapse
24
+ * is recoverable. */
25
+ export declare function scaleAboutXY(points: readonly Writable<Vec>[], pivot: Read<V>): Writable<Vec>;
26
+ /** K points → {point: centroid, direction: principal-axis angle}. Writing
27
+ * `point` translates; writing `direction` rotates all about the centroid. */
28
+ export declare function bestFitLine(points: readonly Writable<Vec>[]): {
29
+ point: Writable<Vec>;
30
+ direction: Writable<Num>;
31
+ };
32
+ /** K points → {center: centroid, radius: mean distance from center}. Writing
33
+ * `center` translates; writing `radius` scales all about the center. */
34
+ export declare function bestFitCircle(points: readonly Writable<Vec>[]): {
35
+ center: Writable<Vec>;
36
+ radius: Writable<Num>;
37
+ };
38
+ /** K points → {mean: centroid, rotation: dominant-eigenvector angle,
39
+ * majorLength/minorLength: per-axis std-devs (√λ)}. Each write is a single
40
+ * group action about the mean, so all pairs are cross-channel invariant. */
41
+ export declare function pca(points: readonly Writable<Vec>[]): {
42
+ mean: Writable<Vec>;
43
+ rotation: Writable<Num>;
44
+ majorLength: Writable<Num>;
45
+ minorLength: Writable<Num>;
46
+ };
47
+ /** Writable total over K parts; write scales all parts proportionally,
48
+ * preserving their ratios. A collapse to zero reinflates the stored ratios,
49
+ * seeded uniform so an all-zero start splits evenly. */
50
+ export declare function total(parts: readonly Writable<Num>[]): Writable<Num>;
51
+ /** K Vecs → {centroid, rotation (angle of point[0] about centroid), scale
52
+ * (its distance from centroid)}. Each write is a closed-form transform about
53
+ * the centroid (translate / rotate / scale), so the three are cross-channel
54
+ * invariant. A collapsed cluster makes rotation singular and scale a no-op. */
55
+ export declare function procrustes(points: readonly Writable<Vec>[]): {
56
+ centroid: Writable<Vec>;
57
+ rotation: Writable<Num>;
58
+ scale: Writable<Num>;
59
+ };
60
+ /** K Vecs → {center, size} of the axis-aligned bounding box. Writing `center`
61
+ * translates; writing `size` scales all about the center per-axis. Degenerate
62
+ * axes (size = 0) write as no-ops; negative size reflects. */
63
+ export declare function bbox(points: readonly Writable<Vec>[]): {
64
+ center: Writable<Vec>;
65
+ size: Writable<Vec>;
66
+ };
67
+ export {};