bireactive 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -7
- package/dist/automerge/doc-cell.d.ts +20 -0
- package/dist/automerge/doc-cell.js +80 -0
- package/dist/automerge/index.d.ts +3 -0
- package/dist/automerge/index.js +12 -0
- package/dist/automerge/reconcile.d.ts +5 -0
- package/dist/automerge/reconcile.js +63 -0
- package/dist/core/_counts.d.ts +48 -0
- package/dist/core/_counts.js +51 -0
- package/dist/core/cell.d.ts +148 -112
- package/dist/core/cell.js +945 -768
- package/dist/core/debug.d.ts +25 -0
- package/dist/core/debug.js +121 -0
- package/dist/core/derived-geometry.js +4 -7
- package/dist/core/index.d.ts +9 -2
- package/dist/core/index.js +8 -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 +6 -6
- package/dist/core/lenses/index.js +6 -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} +226 -84
- package/dist/core/lenses/snap.d.ts +18 -0
- package/dist/core/lenses/snap.js +138 -0
- 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.d.ts +13 -0
- package/dist/core/optic.js +39 -0
- package/dist/core/optics.d.ts +10 -0
- package/dist/core/optics.js +26 -0
- package/dist/core/store.d.ts +9 -0
- package/dist/core/store.js +77 -0
- 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 +11 -28
- 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 +32 -66
- 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/formats/lens.js +6 -9
- package/dist/index.d.ts +0 -11
- package/dist/index.js +1 -11
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.js +5 -0
- package/dist/jsx-runtime.d.ts +54 -0
- package/dist/jsx-runtime.js +219 -0
- package/dist/schema/lens.js +5 -5
- package/dist/shapes/drag-behaviors.d.ts +56 -0
- package/dist/shapes/drag-behaviors.js +102 -0
- package/dist/shapes/drag-spec.d.ts +52 -0
- package/dist/shapes/drag-spec.js +112 -0
- package/dist/shapes/index.d.ts +3 -1
- package/dist/shapes/index.js +3 -1
- package/dist/shapes/interaction.d.ts +2 -3
- package/dist/shapes/interaction.js +77 -56
- package/dist/shapes/label.js +6 -0
- package/dist/shapes/layout.d.ts +47 -1
- package/dist/shapes/layout.js +59 -1
- package/package.json +22 -1
- 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
package/dist/core/values/box.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// box.ts — reactive axis-aligned rectangle.
|
|
2
|
-
//
|
|
3
|
-
// Invertibles (`add`, `sub`, `scale`, `expand`) return `: this` and ride
|
|
4
|
-
// on `Cell#lens(fwd, bwd)`. Chained calls compose into a lens chain.
|
|
5
1
|
import { tween } from "../../animation/index.js";
|
|
6
2
|
import { Cell, cachedDerive, fieldLens, isReadonly, lazy, reader, readNow, SKIP, } from "../cell.js";
|
|
7
3
|
import { Bool } from "./bool.js";
|
|
@@ -17,7 +13,7 @@ export const lerp = (a, b, t) => ({
|
|
|
17
13
|
h: a.h + (b.h - a.h) * t,
|
|
18
14
|
});
|
|
19
15
|
export const equals = (a, b) => a === b || (a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h);
|
|
20
|
-
/**
|
|
16
|
+
/** Euclidean distance over (x, y, w, h). */
|
|
21
17
|
export const metric = (a, b) => Math.hypot(a.x - b.x, a.y - b.y, a.w - b.w, a.h - b.h);
|
|
22
18
|
export const expand = (b, n) => ({
|
|
23
19
|
x: b.x - n,
|
|
@@ -26,15 +22,14 @@ export const expand = (b, n) => ({
|
|
|
26
22
|
h: b.h + 2 * n,
|
|
27
23
|
});
|
|
28
24
|
export const contains = (b, p) => p.x >= b.x && p.x <= b.x + b.w && p.y >= b.y && p.y <= b.y + b.h;
|
|
29
|
-
/** Closest point inside `b` to `p
|
|
30
|
-
*
|
|
25
|
+
/** Closest point inside `b` to `p`; already-inside is identity, outside snaps
|
|
26
|
+
* to the nearest boundary point. */
|
|
31
27
|
export const clampToBox = (p, b) => ({
|
|
32
28
|
x: Math.max(b.x, Math.min(b.x + b.w, p.x)),
|
|
33
29
|
y: Math.max(b.y, Math.min(b.y + b.h, p.y)),
|
|
34
30
|
});
|
|
35
|
-
/** Closest point strictly outside `b` to `p`, displaced past the nearest
|
|
36
|
-
*
|
|
37
|
-
* false-side bwd. */
|
|
31
|
+
/** Closest point strictly outside `b` to `p`, displaced past the nearest edge
|
|
32
|
+
* by `eps`; already-outside is identity. */
|
|
38
33
|
export const ejectFromBox = (p, b, eps = 1e-6) => {
|
|
39
34
|
if (!contains(b, p))
|
|
40
35
|
return p;
|
|
@@ -70,7 +65,7 @@ export function union(...bs) {
|
|
|
70
65
|
}
|
|
71
66
|
return { x: xMin, y: yMin, w: xMax - xMin, h: yMax - yMin };
|
|
72
67
|
}
|
|
73
|
-
/** Perimeter point on a
|
|
68
|
+
/** Perimeter point on a box facing `toward`. */
|
|
74
69
|
export function edgeFrom(b, toward) {
|
|
75
70
|
const cx = b.x + b.w / 2;
|
|
76
71
|
const cy = b.y + b.h / 2;
|
|
@@ -122,18 +117,16 @@ export class Box extends Cell {
|
|
|
122
117
|
lerp(b, t) {
|
|
123
118
|
return Box.derive(() => lerp(this.value, readNow(b), readNow(t)));
|
|
124
119
|
}
|
|
125
|
-
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* nearest edge by `eps`. Literal / RO inputs yield a bare RO `Bool`. */
|
|
120
|
+
/** True when `p` is inside the box. A writable `Vec` yields a `Writable<Bool>`:
|
|
121
|
+
* flipping it clamps `p` to the nearest in-box point (`true`) or ejects it
|
|
122
|
+
* past the nearest edge (`false`). Literal/RO inputs yield a read-only `Bool`. */
|
|
129
123
|
contains(p) {
|
|
130
124
|
if (p instanceof Vec) {
|
|
131
|
-
//
|
|
132
|
-
// writable lenses accept write-back.
|
|
125
|
+
// RO Vec has no backward path; only writable Vecs accept write-back.
|
|
133
126
|
if (!isReadonly(p)) {
|
|
134
|
-
// `.bind(Bool)` + cast
|
|
135
|
-
// mapped-tuple inference over the full class types
|
|
136
|
-
//
|
|
127
|
+
// `.bind(Bool)` + cast sidesteps the generic overloads, whose
|
|
128
|
+
// mapped-tuple inference over the full class types blows the
|
|
129
|
+
// instantiation depth.
|
|
137
130
|
const mk = Bool.lens.bind(Bool);
|
|
138
131
|
return mk([this, p], vals => contains(vals[0], vals[1]), (target, vals) => {
|
|
139
132
|
const [b, v] = vals;
|
|
@@ -160,15 +153,12 @@ export class Box extends Cell {
|
|
|
160
153
|
get area() {
|
|
161
154
|
return cachedDerive(this, "area", Num, b => b.w * b.h);
|
|
162
155
|
}
|
|
163
|
-
/** Vec at parametric (u, v)
|
|
164
|
-
*
|
|
165
|
-
* (`.center`, `.top`, …) for stable identity. */
|
|
156
|
+
/** Vec at parametric `(u, v)` in `[0,1]²`. Not memoised; use the named edge
|
|
157
|
+
* getters (`.center`, `.top`, …) for stable identity. */
|
|
166
158
|
at(u, v) {
|
|
167
159
|
return Vec.derive(this, b => ({ x: b.x + u * b.w, y: b.y + v * b.h }));
|
|
168
160
|
}
|
|
169
|
-
// Named edges
|
|
170
|
-
// so subscribers always see the same Vec. `lazy()` directly since
|
|
171
|
-
// `at()` already returns a Vec.
|
|
161
|
+
// Named edges: memoised views over `at(u, v)` with stable identity.
|
|
172
162
|
get center() {
|
|
173
163
|
return lazy(this, "center", () => this.at(0.5, 0.5));
|
|
174
164
|
}
|
|
@@ -184,16 +174,14 @@ export class Box extends Cell {
|
|
|
184
174
|
get right() {
|
|
185
175
|
return lazy(this, "right", () => this.at(1, 0.5));
|
|
186
176
|
}
|
|
187
|
-
/** Tween-builder
|
|
177
|
+
/** Tween-builder. */
|
|
188
178
|
to(target, dur, ease) {
|
|
189
179
|
return tween(this, target, dur, ease);
|
|
190
180
|
}
|
|
191
181
|
}
|
|
192
|
-
/** Writable `Box` at `(x, y, w, h)`. Each component is a literal
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* `Box.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
196
|
-
* snapshot. Lock a component with `Num.pin(c)`. */
|
|
182
|
+
/** Writable `Box` at `(x, y, w, h)`. Each component is a literal (new cell) or
|
|
183
|
+
* existing writable (passed through); for read-only sources use `Box.derive`.
|
|
184
|
+
* Lock a component with `Num.pin`. */
|
|
197
185
|
export function box(x = 0, y = 0, w = 0, h = 0) {
|
|
198
186
|
if (typeof x === "number" &&
|
|
199
187
|
typeof y === "number" &&
|
|
@@ -205,6 +193,5 @@ export function box(x = 0, y = 0, w = 0, h = 0) {
|
|
|
205
193
|
const yN = num(y);
|
|
206
194
|
const wN = num(w);
|
|
207
195
|
const hN = num(h);
|
|
208
|
-
// The view fully reconstructs all 4 axes (1-arg bwd ⇒ no source read).
|
|
209
196
|
return Box.lens([xN, yN, wN, hN], ([bx, by, bw, bh]) => ({ x: bx, y: by, w: bw, h: bh }), v => [v.x, v.y, v.w, v.h]);
|
|
210
197
|
}
|
|
@@ -3,8 +3,8 @@ import { Bool } from "./bool.js";
|
|
|
3
3
|
import { Color } from "./color.js";
|
|
4
4
|
import { Spring, type SpringOpts } from "./gpu.js";
|
|
5
5
|
import { Vec } from "./vec.js";
|
|
6
|
-
/** Raster header
|
|
7
|
-
* the shared GL context
|
|
6
|
+
/** Raster header; the graph compares `epoch`. `tex` is an RGBA32F texture in
|
|
7
|
+
* the shared GL context. */
|
|
8
8
|
export interface Raster {
|
|
9
9
|
readonly tex: WebGLTexture;
|
|
10
10
|
readonly w: number;
|
|
@@ -21,20 +21,18 @@ export declare class Canvas extends Cell<V> {
|
|
|
21
21
|
};
|
|
22
22
|
readonly _t: typeof Canvas.traits;
|
|
23
23
|
constructor(v?: V);
|
|
24
|
-
/** Per-channel invert (alpha preserved).
|
|
24
|
+
/** Per-channel invert (alpha preserved). */
|
|
25
25
|
invert(): this;
|
|
26
|
-
/** Horizontal flip.
|
|
26
|
+
/** Horizontal flip. */
|
|
27
27
|
flipH(): this;
|
|
28
28
|
/** Multiply RGB by reactive `k` (alpha preserved). Invertible while
|
|
29
29
|
* k ≠ 0. */
|
|
30
30
|
brightness(k: Val<number>): this;
|
|
31
|
-
/** Grayscale (Rec.601 luma) view; complement is the per-pixel chroma
|
|
32
|
-
* residual `(r−Y, g−Y, b−Y)`, so editing the gray view recolours the
|
|
33
|
-
* source. The raster analog of `str.lowercase()`. */
|
|
31
|
+
/** Grayscale (Rec.601 luma) view; the complement is the per-pixel chroma
|
|
32
|
+
* residual `(r−Y, g−Y, b−Y)`, so editing the gray view recolours the source. */
|
|
34
33
|
grayscale(): Writable<Canvas>;
|
|
35
|
-
/** Chroma view
|
|
36
|
-
*
|
|
37
|
-
* it rewrites hue while keeping the original brightness. */
|
|
34
|
+
/** Chroma view: `(r−Y, g−Y, b−Y)` on a mid-grey base, complement is the luma
|
|
35
|
+
* `Y`. Editing it rewrites hue while keeping the original brightness. */
|
|
38
36
|
chroma(): Writable<Canvas>;
|
|
39
37
|
/** Sub-rectangle view (reactive `x,y,w,h`). Editing the crop composites
|
|
40
38
|
* back into the source; the surround reads straight from the parent. */
|
|
@@ -43,26 +41,21 @@ export declare class Canvas extends Cell<V> {
|
|
|
43
41
|
* Laplacian residual `source − up(down(source))`; editing the thumbnail
|
|
44
42
|
* reconstructs full-res detail on top of the edit. */
|
|
45
43
|
downsample(factor: number): Writable<Canvas>;
|
|
46
|
-
/** Gaussian blur (reactive `radius`).
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* back-solves to a sharp pre-image with far less ringing than an additive
|
|
51
|
-
* solve. PutGet, not exact GetPut — the residual is the honest signature
|
|
52
|
-
* of an ill-posed inverse. */
|
|
44
|
+
/** Gaussian blur (reactive `radius`). The backward pass runs an iterated
|
|
45
|
+
* Richardson–Lucy deconvolution seeded from the source
|
|
46
|
+
* (`x ← x · H(target / H(x))`), so untouched regions stay fixed. PutGet,
|
|
47
|
+
* not exact GetPut. */
|
|
53
48
|
blur(radius: Val<number>): this;
|
|
54
|
-
/** Mean colour (0–1) as a writable `Color`; the GPU reduces,
|
|
55
|
-
*
|
|
49
|
+
/** Mean colour (0–1) as a writable `Color`; the GPU reduces, a write shifts
|
|
50
|
+
* every pixel by the delta. */
|
|
56
51
|
meanColor(): Writable<Color>;
|
|
57
|
-
/** Mean luma ≥
|
|
58
|
-
*
|
|
59
|
-
* until the mean crosses the line. */
|
|
52
|
+
/** Mean luma ≥ `threshold` (0–1) as a writable `Bool`; flipping the bit
|
|
53
|
+
* auto-exposes by iterating the gain until the mean crosses the threshold. */
|
|
60
54
|
brighterThan(threshold: Val<number>): Writable<Bool>;
|
|
61
55
|
/** Dimensions `(w, h)` as a read-only `Vec`. */
|
|
62
56
|
get dimensions(): Vec;
|
|
63
|
-
/**
|
|
64
|
-
*
|
|
65
|
-
* settle is the GPU energy reduction. */
|
|
57
|
+
/** GPU per-pixel spring driver seeded from this value's texture. The host
|
|
58
|
+
* steps it and writes `current()` back each frame. */
|
|
66
59
|
spring(opts?: SpringOpts): Spring;
|
|
67
60
|
}
|
|
68
61
|
/** Writable `Canvas` of size `w×h`. `painter` fills it pixel-by-pixel
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
// canvas.ts — GPU-resident reactive raster (handle-as-value).
|
|
2
|
-
//
|
|
3
|
-
// Every Canvas value is a float texture in the shared WebGL2 context (see
|
|
4
|
-
// gpu.ts); the reactive graph transports only a tiny HEADER {tex, w, h,
|
|
5
|
-
// epoch}, comparing the monotonic epoch, so propagation never reads a pixel
|
|
6
|
-
// and nothing is copied across the bus. Forward lenses render into a per-lens
|
|
7
|
-
// scratch texture (reused across recomputes); the single ownership rule that
|
|
8
|
-
// keeps no-copy safe is the same as on the CPU — a node writes only its own
|
|
9
|
-
// texture, never one another live node still reads, which holds under the
|
|
10
|
-
// engine's glitch-free flush.
|
|
11
|
-
//
|
|
12
|
-
// Tiers mirror the rest of values/:
|
|
13
|
-
// - pure isomorphisms (`invert`, `flipH`) — one pointwise shader each way.
|
|
14
|
-
// - reactive-param invertibles (`brightness(k)`) — read `Val<number>`.
|
|
15
|
-
// - complement projections (`grayscale`, `downsample`) — the lossy view
|
|
16
|
-
// plus a complement texture (chroma / Laplacian residual) recovered on
|
|
17
|
-
// write-back, the raster analog of str.ts's case-preserving lenses.
|
|
18
|
-
// - cross-type lenses (`meanColor` → Color, `brighterThan` → Bool).
|
|
19
|
-
//
|
|
20
|
-
// A backward pass that produces a ROOT cell's next value reads that root and
|
|
21
|
-
// writes a result that becomes its value, so it uses `scratch2` (a feedback-
|
|
22
|
-
// safe pair) to avoid reading and writing the same texture.
|
|
23
1
|
import { Cell, cachedDerive, reader } from "../cell.js";
|
|
24
2
|
import { Bool } from "./bool.js";
|
|
25
3
|
import { Color } from "./color.js";
|
|
@@ -98,9 +76,7 @@ void main() {
|
|
|
98
76
|
o = vec4(acc / wsum, a);
|
|
99
77
|
}`;
|
|
100
78
|
// Richardson–Lucy step. RL_RATIO forms target / blur(estimate) (guarded);
|
|
101
|
-
// RL_MUL multiplies the estimate by blur(ratio) and clamps to [0,1].
|
|
102
|
-
// the non-negativity clamp and the multiplicative form keep ringing far
|
|
103
|
-
// below an additive Van-Cittert iteration.
|
|
79
|
+
// RL_MUL multiplies the estimate by blur(ratio) and clamps to [0,1].
|
|
104
80
|
const RL_RATIO = `${HEAD}
|
|
105
81
|
uniform sampler2D u_t; uniform sampler2D u_est;
|
|
106
82
|
void main() {
|
|
@@ -162,7 +138,7 @@ export class Canvas extends Cell {
|
|
|
162
138
|
constructor(v = { tex: null, w: 0, h: 0, epoch: 0 }) {
|
|
163
139
|
super(v, { equals });
|
|
164
140
|
}
|
|
165
|
-
/** Per-channel invert (alpha preserved).
|
|
141
|
+
/** Per-channel invert (alpha preserved). */
|
|
166
142
|
invert() {
|
|
167
143
|
const sf = scratch();
|
|
168
144
|
const sb = scratch();
|
|
@@ -173,7 +149,7 @@ export class Canvas extends Cell {
|
|
|
173
149
|
};
|
|
174
150
|
return this.lens(run(sf), run(sb));
|
|
175
151
|
}
|
|
176
|
-
/** Horizontal flip.
|
|
152
|
+
/** Horizontal flip. */
|
|
177
153
|
flipH() {
|
|
178
154
|
const sf = scratch();
|
|
179
155
|
const sb = scratch();
|
|
@@ -200,9 +176,8 @@ export class Canvas extends Cell {
|
|
|
200
176
|
};
|
|
201
177
|
return this.lens(run(sf, () => kf()), run(sb, () => 1 / kf()));
|
|
202
178
|
}
|
|
203
|
-
/** Grayscale (Rec.601 luma) view; complement is the per-pixel chroma
|
|
204
|
-
* residual `(r−Y, g−Y, b−Y)`, so editing the gray view recolours the
|
|
205
|
-
* source. The raster analog of `str.lowercase()`. */
|
|
179
|
+
/** Grayscale (Rec.601 luma) view; the complement is the per-pixel chroma
|
|
180
|
+
* residual `(r−Y, g−Y, b−Y)`, so editing the gray view recolours the source. */
|
|
206
181
|
grayscale() {
|
|
207
182
|
const sc = scratch();
|
|
208
183
|
const sf = scratch();
|
|
@@ -213,27 +188,25 @@ export class Canvas extends Cell {
|
|
|
213
188
|
return c;
|
|
214
189
|
};
|
|
215
190
|
const self = this;
|
|
216
|
-
return Canvas.lens(
|
|
217
|
-
init:
|
|
218
|
-
|
|
219
|
-
fwd: ([s]) => {
|
|
191
|
+
return Canvas.lens(self, {
|
|
192
|
+
init: s => chromaOf(s),
|
|
193
|
+
fwd: s => {
|
|
220
194
|
const out = sf(s.w, s.h);
|
|
221
195
|
pass(LUMA, out, x => x.tex("u_s", 0, s.tex));
|
|
222
196
|
return stamp(out.tex, s.w, s.h);
|
|
223
197
|
},
|
|
224
|
-
bwd: (target,
|
|
198
|
+
bwd: (target, s, c) => {
|
|
225
199
|
const out = sb(s.w, s.h);
|
|
226
200
|
pass(RECOLOR, out, x => {
|
|
227
201
|
x.tex("u_t", 0, target.tex);
|
|
228
202
|
x.tex("u_c", 1, c.tex);
|
|
229
203
|
});
|
|
230
|
-
return {
|
|
204
|
+
return { update: stamp(out.tex, s.w, s.h), complement: c };
|
|
231
205
|
},
|
|
232
206
|
});
|
|
233
207
|
}
|
|
234
|
-
/** Chroma view
|
|
235
|
-
*
|
|
236
|
-
* it rewrites hue while keeping the original brightness. */
|
|
208
|
+
/** Chroma view: `(r−Y, g−Y, b−Y)` on a mid-grey base, complement is the luma
|
|
209
|
+
* `Y`. Editing it rewrites hue while keeping the original brightness. */
|
|
237
210
|
chroma() {
|
|
238
211
|
const sc = scratch();
|
|
239
212
|
const sf = scratch();
|
|
@@ -244,21 +217,20 @@ export class Canvas extends Cell {
|
|
|
244
217
|
return c;
|
|
245
218
|
};
|
|
246
219
|
const self = this;
|
|
247
|
-
return Canvas.lens(
|
|
248
|
-
init:
|
|
249
|
-
|
|
250
|
-
fwd: ([s]) => {
|
|
220
|
+
return Canvas.lens(self, {
|
|
221
|
+
init: s => lumaOf(s),
|
|
222
|
+
fwd: s => {
|
|
251
223
|
const out = sf(s.w, s.h);
|
|
252
224
|
pass(CHROMA_VIEW, out, x => x.tex("u_s", 0, s.tex));
|
|
253
225
|
return stamp(out.tex, s.w, s.h);
|
|
254
226
|
},
|
|
255
|
-
bwd: (target,
|
|
227
|
+
bwd: (target, s, c) => {
|
|
256
228
|
const out = sb(s.w, s.h);
|
|
257
229
|
pass(DELUMA, out, x => {
|
|
258
230
|
x.tex("u_t", 0, target.tex);
|
|
259
231
|
x.tex("u_c", 1, c.tex);
|
|
260
232
|
});
|
|
261
|
-
return {
|
|
233
|
+
return { update: stamp(out.tex, s.w, s.h), complement: c };
|
|
262
234
|
},
|
|
263
235
|
});
|
|
264
236
|
}
|
|
@@ -332,14 +304,13 @@ export class Canvas extends Cell {
|
|
|
332
304
|
return res;
|
|
333
305
|
};
|
|
334
306
|
const self = this;
|
|
335
|
-
return Canvas.lens(
|
|
336
|
-
init:
|
|
337
|
-
|
|
338
|
-
fwd: ([s]) => {
|
|
307
|
+
return Canvas.lens(self, {
|
|
308
|
+
init: s => residualOf(s),
|
|
309
|
+
fwd: s => {
|
|
339
310
|
const small = down(sdF, s.tex, s.w, s.h);
|
|
340
311
|
return stamp(small.tex, small.w, small.h);
|
|
341
312
|
},
|
|
342
|
-
bwd: (target,
|
|
313
|
+
bwd: (target, s, c) => {
|
|
343
314
|
const up = suB(s.w, s.h);
|
|
344
315
|
pass(UP, up, x => {
|
|
345
316
|
x.tex("u_small", 0, target.tex);
|
|
@@ -351,17 +322,14 @@ export class Canvas extends Cell {
|
|
|
351
322
|
x.tex("u_a", 0, up.tex);
|
|
352
323
|
x.tex("u_b", 1, c.tex);
|
|
353
324
|
});
|
|
354
|
-
return {
|
|
325
|
+
return { update: stamp(out.tex, s.w, s.h), complement: c };
|
|
355
326
|
},
|
|
356
327
|
});
|
|
357
328
|
}
|
|
358
|
-
/** Gaussian blur (reactive `radius`).
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* back-solves to a sharp pre-image with far less ringing than an additive
|
|
363
|
-
* solve. PutGet, not exact GetPut — the residual is the honest signature
|
|
364
|
-
* of an ill-posed inverse. */
|
|
329
|
+
/** Gaussian blur (reactive `radius`). The backward pass runs an iterated
|
|
330
|
+
* Richardson–Lucy deconvolution seeded from the source
|
|
331
|
+
* (`x ← x · H(target / H(x))`), so untouched regions stay fixed. PutGet,
|
|
332
|
+
* not exact GetPut. */
|
|
365
333
|
blur(radius) {
|
|
366
334
|
const rf = reader(radius);
|
|
367
335
|
const fTmp = scratch();
|
|
@@ -405,8 +373,8 @@ export class Canvas extends Cell {
|
|
|
405
373
|
return stamp(cur.tex, v.w, v.h);
|
|
406
374
|
});
|
|
407
375
|
}
|
|
408
|
-
/** Mean colour (0–1) as a writable `Color`; the GPU reduces,
|
|
409
|
-
*
|
|
376
|
+
/** Mean colour (0–1) as a writable `Color`; the GPU reduces, a write shifts
|
|
377
|
+
* every pixel by the delta. */
|
|
410
378
|
meanColor() {
|
|
411
379
|
const self = this;
|
|
412
380
|
const sb = scratch2();
|
|
@@ -423,9 +391,8 @@ export class Canvas extends Cell {
|
|
|
423
391
|
return stamp(out.tex, v.w, v.h);
|
|
424
392
|
});
|
|
425
393
|
}
|
|
426
|
-
/** Mean luma ≥
|
|
427
|
-
*
|
|
428
|
-
* until the mean crosses the line. */
|
|
394
|
+
/** Mean luma ≥ `threshold` (0–1) as a writable `Bool`; flipping the bit
|
|
395
|
+
* auto-exposes by iterating the gain until the mean crosses the threshold. */
|
|
429
396
|
brighterThan(threshold) {
|
|
430
397
|
const tf = reader(threshold);
|
|
431
398
|
const self = this;
|
|
@@ -465,9 +432,8 @@ export class Canvas extends Cell {
|
|
|
465
432
|
get dimensions() {
|
|
466
433
|
return cachedDerive(this, "dimensions", Vec, v => ({ x: v.w, y: v.h }));
|
|
467
434
|
}
|
|
468
|
-
/**
|
|
469
|
-
*
|
|
470
|
-
* settle is the GPU energy reduction. */
|
|
435
|
+
/** GPU per-pixel spring driver seeded from this value's texture. The host
|
|
436
|
+
* steps it and writes `current()` back each frame. */
|
|
471
437
|
spring(opts) {
|
|
472
438
|
const s = new Spring(this.value.w, this.value.h, opts);
|
|
473
439
|
s.seed(this.value.tex);
|
|
@@ -13,7 +13,7 @@ export declare const sub: (a: V, b: V) => V;
|
|
|
13
13
|
export declare const scale: (a: V, k: number) => V;
|
|
14
14
|
export declare const lerp: (a: V, b: V, t: number) => V;
|
|
15
15
|
export declare const equals: (a: V, b: V) => boolean;
|
|
16
|
-
/**
|
|
16
|
+
/** Euclidean distance in RGBA-space. */
|
|
17
17
|
export declare const metric: (a: V, b: V) => number;
|
|
18
18
|
export declare class Color extends Cell<V> {
|
|
19
19
|
static traits: {
|
|
@@ -35,14 +35,12 @@ export declare class Color extends Cell<V> {
|
|
|
35
35
|
get a(): this extends import("../index.js").WritableBrand ? Writable<Num> : Num;
|
|
36
36
|
get luminance(): Num;
|
|
37
37
|
get css(): Cell<string>;
|
|
38
|
-
/** Tween-builder
|
|
38
|
+
/** Tween-builder. */
|
|
39
39
|
to(this: Writable<Color>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
|
|
40
40
|
}
|
|
41
|
-
/** Writable `Color` from RGB channels (alpha = 1). Each channel is a
|
|
42
|
-
*
|
|
43
|
-
* (identity passthrough). All-literal inputs take a fast path; mixed
|
|
44
|
-
* inputs build a lens so channel-writes round-trip to source. */
|
|
41
|
+
/** Writable `Color` from RGB channels (alpha = 1). Each channel is a literal
|
|
42
|
+
* or a `Writable<Num>`. */
|
|
45
43
|
export declare function rgb(r: Init<Num>, g: Init<Num>, b: Init<Num>): Writable<Color>;
|
|
46
|
-
/** Writable `Color` from RGBA channels.
|
|
44
|
+
/** Writable `Color` from RGBA channels. */
|
|
47
45
|
export declare function rgba(r: Init<Num>, g: Init<Num>, b: Init<Num>, a: Init<Num>): Writable<Color>;
|
|
48
46
|
export {};
|
|
@@ -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) {
|