bireactive 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/{core/anim.d.ts → animation/animators.d.ts} +5 -3
- package/dist/{core/anim.js → animation/animators.js} +8 -5
- package/dist/animation/index.d.ts +1 -0
- package/dist/animation/index.js +1 -0
- package/dist/constraints/cluster.js +1 -1
- package/dist/constraints/expose.d.ts +17 -0
- package/dist/constraints/expose.js +61 -0
- package/dist/constraints/index.d.ts +1 -0
- package/dist/constraints/index.js +1 -0
- package/dist/constraints/linalg.d.ts +1 -8
- package/dist/constraints/linalg.js +5 -108
- package/dist/core/{signal.d.ts → cell.d.ts} +29 -6
- package/dist/core/{signal.js → cell.js} +134 -53
- package/dist/core/derived-geometry.d.ts +11 -0
- package/dist/core/derived-geometry.js +32 -0
- package/dist/core/index.d.ts +5 -10
- package/dist/core/index.js +5 -10
- package/dist/core/{aggregates.d.ts → lenses/aggregates.d.ts} +3 -14
- package/dist/core/{aggregates.js → lenses/aggregates.js} +5 -78
- package/dist/core/lenses/closed-form-policies.d.ts +6 -13
- package/dist/core/lenses/closed-form-policies.js +14 -24
- package/dist/core/lenses/decompositions.d.ts +14 -0
- package/dist/core/lenses/decompositions.js +224 -0
- package/dist/core/lenses/domain-aggregates.d.ts +10 -22
- package/dist/core/lenses/domain-aggregates.js +25 -39
- package/dist/core/{new-primitives.d.ts → lenses/geometry.d.ts} +11 -14
- package/dist/core/{new-primitives.js → lenses/geometry.js} +23 -37
- package/dist/core/lenses/index.d.ts +6 -4
- package/dist/core/lenses/index.js +6 -15
- package/dist/core/lenses/typed-factor.d.ts +1 -6
- package/dist/core/lenses/typed-factor.js +12 -114
- package/dist/core/{network-utils.d.ts → lifecycle.d.ts} +1 -1
- package/dist/core/{network-utils.js → lifecycle.js} +2 -2
- package/dist/core/linalg.d.ts +10 -0
- package/dist/core/linalg.js +109 -0
- package/dist/core/values/anchor.d.ts +1 -1
- package/dist/core/values/audio.d.ts +1 -1
- package/dist/core/values/audio.js +1 -1
- package/dist/core/values/bool.d.ts +1 -1
- package/dist/core/values/bool.js +1 -1
- package/dist/core/values/box.d.ts +2 -3
- package/dist/core/values/box.js +5 -6
- package/dist/core/values/canvas.d.ts +1 -1
- package/dist/core/values/canvas.js +2 -3
- package/dist/core/values/color.d.ts +2 -3
- package/dist/core/values/color.js +3 -4
- package/dist/core/values/flags.d.ts +1 -1
- package/dist/core/values/flags.js +1 -1
- package/dist/core/values/matrix.d.ts +1 -1
- package/dist/core/values/matrix.js +2 -3
- package/dist/core/values/num.d.ts +8 -3
- package/dist/core/values/num.js +22 -2
- package/dist/core/values/pose.d.ts +5 -1
- package/dist/core/values/pose.js +11 -1
- package/dist/core/values/range.d.ts +6 -4
- package/dist/core/values/range.js +16 -11
- package/dist/core/values/str.d.ts +1 -1
- package/dist/core/values/str.js +1 -1
- package/dist/core/values/template.d.ts +1 -1
- package/dist/core/values/transform.d.ts +2 -3
- package/dist/core/values/transform.js +2 -3
- package/dist/core/values/tri.d.ts +10 -8
- package/dist/core/values/tri.js +12 -12
- package/dist/core/values/vec.d.ts +10 -4
- package/dist/core/values/vec.js +30 -21
- package/dist/ext/timeline.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/propagators/layout.js +1 -1
- package/dist/shapes/handle.js +4 -4
- package/dist/shapes/shape.js +7 -7
- package/dist/shapes/transitions.js +2 -2
- package/package.json +7 -2
- package/dist/core/introspect.d.ts +0 -5
- package/dist/core/introspect.js +0 -31
- package/dist/core/lenses/factor-lens.d.ts +0 -42
- package/dist/core/lenses/factor-lens.js +0 -419
- package/dist/core/writable.d.ts +0 -15
- package/dist/core/writable.js +0 -29
- /package/dist/{core/tree.d.ts → tree.d.ts} +0 -0
- /package/dist/{core/tree.js → tree.js} +0 -0
package/dist/core/values/pose.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// a 3-DOF block and writes back through it, so renderers, drag handlers,
|
|
5
5
|
// IK, and physics all observe the same value. Vec / Num lenses compose
|
|
6
6
|
// for consumers that care only about translation or rotation.
|
|
7
|
-
import { Cell } from "../
|
|
7
|
+
import { Cell, field } from "../cell.js";
|
|
8
|
+
import { Num } from "./num.js";
|
|
8
9
|
export const add = (a, b) => ({
|
|
9
10
|
x: a.x + b.x,
|
|
10
11
|
y: a.y + b.y,
|
|
@@ -69,6 +70,15 @@ export class Pose extends Cell {
|
|
|
69
70
|
constructor(v = { x: 0, y: 0, theta: 0 }) {
|
|
70
71
|
super(v, { equals });
|
|
71
72
|
}
|
|
73
|
+
get x() {
|
|
74
|
+
return field(this, "x", Num);
|
|
75
|
+
}
|
|
76
|
+
get y() {
|
|
77
|
+
return field(this, "y", Num);
|
|
78
|
+
}
|
|
79
|
+
get theta() {
|
|
80
|
+
return field(this, "theta", Num);
|
|
81
|
+
}
|
|
72
82
|
}
|
|
73
83
|
/** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
|
|
74
84
|
* through by identity. RO sources are rejected at the type level — use
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type
|
|
3
|
-
import { Cell, type Init, type Val, type Writable, type WritableBrand } from "../signal.js";
|
|
1
|
+
import { type Easing, type Tween } from "../../animation/index.js";
|
|
2
|
+
import { Cell, type Init, type Val, type Writable, type WritableBrand } from "../cell.js";
|
|
4
3
|
import type { Linear, Pack } from "../traits.js";
|
|
5
4
|
import { Bool } from "./bool.js";
|
|
6
5
|
import { Num } from "./num.js";
|
|
@@ -44,7 +43,10 @@ export declare class Range extends Cell<V> {
|
|
|
44
43
|
/** End endpoint. Writes preserve `lo` (end-knob semantics). */
|
|
45
44
|
get hi(): this extends WritableBrand ? Writable<Num> : Num;
|
|
46
45
|
get width(): Num;
|
|
47
|
-
|
|
46
|
+
/** Midpoint body-drag: read returns the center; write shifts the range
|
|
47
|
+
* so the center matches (width preserved). `.start` is the lo-anchored
|
|
48
|
+
* variant; `.lo` / `.hi` edit the endpoints. */
|
|
49
|
+
get center(): Writable<Num>;
|
|
48
50
|
/** Translate by `by`. Reads shift the interval; writes shift back. */
|
|
49
51
|
shift(by: Val<number>): this;
|
|
50
52
|
/** Scale uniformly about the origin. Iso for `k ≠ 0`. */
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// range.ts — reactive numeric interval `[lo, hi]`.
|
|
2
2
|
//
|
|
3
3
|
// Home for sliders, scrollbars, and timeline clip spans. Field lenses
|
|
4
|
-
// give start-knob (`.lo`) and end-knob (`.hi`) drag; `.start`
|
|
5
|
-
// body-
|
|
6
|
-
// bidirectional `t ↔ lo + t·(hi - lo)`
|
|
7
|
-
// `equals`, `pack` (scalar-only).
|
|
8
|
-
import { tween } from "
|
|
9
|
-
import { Cell,
|
|
10
|
-
import { derived, field } from "../writable.js";
|
|
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
|
+
import { tween } from "../../animation/index.js";
|
|
9
|
+
import { Cell, cachedDerive, field, isReadonly, reader, readNow, } from "../cell.js";
|
|
11
10
|
import { Bool } from "./bool.js";
|
|
12
11
|
import { Num, num } from "./num.js";
|
|
13
12
|
export const add = (a, b) => ({ lo: a.lo + b.lo, hi: a.hi + b.hi });
|
|
@@ -70,10 +69,16 @@ export class Range extends Cell {
|
|
|
70
69
|
return field(this, "hi", Num);
|
|
71
70
|
}
|
|
72
71
|
get width() {
|
|
73
|
-
return
|
|
72
|
+
return cachedDerive(this, "width", Num, width);
|
|
74
73
|
}
|
|
74
|
+
/** Midpoint body-drag: read returns the center; write shifts the range
|
|
75
|
+
* so the center matches (width preserved). `.start` is the lo-anchored
|
|
76
|
+
* variant; `.lo` / `.hi` edit the endpoints. */
|
|
75
77
|
get center() {
|
|
76
|
-
return
|
|
78
|
+
return Num.lens(this, center, (c, src) => {
|
|
79
|
+
const half = (src.hi - src.lo) / 2;
|
|
80
|
+
return { lo: c - half, hi: c + half };
|
|
81
|
+
});
|
|
77
82
|
}
|
|
78
83
|
/** Translate by `by`. Reads shift the interval; writes shift back. */
|
|
79
84
|
shift(by) {
|
|
@@ -116,9 +121,9 @@ export class Range extends Cell {
|
|
|
116
121
|
* endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
|
|
117
122
|
contains(v) {
|
|
118
123
|
if (v instanceof Num) {
|
|
119
|
-
// RO
|
|
124
|
+
// RO Num has no backward path → RO branch. Sources and
|
|
120
125
|
// writable lenses both accept write-back.
|
|
121
|
-
if (!
|
|
126
|
+
if (!isReadonly(v)) {
|
|
122
127
|
return Bool.lens([this, v], (vals) => contains(vals[0], vals[1]), (target, vals) => {
|
|
123
128
|
const [r, n] = vals;
|
|
124
129
|
if (contains(r, n) === target)
|
package/dist/core/values/str.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// uppercase — dual of lowercase
|
|
17
17
|
// words — separator pattern between words
|
|
18
18
|
// sortedUnique — source positions + original case per unique word
|
|
19
|
-
import { Cell } from "../
|
|
19
|
+
import { Cell } from "../cell.js";
|
|
20
20
|
// Complement-carrying endo lens.
|
|
21
21
|
//
|
|
22
22
|
// The complement is state recorded forward from the source and consumed
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type
|
|
3
|
-
import { Cell, type Inner, type Val, type Writable } from "../signal.js";
|
|
1
|
+
import { type Easing, type Tween } from "../../animation/index.js";
|
|
2
|
+
import { Cell, type Inner, type Val, type Writable } from "../cell.js";
|
|
4
3
|
import type { Linear } from "../traits.js";
|
|
5
4
|
import { Num } from "./num.js";
|
|
6
5
|
import { Vec } from "./vec.js";
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
// Field-lens getters use `field()`, so writability propagates through
|
|
6
6
|
// nested chains
|
|
7
7
|
// (`Transform.translate.x.value = 5` works on writable receivers).
|
|
8
|
-
import { tween } from "
|
|
9
|
-
import { Cell, reader, readNow } from "../
|
|
10
|
-
import { field } from "../writable.js";
|
|
8
|
+
import { tween } from "../../animation/index.js";
|
|
9
|
+
import { Cell, field, reader, readNow } from "../cell.js";
|
|
11
10
|
import { Num } from "./num.js";
|
|
12
11
|
import { Vec, add as vAdd, equals as vEquals, lerp as vLerp, metric as vMetric, scale as vScale, sub as vSub, } from "./vec.js";
|
|
13
12
|
export const DEFAULT = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cell, type Init, type Writable } from "../
|
|
1
|
+
import { Cell, type Init, type Writable } from "../cell.js";
|
|
2
2
|
import type { Bool } from "./bool.js";
|
|
3
3
|
type V = boolean | "mixed";
|
|
4
4
|
/** Kleene negation: `true` / `false` swap, `"mixed"` is fixed. */
|
|
@@ -17,13 +17,15 @@ export declare class Tri extends Cell<V> {
|
|
|
17
17
|
constructor(v?: V);
|
|
18
18
|
/** Kleene negation. Involution; fixed at `"mixed"`. */
|
|
19
19
|
not(): this;
|
|
20
|
-
/** Aggregate over N writable
|
|
21
|
-
* all-false → `false`, disagreement
|
|
22
|
-
* `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
/** Aggregate over N writable `Bool` / `Tri` children. Read: all-true →
|
|
21
|
+
* `true`, all-false → `false`, any disagreement (or any child already
|
|
22
|
+
* `"mixed"`) → `"mixed"`. Write: `true` / `false` broadcast to every
|
|
23
|
+
* child, recursing through nested aggregates; `"mixed"` is a no-op. */
|
|
24
|
+
static allOf(parents: readonly (Bool | Tri)[]): Writable<Tri>;
|
|
25
|
+
/** Dual of `allOf` (Kleene OR) over `Bool` / `Tri` children: any-true →
|
|
26
|
+
* `true`, all-false → `false`, else (or any child `"mixed"`) →
|
|
27
|
+
* `"mixed"`. Same broadcast write policy. */
|
|
28
|
+
static anyOf(parents: readonly (Bool | Tri)[]): Writable<Tri>;
|
|
27
29
|
}
|
|
28
30
|
/** Writable `Tri`. Strict factory: `Tri.value | Writable<Tri>` in,
|
|
29
31
|
* `Writable<Tri>` out. Default initial value is `"mixed"`. */
|
package/dist/core/values/tri.js
CHANGED
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
// `Tri.value ∈ { true, false, "mixed" }` — Bool plus an unknown state
|
|
4
4
|
// fixed under negation. Strong-Kleene AND/OR follow the partial-info
|
|
5
5
|
// reading (`mixed AND false` → `false`, `mixed AND true` → `mixed`).
|
|
6
|
-
|
|
7
|
-
// Headline use: aggregate N booleans via `Tri.allOf` / `Tri.anyOf`
|
|
8
|
-
// (all-agree → that value, disagreement → `"mixed"`). Writing the
|
|
9
|
-
// aggregate broadcasts to every parent ("select all" / "deselect all");
|
|
10
|
-
// writing `"mixed"` is a no-op. Morally `Maybe<Bool>` — the basis for
|
|
11
|
-
// mixed-state checkbox trees and "loading" predicate states.
|
|
12
|
-
import { Cell } from "../signal.js";
|
|
6
|
+
import { Cell } from "../cell.js";
|
|
13
7
|
const equals = (a, b) => a === b;
|
|
14
8
|
/** Kleene negation: `true` / `false` swap, `"mixed"` is fixed. */
|
|
15
9
|
export const not = (a) => (a === "mixed" ? "mixed" : !a);
|
|
@@ -40,14 +34,17 @@ export class Tri extends Cell {
|
|
|
40
34
|
not() {
|
|
41
35
|
return this.lens(not, not);
|
|
42
36
|
}
|
|
43
|
-
/** Aggregate over N writable
|
|
44
|
-
* all-false → `false`, disagreement
|
|
45
|
-
* `
|
|
37
|
+
/** Aggregate over N writable `Bool` / `Tri` children. Read: all-true →
|
|
38
|
+
* `true`, all-false → `false`, any disagreement (or any child already
|
|
39
|
+
* `"mixed"`) → `"mixed"`. Write: `true` / `false` broadcast to every
|
|
40
|
+
* child, recursing through nested aggregates; `"mixed"` is a no-op. */
|
|
46
41
|
static allOf(parents) {
|
|
47
42
|
return Tri.lens(parents, (vs) => {
|
|
48
43
|
let anyT = false;
|
|
49
44
|
let anyF = false;
|
|
50
45
|
for (const v of vs) {
|
|
46
|
+
if (v === "mixed")
|
|
47
|
+
return "mixed";
|
|
51
48
|
if (v)
|
|
52
49
|
anyT = true;
|
|
53
50
|
else
|
|
@@ -62,13 +59,16 @@ export class Tri extends Cell {
|
|
|
62
59
|
return parents.map(() => target);
|
|
63
60
|
});
|
|
64
61
|
}
|
|
65
|
-
/** Dual of `allOf` (Kleene OR)
|
|
66
|
-
* `false`, else `"mixed"
|
|
62
|
+
/** Dual of `allOf` (Kleene OR) over `Bool` / `Tri` children: any-true →
|
|
63
|
+
* `true`, all-false → `false`, else (or any child `"mixed"`) →
|
|
64
|
+
* `"mixed"`. Same broadcast write policy. */
|
|
67
65
|
static anyOf(parents) {
|
|
68
66
|
return Tri.lens(parents, (vs) => {
|
|
69
67
|
let anyT = false;
|
|
70
68
|
let anyF = false;
|
|
71
69
|
for (const v of vs) {
|
|
70
|
+
if (v === "mixed")
|
|
71
|
+
return "mixed";
|
|
72
72
|
if (v)
|
|
73
73
|
anyT = true;
|
|
74
74
|
else
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type
|
|
3
|
-
import { Cell, type Init, type Val, type Writable } from "../signal.js";
|
|
1
|
+
import { type Easing, type Tween } from "../../animation/index.js";
|
|
2
|
+
import { Cell, type Init, type Val, type Writable } from "../cell.js";
|
|
4
3
|
import type { Linear, Pack, Pivotal } from "../traits.js";
|
|
5
4
|
import { Num } from "./num.js";
|
|
6
5
|
type V = {
|
|
@@ -15,6 +14,8 @@ export declare const metric: (a: V, b: V) => number;
|
|
|
15
14
|
export declare const equals: (a: V, b: V) => boolean;
|
|
16
15
|
export declare const normalize: (v: V) => V;
|
|
17
16
|
export declare const perp: (v: V) => V;
|
|
17
|
+
export declare const rotateAbout: (v: V, p: V, dθ: number) => V;
|
|
18
|
+
export declare const scaleAbout: (v: V, p: V, k: number) => V;
|
|
18
19
|
/** Tangent point on the circle (radius `r`, centre `c`) from external
|
|
19
20
|
* point `p`. `side: -1` picks the CCW tangent from `pc`, `+1` the CW
|
|
20
21
|
* (y-down screen coords flip the visual sense). Returns `c` if `p` is
|
|
@@ -33,7 +34,12 @@ export declare class Vec extends Cell<V> {
|
|
|
33
34
|
constructor(v?: V);
|
|
34
35
|
add(b: Val<V>): this;
|
|
35
36
|
sub(b: Val<V>): this;
|
|
36
|
-
scale
|
|
37
|
+
/** Uniform scale by `k` about `pivot` (default origin). Inverse scales
|
|
38
|
+
* by `1/k`; exact bijection for `k ≠ 0`. */
|
|
39
|
+
scale(k: Val<number>, pivot?: Val<V>): this;
|
|
40
|
+
/** Rotate by `angle` (radians) about `pivot` (default origin). Inverse
|
|
41
|
+
* rotates by `−angle`; exact bijection. */
|
|
42
|
+
rotate(angle: Val<number>, pivot?: Val<V>): this;
|
|
37
43
|
offset(dx: Val<number>, dy: Val<number>): this;
|
|
38
44
|
up(n: Val<number>): this;
|
|
39
45
|
down(n: Val<number>): this;
|
package/dist/core/values/vec.js
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Invertibles return `: this` and ride on `Cell#lens(fwd, bwd)`;
|
|
4
4
|
// chained calls compose into a lens chain. Field-lens getters use
|
|
5
|
-
// `field()` (propagates writability); `
|
|
6
|
-
import { tween } from "
|
|
7
|
-
import { Cell, reader, readNow } from "../
|
|
8
|
-
import { derived, field } from "../writable.js";
|
|
5
|
+
// `field()` (propagates writability); `cachedDerive()` wraps RO views.
|
|
6
|
+
import { tween } from "../../animation/index.js";
|
|
7
|
+
import { Cell, cachedDerive, field, reader, readNow, } from "../cell.js";
|
|
9
8
|
import { Num, num } from "./num.js";
|
|
10
9
|
export const add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
11
10
|
export const sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
@@ -21,6 +20,18 @@ export const normalize = (v) => {
|
|
|
21
20
|
return m === 0 ? { x: 0, y: 0 } : { x: v.x / m, y: v.y / m };
|
|
22
21
|
};
|
|
23
22
|
export const perp = (v) => ({ x: v.y, y: -v.x });
|
|
23
|
+
export const rotateAbout = (v, p, dθ) => {
|
|
24
|
+
const cos = Math.cos(dθ);
|
|
25
|
+
const sin = Math.sin(dθ);
|
|
26
|
+
const dx = v.x - p.x;
|
|
27
|
+
const dy = v.y - p.y;
|
|
28
|
+
return { x: p.x + cos * dx - sin * dy, y: p.y + sin * dx + cos * dy };
|
|
29
|
+
};
|
|
30
|
+
export const scaleAbout = (v, p, k) => ({
|
|
31
|
+
x: p.x + k * (v.x - p.x),
|
|
32
|
+
y: p.y + k * (v.y - p.y),
|
|
33
|
+
});
|
|
34
|
+
const ORIGIN = { x: 0, y: 0 };
|
|
24
35
|
/** Tangent point on the circle (radius `r`, centre `c`) from external
|
|
25
36
|
* point `p`. `side: -1` picks the CCW tangent from `pc`, `+1` the CW
|
|
26
37
|
* (y-down screen coords flip the visual sense). Returns `c` if `p` is
|
|
@@ -50,19 +61,7 @@ const packImpl = {
|
|
|
50
61
|
},
|
|
51
62
|
write: (a, o) => ({ x: a[o], y: a[o + 1] }),
|
|
52
63
|
};
|
|
53
|
-
const pivotalImpl = {
|
|
54
|
-
rotateAbout: (v, p, dθ) => {
|
|
55
|
-
const cos = Math.cos(dθ);
|
|
56
|
-
const sin = Math.sin(dθ);
|
|
57
|
-
const dx = v.x - p.x;
|
|
58
|
-
const dy = v.y - p.y;
|
|
59
|
-
return { x: p.x + cos * dx - sin * dy, y: p.y + sin * dx + cos * dy };
|
|
60
|
-
},
|
|
61
|
-
scaleAbout: (v, p, k) => ({
|
|
62
|
-
x: p.x + k * (v.x - p.x),
|
|
63
|
-
y: p.y + k * (v.y - p.y),
|
|
64
|
-
}),
|
|
65
|
-
};
|
|
64
|
+
const pivotalImpl = { rotateAbout, scaleAbout };
|
|
66
65
|
export class Vec extends Cell {
|
|
67
66
|
static traits = {
|
|
68
67
|
linear: linearImpl,
|
|
@@ -95,16 +94,26 @@ export class Vec extends Cell {
|
|
|
95
94
|
return { x: n.x + o.x, y: n.y + o.y };
|
|
96
95
|
});
|
|
97
96
|
}
|
|
98
|
-
scale(
|
|
97
|
+
/** Uniform scale by `k` about `pivot` (default origin). Inverse scales
|
|
98
|
+
* by `1/k`; exact bijection for `k ≠ 0`. */
|
|
99
|
+
scale(k, pivot) {
|
|
99
100
|
const kf = reader(k);
|
|
101
|
+
const pf = pivot === undefined ? undefined : reader(pivot);
|
|
100
102
|
return this.lens(v => {
|
|
101
103
|
const k = kf();
|
|
102
|
-
return { x: v.x * k, y: v.y * k };
|
|
104
|
+
return pf ? scaleAbout(v, pf(), k) : { x: v.x * k, y: v.y * k };
|
|
103
105
|
}, n => {
|
|
104
106
|
const k = kf();
|
|
105
|
-
return { x: n.x / k, y: n.y / k };
|
|
107
|
+
return pf ? scaleAbout(n, pf(), 1 / k) : { x: n.x / k, y: n.y / k };
|
|
106
108
|
});
|
|
107
109
|
}
|
|
110
|
+
/** Rotate by `angle` (radians) about `pivot` (default origin). Inverse
|
|
111
|
+
* rotates by `−angle`; exact bijection. */
|
|
112
|
+
rotate(angle, pivot = ORIGIN) {
|
|
113
|
+
const af = reader(angle);
|
|
114
|
+
const pf = reader(pivot);
|
|
115
|
+
return this.lens(v => rotateAbout(v, pf(), af()), n => rotateAbout(n, pf(), -af()));
|
|
116
|
+
}
|
|
108
117
|
offset(dx, dy) {
|
|
109
118
|
const xf = reader(dx);
|
|
110
119
|
const yf = reader(dy);
|
|
@@ -146,7 +155,7 @@ export class Vec extends Cell {
|
|
|
146
155
|
return field(this, "y", Num);
|
|
147
156
|
}
|
|
148
157
|
get magnitude() {
|
|
149
|
-
return
|
|
158
|
+
return cachedDerive(this, "magnitude", Num, v => Math.hypot(v.x, v.y));
|
|
150
159
|
}
|
|
151
160
|
/** Tween-builder; `this: Writable<Vec>` gates the call to writable
|
|
152
161
|
* receivers. */
|
package/dist/ext/timeline.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Timeline + Clip. A timeline is a clock plus named clips (each an
|
|
2
2
|
// `[at, at + dur)` interval); `yield* tl` advances the clock to
|
|
3
3
|
// `duration`. `sequential({...})` produces cumulative-start specs.
|
|
4
|
-
import { derive,
|
|
4
|
+
import { derive, isReadonly, Num, num, Range, span, } from "../core/index.js";
|
|
5
5
|
class TimelineImpl {
|
|
6
6
|
clock;
|
|
7
7
|
duration;
|
|
@@ -36,8 +36,8 @@ function makeClip(spec, clock) {
|
|
|
36
36
|
const dur = Num.from(spec.dur);
|
|
37
37
|
const end = Num.derive(() => at.value + dur.value);
|
|
38
38
|
// Bidirectional span when both inputs are writable; RO derive when
|
|
39
|
-
// either is
|
|
40
|
-
const sp =
|
|
39
|
+
// either is read-only. Mirrors `ResolvedSpan`.
|
|
40
|
+
const sp = isReadonly(at) || isReadonly(dur)
|
|
41
41
|
? Range.derive(() => ({ lo: at.value, hi: at.value + dur.value }))
|
|
42
42
|
: span(at, dur);
|
|
43
43
|
const t = Num.derive(() => {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ export * from "./core/index.js";
|
|
|
5
5
|
export * from "./ext/index.js";
|
|
6
6
|
export * from "./shapes/index.js";
|
|
7
7
|
export * from "./tex/index.js";
|
|
8
|
+
export { allNodes, atPath, isLeaf, leavesOf, node as treeNode, nodeCount, type TreeNode, walkTree, } from "./tree.js";
|
|
8
9
|
export * from "./web/index.js";
|
package/dist/index.js
CHANGED
|
@@ -7,4 +7,5 @@ export * from "./core/index.js";
|
|
|
7
7
|
export * from "./ext/index.js";
|
|
8
8
|
export * from "./shapes/index.js";
|
|
9
9
|
export * from "./tex/index.js";
|
|
10
|
+
export { allNodes, atPath, isLeaf, leavesOf, node as treeNode, nodeCount, walkTree, } from "./tree.js";
|
|
10
11
|
export * from "./web/index.js";
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// `hstack` / `vstack` are CSS-flex-shaped (per-item grow/shrink vs
|
|
11
11
|
// min/max). For rigid edge-to-edge layouts use `attach`,
|
|
12
12
|
// `centerInside`, etc.
|
|
13
|
-
import { isCell, readNow } from "../core/index.js";
|
|
13
|
+
import { isCell, readNow, } from "../core/index.js";
|
|
14
14
|
import { propagator } from "./propagator.js";
|
|
15
15
|
const asW = (n) => n;
|
|
16
16
|
function readDeps(...vs) {
|
package/dist/shapes/handle.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// handle.* — writable derived shapes (draggable circles wired to a Vec).
|
|
2
|
-
import { cell,
|
|
2
|
+
import { cell, mean, polar as polarLens, Vec, } from "../core/index.js";
|
|
3
3
|
import { Circle } from "./circle.js";
|
|
4
4
|
import { drag } from "./interaction.js";
|
|
5
5
|
const COLOR = "var(--bireactive-handle, #2563eb)";
|
|
@@ -32,10 +32,10 @@ const anchor = (shape, u, v, opts) => handleFn(shape.at(u, v), opts);
|
|
|
32
32
|
/** Drag handle at the centroid of N shapes' visual centers; drags translate
|
|
33
33
|
* all shapes rigidly. Reads the centroid of visible positions (cf. `centroid`
|
|
34
34
|
* in `shape.ts`, which works on translate deltas). */
|
|
35
|
-
const centroidHandle = (...shapes) => handleFn(
|
|
35
|
+
const centroidHandle = (...shapes) => handleFn(mean(shapes.map(s => s.center)));
|
|
36
36
|
/** Drag handle at the midpoint of two writable Points — drags both
|
|
37
37
|
* along with it. */
|
|
38
|
-
const midpoint = (a, b, opts) => handleFn(
|
|
38
|
+
const midpoint = (a, b, opts) => handleFn(mean([a, b]), opts);
|
|
39
39
|
/** Rotation knob orbiting the shape's center at `radius`; drag to write
|
|
40
40
|
* `shape.rotate`. */
|
|
41
41
|
const rotate = (shape, radius = 40, opts) => {
|
|
@@ -73,7 +73,7 @@ const tOnPath = (p, t, opts) => {
|
|
|
73
73
|
}
|
|
74
74
|
return bestT;
|
|
75
75
|
};
|
|
76
|
-
const pos = Vec.lens(
|
|
76
|
+
const pos = Vec.lens(t, tv => p.pointAt(tv).value, target => project(target));
|
|
77
77
|
return handleFn(pos, opts);
|
|
78
78
|
};
|
|
79
79
|
/** `handle(point)` is the atom; `.move`, `.centroid`, etc. are sugar. */
|
package/dist/shapes/shape.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { suspend } from "../animation/index.js";
|
|
2
|
-
import { Box, BoxMath, Cell, cell,
|
|
2
|
+
import { Box, BoxMath, Cell, cell, derive, effect, lazy, MatrixMath, mean, Num, readNow, transformBox, transformPoint, Vec, } from "../core/index.js";
|
|
3
3
|
import { dashedPath } from "./dashed.js";
|
|
4
4
|
import { tokens } from "./tokens.js";
|
|
5
5
|
export const SVG_NS = "http://www.w3.org/2000/svg";
|
|
@@ -79,12 +79,12 @@ export class Shape {
|
|
|
79
79
|
const r = this.rotate.value;
|
|
80
80
|
const sc = this.scale.value;
|
|
81
81
|
if (t.x === 0 && t.y === 0 && r === 0 && sc.x === 1 && sc.y === 1) {
|
|
82
|
-
return compose(t, r, sc, { x: 0, y: 0 });
|
|
82
|
+
return MatrixMath.compose(t, r, sc, { x: 0, y: 0 });
|
|
83
83
|
}
|
|
84
|
-
return compose(t, r, sc, this.origin.value);
|
|
84
|
+
return MatrixMath.compose(t, r, sc, this.origin.value);
|
|
85
85
|
});
|
|
86
86
|
this.disposers.push(effect(() => {
|
|
87
|
-
this.el.style.transform = toMatrixString(this.localFrame.value);
|
|
87
|
+
this.el.style.transform = MatrixMath.toMatrixString(this.localFrame.value);
|
|
88
88
|
this.el.style.opacity = String(this.opacity.value);
|
|
89
89
|
}));
|
|
90
90
|
}
|
|
@@ -276,15 +276,15 @@ export class Shape {
|
|
|
276
276
|
// evenly to all members.
|
|
277
277
|
/** Writable centroid of shapes' translates. */
|
|
278
278
|
export function centroid(...shapes) {
|
|
279
|
-
return
|
|
279
|
+
return mean(shapes.map(s => s.translate));
|
|
280
280
|
}
|
|
281
281
|
/** Writable mean rotation. */
|
|
282
282
|
export function meanRotation(...shapes) {
|
|
283
|
-
return
|
|
283
|
+
return mean(shapes.map(s => s.rotate));
|
|
284
284
|
}
|
|
285
285
|
/** Writable mean scale. */
|
|
286
286
|
export function meanScale(...shapes) {
|
|
287
|
-
return
|
|
287
|
+
return mean(shapes.map(s => s.scale));
|
|
288
288
|
}
|
|
289
289
|
/** Lift a `Val<T>` to a `Writable<Cls<T>>` for Shape's animatable surface.
|
|
290
290
|
* Writable passes through; literal seeds a cell; signal/thunk drives it via
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Bounded transitions. Initial pose is written synchronously; the return value
|
|
2
2
|
// is the time-varying part only — a `Tween` (single-axis) or `Yieldable[]`
|
|
3
3
|
// (multi-axis). Either way callers `yield transitionName(s)`.
|
|
4
|
-
import { easeIn, easeInOut, easeOut } from "../animation/index.js";
|
|
5
|
-
import { Dir
|
|
4
|
+
import { easeIn, easeInOut, easeOut, tween, } from "../animation/index.js";
|
|
5
|
+
import { Dir } from "../core/index.js";
|
|
6
6
|
/** Fade opacity 0 → 1. */
|
|
7
7
|
export const fadeIn = (s, sec = 0.3, ease = easeOut) => s.opacity.to(1, sec, ease).from(0);
|
|
8
8
|
/** Fade opacity 1 → 0. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bireactive",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Bi-directional reactive programming.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -40,7 +40,12 @@
|
|
|
40
40
|
"bench": "node --expose-gc node_modules/.bin/vite-node",
|
|
41
41
|
"typecheck": "tsc --noEmit --pretty",
|
|
42
42
|
"fmt": "biome check --write .",
|
|
43
|
-
"prepublishOnly": "npm test && npm run build"
|
|
43
|
+
"prepublishOnly": "npm test && npm run build",
|
|
44
|
+
"release": "npm run release:patch",
|
|
45
|
+
"release:patch": "npm version patch",
|
|
46
|
+
"release:minor": "npm version minor",
|
|
47
|
+
"release:major": "npm version major",
|
|
48
|
+
"postversion": "npm publish && git push --follow-tags"
|
|
44
49
|
},
|
|
45
50
|
"dependencies": {
|
|
46
51
|
"prism-esm": "^1.29.0-fix.6",
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { Cell } from "./signal.js";
|
|
2
|
-
/** Every cell `s` transitively depends on, including itself. Raw cells
|
|
3
|
-
* return `{s}`; lens chains return the chain plus all parents. BFS,
|
|
4
|
-
* peeking each Computed to populate deps; the `seen` set breaks cycles. */
|
|
5
|
-
export declare function transitiveDeps(s: Cell<unknown>): Set<Cell<unknown>>;
|
package/dist/core/introspect.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// introspect.ts — read-only inspection of a cell's dependency graph.
|
|
2
|
-
//
|
|
3
|
-
// Used by `Propagators` to expand declared reads into their transitive
|
|
4
|
-
// parent set, so a propagator reading a lens chain reacts to writes that
|
|
5
|
-
// touch the chain's parents but not the chain's identity. Inspection is
|
|
6
|
-
// safe: it only reads engine state and peeks `.value` to populate deps
|
|
7
|
-
// for lazy Computeds (idempotent).
|
|
8
|
-
/** Every cell `s` transitively depends on, including itself. Raw cells
|
|
9
|
-
* return `{s}`; lens chains return the chain plus all parents. BFS,
|
|
10
|
-
* peeking each Computed to populate deps; the `seen` set breaks cycles. */
|
|
11
|
-
export function transitiveDeps(s) {
|
|
12
|
-
const seen = new Set();
|
|
13
|
-
const queue = [s];
|
|
14
|
-
while (queue.length > 0) {
|
|
15
|
-
const cur = queue.shift();
|
|
16
|
-
if (seen.has(cur))
|
|
17
|
-
continue;
|
|
18
|
-
seen.add(cur);
|
|
19
|
-
// Cast to reach engine fields the typed Cell<T> shape doesn't surface.
|
|
20
|
-
const c = cur;
|
|
21
|
-
if (c.getter !== undefined) {
|
|
22
|
-
void cur.value;
|
|
23
|
-
let l = c.deps;
|
|
24
|
-
while (l !== undefined) {
|
|
25
|
-
queue.push(l.dep);
|
|
26
|
-
l = l.nextDep;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return seen;
|
|
31
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { type Cell, Num, Vec, type Writable } from "../index.js";
|
|
2
|
-
export interface FactorLensOpts {
|
|
3
|
-
/** Per-input mobility weights. 0 = pinned input. Defaults to all 1. */
|
|
4
|
-
inputWeights?: readonly number[];
|
|
5
|
-
/** Levenberg-Marquardt damping on the M×M normal matrix. Default 1e-6. */
|
|
6
|
-
damping?: number;
|
|
7
|
-
/** Finite-difference epsilon. Default 1e-5. */
|
|
8
|
-
eps?: number;
|
|
9
|
-
}
|
|
10
|
-
export declare function factorLens(inputs: readonly Num[], forwards: readonly ((xs: readonly number[]) => number)[], opts?: FactorLensOpts): Writable<Num>[];
|
|
11
|
-
export declare function meanDiffLens(a: Num, b: Num): {
|
|
12
|
-
mean: Writable<Num>;
|
|
13
|
-
diff: Writable<Num>;
|
|
14
|
-
};
|
|
15
|
-
export declare function procrustesLens(points: readonly Writable<Vec>[]): {
|
|
16
|
-
centroid: Writable<Vec>;
|
|
17
|
-
rotation: Writable<Num>;
|
|
18
|
-
scale: Writable<Num>;
|
|
19
|
-
};
|
|
20
|
-
export declare function bboxLens(points: readonly Writable<Vec>[]): {
|
|
21
|
-
center: Writable<Vec>;
|
|
22
|
-
size: Writable<Vec>;
|
|
23
|
-
};
|
|
24
|
-
export declare function procrustesJacobianLens(points: readonly Writable<Vec>[]): {
|
|
25
|
-
centroidX: Writable<Num>;
|
|
26
|
-
centroidY: Writable<Num>;
|
|
27
|
-
rotation: Writable<Num>;
|
|
28
|
-
scale: Writable<Num>;
|
|
29
|
-
};
|
|
30
|
-
type PoseV = {
|
|
31
|
-
x: number;
|
|
32
|
-
y: number;
|
|
33
|
-
theta: number;
|
|
34
|
-
};
|
|
35
|
-
export declare function bundleLens(pose: Writable<Cell<PoseV>>, rotateAbout: {
|
|
36
|
-
x: number;
|
|
37
|
-
y: number;
|
|
38
|
-
}): {
|
|
39
|
-
position: Writable<Vec>;
|
|
40
|
-
rotation: Writable<Num>;
|
|
41
|
-
};
|
|
42
|
-
export {};
|