bireactive 0.3.1 → 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.
- package/README.md +14 -7
- package/dist/core/_counts.js +5 -12
- package/dist/core/cell.d.ts +3 -3
- package/dist/core/cell.js +6 -7
- package/dist/core/derived-geometry.js +4 -7
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +3 -1
- package/dist/core/lenses/aggregates.d.ts +42 -52
- package/dist/core/lenses/aggregates.js +225 -116
- package/dist/core/lenses/geometry.d.ts +22 -4
- package/dist/core/lenses/geometry.js +59 -27
- package/dist/core/lenses/index.d.ts +5 -6
- package/dist/core/lenses/index.js +5 -6
- package/dist/core/lenses/memory.js +4 -17
- package/dist/core/lenses/numerical.d.ts +100 -0
- package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
- package/dist/core/lenses/point-cloud.d.ts +67 -0
- package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +218 -81
- package/dist/core/lenses/snap.d.ts +1 -1
- package/dist/core/lenses/snap.js +3 -10
- package/dist/core/lenses/text.d.ts +40 -0
- package/dist/core/lenses/text.js +202 -0
- package/dist/core/lifecycle.js +3 -6
- package/dist/core/linalg.js +5 -11
- package/dist/core/optic.js +10 -15
- package/dist/core/optics.js +4 -8
- package/dist/core/store.d.ts +1 -2
- package/dist/core/store.js +7 -15
- package/dist/core/traits.d.ts +4 -7
- package/dist/core/traits.js +8 -12
- package/dist/core/values/anchor.js +0 -4
- package/dist/core/values/arr.d.ts +110 -0
- package/dist/core/values/arr.js +336 -0
- package/dist/core/values/audio.d.ts +8 -9
- package/dist/core/values/audio.js +7 -23
- package/dist/core/values/bool.d.ts +11 -11
- package/dist/core/values/bool.js +12 -22
- package/dist/core/values/box.d.ts +15 -20
- package/dist/core/values/box.js +20 -33
- package/dist/core/values/canvas.d.ts +18 -25
- package/dist/core/values/canvas.js +17 -48
- package/dist/core/values/color.d.ts +5 -7
- package/dist/core/values/color.js +5 -11
- package/dist/core/values/field.d.ts +6 -7
- package/dist/core/values/field.js +10 -35
- package/dist/core/values/flags.d.ts +1 -2
- package/dist/core/values/flags.js +1 -17
- package/dist/core/values/gpu.d.ts +6 -10
- package/dist/core/values/gpu.js +8 -22
- package/dist/core/values/matrix.d.ts +2 -4
- package/dist/core/values/matrix.js +2 -12
- package/dist/core/values/num.d.ts +19 -28
- package/dist/core/values/num.js +23 -41
- package/dist/core/values/pose.d.ts +2 -4
- package/dist/core/values/pose.js +3 -12
- package/dist/core/values/range.d.ts +18 -26
- package/dist/core/values/range.js +22 -39
- package/dist/core/values/reg/ambiguity.d.ts +8 -0
- package/dist/core/values/reg/ambiguity.js +131 -0
- package/dist/core/values/reg/engine.d.ts +91 -0
- package/dist/core/values/reg/engine.js +373 -0
- package/dist/core/values/reg/nfa.d.ts +42 -0
- package/dist/core/values/reg/nfa.js +391 -0
- package/dist/core/values/reg/regex.d.ts +7 -0
- package/dist/core/values/reg/regex.js +318 -0
- package/dist/core/values/reg/types.d.ts +60 -0
- package/dist/core/values/reg/types.js +3 -0
- package/dist/core/values/reg.d.ts +250 -0
- package/dist/core/values/reg.js +649 -0
- package/dist/core/values/str.d.ts +16 -60
- package/dist/core/values/str.js +133 -315
- package/dist/core/values/template.js +1 -24
- package/dist/core/values/transform.d.ts +3 -5
- package/dist/core/values/transform.js +3 -12
- package/dist/core/values/tri.d.ts +9 -10
- package/dist/core/values/tri.js +9 -15
- package/dist/core/values/vec.d.ts +9 -24
- package/dist/core/values/vec.js +9 -64
- package/dist/index.d.ts +0 -11
- package/dist/index.js +1 -11
- package/package.json +6 -7
- package/dist/coll.d.ts +0 -74
- package/dist/coll.js +0 -210
- package/dist/core/lenses/closed-form-policies.d.ts +0 -57
- package/dist/core/lenses/decompositions.d.ts +0 -14
- package/dist/core/lenses/decompositions.js +0 -224
- package/dist/core/lenses/domain-aggregates.d.ts +0 -42
- package/dist/core/lenses/domain-aggregates.js +0 -245
- 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
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
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(`
|
|
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(`
|
|
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("
|
|
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("
|
|
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(`
|
|
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
|
-
|
|
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
|
-
|
|
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 {};
|