bireactive 0.2.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.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/animation/anim.d.ts +57 -0
- package/dist/animation/anim.js +318 -0
- package/dist/animation/combinators.d.ts +39 -0
- package/dist/animation/combinators.js +113 -0
- package/dist/animation/easings.d.ts +5 -0
- package/dist/animation/easings.js +5 -0
- package/dist/animation/index.d.ts +3 -0
- package/dist/animation/index.js +3 -0
- package/dist/assert/algebra.d.ts +20 -0
- package/dist/assert/algebra.js +79 -0
- package/dist/assert/claim.d.ts +40 -0
- package/dist/assert/claim.js +129 -0
- package/dist/assert/index.d.ts +7 -0
- package/dist/assert/index.js +19 -0
- package/dist/assert/predicates.d.ts +18 -0
- package/dist/assert/predicates.js +43 -0
- package/dist/assert/record.d.ts +20 -0
- package/dist/assert/record.js +78 -0
- package/dist/assert/scope.d.ts +42 -0
- package/dist/assert/scope.js +233 -0
- package/dist/assert/span.d.ts +37 -0
- package/dist/assert/span.js +68 -0
- package/dist/assert/tree.d.ts +22 -0
- package/dist/assert/tree.js +65 -0
- package/dist/code/code.d.ts +70 -0
- package/dist/code/code.js +361 -0
- package/dist/code/index.d.ts +2 -0
- package/dist/code/index.js +9 -0
- package/dist/code/morph.d.ts +5 -0
- package/dist/code/morph.js +194 -0
- package/dist/code/tokenize.d.ts +8 -0
- package/dist/code/tokenize.js +51 -0
- package/dist/constraints/cluster.d.ts +83 -0
- package/dist/constraints/cluster.js +213 -0
- package/dist/constraints/drivers.d.ts +15 -0
- package/dist/constraints/drivers.js +40 -0
- package/dist/constraints/factories.d.ts +73 -0
- package/dist/constraints/factories.js +248 -0
- package/dist/constraints/index.d.ts +11 -0
- package/dist/constraints/index.js +39 -0
- package/dist/constraints/interaction.d.ts +21 -0
- package/dist/constraints/interaction.js +148 -0
- package/dist/constraints/linalg.d.ts +18 -0
- package/dist/constraints/linalg.js +141 -0
- package/dist/constraints/phases.d.ts +21 -0
- package/dist/constraints/phases.js +60 -0
- package/dist/constraints/physics.d.ts +34 -0
- package/dist/constraints/physics.js +128 -0
- package/dist/constraints/rigid.d.ts +210 -0
- package/dist/constraints/rigid.js +835 -0
- package/dist/constraints/solver.d.ts +107 -0
- package/dist/constraints/solver.js +510 -0
- package/dist/constraints/term.d.ts +50 -0
- package/dist/constraints/term.js +80 -0
- package/dist/constraints/terms.d.ts +80 -0
- package/dist/constraints/terms.js +302 -0
- package/dist/constraints/world.d.ts +31 -0
- package/dist/constraints/world.js +245 -0
- package/dist/core/aggregates.d.ts +64 -0
- package/dist/core/aggregates.js +198 -0
- package/dist/core/anim.d.ts +84 -0
- package/dist/core/anim.js +301 -0
- package/dist/core/index.d.ts +38 -0
- package/dist/core/index.js +38 -0
- package/dist/core/introspect.d.ts +5 -0
- package/dist/core/introspect.js +31 -0
- package/dist/core/lenses/closed-form-policies.d.ts +64 -0
- package/dist/core/lenses/closed-form-policies.js +452 -0
- package/dist/core/lenses/domain-aggregates.d.ts +54 -0
- package/dist/core/lenses/domain-aggregates.js +259 -0
- package/dist/core/lenses/factor-lens.d.ts +42 -0
- package/dist/core/lenses/factor-lens.js +419 -0
- package/dist/core/lenses/index.d.ts +5 -0
- package/dist/core/lenses/index.js +16 -0
- package/dist/core/lenses/memory.d.ts +47 -0
- package/dist/core/lenses/memory.js +102 -0
- package/dist/core/lenses/typed-factor.d.ts +45 -0
- package/dist/core/lenses/typed-factor.js +376 -0
- package/dist/core/network-utils.d.ts +14 -0
- package/dist/core/network-utils.js +62 -0
- package/dist/core/new-primitives.d.ts +33 -0
- package/dist/core/new-primitives.js +113 -0
- package/dist/core/signal.d.ts +254 -0
- package/dist/core/signal.js +1349 -0
- package/dist/core/traits.d.ts +61 -0
- package/dist/core/traits.js +56 -0
- package/dist/core/tree.d.ts +23 -0
- package/dist/core/tree.js +62 -0
- package/dist/core/values/anchor.d.ts +23 -0
- package/dist/core/values/anchor.js +23 -0
- package/dist/core/values/audio.d.ts +33 -0
- package/dist/core/values/audio.js +107 -0
- package/dist/core/values/bool.d.ts +37 -0
- package/dist/core/values/bool.js +75 -0
- package/dist/core/values/box.d.ts +77 -0
- package/dist/core/values/box.js +211 -0
- package/dist/core/values/canvas.d.ts +71 -0
- package/dist/core/values/canvas.js +495 -0
- package/dist/core/values/color.d.ts +49 -0
- package/dist/core/values/color.js +106 -0
- package/dist/core/values/flags.d.ts +18 -0
- package/dist/core/values/flags.js +50 -0
- package/dist/core/values/gpu.d.ts +74 -0
- package/dist/core/values/gpu.js +426 -0
- package/dist/core/values/matrix.d.ts +53 -0
- package/dist/core/values/matrix.js +140 -0
- package/dist/core/values/num.d.ts +62 -0
- package/dist/core/values/num.js +166 -0
- package/dist/core/values/pose.d.ts +31 -0
- package/dist/core/values/pose.js +83 -0
- package/dist/core/values/range.d.ts +83 -0
- package/dist/core/values/range.js +167 -0
- package/dist/core/values/str.d.ts +76 -0
- package/dist/core/values/str.js +346 -0
- package/dist/core/values/template.d.ts +49 -0
- package/dist/core/values/template.js +148 -0
- package/dist/core/values/transform.d.ts +49 -0
- package/dist/core/values/transform.js +115 -0
- package/dist/core/values/tri.d.ts +31 -0
- package/dist/core/values/tri.js +95 -0
- package/dist/core/values/vec.d.ts +72 -0
- package/dist/core/values/vec.js +219 -0
- package/dist/core/writable.d.ts +15 -0
- package/dist/core/writable.js +29 -0
- package/dist/ext/events.d.ts +10 -0
- package/dist/ext/events.js +31 -0
- package/dist/ext/index.d.ts +4 -0
- package/dist/ext/index.js +4 -0
- package/dist/ext/snapshot.d.ts +8 -0
- package/dist/ext/snapshot.js +29 -0
- package/dist/ext/timeline.d.ts +56 -0
- package/dist/ext/timeline.js +94 -0
- package/dist/ext/waapi.d.ts +25 -0
- package/dist/ext/waapi.js +198 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/propagators/index.d.ts +6 -0
- package/dist/propagators/index.js +6 -0
- package/dist/propagators/layout.d.ts +68 -0
- package/dist/propagators/layout.js +336 -0
- package/dist/propagators/network.d.ts +52 -0
- package/dist/propagators/network.js +185 -0
- package/dist/propagators/propagator.d.ts +12 -0
- package/dist/propagators/propagator.js +16 -0
- package/dist/propagators/range.d.ts +45 -0
- package/dist/propagators/range.js +147 -0
- package/dist/propagators/relations.d.ts +60 -0
- package/dist/propagators/relations.js +343 -0
- package/dist/shapes/annular-sector.d.ts +15 -0
- package/dist/shapes/annular-sector.js +64 -0
- package/dist/shapes/button.d.ts +14 -0
- package/dist/shapes/button.js +31 -0
- package/dist/shapes/choreographers.d.ts +22 -0
- package/dist/shapes/choreographers.js +69 -0
- package/dist/shapes/circle.d.ts +17 -0
- package/dist/shapes/circle.js +57 -0
- package/dist/shapes/clip.d.ts +5 -0
- package/dist/shapes/clip.js +31 -0
- package/dist/shapes/connect.d.ts +16 -0
- package/dist/shapes/connect.js +70 -0
- package/dist/shapes/curve.d.ts +60 -0
- package/dist/shapes/curve.js +285 -0
- package/dist/shapes/dashed.d.ts +16 -0
- package/dist/shapes/dashed.js +142 -0
- package/dist/shapes/debug.d.ts +43 -0
- package/dist/shapes/debug.js +97 -0
- package/dist/shapes/group.d.ts +5 -0
- package/dist/shapes/group.js +10 -0
- package/dist/shapes/handle.d.ts +32 -0
- package/dist/shapes/handle.js +88 -0
- package/dist/shapes/index.d.ts +23 -0
- package/dist/shapes/index.js +23 -0
- package/dist/shapes/interaction.d.ts +32 -0
- package/dist/shapes/interaction.js +187 -0
- package/dist/shapes/label.d.ts +20 -0
- package/dist/shapes/label.js +42 -0
- package/dist/shapes/layout.d.ts +29 -0
- package/dist/shapes/layout.js +74 -0
- package/dist/shapes/line.d.ts +21 -0
- package/dist/shapes/line.js +79 -0
- package/dist/shapes/list.d.ts +18 -0
- package/dist/shapes/list.js +51 -0
- package/dist/shapes/mount.d.ts +7 -0
- package/dist/shapes/mount.js +10 -0
- package/dist/shapes/path.d.ts +77 -0
- package/dist/shapes/path.js +227 -0
- package/dist/shapes/rect.d.ts +30 -0
- package/dist/shapes/rect.js +131 -0
- package/dist/shapes/shape.d.ts +132 -0
- package/dist/shapes/shape.js +306 -0
- package/dist/shapes/text.d.ts +24 -0
- package/dist/shapes/text.js +53 -0
- package/dist/shapes/tokens.d.ts +28 -0
- package/dist/shapes/tokens.js +27 -0
- package/dist/shapes/transitions.d.ts +23 -0
- package/dist/shapes/transitions.js +62 -0
- package/dist/tex/decorations.d.ts +26 -0
- package/dist/tex/decorations.js +116 -0
- package/dist/tex/index.d.ts +5 -0
- package/dist/tex/index.js +5 -0
- package/dist/tex/marker.d.ts +17 -0
- package/dist/tex/marker.js +63 -0
- package/dist/tex/motion.d.ts +43 -0
- package/dist/tex/motion.js +290 -0
- package/dist/tex/parts.d.ts +65 -0
- package/dist/tex/parts.js +149 -0
- package/dist/tex/tex.d.ts +45 -0
- package/dist/tex/tex.js +244 -0
- package/dist/web/attr.d.ts +16 -0
- package/dist/web/attr.js +98 -0
- package/dist/web/diagram.d.ts +49 -0
- package/dist/web/diagram.js +260 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +6 -0
- package/dist/web/md-marker.d.ts +6 -0
- package/dist/web/md-marker.js +39 -0
- package/dist/web/md-tex.d.ts +6 -0
- package/dist/web/md-tex.js +61 -0
- package/dist/web/raf.d.ts +6 -0
- package/dist/web/raf.js +24 -0
- package/dist/web/viewport.d.ts +7 -0
- package/dist/web/viewport.js +13 -0
- package/package.json +87 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// flags.ts — named bit-flags over a packed integer.
|
|
2
|
+
//
|
|
3
|
+
// A `number` bitmask whose bits are named at construction. `flag(name)` is a
|
|
4
|
+
// `Writable<Bool>` mask lens onto one bit — set/clear round-trips through the
|
|
5
|
+
// packed value, so the integer and its named booleans are one source seen
|
|
6
|
+
// two ways. Sparse-trait (`equals` only): bits are discrete, so there's no
|
|
7
|
+
// linear/pack/factor surface. Bit `i` carries value `2^i`, so a flag list
|
|
8
|
+
// ordered low→high makes the packed int equal the conventional mask (e.g.
|
|
9
|
+
// chmod octal). Two front-ends: variadic names, or an object of defaults.
|
|
10
|
+
//
|
|
11
|
+
// `flag()` is the collision-free accessor (a single generic method, names
|
|
12
|
+
// checked as a union) rather than dynamic `.name` members — no risk of a
|
|
13
|
+
// flag named "value"/"flag" shadowing the class. Since each flag is a
|
|
14
|
+
// writable Bool, `Tri.allOf([f.flag(a), f.flag(b)])` gives all/none/mixed
|
|
15
|
+
// group toggles for free.
|
|
16
|
+
import { Cell } from "../signal.js";
|
|
17
|
+
import { Bool } from "./bool.js";
|
|
18
|
+
const equals = (a, b) => a === b;
|
|
19
|
+
export class Flags extends Cell {
|
|
20
|
+
names;
|
|
21
|
+
static traits = { equals };
|
|
22
|
+
#bits = new Map();
|
|
23
|
+
constructor(names, v = 0) {
|
|
24
|
+
super(v, { equals });
|
|
25
|
+
this.names = names;
|
|
26
|
+
}
|
|
27
|
+
/** Bit lens for `name`; set/clear round-trips through the packed mask.
|
|
28
|
+
* Cached per name so repeated calls return the same lens. */
|
|
29
|
+
flag(name) {
|
|
30
|
+
let lens = this.#bits.get(name);
|
|
31
|
+
if (lens === undefined) {
|
|
32
|
+
const i = this.names.indexOf(name);
|
|
33
|
+
if (i < 0)
|
|
34
|
+
throw new Error(`Flags: unknown flag "${String(name)}"`);
|
|
35
|
+
lens = Bool.lens(this, v => ((v >> i) & 1) === 1, (on, cur) => (on ? cur | (1 << i) : cur & ~(1 << i)));
|
|
36
|
+
this.#bits.set(name, lens);
|
|
37
|
+
}
|
|
38
|
+
return lens;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function flags(arg, ...rest) {
|
|
42
|
+
if (typeof arg === "string")
|
|
43
|
+
return new Flags([arg, ...rest]);
|
|
44
|
+
const names = Object.keys(arg);
|
|
45
|
+
let v = 0;
|
|
46
|
+
for (let i = 0; i < names.length; i++)
|
|
47
|
+
if (arg[names[i]])
|
|
48
|
+
v |= 1 << i;
|
|
49
|
+
return new Flags(names, v);
|
|
50
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** Lazily create the shared WebGL2 context (browser-only). */
|
|
2
|
+
export declare function gl(): WebGL2RenderingContext;
|
|
3
|
+
/** A GPU-resident image: a texture plus its dimensions. */
|
|
4
|
+
export interface Tex {
|
|
5
|
+
tex: WebGLTexture;
|
|
6
|
+
w: number;
|
|
7
|
+
h: number;
|
|
8
|
+
}
|
|
9
|
+
/** Allocate an RGBA32F texture (`data` in 0–1 floats, or null). */
|
|
10
|
+
export declare function newTex(w: number, h: number, data?: Float32Array | null): WebGLTexture;
|
|
11
|
+
export declare function disposeTex(t: WebGLTexture): void;
|
|
12
|
+
/** A reusable owned texture, reallocated on size change — the GPU analog of
|
|
13
|
+
* the CPU scratch buffer. */
|
|
14
|
+
export declare function scratch(): (w: number, h: number) => Tex;
|
|
15
|
+
/** A feedback-safe scratch pair: returns an owned texture guaranteed not to
|
|
16
|
+
* equal `avoid` (the input being read), so a pass never reads and writes the
|
|
17
|
+
* same texture. Needed for backward passes that produce a root cell's next
|
|
18
|
+
* value (whose current value may be this same scratch). */
|
|
19
|
+
export declare function scratch2(): (w: number, h: number, avoid: WebGLTexture | null) => Tex;
|
|
20
|
+
/** Copy `srcTex` into `dst` (distinct textures). */
|
|
21
|
+
export declare function copy(srcTex: WebGLTexture, dst: Tex): void;
|
|
22
|
+
/** Uniform setters passed to a `pass` callback. */
|
|
23
|
+
export interface Setup {
|
|
24
|
+
tex(name: string, unit: number, t: WebGLTexture): void;
|
|
25
|
+
f(name: string, v: number): void;
|
|
26
|
+
i(name: string, v: number): void;
|
|
27
|
+
v2(name: string, a: number, b: number): void;
|
|
28
|
+
v3(name: string, a: number, b: number, c: number): void;
|
|
29
|
+
i2(name: string, a: number, b: number): void;
|
|
30
|
+
}
|
|
31
|
+
/** Run a fragment shader into `target`, configured by `setup`. */
|
|
32
|
+
export declare function pass(frag: string, target: Tex, setup: (s: Setup) => void): void;
|
|
33
|
+
/** Mean of all texels (RGBA, 0–1) via GPU pyramid reduction; one 1×1
|
|
34
|
+
* readback. */
|
|
35
|
+
export declare function reduceMean(src: Tex): [number, number, number, number];
|
|
36
|
+
/** Mean of squared texels — the spring settle metric. */
|
|
37
|
+
export declare function reduceMeanSquare(src: Tex): number;
|
|
38
|
+
/** Render `src` to a 2D canvas (GPU draw + drawImage; no CPU readback). */
|
|
39
|
+
export declare function blit(src: Tex, ctx: CanvasRenderingContext2D): void;
|
|
40
|
+
/** Stamp a soft disc of `color` (0–1) at data pixel `(cx,cy)` into `dst`,
|
|
41
|
+
* reading from `srcTex`. Caller supplies a `dst` distinct from `srcTex`. */
|
|
42
|
+
export declare function brush(srcTex: WebGLTexture, dst: Tex, cx: number, cy: number, radius: number, color: [number, number, number]): void;
|
|
43
|
+
export interface SpringOpts {
|
|
44
|
+
stiffness?: number;
|
|
45
|
+
damping?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Per-pixel damped oscillator field on the GPU. Position and velocity live
|
|
48
|
+
* in float textures and never leave the card; `current()` exposes position
|
|
49
|
+
* as a `Tex` so it can back a reactive Canvas value frame by frame. */
|
|
50
|
+
export declare class Spring {
|
|
51
|
+
readonly w: number;
|
|
52
|
+
readonly h: number;
|
|
53
|
+
stiffness: number;
|
|
54
|
+
damping: number;
|
|
55
|
+
private pos;
|
|
56
|
+
private vel;
|
|
57
|
+
private targetTex;
|
|
58
|
+
private cur;
|
|
59
|
+
private springFbo;
|
|
60
|
+
constructor(w: number, h: number, opts?: SpringOpts);
|
|
61
|
+
private clearTex;
|
|
62
|
+
/** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
|
|
63
|
+
* alias one of our own position textures (the host feeds `current()` back
|
|
64
|
+
* as the root value), so skip any copy that would read and write the same
|
|
65
|
+
* texture — illegal GL feedback, and a no-op anyway. */
|
|
66
|
+
seed(srcTex: WebGLTexture): void;
|
|
67
|
+
/** Retarget (keeps current position + velocity). */
|
|
68
|
+
setTarget(srcTex: WebGLTexture): void;
|
|
69
|
+
step(dt: number): void;
|
|
70
|
+
current(): Tex;
|
|
71
|
+
/** Mean kinetic energy (velocity²) — the settle metric. */
|
|
72
|
+
energy(): number;
|
|
73
|
+
dispose(): void;
|
|
74
|
+
}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
// gpu.ts — shared WebGL2 core for GPU-resident Canvas values.
|
|
2
|
+
//
|
|
3
|
+
// There is ONE compute context (a hidden canvas). That is a hard WebGL
|
|
4
|
+
// constraint, not a shared buffer: a lens pass samples its parent's texture
|
|
5
|
+
// and renders into its child's, and textures can't cross contexts (browsers
|
|
6
|
+
// also cap contexts at ~16). Every Canvas value still owns its own float
|
|
7
|
+
// texture(s); display blits each value into its own DOM canvas. The only
|
|
8
|
+
// CPU readbacks are 1×1 reductions (mean colour, settle energy).
|
|
9
|
+
//
|
|
10
|
+
// Coordinate convention: data row 0 lives at texture v=0, so every compute
|
|
11
|
+
// pass works in a single consistent data space (no flips); only `blit`
|
|
12
|
+
// flips Y so the image reads upright on screen.
|
|
13
|
+
let _gl = null;
|
|
14
|
+
/** Lazily create the shared WebGL2 context (browser-only). */
|
|
15
|
+
export function gl() {
|
|
16
|
+
if (_gl)
|
|
17
|
+
return _gl;
|
|
18
|
+
const canvas = document.createElement("canvas");
|
|
19
|
+
const ctx = canvas.getContext("webgl2", { antialias: false, premultipliedAlpha: false });
|
|
20
|
+
if (!ctx)
|
|
21
|
+
throw new Error("WebGL2 unavailable");
|
|
22
|
+
if (!ctx.getExtension("EXT_color_buffer_float"))
|
|
23
|
+
throw new Error("EXT_color_buffer_float unavailable");
|
|
24
|
+
_gl = ctx;
|
|
25
|
+
return ctx;
|
|
26
|
+
}
|
|
27
|
+
const QUAD = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
|
|
28
|
+
const VERT = `#version 300 es
|
|
29
|
+
in vec2 a_pos;
|
|
30
|
+
out vec2 v_uv;
|
|
31
|
+
void main() {
|
|
32
|
+
v_uv = a_pos * 0.5 + 0.5;
|
|
33
|
+
gl_Position = vec4(a_pos, 0.0, 1.0);
|
|
34
|
+
}`;
|
|
35
|
+
const progCache = new Map();
|
|
36
|
+
function compile(g, type, src) {
|
|
37
|
+
const sh = g.createShader(type);
|
|
38
|
+
g.shaderSource(sh, src);
|
|
39
|
+
g.compileShader(sh);
|
|
40
|
+
if (!g.getShaderParameter(sh, g.COMPILE_STATUS)) {
|
|
41
|
+
const log = g.getShaderInfoLog(sh);
|
|
42
|
+
g.deleteShader(sh);
|
|
43
|
+
throw new Error(`shader compile: ${log}`);
|
|
44
|
+
}
|
|
45
|
+
return sh;
|
|
46
|
+
}
|
|
47
|
+
function program(frag) {
|
|
48
|
+
let p = progCache.get(frag);
|
|
49
|
+
if (p)
|
|
50
|
+
return p;
|
|
51
|
+
const g = gl();
|
|
52
|
+
const prog = g.createProgram();
|
|
53
|
+
g.attachShader(prog, compile(g, g.VERTEX_SHADER, VERT));
|
|
54
|
+
g.attachShader(prog, compile(g, g.FRAGMENT_SHADER, frag));
|
|
55
|
+
g.bindAttribLocation(prog, 0, "a_pos");
|
|
56
|
+
g.linkProgram(prog);
|
|
57
|
+
if (!g.getProgramParameter(prog, g.LINK_STATUS))
|
|
58
|
+
throw new Error(`link: ${g.getProgramInfoLog(prog)}`);
|
|
59
|
+
p = { prog, locs: new Map() };
|
|
60
|
+
progCache.set(frag, p);
|
|
61
|
+
return p;
|
|
62
|
+
}
|
|
63
|
+
let _vao = null;
|
|
64
|
+
function vao() {
|
|
65
|
+
if (_vao)
|
|
66
|
+
return _vao;
|
|
67
|
+
const g = gl();
|
|
68
|
+
_vao = g.createVertexArray();
|
|
69
|
+
g.bindVertexArray(_vao);
|
|
70
|
+
const buf = g.createBuffer();
|
|
71
|
+
g.bindBuffer(g.ARRAY_BUFFER, buf);
|
|
72
|
+
g.bufferData(g.ARRAY_BUFFER, QUAD, g.STATIC_DRAW);
|
|
73
|
+
g.enableVertexAttribArray(0);
|
|
74
|
+
g.vertexAttribPointer(0, 2, g.FLOAT, false, 0, 0);
|
|
75
|
+
g.bindVertexArray(null);
|
|
76
|
+
return _vao;
|
|
77
|
+
}
|
|
78
|
+
let _fbo = null;
|
|
79
|
+
function fbo() {
|
|
80
|
+
if (!_fbo)
|
|
81
|
+
_fbo = gl().createFramebuffer();
|
|
82
|
+
return _fbo;
|
|
83
|
+
}
|
|
84
|
+
/** Allocate an RGBA32F texture (`data` in 0–1 floats, or null). */
|
|
85
|
+
export function newTex(w, h, data = null) {
|
|
86
|
+
const g = gl();
|
|
87
|
+
const t = g.createTexture();
|
|
88
|
+
g.bindTexture(g.TEXTURE_2D, t);
|
|
89
|
+
g.texImage2D(g.TEXTURE_2D, 0, g.RGBA32F, w, h, 0, g.RGBA, g.FLOAT, data);
|
|
90
|
+
g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MIN_FILTER, g.NEAREST);
|
|
91
|
+
g.texParameteri(g.TEXTURE_2D, g.TEXTURE_MAG_FILTER, g.NEAREST);
|
|
92
|
+
g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_S, g.CLAMP_TO_EDGE);
|
|
93
|
+
g.texParameteri(g.TEXTURE_2D, g.TEXTURE_WRAP_T, g.CLAMP_TO_EDGE);
|
|
94
|
+
return t;
|
|
95
|
+
}
|
|
96
|
+
export function disposeTex(t) {
|
|
97
|
+
gl().deleteTexture(t);
|
|
98
|
+
}
|
|
99
|
+
/** A reusable owned texture, reallocated on size change — the GPU analog of
|
|
100
|
+
* the CPU scratch buffer. */
|
|
101
|
+
export function scratch() {
|
|
102
|
+
let t = null;
|
|
103
|
+
let cw = 0;
|
|
104
|
+
let ch = 0;
|
|
105
|
+
return (w, h) => {
|
|
106
|
+
if (!t || cw !== w || ch !== h) {
|
|
107
|
+
if (t)
|
|
108
|
+
disposeTex(t);
|
|
109
|
+
t = newTex(w, h);
|
|
110
|
+
cw = w;
|
|
111
|
+
ch = h;
|
|
112
|
+
}
|
|
113
|
+
return { tex: t, w, h };
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** A feedback-safe scratch pair: returns an owned texture guaranteed not to
|
|
117
|
+
* equal `avoid` (the input being read), so a pass never reads and writes the
|
|
118
|
+
* same texture. Needed for backward passes that produce a root cell's next
|
|
119
|
+
* value (whose current value may be this same scratch). */
|
|
120
|
+
export function scratch2() {
|
|
121
|
+
let a = null;
|
|
122
|
+
let b = null;
|
|
123
|
+
let cw = 0;
|
|
124
|
+
let ch = 0;
|
|
125
|
+
return (w, h, avoid) => {
|
|
126
|
+
if (!a || !b || cw !== w || ch !== h) {
|
|
127
|
+
if (a)
|
|
128
|
+
disposeTex(a);
|
|
129
|
+
if (b)
|
|
130
|
+
disposeTex(b);
|
|
131
|
+
a = newTex(w, h);
|
|
132
|
+
b = newTex(w, h);
|
|
133
|
+
cw = w;
|
|
134
|
+
ch = h;
|
|
135
|
+
}
|
|
136
|
+
return avoid === a ? { tex: b, w, h } : { tex: a, w, h };
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const COPY = `#version 300 es
|
|
140
|
+
precision highp float;
|
|
141
|
+
in vec2 v_uv;
|
|
142
|
+
uniform sampler2D u_s;
|
|
143
|
+
out vec4 o;
|
|
144
|
+
void main() { o = texture(u_s, v_uv); }`;
|
|
145
|
+
/** Copy `srcTex` into `dst` (distinct textures). */
|
|
146
|
+
export function copy(srcTex, dst) {
|
|
147
|
+
pass(COPY, dst, s => s.tex("u_s", 0, srcTex));
|
|
148
|
+
}
|
|
149
|
+
function loc(p, name) {
|
|
150
|
+
if (p.locs.has(name))
|
|
151
|
+
return p.locs.get(name);
|
|
152
|
+
const l = gl().getUniformLocation(p.prog, name);
|
|
153
|
+
p.locs.set(name, l);
|
|
154
|
+
return l;
|
|
155
|
+
}
|
|
156
|
+
/** Run a fragment shader into `target`, configured by `setup`. */
|
|
157
|
+
export function pass(frag, target, setup) {
|
|
158
|
+
const g = gl();
|
|
159
|
+
const p = program(frag);
|
|
160
|
+
g.bindFramebuffer(g.FRAMEBUFFER, fbo());
|
|
161
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT0, g.TEXTURE_2D, target.tex, 0);
|
|
162
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT1, g.TEXTURE_2D, null, 0);
|
|
163
|
+
g.drawBuffers([g.COLOR_ATTACHMENT0]);
|
|
164
|
+
g.viewport(0, 0, target.w, target.h);
|
|
165
|
+
g.useProgram(p.prog);
|
|
166
|
+
const s = {
|
|
167
|
+
tex: (name, unit, t) => {
|
|
168
|
+
g.activeTexture(g.TEXTURE0 + unit);
|
|
169
|
+
g.bindTexture(g.TEXTURE_2D, t);
|
|
170
|
+
g.uniform1i(loc(p, name), unit);
|
|
171
|
+
},
|
|
172
|
+
f: (name, v) => g.uniform1f(loc(p, name), v),
|
|
173
|
+
i: (name, v) => g.uniform1i(loc(p, name), v),
|
|
174
|
+
v2: (name, a, b) => g.uniform2f(loc(p, name), a, b),
|
|
175
|
+
v3: (name, a, b, c) => g.uniform3f(loc(p, name), a, b, c),
|
|
176
|
+
i2: (name, a, b) => g.uniform2i(loc(p, name), a, b),
|
|
177
|
+
};
|
|
178
|
+
setup(s);
|
|
179
|
+
g.bindVertexArray(vao());
|
|
180
|
+
g.drawArrays(g.TRIANGLE_STRIP, 0, 4);
|
|
181
|
+
g.bindVertexArray(null);
|
|
182
|
+
}
|
|
183
|
+
const SUM4 = `#version 300 es
|
|
184
|
+
precision highp float;
|
|
185
|
+
uniform highp sampler2D u_src;
|
|
186
|
+
uniform ivec2 u_size;
|
|
187
|
+
out vec4 o;
|
|
188
|
+
void main() {
|
|
189
|
+
ivec2 d = ivec2(gl_FragCoord.xy);
|
|
190
|
+
ivec2 s = d * 2;
|
|
191
|
+
vec4 sum = texelFetch(u_src, s, 0);
|
|
192
|
+
bool rx = s.x + 1 < u_size.x;
|
|
193
|
+
bool ry = s.y + 1 < u_size.y;
|
|
194
|
+
if (rx) sum += texelFetch(u_src, ivec2(s.x + 1, s.y), 0);
|
|
195
|
+
if (ry) sum += texelFetch(u_src, ivec2(s.x, s.y + 1), 0);
|
|
196
|
+
if (rx && ry) sum += texelFetch(u_src, ivec2(s.x + 1, s.y + 1), 0);
|
|
197
|
+
o = sum;
|
|
198
|
+
}`;
|
|
199
|
+
const SQUARE = `#version 300 es
|
|
200
|
+
precision highp float;
|
|
201
|
+
in vec2 v_uv;
|
|
202
|
+
uniform sampler2D u_src;
|
|
203
|
+
out vec4 o;
|
|
204
|
+
void main() { vec4 v = texture(u_src, v_uv); o = v * v; }`;
|
|
205
|
+
const reducePool = new Map();
|
|
206
|
+
function poolTex(w, h) {
|
|
207
|
+
const key = `${w}x${h}`;
|
|
208
|
+
let t = reducePool.get(key);
|
|
209
|
+
if (!t) {
|
|
210
|
+
t = { tex: newTex(w, h), w, h };
|
|
211
|
+
reducePool.set(key, t);
|
|
212
|
+
}
|
|
213
|
+
return t;
|
|
214
|
+
}
|
|
215
|
+
/** Mean of all texels (RGBA, 0–1) via GPU pyramid reduction; one 1×1
|
|
216
|
+
* readback. */
|
|
217
|
+
export function reduceMean(src) {
|
|
218
|
+
let cur = src.tex;
|
|
219
|
+
let cw = src.w;
|
|
220
|
+
let ch = src.h;
|
|
221
|
+
while (cw > 1 || ch > 1) {
|
|
222
|
+
const nw = Math.ceil(cw / 2);
|
|
223
|
+
const nh = Math.ceil(ch / 2);
|
|
224
|
+
const dst = poolTex(nw, nh);
|
|
225
|
+
pass(SUM4, dst, s => {
|
|
226
|
+
s.tex("u_src", 0, cur);
|
|
227
|
+
s.i2("u_size", cw, ch);
|
|
228
|
+
});
|
|
229
|
+
cur = dst.tex;
|
|
230
|
+
cw = nw;
|
|
231
|
+
ch = nh;
|
|
232
|
+
}
|
|
233
|
+
const g = gl();
|
|
234
|
+
g.bindFramebuffer(g.FRAMEBUFFER, fbo());
|
|
235
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT0, g.TEXTURE_2D, cur, 0);
|
|
236
|
+
const buf = new Float32Array(4);
|
|
237
|
+
g.readPixels(0, 0, 1, 1, g.RGBA, g.FLOAT, buf);
|
|
238
|
+
const n = src.w * src.h;
|
|
239
|
+
return [buf[0] / n, buf[1] / n, buf[2] / n, buf[3] / n];
|
|
240
|
+
}
|
|
241
|
+
let _sq = null;
|
|
242
|
+
/** Mean of squared texels — the spring settle metric. */
|
|
243
|
+
export function reduceMeanSquare(src) {
|
|
244
|
+
if (!_sq || _sq.w !== src.w || _sq.h !== src.h) {
|
|
245
|
+
if (_sq)
|
|
246
|
+
disposeTex(_sq.tex);
|
|
247
|
+
_sq = { tex: newTex(src.w, src.h), w: src.w, h: src.h };
|
|
248
|
+
}
|
|
249
|
+
pass(SQUARE, _sq, s => s.tex("u_src", 0, src.tex));
|
|
250
|
+
const m = reduceMean(_sq);
|
|
251
|
+
return (m[0] + m[1] + m[2] + m[3]) / 4;
|
|
252
|
+
}
|
|
253
|
+
const DISPLAY = `#version 300 es
|
|
254
|
+
precision highp float;
|
|
255
|
+
in vec2 v_uv;
|
|
256
|
+
uniform sampler2D u_src;
|
|
257
|
+
out vec4 o;
|
|
258
|
+
void main() {
|
|
259
|
+
vec3 c = texture(u_src, vec2(v_uv.x, 1.0 - v_uv.y)).rgb;
|
|
260
|
+
o = vec4(clamp(c, 0.0, 1.0), 1.0);
|
|
261
|
+
}`;
|
|
262
|
+
/** Render `src` to a 2D canvas (GPU draw + drawImage; no CPU readback). */
|
|
263
|
+
export function blit(src, ctx) {
|
|
264
|
+
const g = gl();
|
|
265
|
+
const glc = g.canvas;
|
|
266
|
+
if (glc.width !== src.w || glc.height !== src.h) {
|
|
267
|
+
glc.width = src.w;
|
|
268
|
+
glc.height = src.h;
|
|
269
|
+
}
|
|
270
|
+
g.bindFramebuffer(g.FRAMEBUFFER, null);
|
|
271
|
+
g.viewport(0, 0, src.w, src.h);
|
|
272
|
+
const p = program(DISPLAY);
|
|
273
|
+
g.useProgram(p.prog);
|
|
274
|
+
g.activeTexture(g.TEXTURE0);
|
|
275
|
+
g.bindTexture(g.TEXTURE_2D, src.tex);
|
|
276
|
+
g.uniform1i(loc(p, "u_src"), 0);
|
|
277
|
+
g.bindVertexArray(vao());
|
|
278
|
+
g.drawArrays(g.TRIANGLE_STRIP, 0, 4);
|
|
279
|
+
g.bindVertexArray(null);
|
|
280
|
+
const cv = ctx.canvas;
|
|
281
|
+
if (cv.width !== src.w || cv.height !== src.h) {
|
|
282
|
+
cv.width = src.w;
|
|
283
|
+
cv.height = src.h;
|
|
284
|
+
}
|
|
285
|
+
ctx.clearRect(0, 0, cv.width, cv.height);
|
|
286
|
+
ctx.drawImage(glc, 0, 0);
|
|
287
|
+
}
|
|
288
|
+
const BRUSH = `#version 300 es
|
|
289
|
+
precision highp float;
|
|
290
|
+
in vec2 v_uv;
|
|
291
|
+
uniform sampler2D u_src;
|
|
292
|
+
uniform vec2 u_res;
|
|
293
|
+
uniform vec2 u_center;
|
|
294
|
+
uniform float u_radius;
|
|
295
|
+
uniform vec3 u_color;
|
|
296
|
+
out vec4 o;
|
|
297
|
+
void main() {
|
|
298
|
+
vec4 s = texture(u_src, v_uv);
|
|
299
|
+
vec2 px = v_uv * u_res;
|
|
300
|
+
float d = distance(px, u_center);
|
|
301
|
+
float f = clamp(1.0 - d / u_radius, 0.0, 1.0);
|
|
302
|
+
float a = f * f * 0.9;
|
|
303
|
+
o = vec4(mix(s.rgb, u_color, a), 1.0);
|
|
304
|
+
}`;
|
|
305
|
+
/** Stamp a soft disc of `color` (0–1) at data pixel `(cx,cy)` into `dst`,
|
|
306
|
+
* reading from `srcTex`. Caller supplies a `dst` distinct from `srcTex`. */
|
|
307
|
+
export function brush(srcTex, dst, cx, cy, radius, color) {
|
|
308
|
+
pass(BRUSH, dst, s => {
|
|
309
|
+
s.tex("u_src", 0, srcTex);
|
|
310
|
+
s.v2("u_res", dst.w, dst.h);
|
|
311
|
+
s.v2("u_center", cx, cy);
|
|
312
|
+
s.f("u_radius", radius);
|
|
313
|
+
s.v3("u_color", color[0], color[1], color[2]);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
const INTEGRATE = `#version 300 es
|
|
317
|
+
precision highp float;
|
|
318
|
+
in vec2 v_uv;
|
|
319
|
+
uniform sampler2D u_pos;
|
|
320
|
+
uniform sampler2D u_vel;
|
|
321
|
+
uniform sampler2D u_target;
|
|
322
|
+
uniform float u_stiff;
|
|
323
|
+
uniform float u_damp;
|
|
324
|
+
uniform float u_dt;
|
|
325
|
+
layout(location = 0) out vec4 o_pos;
|
|
326
|
+
layout(location = 1) out vec4 o_vel;
|
|
327
|
+
void main() {
|
|
328
|
+
vec4 p = texture(u_pos, v_uv);
|
|
329
|
+
vec4 t = texture(u_target, v_uv);
|
|
330
|
+
vec4 v = texture(u_vel, v_uv);
|
|
331
|
+
vec4 acc = (t - p) * u_stiff - v * u_damp;
|
|
332
|
+
vec4 nv = v + acc * u_dt;
|
|
333
|
+
o_vel = nv;
|
|
334
|
+
o_pos = p + nv * u_dt;
|
|
335
|
+
}`;
|
|
336
|
+
/** Per-pixel damped oscillator field on the GPU. Position and velocity live
|
|
337
|
+
* in float textures and never leave the card; `current()` exposes position
|
|
338
|
+
* as a `Tex` so it can back a reactive Canvas value frame by frame. */
|
|
339
|
+
export class Spring {
|
|
340
|
+
w;
|
|
341
|
+
h;
|
|
342
|
+
stiffness;
|
|
343
|
+
damping;
|
|
344
|
+
pos;
|
|
345
|
+
vel;
|
|
346
|
+
targetTex;
|
|
347
|
+
cur = 0;
|
|
348
|
+
springFbo;
|
|
349
|
+
constructor(w, h, opts = {}) {
|
|
350
|
+
this.w = w;
|
|
351
|
+
this.h = h;
|
|
352
|
+
this.stiffness = opts.stiffness ?? 100;
|
|
353
|
+
this.damping = opts.damping ?? 2;
|
|
354
|
+
this.pos = [newTex(w, h), newTex(w, h)];
|
|
355
|
+
this.vel = [newTex(w, h), newTex(w, h)];
|
|
356
|
+
this.targetTex = newTex(w, h);
|
|
357
|
+
this.springFbo = gl().createFramebuffer();
|
|
358
|
+
}
|
|
359
|
+
clearTex(t) {
|
|
360
|
+
const g = gl();
|
|
361
|
+
g.bindFramebuffer(g.FRAMEBUFFER, this.springFbo);
|
|
362
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT1, g.TEXTURE_2D, null, 0);
|
|
363
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT0, g.TEXTURE_2D, t, 0);
|
|
364
|
+
g.drawBuffers([g.COLOR_ATTACHMENT0]);
|
|
365
|
+
g.viewport(0, 0, this.w, this.h);
|
|
366
|
+
g.clearColor(0, 0, 0, 0);
|
|
367
|
+
g.clear(g.COLOR_BUFFER_BIT);
|
|
368
|
+
}
|
|
369
|
+
/** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
|
|
370
|
+
* alias one of our own position textures (the host feeds `current()` back
|
|
371
|
+
* as the root value), so skip any copy that would read and write the same
|
|
372
|
+
* texture — illegal GL feedback, and a no-op anyway. */
|
|
373
|
+
seed(srcTex) {
|
|
374
|
+
for (const tex of [this.pos[0], this.pos[1], this.targetTex])
|
|
375
|
+
if (srcTex !== tex)
|
|
376
|
+
copy(srcTex, { tex, w: this.w, h: this.h });
|
|
377
|
+
this.clearTex(this.vel[0]);
|
|
378
|
+
this.clearTex(this.vel[1]);
|
|
379
|
+
this.cur = 0;
|
|
380
|
+
}
|
|
381
|
+
/** Retarget (keeps current position + velocity). */
|
|
382
|
+
setTarget(srcTex) {
|
|
383
|
+
copy(srcTex, { tex: this.targetTex, w: this.w, h: this.h });
|
|
384
|
+
}
|
|
385
|
+
step(dt) {
|
|
386
|
+
const g = gl();
|
|
387
|
+
const src = this.cur;
|
|
388
|
+
const dst = 1 - this.cur;
|
|
389
|
+
const p = program(INTEGRATE);
|
|
390
|
+
g.bindFramebuffer(g.FRAMEBUFFER, this.springFbo);
|
|
391
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT0, g.TEXTURE_2D, this.pos[dst], 0);
|
|
392
|
+
g.framebufferTexture2D(g.FRAMEBUFFER, g.COLOR_ATTACHMENT1, g.TEXTURE_2D, this.vel[dst], 0);
|
|
393
|
+
g.drawBuffers([g.COLOR_ATTACHMENT0, g.COLOR_ATTACHMENT1]);
|
|
394
|
+
g.viewport(0, 0, this.w, this.h);
|
|
395
|
+
g.useProgram(p.prog);
|
|
396
|
+
g.activeTexture(g.TEXTURE0);
|
|
397
|
+
g.bindTexture(g.TEXTURE_2D, this.pos[src]);
|
|
398
|
+
g.uniform1i(loc(p, "u_pos"), 0);
|
|
399
|
+
g.activeTexture(g.TEXTURE1);
|
|
400
|
+
g.bindTexture(g.TEXTURE_2D, this.vel[src]);
|
|
401
|
+
g.uniform1i(loc(p, "u_vel"), 1);
|
|
402
|
+
g.activeTexture(g.TEXTURE2);
|
|
403
|
+
g.bindTexture(g.TEXTURE_2D, this.targetTex);
|
|
404
|
+
g.uniform1i(loc(p, "u_target"), 2);
|
|
405
|
+
g.uniform1f(loc(p, "u_stiff"), this.stiffness);
|
|
406
|
+
g.uniform1f(loc(p, "u_damp"), this.damping);
|
|
407
|
+
g.uniform1f(loc(p, "u_dt"), Math.min(dt, 1 / 30));
|
|
408
|
+
g.bindVertexArray(vao());
|
|
409
|
+
g.drawArrays(g.TRIANGLE_STRIP, 0, 4);
|
|
410
|
+
g.bindVertexArray(null);
|
|
411
|
+
this.cur = dst;
|
|
412
|
+
}
|
|
413
|
+
current() {
|
|
414
|
+
return { tex: this.pos[this.cur], w: this.w, h: this.h };
|
|
415
|
+
}
|
|
416
|
+
/** Mean kinetic energy (velocity²) — the settle metric. */
|
|
417
|
+
energy() {
|
|
418
|
+
return reduceMeanSquare({ tex: this.vel[this.cur], w: this.w, h: this.h });
|
|
419
|
+
}
|
|
420
|
+
dispose() {
|
|
421
|
+
const g = gl();
|
|
422
|
+
g.deleteFramebuffer(this.springFbo);
|
|
423
|
+
for (const t of [...this.pos, ...this.vel, this.targetTex])
|
|
424
|
+
g.deleteTexture(t);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Cell, type Init, type Inner, type Val, type Writable } from "../signal.js";
|
|
2
|
+
import { Num } from "./num.js";
|
|
3
|
+
import type { Vec } from "./vec.js";
|
|
4
|
+
type V = {
|
|
5
|
+
a: number;
|
|
6
|
+
b: number;
|
|
7
|
+
c: number;
|
|
8
|
+
d: number;
|
|
9
|
+
e: number;
|
|
10
|
+
f: number;
|
|
11
|
+
};
|
|
12
|
+
type BoxV = {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
w: number;
|
|
16
|
+
h: number;
|
|
17
|
+
};
|
|
18
|
+
export declare const identity: () => V;
|
|
19
|
+
export declare const fromTranslate: (x: number, y: number) => V;
|
|
20
|
+
export declare const fromScale: (x: number, y: number) => V;
|
|
21
|
+
export declare const fromRotate: (angle: number) => V;
|
|
22
|
+
export declare const isIdentity: (m: V) => boolean;
|
|
23
|
+
export declare const equals: (m: V, n: V) => boolean;
|
|
24
|
+
export declare function multiply(a: V, b: V): V;
|
|
25
|
+
export declare function invert(m: V): V;
|
|
26
|
+
export declare const determinant: (m: V) => number;
|
|
27
|
+
export declare const transformPoint: (m: V, p: Inner<Vec>) => Inner<Vec>;
|
|
28
|
+
export declare function transformBox(m: V, b: BoxV): BoxV;
|
|
29
|
+
export declare function compose(t: Inner<Vec>, r: number, s: Inner<Vec>, pivot: Inner<Vec>): V;
|
|
30
|
+
export declare const toMatrixString: (m: V) => string;
|
|
31
|
+
export declare class Matrix extends Cell<V> {
|
|
32
|
+
static traits: {
|
|
33
|
+
equals: (m: V, n: V) => boolean;
|
|
34
|
+
};
|
|
35
|
+
readonly _t: typeof Matrix.traits;
|
|
36
|
+
constructor(v?: V);
|
|
37
|
+
multiply(b: Val<V>): this;
|
|
38
|
+
invert(): this;
|
|
39
|
+
get a(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
40
|
+
get b(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
41
|
+
get c(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
42
|
+
get d(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
43
|
+
get e(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
44
|
+
get f(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
45
|
+
get determinant(): Num;
|
|
46
|
+
}
|
|
47
|
+
/** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
|
|
48
|
+
* Each entry is a literal `number` (lifted to a fresh seed) or an existing
|
|
49
|
+
* `Writable<Num>` (identity passthrough). RO sources are rejected at the
|
|
50
|
+
* type level — use `Matrix.derive(...)` for reactive RO tracking, or
|
|
51
|
+
* `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
|
|
52
|
+
export declare function matrix(a?: Init<Num>, b?: Init<Num>, c?: Init<Num>, d?: Init<Num>, e?: Init<Num>, f?: Init<Num>): Writable<Matrix>;
|
|
53
|
+
export {};
|