bireactive 0.2.0 → 0.2.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 +7 -7
- package/dist/{core/anim.d.ts → animation/animators.d.ts} +5 -3
- package/dist/{core/anim.js → animation/animators.js} +8 -5
- package/dist/animation/index.d.ts +1 -0
- package/dist/animation/index.js +1 -0
- package/dist/constraints/cluster.js +1 -1
- package/dist/constraints/expose.d.ts +17 -0
- package/dist/constraints/expose.js +61 -0
- package/dist/constraints/index.d.ts +1 -0
- package/dist/constraints/index.js +1 -0
- package/dist/constraints/linalg.d.ts +1 -8
- package/dist/constraints/linalg.js +5 -108
- package/dist/core/{signal.d.ts → cell.d.ts} +29 -6
- package/dist/core/{signal.js → cell.js} +134 -53
- package/dist/core/derived-geometry.d.ts +11 -0
- package/dist/core/derived-geometry.js +32 -0
- package/dist/core/index.d.ts +5 -10
- package/dist/core/index.js +5 -10
- package/dist/core/{aggregates.d.ts → lenses/aggregates.d.ts} +3 -14
- package/dist/core/{aggregates.js → lenses/aggregates.js} +5 -78
- package/dist/core/lenses/closed-form-policies.d.ts +6 -13
- package/dist/core/lenses/closed-form-policies.js +14 -24
- package/dist/core/lenses/decompositions.d.ts +14 -0
- package/dist/core/lenses/decompositions.js +224 -0
- package/dist/core/lenses/domain-aggregates.d.ts +10 -22
- package/dist/core/lenses/domain-aggregates.js +25 -39
- package/dist/core/{new-primitives.d.ts → lenses/geometry.d.ts} +11 -14
- package/dist/core/{new-primitives.js → lenses/geometry.js} +23 -37
- package/dist/core/lenses/index.d.ts +6 -4
- package/dist/core/lenses/index.js +6 -15
- package/dist/core/lenses/typed-factor.d.ts +1 -6
- package/dist/core/lenses/typed-factor.js +12 -114
- package/dist/core/{network-utils.d.ts → lifecycle.d.ts} +1 -1
- package/dist/core/{network-utils.js → lifecycle.js} +2 -2
- package/dist/core/linalg.d.ts +10 -0
- package/dist/core/linalg.js +109 -0
- package/dist/core/values/anchor.d.ts +1 -1
- package/dist/core/values/audio.d.ts +1 -1
- package/dist/core/values/audio.js +1 -1
- package/dist/core/values/bool.d.ts +1 -1
- package/dist/core/values/bool.js +1 -1
- package/dist/core/values/box.d.ts +2 -3
- package/dist/core/values/box.js +5 -6
- package/dist/core/values/canvas.d.ts +1 -1
- package/dist/core/values/canvas.js +2 -3
- package/dist/core/values/color.d.ts +2 -3
- package/dist/core/values/color.js +3 -4
- package/dist/core/values/flags.d.ts +1 -1
- package/dist/core/values/flags.js +1 -1
- package/dist/core/values/matrix.d.ts +1 -1
- package/dist/core/values/matrix.js +2 -3
- package/dist/core/values/num.d.ts +8 -3
- package/dist/core/values/num.js +22 -2
- package/dist/core/values/pose.d.ts +5 -1
- package/dist/core/values/pose.js +11 -1
- package/dist/core/values/range.d.ts +6 -4
- package/dist/core/values/range.js +16 -11
- package/dist/core/values/str.d.ts +1 -1
- package/dist/core/values/str.js +1 -1
- package/dist/core/values/template.d.ts +1 -1
- package/dist/core/values/transform.d.ts +2 -3
- package/dist/core/values/transform.js +2 -3
- package/dist/core/values/tri.d.ts +10 -8
- package/dist/core/values/tri.js +12 -12
- package/dist/core/values/vec.d.ts +10 -4
- package/dist/core/values/vec.js +30 -21
- package/dist/ext/timeline.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/propagators/layout.js +1 -1
- package/dist/shapes/handle.js +4 -4
- package/dist/shapes/shape.js +7 -7
- package/dist/shapes/transitions.js +2 -2
- package/package.json +7 -2
- package/dist/core/introspect.d.ts +0 -5
- package/dist/core/introspect.js +0 -31
- package/dist/core/lenses/factor-lens.d.ts +0 -42
- package/dist/core/lenses/factor-lens.js +0 -419
- package/dist/core/writable.d.ts +0 -15
- package/dist/core/writable.js +0 -29
- /package/dist/{core/tree.d.ts → tree.d.ts} +0 -0
- /package/dist/{core/tree.js → tree.js} +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
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,
|
|
7
|
+
export { bundle, type FactorOpts, type FactorResult, factor, factorTuple, type OutputSpec, type PackedInput, } from "./typed-factor.js";
|
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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,
|
|
7
|
+
export { bundle, factor, factorTuple, } from "./typed-factor.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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 `
|
|
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 {
|
|
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
|
-
|
|
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 (!
|
|
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] *
|
|
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,10 +1,10 @@
|
|
|
1
|
-
//
|
|
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 "./
|
|
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 { Cell, type Init, type Val, type Writable } from "../
|
|
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 "../
|
|
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. */
|
package/dist/core/values/bool.js
CHANGED
|
@@ -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 "../
|
|
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
|
|
2
|
-
import { type
|
|
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";
|
package/dist/core/values/box.js
CHANGED
|
@@ -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 "
|
|
6
|
-
import { Cell,
|
|
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
|
|
131
|
+
// A read-only Vec has no backward path → RO branch; sources and
|
|
133
132
|
// writable lenses accept write-back.
|
|
134
|
-
if (!
|
|
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
|
|
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
|
|
@@ -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 "../
|
|
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
|
|
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
|
|
2
|
-
import { type
|
|
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 "
|
|
6
|
-
import { Cell, derive, lazy, reader, readNow } from "../
|
|
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
|
|
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(() => {
|
|
@@ -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 "../
|
|
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 {
|
|
@@ -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 "../
|
|
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
|
|
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
|
|
2
|
-
import { type
|
|
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;
|
|
@@ -26,6 +25,12 @@ export declare class Num extends Cell<V> {
|
|
|
26
25
|
/** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
|
|
27
26
|
* for `.scale(k).add(off)`. */
|
|
28
27
|
affine(k: Val<number>, off: Val<number>): this;
|
|
28
|
+
/** `sin(this)` (radians). Forward lands in [−1, 1]; the inverse is
|
|
29
|
+
* multi-valued, so a write clamps to that domain and returns the
|
|
30
|
+
* pre-image nearest the current source — the drag stays on its branch. */
|
|
31
|
+
sin(): this;
|
|
32
|
+
/** `exp(this)` — bijection on the reals; inverse is the natural log. */
|
|
33
|
+
exp(): this;
|
|
29
34
|
/** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
|
|
30
35
|
* the range reads back clamped, not as written). */
|
|
31
36
|
clamp(lo: Val<V>, hi: Val<V>): this;
|
package/dist/core/values/num.js
CHANGED
|
@@ -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 "
|
|
6
|
-
import { Cell, lazy, reader, } from "../
|
|
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;
|
|
@@ -11,6 +11,11 @@ export const scale = (a, k) => a * k;
|
|
|
11
11
|
export const lerp = (a, b, t) => a + (b - a) * t;
|
|
12
12
|
export const metric = (a, b) => Math.abs(a - b);
|
|
13
13
|
export const equals = (a, b) => a === b;
|
|
14
|
+
const TAU = 2 * Math.PI;
|
|
15
|
+
/** Representative of `x + 2πk` nearest `s` (shortest-arc branch pick). */
|
|
16
|
+
const nearestTo = (s, x) => x + TAU * Math.round((s - x) / TAU);
|
|
17
|
+
/** Clamp to the sin/cos domain `[-1, 1]`. */
|
|
18
|
+
const unit = (t) => (t < -1 ? -1 : t > 1 ? 1 : t);
|
|
14
19
|
const linearImpl = { add, sub, scale };
|
|
15
20
|
const packImpl = {
|
|
16
21
|
dim: 1,
|
|
@@ -49,6 +54,21 @@ export class Num extends Cell {
|
|
|
49
54
|
const of = reader(off);
|
|
50
55
|
return this.lens(v => v * kf() + of(), n => (n - of()) / kf());
|
|
51
56
|
}
|
|
57
|
+
/** `sin(this)` (radians). Forward lands in [−1, 1]; the inverse is
|
|
58
|
+
* multi-valued, so a write clamps to that domain and returns the
|
|
59
|
+
* pre-image nearest the current source — the drag stays on its branch. */
|
|
60
|
+
sin() {
|
|
61
|
+
return this.lens(v => Math.sin(v), (target, s) => {
|
|
62
|
+
const p = Math.asin(unit(target));
|
|
63
|
+
const a = nearestTo(s, p);
|
|
64
|
+
const b = nearestTo(s, Math.PI - p);
|
|
65
|
+
return Math.abs(a - s) <= Math.abs(b - s) ? a : b;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** `exp(this)` — bijection on the reals; inverse is the natural log. */
|
|
69
|
+
exp() {
|
|
70
|
+
return this.lens(v => Math.exp(v), n => Math.log(n));
|
|
71
|
+
}
|
|
52
72
|
/** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
|
|
53
73
|
* the range reads back clamped, not as written). */
|
|
54
74
|
clamp(lo, hi) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Cell, type Init, type Writable } from "../
|
|
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
|