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
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// color.ts — reactive RGBA color.
|
|
2
|
-
//
|
|
3
|
-
// Invertibles (`add`, `sub`, `scale`) return `: this` and ride on
|
|
4
|
-
// `Cell#lens(fwd, bwd)`. Chained calls compose into a lens chain.
|
|
5
1
|
import { tween } from "../../animation/index.js";
|
|
6
2
|
import { Cell, cachedDerive, derive, fieldLens, lazy, reader, readNow, } from "../cell.js";
|
|
7
3
|
import { Num, num } from "./num.js";
|
|
@@ -15,7 +11,7 @@ export const lerp = (a, b, t) => ({
|
|
|
15
11
|
a: a.a + (b.a - a.a) * t,
|
|
16
12
|
});
|
|
17
13
|
export const equals = (a, b) => a === b || (a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a);
|
|
18
|
-
/**
|
|
14
|
+
/** Euclidean distance in RGBA-space. */
|
|
19
15
|
export const metric = (a, b) => Math.hypot(a.r - b.r, a.g - b.g, a.b - b.b, a.a - b.a);
|
|
20
16
|
const linearImpl = { add, sub, scale };
|
|
21
17
|
const packImpl = {
|
|
@@ -78,22 +74,20 @@ export class Color extends Cell {
|
|
|
78
74
|
return `rgba(${r}, ${g}, ${b}, ${c.a})`;
|
|
79
75
|
}));
|
|
80
76
|
}
|
|
81
|
-
/** Tween-builder
|
|
77
|
+
/** Tween-builder. */
|
|
82
78
|
to(target, dur, ease) {
|
|
83
79
|
return tween(this, target, dur, ease);
|
|
84
80
|
}
|
|
85
81
|
}
|
|
86
|
-
/** Writable `Color` from RGB channels (alpha = 1). Each channel is a
|
|
87
|
-
*
|
|
88
|
-
* (identity passthrough). All-literal inputs take a fast path; mixed
|
|
89
|
-
* inputs build a lens so channel-writes round-trip to source. */
|
|
82
|
+
/** Writable `Color` from RGB channels (alpha = 1). Each channel is a literal
|
|
83
|
+
* or a `Writable<Num>`. */
|
|
90
84
|
export function rgb(r, g, b) {
|
|
91
85
|
if (typeof r === "number" && typeof g === "number" && typeof b === "number") {
|
|
92
86
|
return new Color({ r, g, b, a: 1 });
|
|
93
87
|
}
|
|
94
88
|
return Color.lens([num(r), num(g), num(b)], ([r, g, b]) => ({ r, g, b, a: 1 }), target => [target.r, target.g, target.b]);
|
|
95
89
|
}
|
|
96
|
-
/** Writable `Color` from RGBA channels.
|
|
90
|
+
/** Writable `Color` from RGBA channels. */
|
|
97
91
|
export function rgba(r, g, b, a) {
|
|
98
92
|
if (typeof r === "number" &&
|
|
99
93
|
typeof g === "number" &&
|
|
@@ -46,22 +46,21 @@ export declare class Field<T> extends Cell<FieldVal> {
|
|
|
46
46
|
* field as `u_src`; `u_texel` = 1/size is provided). One value is committed
|
|
47
47
|
* after the substeps, so the reactive graph sees a single new epoch/frame. */
|
|
48
48
|
evolve(frag: string, uniforms?: Record<string, number | readonly number[]>, steps?: number): void;
|
|
49
|
-
/** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`.
|
|
50
|
-
* raster interaction primitive — seed a sim, inject, paint density. */
|
|
49
|
+
/** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`. */
|
|
51
50
|
splat(x: number, y: number, r: number, value: T, strength?: number): void;
|
|
52
|
-
/** Whole-field mean as a read-only `T` cell.
|
|
53
|
-
*
|
|
51
|
+
/** Whole-field mean as a read-only `T` cell. Recomputes per epoch; one 1×1
|
|
52
|
+
* GPU readback. */
|
|
54
53
|
mean(): Read<T>;
|
|
55
54
|
/** Mean over a sub-rectangle (data pixels, reactive `box`) as a read-only
|
|
56
|
-
* `T` cell
|
|
55
|
+
* `T` cell. */
|
|
57
56
|
regionMean(box: Val<{
|
|
58
57
|
x: number;
|
|
59
58
|
y: number;
|
|
60
59
|
w: number;
|
|
61
60
|
h: number;
|
|
62
61
|
}>): Read<T>;
|
|
63
|
-
/** Render `channel` through a colormap to a read-only `Canvas
|
|
64
|
-
*
|
|
62
|
+
/** Render `channel` through a colormap to a read-only `Canvas`; re-renders
|
|
63
|
+
* per epoch. */
|
|
65
64
|
colormap(channel: number, stops: readonly ColorStop[]): Canvas;
|
|
66
65
|
}
|
|
67
66
|
/** Writable `Field<T>` of size `w×h`. `painter` fills each texel (returns a
|
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
// field.ts — dense, GPU-resident reactive field: a grid of `T` as ONE cell.
|
|
2
|
-
//
|
|
3
|
-
// A Field is a single reactive node, not one-per-texel: the value is a header
|
|
4
|
-
// {tex, w, h, epoch} (the Canvas handle trick, see gpu.ts), the graph compares
|
|
5
|
-
// `epoch`, and no pixel crosses the bus. So a 1000×1000 field costs the engine
|
|
6
|
-
// exactly what a 4×4 does — graph size is the lens-chain length, independent of
|
|
7
|
-
// resolution. Per-frame work is allocation-free: `evolve`/`splat` ping-pong
|
|
8
|
-
// through reused scratch textures and mint only the small header.
|
|
9
|
-
//
|
|
10
|
-
// Generic over the texel encoding via `Kind<T>` (channel `dim` + the `Pack`
|
|
11
|
-
// codec + the boundary cell class). The dense interior stays a flat RGBA32F
|
|
12
|
-
// texture forever; `T` (Num/Vec/Color) is instantiated ONLY at the boundary —
|
|
13
|
-
// `mean`, `regionMean` — never per-texel. `dim` (1/2/4) selects the channel
|
|
14
|
-
// mask: scalar (heatmap / SDF / density), vector (flow / gradient), colour
|
|
15
|
-
// (i.e. a Canvas).
|
|
16
|
-
//
|
|
17
|
-
// The point of interest is the GPU↔reactivity bridge. `evolve` runs a sim on
|
|
18
|
-
// its own clock (a raf/Anim loop), committing one new value per frame; the
|
|
19
|
-
// reductions (`mean`, `regionMean`) are ordinary read-only derived cells, so
|
|
20
|
-
// they recompute when the field's epoch changes and only *propagate* when their
|
|
21
|
-
// scalar actually moves. Reactive logic downstream (`density > 0.5` → effect)
|
|
22
|
-
// is thereby loosely coupled to time: it observes a continuously-running GPU
|
|
23
|
-
// simulation without knowing a frame exists.
|
|
24
1
|
import { Cell, reader } from "../cell.js";
|
|
25
2
|
import { Canvas, stamp as canvasStamp } from "./canvas.js";
|
|
26
3
|
import { Color } from "./color.js";
|
|
@@ -70,8 +47,7 @@ void main() {
|
|
|
70
47
|
/** GLSL float literal (always carries a decimal point). */
|
|
71
48
|
const glf = (n) => (Number.isInteger(n) ? `${n}.0` : String(n));
|
|
72
49
|
const glv3 = (c) => `${glf(c[0])}, ${glf(c[1])}, ${glf(c[2])}`;
|
|
73
|
-
/** Build a piecewise-linear ramp shader over `channel
|
|
74
|
-
* cached by source string in gpu.ts's program cache). */
|
|
50
|
+
/** Build a piecewise-linear ramp shader over `channel`. */
|
|
75
51
|
function rampFrag(channel, stops) {
|
|
76
52
|
const ch = "rgba"[channel] ?? "r";
|
|
77
53
|
let body = ` vec3 c = vec3(${glv3(stops[0][1])});\n`;
|
|
@@ -87,9 +63,9 @@ void main() {
|
|
|
87
63
|
${body} o = vec4(c, 1.0);
|
|
88
64
|
}`;
|
|
89
65
|
}
|
|
90
|
-
/** Read-only typed view via the kind's boundary class
|
|
91
|
-
*
|
|
92
|
-
*
|
|
66
|
+
/** Read-only typed view via the kind's boundary class. The cast bypasses the
|
|
67
|
+
* polymorphic `this` rejecting the abstract `Ctor<T>`; `k.cls` is concrete at
|
|
68
|
+
* runtime. */
|
|
93
69
|
function deriveT(k, parent, fn) {
|
|
94
70
|
const d = k.cls;
|
|
95
71
|
return d.derive(parent, fn);
|
|
@@ -152,8 +128,7 @@ export class Field extends Cell {
|
|
|
152
128
|
if (last)
|
|
153
129
|
this.value = stamp(last.tex, w, h);
|
|
154
130
|
}
|
|
155
|
-
/** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`.
|
|
156
|
-
* raster interaction primitive — seed a sim, inject, paint density. */
|
|
131
|
+
/** Stamp a Gaussian disc of `value` at data pixel `(x, y)`, radius `r`. */
|
|
157
132
|
splat(x, y, r, value, strength = 1) {
|
|
158
133
|
const ping = this.pingTex();
|
|
159
134
|
const v = this.peek();
|
|
@@ -169,14 +144,14 @@ export class Field extends Cell {
|
|
|
169
144
|
});
|
|
170
145
|
this.value = stamp(dst.tex, v.w, v.h);
|
|
171
146
|
}
|
|
172
|
-
/** Whole-field mean as a read-only `T` cell.
|
|
173
|
-
*
|
|
147
|
+
/** Whole-field mean as a read-only `T` cell. Recomputes per epoch; one 1×1
|
|
148
|
+
* GPU readback. */
|
|
174
149
|
mean() {
|
|
175
150
|
const k = this.kind;
|
|
176
151
|
return deriveT(k, this, v => packT(k, reduceMean({ tex: v.tex, w: v.w, h: v.h })));
|
|
177
152
|
}
|
|
178
153
|
/** Mean over a sub-rectangle (data pixels, reactive `box`) as a read-only
|
|
179
|
-
* `T` cell
|
|
154
|
+
* `T` cell. */
|
|
180
155
|
regionMean(box) {
|
|
181
156
|
const k = this.kind;
|
|
182
157
|
const bf = reader(box);
|
|
@@ -197,8 +172,8 @@ export class Field extends Cell {
|
|
|
197
172
|
return packT(k, [m[0] / cov, m[1] / cov, m[2] / cov, 1]);
|
|
198
173
|
});
|
|
199
174
|
}
|
|
200
|
-
/** Render `channel` through a colormap to a read-only `Canvas
|
|
201
|
-
*
|
|
175
|
+
/** Render `channel` through a colormap to a read-only `Canvas`; re-renders
|
|
176
|
+
* per epoch. */
|
|
202
177
|
colormap(channel, stops) {
|
|
203
178
|
const frag = rampFrag(channel, stops);
|
|
204
179
|
const sc = scratch();
|
|
@@ -8,8 +8,7 @@ export declare class Flags<K extends string> extends Cell<number> {
|
|
|
8
8
|
};
|
|
9
9
|
readonly _t: typeof Flags.traits;
|
|
10
10
|
constructor(names: readonly K[], v?: number);
|
|
11
|
-
/**
|
|
12
|
-
* Cached per name so repeated calls return the same lens. */
|
|
11
|
+
/** Cached writable bit lens for `name`. */
|
|
13
12
|
flag<F extends K>(name: F): Writable<Bool>;
|
|
14
13
|
}
|
|
15
14
|
/** Writable `Flags` from variadic bit names (bit `i` = the i-th name), or
|
|
@@ -1,18 +1,3 @@
|
|
|
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
1
|
import { Cell } from "../cell.js";
|
|
17
2
|
import { Bool } from "./bool.js";
|
|
18
3
|
const equals = (a, b) => a === b;
|
|
@@ -24,8 +9,7 @@ export class Flags extends Cell {
|
|
|
24
9
|
super(v, { equals });
|
|
25
10
|
this.names = names;
|
|
26
11
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Cached per name so repeated calls return the same lens. */
|
|
12
|
+
/** Cached writable bit lens for `name`. */
|
|
29
13
|
flag(name) {
|
|
30
14
|
let lens = this.#bits.get(name);
|
|
31
15
|
if (lens === undefined) {
|
|
@@ -11,13 +11,10 @@ export interface Tex {
|
|
|
11
11
|
* downgraded to NEAREST where `OES_texture_float_linear` is unavailable. */
|
|
12
12
|
export declare function newTex(w: number, h: number, data?: Float32Array | null, linear?: boolean): WebGLTexture;
|
|
13
13
|
export declare function disposeTex(t: WebGLTexture): void;
|
|
14
|
-
/** A reusable owned texture, reallocated on size change
|
|
15
|
-
* the CPU scratch buffer. */
|
|
14
|
+
/** A reusable owned texture, reallocated on size change. */
|
|
16
15
|
export declare function scratch(): (w: number, h: number) => Tex;
|
|
17
16
|
/** A feedback-safe scratch pair: returns an owned texture guaranteed not to
|
|
18
|
-
* equal `avoid
|
|
19
|
-
* same texture. Needed for backward passes that produce a root cell's next
|
|
20
|
-
* value (whose current value may be this same scratch). */
|
|
17
|
+
* equal `avoid`, so a pass never reads and writes the same texture. */
|
|
21
18
|
export declare function scratch2(): (w: number, h: number, avoid: WebGLTexture | null) => Tex;
|
|
22
19
|
/** Copy `srcTex` into `dst` (distinct textures). */
|
|
23
20
|
export declare function copy(srcTex: WebGLTexture, dst: Tex): void;
|
|
@@ -35,7 +32,7 @@ export declare function pass(frag: string, target: Tex, setup: (s: Setup) => voi
|
|
|
35
32
|
/** Mean of all texels (RGBA, 0–1) via GPU pyramid reduction; one 1×1
|
|
36
33
|
* readback. */
|
|
37
34
|
export declare function reduceMean(src: Tex): [number, number, number, number];
|
|
38
|
-
/** Mean of squared texels
|
|
35
|
+
/** Mean of squared texels (spring settle metric). */
|
|
39
36
|
export declare function reduceMeanSquare(src: Tex): number;
|
|
40
37
|
/** Render `src` to a 2D canvas (GPU draw + drawImage; no CPU readback). */
|
|
41
38
|
export declare function blit(src: Tex, ctx: CanvasRenderingContext2D): void;
|
|
@@ -61,10 +58,9 @@ export declare class Spring {
|
|
|
61
58
|
private springFbo;
|
|
62
59
|
constructor(w: number, h: number, opts?: SpringOpts);
|
|
63
60
|
private clearTex;
|
|
64
|
-
/** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* texture — illegal GL feedback, and a no-op anyway. */
|
|
61
|
+
/** Snap position + target to `srcTex`, zero the velocity. `srcTex` may alias
|
|
62
|
+
* one of our own position textures, so skip a copy that would read and write
|
|
63
|
+
* the same texture (illegal GL feedback). */
|
|
68
64
|
seed(srcTex: WebGLTexture): void;
|
|
69
65
|
/** Retarget (keeps current position + velocity). */
|
|
70
66
|
setTarget(srcTex: WebGLTexture): void;
|
package/dist/core/values/gpu.js
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
//
|
|
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.
|
|
1
|
+
// Coordinate convention: data row 0 lives at texture v=0, so every compute pass
|
|
2
|
+
// works in one consistent data space; only `blit` flips Y for the screen.
|
|
13
3
|
let _gl = null;
|
|
14
4
|
/** Lazily create the shared WebGL2 context (browser-only). */
|
|
15
5
|
export function gl() {
|
|
@@ -103,8 +93,7 @@ export function newTex(w, h, data = null, linear = false) {
|
|
|
103
93
|
export function disposeTex(t) {
|
|
104
94
|
gl().deleteTexture(t);
|
|
105
95
|
}
|
|
106
|
-
/** A reusable owned texture, reallocated on size change
|
|
107
|
-
* the CPU scratch buffer. */
|
|
96
|
+
/** A reusable owned texture, reallocated on size change. */
|
|
108
97
|
export function scratch() {
|
|
109
98
|
let t = null;
|
|
110
99
|
let cw = 0;
|
|
@@ -121,9 +110,7 @@ export function scratch() {
|
|
|
121
110
|
};
|
|
122
111
|
}
|
|
123
112
|
/** A feedback-safe scratch pair: returns an owned texture guaranteed not to
|
|
124
|
-
* equal `avoid
|
|
125
|
-
* same texture. Needed for backward passes that produce a root cell's next
|
|
126
|
-
* value (whose current value may be this same scratch). */
|
|
113
|
+
* equal `avoid`, so a pass never reads and writes the same texture. */
|
|
127
114
|
export function scratch2() {
|
|
128
115
|
let a = null;
|
|
129
116
|
let b = null;
|
|
@@ -246,7 +233,7 @@ export function reduceMean(src) {
|
|
|
246
233
|
return [buf[0] / n, buf[1] / n, buf[2] / n, buf[3] / n];
|
|
247
234
|
}
|
|
248
235
|
let _sq = null;
|
|
249
|
-
/** Mean of squared texels
|
|
236
|
+
/** Mean of squared texels (spring settle metric). */
|
|
250
237
|
export function reduceMeanSquare(src) {
|
|
251
238
|
if (!_sq || _sq.w !== src.w || _sq.h !== src.h) {
|
|
252
239
|
if (_sq)
|
|
@@ -373,10 +360,9 @@ export class Spring {
|
|
|
373
360
|
g.clearColor(0, 0, 0, 0);
|
|
374
361
|
g.clear(g.COLOR_BUFFER_BIT);
|
|
375
362
|
}
|
|
376
|
-
/** Snap position + target to `srcTex`, zero the velocity. `srcTex` may
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
* texture — illegal GL feedback, and a no-op anyway. */
|
|
363
|
+
/** Snap position + target to `srcTex`, zero the velocity. `srcTex` may alias
|
|
364
|
+
* one of our own position textures, so skip a copy that would read and write
|
|
365
|
+
* the same texture (illegal GL feedback). */
|
|
380
366
|
seed(srcTex) {
|
|
381
367
|
for (const tex of [this.pos[0], this.pos[1], this.targetTex])
|
|
382
368
|
if (srcTex !== tex)
|
|
@@ -45,9 +45,7 @@ export declare class Matrix extends Cell<V> {
|
|
|
45
45
|
get determinant(): Num;
|
|
46
46
|
}
|
|
47
47
|
/** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
|
|
48
|
-
* Each entry is a literal
|
|
49
|
-
*
|
|
50
|
-
* type level — use `Matrix.derive(...)` for reactive RO tracking, or
|
|
51
|
-
* `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
|
|
48
|
+
* Each entry is a literal (new cell) or existing writable (passed through);
|
|
49
|
+
* for read-only sources use `Matrix.derive`. Lock an entry with `Num.pin`. */
|
|
52
50
|
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
51
|
export {};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// matrix.ts — reactive 2D affine matrix (SVG/Canvas convention).
|
|
2
|
-
//
|
|
3
|
-
// Sparse-trait: only `equals`. Element-wise linear combine / lerp don't
|
|
4
|
-
// decompose for matrices, so `spring`/`tween`/`mean` reject Matrix at
|
|
5
|
-
// compile time. Two invertibles, both `: this` via `Cell#lens`:
|
|
6
|
-
// - `multiply(b)` — inverse multiplies by `invert(b)`
|
|
7
|
-
// - `invert()` — its own inverse
|
|
8
1
|
import { Cell, cachedDerive, fieldLens, reader, } from "../cell.js";
|
|
9
2
|
import { Num, num } from "./num.js";
|
|
10
3
|
export const identity = () => ({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 });
|
|
@@ -115,10 +108,8 @@ export class Matrix extends Cell {
|
|
|
115
108
|
}
|
|
116
109
|
}
|
|
117
110
|
/** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
|
|
118
|
-
* Each entry is a literal
|
|
119
|
-
*
|
|
120
|
-
* type level — use `Matrix.derive(...)` for reactive RO tracking, or
|
|
121
|
-
* `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
|
|
111
|
+
* Each entry is a literal (new cell) or existing writable (passed through);
|
|
112
|
+
* for read-only sources use `Matrix.derive`. Lock an entry with `Num.pin`. */
|
|
122
113
|
export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
|
|
123
114
|
if (typeof a === "number" &&
|
|
124
115
|
typeof b === "number" &&
|
|
@@ -134,6 +125,5 @@ export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
|
|
|
134
125
|
const dN = num(d);
|
|
135
126
|
const eN = num(e);
|
|
136
127
|
const fN = num(f);
|
|
137
|
-
// The view fully reconstructs all 6 cells (1-arg bwd ⇒ no source read).
|
|
138
128
|
return Matrix.lens([aN, bN, cN, dN, eN, fN], ([a, b, c, d, e, f]) => ({ a, b, c, d, e, f }), v => [v.a, v.b, v.c, v.d, v.e, v.f]);
|
|
139
129
|
}
|
|
@@ -22,46 +22,37 @@ export declare class Num extends Cell<V> {
|
|
|
22
22
|
add(b: Val<V>): this;
|
|
23
23
|
sub(b: Val<V>): this;
|
|
24
24
|
scale(k: Val<number>): this;
|
|
25
|
-
/** Affine `v ↦ k·v + off`. Invertible
|
|
26
|
-
* for `.scale(k).add(off)`. */
|
|
25
|
+
/** Affine map `v ↦ k·v + off`. Invertible when k ≠ 0. */
|
|
27
26
|
affine(k: Val<number>, off: Val<number>): this;
|
|
28
|
-
/** `
|
|
29
|
-
*
|
|
30
|
-
* pre-image nearest the current source — the drag stays on its branch. */
|
|
27
|
+
/** Sine of `this` (radians). The inverse is multi-valued; a write picks the
|
|
28
|
+
* angle nearest the current value, so a drag stays on its branch. */
|
|
31
29
|
sin(): this;
|
|
32
|
-
/**
|
|
30
|
+
/** Natural exponential; inverts via log. */
|
|
33
31
|
exp(): this;
|
|
34
|
-
/**
|
|
35
|
-
* the range reads back clamped, not as written). */
|
|
32
|
+
/** Clamp to `[lo, hi]`. Lossy: a write outside the range reads back clamped. */
|
|
36
33
|
clamp(lo: Val<V>, hi: Val<V>): this;
|
|
37
|
-
/**
|
|
34
|
+
/** Snap reads and writes to the nearest multiple of `step` (lossy). */
|
|
38
35
|
quantize(step: Val<number>): this;
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* angle never jumps a full revolution. The 2-arg bwd is arity-detected
|
|
42
|
-
* as stateful, threading the accumulated value through `s`. */
|
|
36
|
+
/** Reads pass through; a write picks the value closest to the current one
|
|
37
|
+
* modulo `period`, so dragging an angle never jumps a full turn. */
|
|
43
38
|
cyclic(period: Val<number>): this;
|
|
44
|
-
/** `this > t` as a Bool. Flipping
|
|
45
|
-
*
|
|
39
|
+
/** `this > t` as a Bool. Flipping it bumps the source across the
|
|
40
|
+
* threshold by `eps`. */
|
|
46
41
|
greaterThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
|
|
47
|
-
/** `this < t
|
|
42
|
+
/** `this < t` as a Bool. */
|
|
48
43
|
lessThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
|
|
49
|
-
/** `round(this)
|
|
50
|
-
*
|
|
51
|
-
* multiple of `d`; to make non-divisible, bump by `+1`; no-op when
|
|
52
|
-
* the class already matches. */
|
|
44
|
+
/** True when `round(this)` is divisible by `d`. A write snaps to the nearest
|
|
45
|
+
* multiple of `d` to make it divisible, or bumps by 1 to make it not. */
|
|
53
46
|
divisibleBy<T extends Num>(this: T, d: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
|
|
54
|
-
/**
|
|
47
|
+
/** True when even. */
|
|
55
48
|
get isEven(): this extends WritableBrand ? Writable<Bool> : Bool;
|
|
56
|
-
/**
|
|
49
|
+
/** True when odd. */
|
|
57
50
|
get isOdd(): this extends WritableBrand ? Writable<Bool> : Bool;
|
|
58
|
-
/** Tween-builder
|
|
59
|
-
* receivers. */
|
|
51
|
+
/** Tween-builder. */
|
|
60
52
|
to(this: Writable<Num>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
|
|
61
53
|
}
|
|
62
|
-
/** Writable `Num
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* the permissive lift over any `Val<number>`. */
|
|
54
|
+
/** Writable `Num` from a literal (new cell) or existing writable (passed
|
|
55
|
+
* through). For read-only sources use `Num.derive`, or `Num.coerce` to lift
|
|
56
|
+
* any `Val<number>`. */
|
|
66
57
|
export declare function num(v?: Init<Num>): Writable<Num>;
|
|
67
58
|
export {};
|
package/dist/core/values/num.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// num.ts — reactive scalar.
|
|
2
|
-
//
|
|
3
|
-
// Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
|
|
4
|
-
// chained calls compose into a lens chain.
|
|
5
1
|
import { tween } from "../../animation/index.js";
|
|
6
2
|
import { Cell, lazy, reader, } from "../cell.js";
|
|
7
3
|
import { Bool } from "./bool.js";
|
|
@@ -47,16 +43,14 @@ export class Num extends Cell {
|
|
|
47
43
|
const kf = reader(k);
|
|
48
44
|
return this.lens(v => v * kf(), n => n / kf());
|
|
49
45
|
}
|
|
50
|
-
/** Affine `v ↦ k·v + off`. Invertible
|
|
51
|
-
* for `.scale(k).add(off)`. */
|
|
46
|
+
/** Affine map `v ↦ k·v + off`. Invertible when k ≠ 0. */
|
|
52
47
|
affine(k, off) {
|
|
53
48
|
const kf = reader(k);
|
|
54
49
|
const of = reader(off);
|
|
55
50
|
return this.lens(v => v * kf() + of(), n => (n - of()) / kf());
|
|
56
51
|
}
|
|
57
|
-
/** `
|
|
58
|
-
*
|
|
59
|
-
* pre-image nearest the current source — the drag stays on its branch. */
|
|
52
|
+
/** Sine of `this` (radians). The inverse is multi-valued; a write picks the
|
|
53
|
+
* angle nearest the current value, so a drag stays on its branch. */
|
|
60
54
|
sin() {
|
|
61
55
|
return this.lens(v => Math.sin(v), (target, s) => {
|
|
62
56
|
const p = Math.asin(unit(target));
|
|
@@ -65,12 +59,11 @@ export class Num extends Cell {
|
|
|
65
59
|
return Math.abs(a - s) <= Math.abs(b - s) ? a : b;
|
|
66
60
|
});
|
|
67
61
|
}
|
|
68
|
-
/**
|
|
62
|
+
/** Natural exponential; inverts via log. */
|
|
69
63
|
exp() {
|
|
70
64
|
return this.lens(v => Math.exp(v), n => Math.log(n));
|
|
71
65
|
}
|
|
72
|
-
/**
|
|
73
|
-
* the range reads back clamped, not as written). */
|
|
66
|
+
/** Clamp to `[lo, hi]`. Lossy: a write outside the range reads back clamped. */
|
|
74
67
|
clamp(lo, hi) {
|
|
75
68
|
const lf = reader(lo);
|
|
76
69
|
const hf = reader(hi);
|
|
@@ -78,31 +71,29 @@ export class Num extends Cell {
|
|
|
78
71
|
const l = lf(), h = hf();
|
|
79
72
|
return v < l ? l : v > h ? h : v;
|
|
80
73
|
};
|
|
81
|
-
//
|
|
82
|
-
//
|
|
74
|
+
// If the clamped write matches the current view, keep the source
|
|
75
|
+
// (preserves an off-range source value).
|
|
83
76
|
return this.lens(c, (v, s) => {
|
|
84
77
|
const cv = c(v);
|
|
85
78
|
return cv === c(s) ? s : cv;
|
|
86
79
|
});
|
|
87
80
|
}
|
|
88
|
-
/**
|
|
81
|
+
/** Snap reads and writes to the nearest multiple of `step` (lossy). */
|
|
89
82
|
quantize(step) {
|
|
90
83
|
const sf = reader(step);
|
|
91
84
|
const q = (v) => {
|
|
92
85
|
const s = sf();
|
|
93
86
|
return Math.round(v / s) * s;
|
|
94
87
|
};
|
|
95
|
-
//
|
|
96
|
-
//
|
|
88
|
+
// If the write snaps to the current bucket, keep the source
|
|
89
|
+
// (preserves an off-grid remainder).
|
|
97
90
|
return this.lens(q, (v, src) => {
|
|
98
91
|
const qv = q(v);
|
|
99
92
|
return qv === q(src) ? src : qv;
|
|
100
93
|
});
|
|
101
94
|
}
|
|
102
|
-
/**
|
|
103
|
-
*
|
|
104
|
-
* angle never jumps a full revolution. The 2-arg bwd is arity-detected
|
|
105
|
-
* as stateful, threading the accumulated value through `s`. */
|
|
95
|
+
/** Reads pass through; a write picks the value closest to the current one
|
|
96
|
+
* modulo `period`, so dragging an angle never jumps a full turn. */
|
|
106
97
|
cyclic(period) {
|
|
107
98
|
const pf = reader(period);
|
|
108
99
|
return this.lens(v => v, (v, s) => {
|
|
@@ -111,13 +102,8 @@ export class Num extends Cell {
|
|
|
111
102
|
return s + delta - p * Math.round(delta / p);
|
|
112
103
|
});
|
|
113
104
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// Cross-type quotient lenses projecting Num through a boolean
|
|
117
|
-
// predicate. Conditional return type: writable receiver yields
|
|
118
|
-
// `Writable<Bool>`, RO receiver yields RO `Bool`.
|
|
119
|
-
/** `this > t` as a Bool. Flipping the view bumps the source across
|
|
120
|
-
* the threshold by `eps`. */
|
|
105
|
+
/** `this > t` as a Bool. Flipping it bumps the source across the
|
|
106
|
+
* threshold by `eps`. */
|
|
121
107
|
greaterThan(t, eps = 1e-6) {
|
|
122
108
|
const tf = reader(t);
|
|
123
109
|
const ef = reader(eps);
|
|
@@ -128,7 +114,7 @@ export class Num extends Cell {
|
|
|
128
114
|
return target ? th + ef() : th - ef();
|
|
129
115
|
});
|
|
130
116
|
}
|
|
131
|
-
/** `this < t
|
|
117
|
+
/** `this < t` as a Bool. */
|
|
132
118
|
lessThan(t, eps = 1e-6) {
|
|
133
119
|
const tf = reader(t);
|
|
134
120
|
const ef = reader(eps);
|
|
@@ -139,10 +125,8 @@ export class Num extends Cell {
|
|
|
139
125
|
return target ? th - ef() : th + ef();
|
|
140
126
|
});
|
|
141
127
|
}
|
|
142
|
-
/** `round(this)
|
|
143
|
-
*
|
|
144
|
-
* multiple of `d`; to make non-divisible, bump by `+1`; no-op when
|
|
145
|
-
* the class already matches. */
|
|
128
|
+
/** True when `round(this)` is divisible by `d`. A write snaps to the nearest
|
|
129
|
+
* multiple of `d` to make it divisible, or bumps by 1 to make it not. */
|
|
146
130
|
divisibleBy(d) {
|
|
147
131
|
const df = reader(d);
|
|
148
132
|
return Bool.lens(this, v => Math.round(v) % df() === 0, (target, current) => {
|
|
@@ -161,24 +145,22 @@ export class Num extends Cell {
|
|
|
161
145
|
return r + 1;
|
|
162
146
|
});
|
|
163
147
|
}
|
|
164
|
-
/**
|
|
148
|
+
/** True when even. */
|
|
165
149
|
get isEven() {
|
|
166
150
|
return lazy(this, "isEven", () => this.divisibleBy(2));
|
|
167
151
|
}
|
|
168
|
-
/**
|
|
152
|
+
/** True when odd. */
|
|
169
153
|
get isOdd() {
|
|
170
154
|
return lazy(this, "isOdd", () => this.divisibleBy(2).not());
|
|
171
155
|
}
|
|
172
|
-
/** Tween-builder
|
|
173
|
-
* receivers. */
|
|
156
|
+
/** Tween-builder. */
|
|
174
157
|
to(target, dur, ease) {
|
|
175
158
|
return tween(this, target, dur, ease);
|
|
176
159
|
}
|
|
177
160
|
}
|
|
178
|
-
/** Writable `Num
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* the permissive lift over any `Val<number>`. */
|
|
161
|
+
/** Writable `Num` from a literal (new cell) or existing writable (passed
|
|
162
|
+
* through). For read-only sources use `Num.derive`, or `Num.coerce` to lift
|
|
163
|
+
* any `Val<number>`. */
|
|
182
164
|
export function num(v = 0) {
|
|
183
165
|
if (v instanceof Num)
|
|
184
166
|
return v;
|
|
@@ -27,9 +27,7 @@ export declare class Pose extends Cell<V> {
|
|
|
27
27
|
get y(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
28
28
|
get theta(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
29
29
|
}
|
|
30
|
-
/** Writable `Pose
|
|
31
|
-
* through
|
|
32
|
-
* `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
33
|
-
* snapshot. */
|
|
30
|
+
/** Writable `Pose` from a literal (new cell) or existing writable (passed
|
|
31
|
+
* through). For read-only sources use `Pose.derive`. */
|
|
34
32
|
export declare function pose(v?: Init<Pose>): Writable<Pose>;
|
|
35
33
|
export {};
|
package/dist/core/values/pose.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
// pose.ts — reactive 2D rigid-body pose: { x, y, theta }.
|
|
2
|
-
//
|
|
3
|
-
// Single source of truth for a rigid body. The solver binds the cell as
|
|
4
|
-
// a 3-DOF block and writes back through it, so renderers, drag handlers,
|
|
5
|
-
// IK, and physics all observe the same value. Vec / Num lenses compose
|
|
6
|
-
// for consumers that care only about translation or rotation.
|
|
7
1
|
import { Cell, fieldLens } from "../cell.js";
|
|
8
2
|
import { Num } from "./num.js";
|
|
9
3
|
export const add = (a, b) => ({
|
|
@@ -38,8 +32,7 @@ const packImpl = {
|
|
|
38
32
|
},
|
|
39
33
|
write: (a, o) => ({ x: a[o], y: a[o + 1], theta: a[o + 2] }),
|
|
40
34
|
};
|
|
41
|
-
|
|
42
|
-
* scale-about-pivot scales position, orientation untouched. */
|
|
35
|
+
// rotateAbout moves position and adds dθ; scaleAbout scales position, leaves θ.
|
|
43
36
|
const pivotalImpl = {
|
|
44
37
|
rotateAbout: (v, p, dθ) => {
|
|
45
38
|
const cos = Math.cos(dθ);
|
|
@@ -80,10 +73,8 @@ export class Pose extends Cell {
|
|
|
80
73
|
return fieldLens(this, "theta", Num);
|
|
81
74
|
}
|
|
82
75
|
}
|
|
83
|
-
/** Writable `Pose
|
|
84
|
-
* through
|
|
85
|
-
* `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
86
|
-
* snapshot. */
|
|
76
|
+
/** Writable `Pose` from a literal (new cell) or existing writable (passed
|
|
77
|
+
* through). For read-only sources use `Pose.derive`. */
|
|
87
78
|
export function pose(v = { x: 0, y: 0, theta: 0 }) {
|
|
88
79
|
if (v instanceof Pose)
|
|
89
80
|
return v;
|