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
|
@@ -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;
|
|
@@ -12,15 +12,14 @@ export declare const sub: (a: V, b: V) => V;
|
|
|
12
12
|
export declare const scale: (a: V, k: number) => V;
|
|
13
13
|
export declare const lerp: (a: V, b: V, t: number) => V;
|
|
14
14
|
export declare const equals: (a: V, b: V) => boolean;
|
|
15
|
-
/**
|
|
15
|
+
/** Euclidean distance over (lo, hi). */
|
|
16
16
|
export declare const metric: (a: V, b: V) => number;
|
|
17
17
|
export declare const width: (r: V) => number;
|
|
18
18
|
export declare const center: (r: V) => number;
|
|
19
19
|
export declare const contains: (r: V, v: number) => boolean;
|
|
20
20
|
export declare const clamp: (r: V, v: number) => number;
|
|
21
|
-
/** Closest value
|
|
22
|
-
*
|
|
23
|
-
* false-side policy. */
|
|
21
|
+
/** Closest value strictly outside `[lo, hi]`, displaced past the nearest
|
|
22
|
+
* endpoint by `eps`. */
|
|
24
23
|
export declare const eject: (r: V, v: number, eps?: number) => number;
|
|
25
24
|
/** Sample at parameter `t`: `lo + t·(hi - lo)`. `t ∈ [0, 1]` stays
|
|
26
25
|
* inside the range; values outside extrapolate linearly. */
|
|
@@ -43,43 +42,36 @@ export declare class Range extends Cell<V> {
|
|
|
43
42
|
/** End endpoint. Writes preserve `lo` (end-knob semantics). */
|
|
44
43
|
get hi(): this extends WritableBrand ? Writable<Num> : Num;
|
|
45
44
|
get width(): Num;
|
|
46
|
-
/** Midpoint body-drag:
|
|
47
|
-
*
|
|
48
|
-
* variant; `.lo` / `.hi` edit the endpoints. */
|
|
45
|
+
/** Midpoint body-drag: reads the center; a write shifts the range so the
|
|
46
|
+
* center matches (width preserved). */
|
|
49
47
|
get center(): Writable<Num>;
|
|
50
|
-
/** Translate by `by`.
|
|
48
|
+
/** Translate by `by`. */
|
|
51
49
|
shift(by: Val<number>): this;
|
|
52
|
-
/** Scale
|
|
50
|
+
/** Scale about the origin. Invertible when k ≠ 0. */
|
|
53
51
|
scale(k: Val<number>): this;
|
|
54
|
-
/** Body-drag handle:
|
|
55
|
-
*
|
|
52
|
+
/** Body-drag handle: reads `lo`; a write shifts the range so `lo` matches
|
|
53
|
+
* (width preserved). */
|
|
56
54
|
get start(): Writable<Num>;
|
|
57
55
|
/** RO sample at `t`. `t ∈ [0, 1]` stays inside; outside extrapolates. */
|
|
58
56
|
sample(t: Val<number>): Num;
|
|
59
57
|
/** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
|
|
60
58
|
* solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
|
|
61
59
|
slider(t: Writable<Num>): Writable<Num>;
|
|
62
|
-
/**
|
|
63
|
-
*
|
|
64
|
-
* (`
|
|
65
|
-
* endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
|
|
60
|
+
/** True when `v` is in `[lo, hi]`. A writable `Num` yields a `Writable<Bool>`:
|
|
61
|
+
* flipping it clamps `v` inside (`true`) or ejects it past the nearest
|
|
62
|
+
* endpoint (`false`). Literal/RO inputs yield a read-only `Bool`. */
|
|
66
63
|
contains<P extends Val<number>>(v: P): P extends WritableBrand ? Writable<Bool> : Bool;
|
|
67
|
-
/**
|
|
68
|
-
* on a single Num, see `Num#clamp(lo, hi)`. */
|
|
64
|
+
/** Read-only clamp of `v` into `[lo, hi]`. */
|
|
69
65
|
clampedRead(v: Val<number>): Num;
|
|
70
66
|
/** Inverse of `sample`: derive the `t` that would produce `v`. */
|
|
71
67
|
paramOf(v: Val<number>): Num;
|
|
72
|
-
/** Tween-builder
|
|
68
|
+
/** Tween-builder. */
|
|
73
69
|
to(this: Writable<Range>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
|
|
74
70
|
}
|
|
75
|
-
/** Range over `[at, at + dur]`,
|
|
76
|
-
* timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
|
|
77
|
-
* body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
|
|
71
|
+
/** Range over `[at, at + dur]`, backed by the live `at` and `dur` Nums. */
|
|
78
72
|
export declare function span(at: Writable<Num>, dur: Writable<Num>): Writable<Range>;
|
|
79
|
-
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* `Range.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
83
|
-
* snapshot. Lock an endpoint with `Num.pin(c)`. */
|
|
73
|
+
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal (new cell) or
|
|
74
|
+
* existing writable (passed through); for read-only sources use `Range.derive`.
|
|
75
|
+
* Lock an endpoint with `Num.pin`. */
|
|
84
76
|
export declare function range(lo?: Init<Num>, hi?: Init<Num>): Writable<Range>;
|
|
85
77
|
export {};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// range.ts — reactive numeric interval `[lo, hi]`.
|
|
2
|
-
//
|
|
3
|
-
// Home for sliders, scrollbars, and timeline clip spans. Field lenses
|
|
4
|
-
// give start-knob (`.lo`) and end-knob (`.hi`) drag; `.start` / `.center`
|
|
5
|
-
// are body-drags (shift both, preserving width — anchored at lo / midpoint
|
|
6
|
-
// respectively); `.slider(t)` is the bidirectional `t ↔ lo + t·(hi - lo)`
|
|
7
|
-
// iso. Traits: `linear`, `lerp`, `equals`, `pack` (scalar-only).
|
|
8
1
|
import { tween } from "../../animation/index.js";
|
|
9
2
|
import { Cell, cachedDerive, fieldLens, isReadonly, reader, readNow, SKIP, } from "../cell.js";
|
|
10
3
|
import { Bool } from "./bool.js";
|
|
@@ -17,15 +10,14 @@ export const lerp = (a, b, t) => ({
|
|
|
17
10
|
hi: a.hi + (b.hi - a.hi) * t,
|
|
18
11
|
});
|
|
19
12
|
export const equals = (a, b) => a === b || (a.lo === b.lo && a.hi === b.hi);
|
|
20
|
-
/**
|
|
13
|
+
/** Euclidean distance over (lo, hi). */
|
|
21
14
|
export const metric = (a, b) => Math.hypot(a.lo - b.lo, a.hi - b.hi);
|
|
22
15
|
export const width = (r) => r.hi - r.lo;
|
|
23
16
|
export const center = (r) => (r.lo + r.hi) / 2;
|
|
24
17
|
export const contains = (r, v) => v >= r.lo && v <= r.hi;
|
|
25
18
|
export const clamp = (r, v) => (v < r.lo ? r.lo : v > r.hi ? r.hi : v);
|
|
26
|
-
/** Closest value
|
|
27
|
-
*
|
|
28
|
-
* false-side policy. */
|
|
19
|
+
/** Closest value strictly outside `[lo, hi]`, displaced past the nearest
|
|
20
|
+
* endpoint by `eps`. */
|
|
29
21
|
export const eject = (r, v, eps = 1e-6) => {
|
|
30
22
|
if (!contains(r, v))
|
|
31
23
|
return v;
|
|
@@ -71,21 +63,20 @@ export class Range extends Cell {
|
|
|
71
63
|
get width() {
|
|
72
64
|
return cachedDerive(this, "width", Num, width);
|
|
73
65
|
}
|
|
74
|
-
/** Midpoint body-drag:
|
|
75
|
-
*
|
|
76
|
-
* variant; `.lo` / `.hi` edit the endpoints. */
|
|
66
|
+
/** Midpoint body-drag: reads the center; a write shifts the range so the
|
|
67
|
+
* center matches (width preserved). */
|
|
77
68
|
get center() {
|
|
78
69
|
return Num.lens(this, center, (c, src) => {
|
|
79
70
|
const half = (src.hi - src.lo) / 2;
|
|
80
71
|
return { lo: c - half, hi: c + half };
|
|
81
72
|
});
|
|
82
73
|
}
|
|
83
|
-
/** Translate by `by`.
|
|
74
|
+
/** Translate by `by`. */
|
|
84
75
|
shift(by) {
|
|
85
76
|
const f = reader(by);
|
|
86
77
|
return this.lens(v => ({ lo: v.lo + f(), hi: v.hi + f() }), n => ({ lo: n.lo - f(), hi: n.hi - f() }));
|
|
87
78
|
}
|
|
88
|
-
/** Scale
|
|
79
|
+
/** Scale about the origin. Invertible when k ≠ 0. */
|
|
89
80
|
scale(k) {
|
|
90
81
|
const kf = reader(k);
|
|
91
82
|
return this.lens(v => {
|
|
@@ -96,8 +87,8 @@ export class Range extends Cell {
|
|
|
96
87
|
return { lo: n.lo / k, hi: n.hi / k };
|
|
97
88
|
});
|
|
98
89
|
}
|
|
99
|
-
/** Body-drag handle:
|
|
100
|
-
*
|
|
90
|
+
/** Body-drag handle: reads `lo`; a write shifts the range so `lo` matches
|
|
91
|
+
* (width preserved). */
|
|
101
92
|
get start() {
|
|
102
93
|
return Num.lens(this, v => v.lo, (newLo, src) => ({ lo: newLo, hi: newLo + (src.hi - src.lo) }));
|
|
103
94
|
}
|
|
@@ -108,21 +99,19 @@ export class Range extends Cell {
|
|
|
108
99
|
/** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
|
|
109
100
|
* solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
|
|
110
101
|
slider(t) {
|
|
111
|
-
// `this as Range` pins the tuple element type
|
|
112
|
-
//
|
|
102
|
+
// `this as Range` pins the tuple element type; polymorphic `this` breaks
|
|
103
|
+
// the mapped-tuple inference.
|
|
113
104
|
return Num.lens([this, t], ([r, tv]) => sample(r, tv), (v, [r]) => {
|
|
114
105
|
const w = r.hi - r.lo;
|
|
115
106
|
return [SKIP, w === 0 ? 0 : (v - r.lo) / w];
|
|
116
107
|
});
|
|
117
108
|
}
|
|
118
|
-
/**
|
|
119
|
-
*
|
|
120
|
-
* (`
|
|
121
|
-
* endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
|
|
109
|
+
/** True when `v` is in `[lo, hi]`. A writable `Num` yields a `Writable<Bool>`:
|
|
110
|
+
* flipping it clamps `v` inside (`true`) or ejects it past the nearest
|
|
111
|
+
* endpoint (`false`). Literal/RO inputs yield a read-only `Bool`. */
|
|
122
112
|
contains(v) {
|
|
123
113
|
if (v instanceof Num) {
|
|
124
|
-
// RO Num has no backward path
|
|
125
|
-
// writable lenses both accept write-back.
|
|
114
|
+
// RO Num has no backward path; only writable Nums accept write-back.
|
|
126
115
|
if (!isReadonly(v)) {
|
|
127
116
|
return Bool.lens([this, v], (vals) => contains(vals[0], vals[1]), (target, vals) => {
|
|
128
117
|
const [r, n] = vals;
|
|
@@ -134,8 +123,7 @@ export class Range extends Cell {
|
|
|
134
123
|
}
|
|
135
124
|
return Bool.derive(() => contains(this.value, readNow(v)));
|
|
136
125
|
}
|
|
137
|
-
/**
|
|
138
|
-
* on a single Num, see `Num#clamp(lo, hi)`. */
|
|
126
|
+
/** Read-only clamp of `v` into `[lo, hi]`. */
|
|
139
127
|
clampedRead(v) {
|
|
140
128
|
return Num.derive(() => clamp(this.value, readNow(v)));
|
|
141
129
|
}
|
|
@@ -143,27 +131,22 @@ export class Range extends Cell {
|
|
|
143
131
|
paramOf(v) {
|
|
144
132
|
return Num.derive(() => paramOf(this.value, readNow(v)));
|
|
145
133
|
}
|
|
146
|
-
/** Tween-builder
|
|
134
|
+
/** Tween-builder. */
|
|
147
135
|
to(target, dur, ease) {
|
|
148
136
|
return tween(this, target, dur, ease);
|
|
149
137
|
}
|
|
150
138
|
}
|
|
151
|
-
/**
|
|
152
|
-
* here after lifting literals. */
|
|
139
|
+
/** Lens combining two writable `Num`s into a `Range`. */
|
|
153
140
|
function ends(lo, hi) {
|
|
154
141
|
return Range.lens([lo, hi], (vals) => ({ lo: vals[0], hi: vals[1] }), (target) => [target.lo, target.hi]);
|
|
155
142
|
}
|
|
156
|
-
/** Range over `[at, at + dur]`,
|
|
157
|
-
* timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
|
|
158
|
-
* body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
|
|
143
|
+
/** Range over `[at, at + dur]`, backed by the live `at` and `dur` Nums. */
|
|
159
144
|
export function span(at, dur) {
|
|
160
145
|
return Range.lens([at, dur], (vals) => ({ lo: vals[0], hi: vals[0] + vals[1] }), (target) => [target.lo, target.hi - target.lo]);
|
|
161
146
|
}
|
|
162
|
-
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* `Range.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
166
|
-
* snapshot. Lock an endpoint with `Num.pin(c)`. */
|
|
147
|
+
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal (new cell) or
|
|
148
|
+
* existing writable (passed through); for read-only sources use `Range.derive`.
|
|
149
|
+
* Lock an endpoint with `Num.pin`. */
|
|
167
150
|
export function range(lo = 0, hi = 1) {
|
|
168
151
|
if (typeof lo === "number" && typeof hi === "number") {
|
|
169
152
|
return new Range({ lo, hi });
|