bireactive 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/dist/animation/anim.d.ts +57 -0
  4. package/dist/animation/anim.js +318 -0
  5. package/dist/animation/combinators.d.ts +39 -0
  6. package/dist/animation/combinators.js +113 -0
  7. package/dist/animation/easings.d.ts +5 -0
  8. package/dist/animation/easings.js +5 -0
  9. package/dist/animation/index.d.ts +3 -0
  10. package/dist/animation/index.js +3 -0
  11. package/dist/assert/algebra.d.ts +20 -0
  12. package/dist/assert/algebra.js +79 -0
  13. package/dist/assert/claim.d.ts +40 -0
  14. package/dist/assert/claim.js +129 -0
  15. package/dist/assert/index.d.ts +7 -0
  16. package/dist/assert/index.js +19 -0
  17. package/dist/assert/predicates.d.ts +18 -0
  18. package/dist/assert/predicates.js +43 -0
  19. package/dist/assert/record.d.ts +20 -0
  20. package/dist/assert/record.js +78 -0
  21. package/dist/assert/scope.d.ts +42 -0
  22. package/dist/assert/scope.js +233 -0
  23. package/dist/assert/span.d.ts +37 -0
  24. package/dist/assert/span.js +68 -0
  25. package/dist/assert/tree.d.ts +22 -0
  26. package/dist/assert/tree.js +65 -0
  27. package/dist/code/code.d.ts +70 -0
  28. package/dist/code/code.js +361 -0
  29. package/dist/code/index.d.ts +2 -0
  30. package/dist/code/index.js +9 -0
  31. package/dist/code/morph.d.ts +5 -0
  32. package/dist/code/morph.js +194 -0
  33. package/dist/code/tokenize.d.ts +8 -0
  34. package/dist/code/tokenize.js +51 -0
  35. package/dist/constraints/cluster.d.ts +83 -0
  36. package/dist/constraints/cluster.js +213 -0
  37. package/dist/constraints/drivers.d.ts +15 -0
  38. package/dist/constraints/drivers.js +40 -0
  39. package/dist/constraints/factories.d.ts +73 -0
  40. package/dist/constraints/factories.js +248 -0
  41. package/dist/constraints/index.d.ts +11 -0
  42. package/dist/constraints/index.js +39 -0
  43. package/dist/constraints/interaction.d.ts +21 -0
  44. package/dist/constraints/interaction.js +148 -0
  45. package/dist/constraints/linalg.d.ts +18 -0
  46. package/dist/constraints/linalg.js +141 -0
  47. package/dist/constraints/phases.d.ts +21 -0
  48. package/dist/constraints/phases.js +60 -0
  49. package/dist/constraints/physics.d.ts +34 -0
  50. package/dist/constraints/physics.js +128 -0
  51. package/dist/constraints/rigid.d.ts +210 -0
  52. package/dist/constraints/rigid.js +835 -0
  53. package/dist/constraints/solver.d.ts +107 -0
  54. package/dist/constraints/solver.js +510 -0
  55. package/dist/constraints/term.d.ts +50 -0
  56. package/dist/constraints/term.js +80 -0
  57. package/dist/constraints/terms.d.ts +80 -0
  58. package/dist/constraints/terms.js +302 -0
  59. package/dist/constraints/world.d.ts +31 -0
  60. package/dist/constraints/world.js +245 -0
  61. package/dist/core/aggregates.d.ts +64 -0
  62. package/dist/core/aggregates.js +198 -0
  63. package/dist/core/anim.d.ts +84 -0
  64. package/dist/core/anim.js +301 -0
  65. package/dist/core/index.d.ts +38 -0
  66. package/dist/core/index.js +38 -0
  67. package/dist/core/introspect.d.ts +5 -0
  68. package/dist/core/introspect.js +31 -0
  69. package/dist/core/lenses/closed-form-policies.d.ts +64 -0
  70. package/dist/core/lenses/closed-form-policies.js +452 -0
  71. package/dist/core/lenses/domain-aggregates.d.ts +54 -0
  72. package/dist/core/lenses/domain-aggregates.js +259 -0
  73. package/dist/core/lenses/factor-lens.d.ts +42 -0
  74. package/dist/core/lenses/factor-lens.js +419 -0
  75. package/dist/core/lenses/index.d.ts +5 -0
  76. package/dist/core/lenses/index.js +16 -0
  77. package/dist/core/lenses/memory.d.ts +47 -0
  78. package/dist/core/lenses/memory.js +102 -0
  79. package/dist/core/lenses/typed-factor.d.ts +45 -0
  80. package/dist/core/lenses/typed-factor.js +376 -0
  81. package/dist/core/network-utils.d.ts +14 -0
  82. package/dist/core/network-utils.js +62 -0
  83. package/dist/core/new-primitives.d.ts +33 -0
  84. package/dist/core/new-primitives.js +113 -0
  85. package/dist/core/signal.d.ts +254 -0
  86. package/dist/core/signal.js +1349 -0
  87. package/dist/core/traits.d.ts +61 -0
  88. package/dist/core/traits.js +56 -0
  89. package/dist/core/tree.d.ts +23 -0
  90. package/dist/core/tree.js +62 -0
  91. package/dist/core/values/anchor.d.ts +23 -0
  92. package/dist/core/values/anchor.js +23 -0
  93. package/dist/core/values/audio.d.ts +33 -0
  94. package/dist/core/values/audio.js +107 -0
  95. package/dist/core/values/bool.d.ts +37 -0
  96. package/dist/core/values/bool.js +75 -0
  97. package/dist/core/values/box.d.ts +77 -0
  98. package/dist/core/values/box.js +211 -0
  99. package/dist/core/values/canvas.d.ts +71 -0
  100. package/dist/core/values/canvas.js +495 -0
  101. package/dist/core/values/color.d.ts +49 -0
  102. package/dist/core/values/color.js +106 -0
  103. package/dist/core/values/flags.d.ts +18 -0
  104. package/dist/core/values/flags.js +50 -0
  105. package/dist/core/values/gpu.d.ts +74 -0
  106. package/dist/core/values/gpu.js +426 -0
  107. package/dist/core/values/matrix.d.ts +53 -0
  108. package/dist/core/values/matrix.js +140 -0
  109. package/dist/core/values/num.d.ts +62 -0
  110. package/dist/core/values/num.js +166 -0
  111. package/dist/core/values/pose.d.ts +31 -0
  112. package/dist/core/values/pose.js +83 -0
  113. package/dist/core/values/range.d.ts +83 -0
  114. package/dist/core/values/range.js +167 -0
  115. package/dist/core/values/str.d.ts +76 -0
  116. package/dist/core/values/str.js +346 -0
  117. package/dist/core/values/template.d.ts +49 -0
  118. package/dist/core/values/template.js +148 -0
  119. package/dist/core/values/transform.d.ts +49 -0
  120. package/dist/core/values/transform.js +115 -0
  121. package/dist/core/values/tri.d.ts +31 -0
  122. package/dist/core/values/tri.js +95 -0
  123. package/dist/core/values/vec.d.ts +72 -0
  124. package/dist/core/values/vec.js +219 -0
  125. package/dist/core/writable.d.ts +15 -0
  126. package/dist/core/writable.js +29 -0
  127. package/dist/ext/events.d.ts +10 -0
  128. package/dist/ext/events.js +31 -0
  129. package/dist/ext/index.d.ts +4 -0
  130. package/dist/ext/index.js +4 -0
  131. package/dist/ext/snapshot.d.ts +8 -0
  132. package/dist/ext/snapshot.js +29 -0
  133. package/dist/ext/timeline.d.ts +56 -0
  134. package/dist/ext/timeline.js +94 -0
  135. package/dist/ext/waapi.d.ts +25 -0
  136. package/dist/ext/waapi.js +198 -0
  137. package/dist/index.d.ts +8 -0
  138. package/dist/index.js +10 -0
  139. package/dist/propagators/index.d.ts +6 -0
  140. package/dist/propagators/index.js +6 -0
  141. package/dist/propagators/layout.d.ts +68 -0
  142. package/dist/propagators/layout.js +336 -0
  143. package/dist/propagators/network.d.ts +52 -0
  144. package/dist/propagators/network.js +185 -0
  145. package/dist/propagators/propagator.d.ts +12 -0
  146. package/dist/propagators/propagator.js +16 -0
  147. package/dist/propagators/range.d.ts +45 -0
  148. package/dist/propagators/range.js +147 -0
  149. package/dist/propagators/relations.d.ts +60 -0
  150. package/dist/propagators/relations.js +343 -0
  151. package/dist/shapes/annular-sector.d.ts +15 -0
  152. package/dist/shapes/annular-sector.js +64 -0
  153. package/dist/shapes/button.d.ts +14 -0
  154. package/dist/shapes/button.js +31 -0
  155. package/dist/shapes/choreographers.d.ts +22 -0
  156. package/dist/shapes/choreographers.js +69 -0
  157. package/dist/shapes/circle.d.ts +17 -0
  158. package/dist/shapes/circle.js +57 -0
  159. package/dist/shapes/clip.d.ts +5 -0
  160. package/dist/shapes/clip.js +31 -0
  161. package/dist/shapes/connect.d.ts +16 -0
  162. package/dist/shapes/connect.js +70 -0
  163. package/dist/shapes/curve.d.ts +60 -0
  164. package/dist/shapes/curve.js +285 -0
  165. package/dist/shapes/dashed.d.ts +16 -0
  166. package/dist/shapes/dashed.js +142 -0
  167. package/dist/shapes/debug.d.ts +43 -0
  168. package/dist/shapes/debug.js +97 -0
  169. package/dist/shapes/group.d.ts +5 -0
  170. package/dist/shapes/group.js +10 -0
  171. package/dist/shapes/handle.d.ts +32 -0
  172. package/dist/shapes/handle.js +88 -0
  173. package/dist/shapes/index.d.ts +23 -0
  174. package/dist/shapes/index.js +23 -0
  175. package/dist/shapes/interaction.d.ts +32 -0
  176. package/dist/shapes/interaction.js +187 -0
  177. package/dist/shapes/label.d.ts +20 -0
  178. package/dist/shapes/label.js +42 -0
  179. package/dist/shapes/layout.d.ts +29 -0
  180. package/dist/shapes/layout.js +74 -0
  181. package/dist/shapes/line.d.ts +21 -0
  182. package/dist/shapes/line.js +79 -0
  183. package/dist/shapes/list.d.ts +18 -0
  184. package/dist/shapes/list.js +51 -0
  185. package/dist/shapes/mount.d.ts +7 -0
  186. package/dist/shapes/mount.js +10 -0
  187. package/dist/shapes/path.d.ts +77 -0
  188. package/dist/shapes/path.js +227 -0
  189. package/dist/shapes/rect.d.ts +30 -0
  190. package/dist/shapes/rect.js +131 -0
  191. package/dist/shapes/shape.d.ts +132 -0
  192. package/dist/shapes/shape.js +306 -0
  193. package/dist/shapes/text.d.ts +24 -0
  194. package/dist/shapes/text.js +53 -0
  195. package/dist/shapes/tokens.d.ts +28 -0
  196. package/dist/shapes/tokens.js +27 -0
  197. package/dist/shapes/transitions.d.ts +23 -0
  198. package/dist/shapes/transitions.js +62 -0
  199. package/dist/tex/decorations.d.ts +26 -0
  200. package/dist/tex/decorations.js +116 -0
  201. package/dist/tex/index.d.ts +5 -0
  202. package/dist/tex/index.js +5 -0
  203. package/dist/tex/marker.d.ts +17 -0
  204. package/dist/tex/marker.js +63 -0
  205. package/dist/tex/motion.d.ts +43 -0
  206. package/dist/tex/motion.js +290 -0
  207. package/dist/tex/parts.d.ts +65 -0
  208. package/dist/tex/parts.js +149 -0
  209. package/dist/tex/tex.d.ts +45 -0
  210. package/dist/tex/tex.js +244 -0
  211. package/dist/web/attr.d.ts +16 -0
  212. package/dist/web/attr.js +98 -0
  213. package/dist/web/diagram.d.ts +49 -0
  214. package/dist/web/diagram.js +260 -0
  215. package/dist/web/index.d.ts +6 -0
  216. package/dist/web/index.js +6 -0
  217. package/dist/web/md-marker.d.ts +6 -0
  218. package/dist/web/md-marker.js +39 -0
  219. package/dist/web/md-tex.d.ts +6 -0
  220. package/dist/web/md-tex.js +61 -0
  221. package/dist/web/raf.d.ts +6 -0
  222. package/dist/web/raf.js +24 -0
  223. package/dist/web/viewport.d.ts +7 -0
  224. package/dist/web/viewport.js +13 -0
  225. package/package.json +87 -0
@@ -0,0 +1,140 @@
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
+ import { Cell, reader } from "../signal.js";
9
+ import { derived, field } from "../writable.js";
10
+ import { Num, num } from "./num.js";
11
+ export const identity = () => ({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 });
12
+ export const fromTranslate = (x, y) => ({ a: 1, b: 0, c: 0, d: 1, e: x, f: y });
13
+ export const fromScale = (x, y) => ({ a: x, b: 0, c: 0, d: y, e: 0, f: 0 });
14
+ export const fromRotate = (angle) => {
15
+ const s = Math.sin(angle);
16
+ const c = Math.cos(angle);
17
+ return { a: c, b: s, c: -s, d: c, e: 0, f: 0 };
18
+ };
19
+ export const isIdentity = (m) => m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1 && m.e === 0 && m.f === 0;
20
+ export const equals = (m, n) => m === n ||
21
+ (m.a === n.a && m.b === n.b && m.c === n.c && m.d === n.d && m.e === n.e && m.f === n.f);
22
+ export function multiply(a, b) {
23
+ return {
24
+ a: a.a * b.a + a.c * b.b,
25
+ b: a.b * b.a + a.d * b.b,
26
+ c: a.a * b.c + a.c * b.d,
27
+ d: a.b * b.c + a.d * b.d,
28
+ e: a.a * b.e + a.c * b.f + a.e,
29
+ f: a.b * b.e + a.d * b.f + a.f,
30
+ };
31
+ }
32
+ export function invert(m) {
33
+ const det = m.a * m.d - m.b * m.c;
34
+ if (det === 0)
35
+ throw new Error("Matrix not invertible");
36
+ const inv = 1 / det;
37
+ return {
38
+ a: m.d * inv,
39
+ b: -m.b * inv,
40
+ c: -m.c * inv,
41
+ d: m.a * inv,
42
+ e: (m.c * m.f - m.d * m.e) * inv,
43
+ f: (m.b * m.e - m.a * m.f) * inv,
44
+ };
45
+ }
46
+ export const determinant = (m) => m.a * m.d - m.b * m.c;
47
+ export const transformPoint = (m, p) => ({
48
+ x: m.a * p.x + m.c * p.y + m.e,
49
+ y: m.b * p.x + m.d * p.y + m.f,
50
+ });
51
+ export function transformBox(m, b) {
52
+ if (isIdentity(m))
53
+ return b;
54
+ const x0 = b.x, y0 = b.y, x1 = b.x + b.w, y1 = b.y + b.h;
55
+ const ax = m.a * x0 + m.c * y0 + m.e;
56
+ const ay = m.b * x0 + m.d * y0 + m.f;
57
+ const bx = m.a * x1 + m.c * y0 + m.e;
58
+ const by = m.b * x1 + m.d * y0 + m.f;
59
+ const cx = m.a * x1 + m.c * y1 + m.e;
60
+ const cy = m.b * x1 + m.d * y1 + m.f;
61
+ const dx = m.a * x0 + m.c * y1 + m.e;
62
+ const dy = m.b * x0 + m.d * y1 + m.f;
63
+ return {
64
+ x: Math.min(ax, bx, cx, dx),
65
+ y: Math.min(ay, by, cy, dy),
66
+ w: Math.max(ax, bx, cx, dx) - Math.min(ax, bx, cx, dx),
67
+ h: Math.max(ay, by, cy, dy) - Math.min(ay, by, cy, dy),
68
+ };
69
+ }
70
+ const SCALE_EPS = 1e-7;
71
+ export function compose(t, r, s, pivot) {
72
+ const sx = Math.abs(s.x) < SCALE_EPS ? (s.x < 0 ? -SCALE_EPS : SCALE_EPS) : s.x;
73
+ const sy = Math.abs(s.y) < SCALE_EPS ? (s.y < 0 ? -SCALE_EPS : SCALE_EPS) : s.y;
74
+ let m = fromTranslate(t.x, t.y);
75
+ m = multiply(m, fromTranslate(pivot.x, pivot.y));
76
+ if (r !== 0)
77
+ m = multiply(m, fromRotate(r));
78
+ if (sx !== 1 || sy !== 1)
79
+ m = multiply(m, fromScale(sx, sy));
80
+ m = multiply(m, fromTranslate(-pivot.x, -pivot.y));
81
+ return m;
82
+ }
83
+ export const toMatrixString = (m) => `matrix(${m.a},${m.b},${m.c},${m.d},${m.e},${m.f})`;
84
+ export class Matrix extends Cell {
85
+ static traits = { equals };
86
+ constructor(v = identity()) {
87
+ super(v, { equals });
88
+ }
89
+ multiply(b) {
90
+ const bf = reader(b);
91
+ return this.lens(v => multiply(v, bf()), n => multiply(n, invert(bf())));
92
+ }
93
+ invert() {
94
+ return this.lens(invert, invert);
95
+ }
96
+ get a() {
97
+ return field(this, "a", Num);
98
+ }
99
+ get b() {
100
+ return field(this, "b", Num);
101
+ }
102
+ get c() {
103
+ return field(this, "c", Num);
104
+ }
105
+ get d() {
106
+ return field(this, "d", Num);
107
+ }
108
+ get e() {
109
+ return field(this, "e", Num);
110
+ }
111
+ get f() {
112
+ return field(this, "f", Num);
113
+ }
114
+ get determinant() {
115
+ return derived(this, "determinant", Num, determinant);
116
+ }
117
+ }
118
+ /** Writable `Matrix` with entries `(a, b, c, d, e, f)` (SVG/Canvas order).
119
+ * Each entry is a literal `number` (lifted to a fresh seed) or an existing
120
+ * `Writable<Num>` (identity passthrough). RO sources are rejected at the
121
+ * type level — use `Matrix.derive(...)` for reactive RO tracking, or
122
+ * `cell.value` to snapshot. Lock an entry with `Num.pin(c)`. */
123
+ export function matrix(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
124
+ if (typeof a === "number" &&
125
+ typeof b === "number" &&
126
+ typeof c === "number" &&
127
+ typeof d === "number" &&
128
+ typeof e === "number" &&
129
+ typeof f === "number") {
130
+ return new Matrix({ a, b, c, d, e, f });
131
+ }
132
+ const aN = num(a);
133
+ const bN = num(b);
134
+ const cN = num(c);
135
+ const dN = num(d);
136
+ const eN = num(e);
137
+ const fN = num(f);
138
+ // The view fully reconstructs all 6 cells (1-arg bwd ⇒ no source read).
139
+ 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]);
140
+ }
@@ -0,0 +1,62 @@
1
+ import type { Easing } from "../../animation/index.js";
2
+ import { type Tween } from "../anim.js";
3
+ import { Cell, type Init, type Val, type Writable, type WritableBrand } from "../signal.js";
4
+ import type { Linear, Pack } from "../traits.js";
5
+ import { Bool } from "./bool.js";
6
+ type V = number;
7
+ export declare const add: (a: V, b: V) => number;
8
+ export declare const sub: (a: V, b: V) => number;
9
+ export declare const scale: (a: V, k: number) => number;
10
+ export declare const lerp: (a: V, b: V, t: number) => number;
11
+ export declare const metric: (a: V, b: V) => number;
12
+ export declare const equals: (a: V, b: V) => boolean;
13
+ export declare class Num extends Cell<V> {
14
+ static traits: {
15
+ linear: Linear<number>;
16
+ lerp: (a: V, b: V, t: number) => number;
17
+ metric: (a: V, b: V) => number;
18
+ equals: (a: V, b: V) => boolean;
19
+ pack: Pack<number>;
20
+ };
21
+ readonly _t: typeof Num.traits;
22
+ constructor(v?: V);
23
+ add(b: Val<V>): this;
24
+ sub(b: Val<V>): this;
25
+ scale(k: Val<number>): this;
26
+ /** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
27
+ * for `.scale(k).add(off)`. */
28
+ affine(k: Val<number>, off: Val<number>): this;
29
+ /** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
30
+ * the range reads back clamped, not as written). */
31
+ clamp(lo: Val<V>, hi: Val<V>): this;
32
+ /** Lossy lens snapping reads/writes to the nearest multiple of `step`. */
33
+ quantize(step: Val<number>): this;
34
+ /** Cyclic-coordinate lens. Reads pass through; writes pick the
35
+ * representative closest to current modulo `period`, so dragging an
36
+ * angle never jumps a full revolution. The 2-arg bwd is arity-detected
37
+ * as stateful, threading the accumulated value through `s`. */
38
+ cyclic(period: Val<number>): this;
39
+ /** `this > t` as a Bool. Flipping the view bumps the source across
40
+ * the threshold by `eps`. */
41
+ greaterThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
42
+ /** `this < t`. Dual of `greaterThan`. */
43
+ lessThan<T extends Num>(this: T, t: Val<V>, eps?: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
44
+ /** `round(this) ≡ 0 (mod d)` as a Bool; pair with `quantize(1)` for
45
+ * integer sliders. Bwd: to make divisible, snap to the nearer
46
+ * multiple of `d`; to make non-divisible, bump by `+1`; no-op when
47
+ * the class already matches. */
48
+ divisibleBy<T extends Num>(this: T, d: Val<V>): T extends WritableBrand ? Writable<Bool> : Bool;
49
+ /** `divisibleBy(2)` — lazy getter for the common case. */
50
+ get isEven(): this extends WritableBrand ? Writable<Bool> : Bool;
51
+ /** `not(divisibleBy(2))` — lazy getter. */
52
+ get isOdd(): this extends WritableBrand ? Writable<Bool> : Bool;
53
+ /** Tween-builder; `this: Writable<Num>` gates the call to writable
54
+ * receivers. */
55
+ to(this: Writable<Num>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
56
+ }
57
+ /** Writable `Num`. Literal seeds a fresh cell; existing `Writable<Num>`
58
+ * passes through by identity. RO sources are rejected at the type level —
59
+ * use `Num.derive(...)` for reactive RO tracking, or `Num.from(...)` for
60
+ * the permissive lift over any `Val<number>`. */
61
+ export declare function num(v?: Init<Num>): Writable<Num>;
62
+ export {};
@@ -0,0 +1,166 @@
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
+ import { tween } from "../anim.js";
6
+ import { Cell, lazy, reader, } from "../signal.js";
7
+ import { Bool } from "./bool.js";
8
+ export const add = (a, b) => a + b;
9
+ export const sub = (a, b) => a - b;
10
+ export const scale = (a, k) => a * k;
11
+ export const lerp = (a, b, t) => a + (b - a) * t;
12
+ export const metric = (a, b) => Math.abs(a - b);
13
+ export const equals = (a, b) => a === b;
14
+ const linearImpl = { add, sub, scale };
15
+ const packImpl = {
16
+ dim: 1,
17
+ read: (v, a, o) => {
18
+ a[o] = v;
19
+ },
20
+ write: (a, o) => a[o],
21
+ };
22
+ export class Num extends Cell {
23
+ static traits = {
24
+ linear: linearImpl,
25
+ lerp,
26
+ metric,
27
+ equals,
28
+ pack: packImpl,
29
+ };
30
+ constructor(v = 0) {
31
+ super(v, { equals });
32
+ }
33
+ add(b) {
34
+ const bf = reader(b);
35
+ return this.lens(v => v + bf(), n => n - bf());
36
+ }
37
+ sub(b) {
38
+ const bf = reader(b);
39
+ return this.lens(v => v - bf(), n => n + bf());
40
+ }
41
+ scale(k) {
42
+ const kf = reader(k);
43
+ return this.lens(v => v * kf(), n => n / kf());
44
+ }
45
+ /** Affine `v ↦ k·v + off`. Invertible iff k ≠ 0; readability alias
46
+ * for `.scale(k).add(off)`. */
47
+ affine(k, off) {
48
+ const kf = reader(k);
49
+ const of = reader(off);
50
+ return this.lens(v => v * kf() + of(), n => (n - of()) / kf());
51
+ }
52
+ /** Lossy clamping lens to `[lo, hi]`. PutGet only (a write outside
53
+ * the range reads back clamped, not as written). */
54
+ clamp(lo, hi) {
55
+ const lf = reader(lo);
56
+ const hf = reader(hi);
57
+ const c = (v) => {
58
+ const l = lf(), h = hf();
59
+ return v < l ? l : v > h ? h : v;
60
+ };
61
+ // A write whose clamped projection matches the current view leaves
62
+ // the source untouched (off-range source preserved).
63
+ return this.lens(c, (v, s) => {
64
+ const cv = c(v);
65
+ return cv === c(s) ? s : cv;
66
+ });
67
+ }
68
+ /** Lossy lens snapping reads/writes to the nearest multiple of `step`. */
69
+ quantize(step) {
70
+ const sf = reader(step);
71
+ const q = (v) => {
72
+ const s = sf();
73
+ return Math.round(v / s) * s;
74
+ };
75
+ // A write that snaps to the current bucket leaves the source
76
+ // untouched (off-grid remainder preserved).
77
+ return this.lens(q, (v, src) => {
78
+ const qv = q(v);
79
+ return qv === q(src) ? src : qv;
80
+ });
81
+ }
82
+ /** Cyclic-coordinate lens. Reads pass through; writes pick the
83
+ * representative closest to current modulo `period`, so dragging an
84
+ * angle never jumps a full revolution. The 2-arg bwd is arity-detected
85
+ * as stateful, threading the accumulated value through `s`. */
86
+ cyclic(period) {
87
+ const pf = reader(period);
88
+ return this.lens(v => v, (v, s) => {
89
+ const p = pf();
90
+ const delta = v - s;
91
+ return s + delta - p * Math.round(delta / p);
92
+ });
93
+ }
94
+ // Predicate bridges to Bool.
95
+ //
96
+ // Cross-type quotient lenses projecting Num through a boolean
97
+ // predicate. Conditional return type: writable receiver yields
98
+ // `Writable<Bool>`, RO receiver yields RO `Bool`.
99
+ /** `this > t` as a Bool. Flipping the view bumps the source across
100
+ * the threshold by `eps`. */
101
+ greaterThan(t, eps = 1e-6) {
102
+ const tf = reader(t);
103
+ const ef = reader(eps);
104
+ return Bool.lens(this, v => v > tf(), (target, current) => {
105
+ const th = tf();
106
+ if (target === current > th)
107
+ return current;
108
+ return target ? th + ef() : th - ef();
109
+ });
110
+ }
111
+ /** `this < t`. Dual of `greaterThan`. */
112
+ lessThan(t, eps = 1e-6) {
113
+ const tf = reader(t);
114
+ const ef = reader(eps);
115
+ return Bool.lens(this, v => v < tf(), (target, current) => {
116
+ const th = tf();
117
+ if (target === current < th)
118
+ return current;
119
+ return target ? th - ef() : th + ef();
120
+ });
121
+ }
122
+ /** `round(this) ≡ 0 (mod d)` as a Bool; pair with `quantize(1)` for
123
+ * integer sliders. Bwd: to make divisible, snap to the nearer
124
+ * multiple of `d`; to make non-divisible, bump by `+1`; no-op when
125
+ * the class already matches. */
126
+ divisibleBy(d) {
127
+ const df = reader(d);
128
+ return Bool.lens(this, v => Math.round(v) % df() === 0, (target, current) => {
129
+ const dv = df();
130
+ const r = Math.round(current);
131
+ // ((a % b) + b) % b handles negative `r` cleanly.
132
+ const mod = ((r % dv) + dv) % dv;
133
+ const isDiv = mod === 0;
134
+ if (target === isDiv)
135
+ return current;
136
+ if (target) {
137
+ const down = r - mod;
138
+ const up = r + (dv - mod);
139
+ return Math.abs(current - down) <= Math.abs(current - up) ? down : up;
140
+ }
141
+ return r + 1;
142
+ });
143
+ }
144
+ /** `divisibleBy(2)` — lazy getter for the common case. */
145
+ get isEven() {
146
+ return lazy(this, "isEven", () => this.divisibleBy(2));
147
+ }
148
+ /** `not(divisibleBy(2))` — lazy getter. */
149
+ get isOdd() {
150
+ return lazy(this, "isOdd", () => this.divisibleBy(2).not());
151
+ }
152
+ /** Tween-builder; `this: Writable<Num>` gates the call to writable
153
+ * receivers. */
154
+ to(target, dur, ease) {
155
+ return tween(this, target, dur, ease);
156
+ }
157
+ }
158
+ /** Writable `Num`. Literal seeds a fresh cell; existing `Writable<Num>`
159
+ * passes through by identity. RO sources are rejected at the type level —
160
+ * use `Num.derive(...)` for reactive RO tracking, or `Num.from(...)` for
161
+ * the permissive lift over any `Val<number>`. */
162
+ export function num(v = 0) {
163
+ if (v instanceof Num)
164
+ return v;
165
+ return new Num(v);
166
+ }
@@ -0,0 +1,31 @@
1
+ import { Cell, type Init, type Writable } from "../signal.js";
2
+ import type { Linear, Pack, Pivotal } from "../traits.js";
3
+ type V = {
4
+ x: number;
5
+ y: number;
6
+ theta: number;
7
+ };
8
+ export declare const add: (a: V, b: V) => V;
9
+ export declare const sub: (a: V, b: V) => V;
10
+ export declare const scale: (a: V, k: number) => V;
11
+ export declare const lerp: (a: V, b: V, t: number) => V;
12
+ export declare const metric: (a: V, b: V) => number;
13
+ export declare const equals: (a: V, b: V) => boolean;
14
+ export declare class Pose extends Cell<V> {
15
+ static traits: {
16
+ linear: Linear<V>;
17
+ lerp: (a: V, b: V, t: number) => V;
18
+ metric: (a: V, b: V) => number;
19
+ equals: (a: V, b: V) => boolean;
20
+ pack: Pack<V>;
21
+ pivotal: Pivotal<V>;
22
+ };
23
+ readonly _t: typeof Pose.traits;
24
+ constructor(v?: V);
25
+ }
26
+ /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
27
+ * through by identity. RO sources are rejected at the type level — use
28
+ * `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
29
+ * snapshot. */
30
+ export declare function pose(v?: Init<Pose>): Writable<Pose>;
31
+ export {};
@@ -0,0 +1,83 @@
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
+ import { Cell } from "../signal.js";
8
+ export const add = (a, b) => ({
9
+ x: a.x + b.x,
10
+ y: a.y + b.y,
11
+ theta: a.theta + b.theta,
12
+ });
13
+ export const sub = (a, b) => ({
14
+ x: a.x - b.x,
15
+ y: a.y - b.y,
16
+ theta: a.theta - b.theta,
17
+ });
18
+ export const scale = (a, k) => ({
19
+ x: a.x * k,
20
+ y: a.y * k,
21
+ theta: a.theta * k,
22
+ });
23
+ export const lerp = (a, b, t) => ({
24
+ x: a.x + (b.x - a.x) * t,
25
+ y: a.y + (b.y - a.y) * t,
26
+ theta: a.theta + (b.theta - a.theta) * t,
27
+ });
28
+ export const metric = (a, b) => Math.hypot(a.x - b.x, a.y - b.y, a.theta - b.theta);
29
+ export const equals = (a, b) => a === b || (a.x === b.x && a.y === b.y && a.theta === b.theta);
30
+ const linearImpl = { add, sub, scale };
31
+ const packImpl = {
32
+ dim: 3,
33
+ read: (v, a, o) => {
34
+ a[o] = v.x;
35
+ a[o + 1] = v.y;
36
+ a[o + 2] = v.theta;
37
+ },
38
+ write: (a, o) => ({ x: a[o], y: a[o + 1], theta: a[o + 2] }),
39
+ };
40
+ /** Rotate-about-pivot moves the position and adds dθ to orientation;
41
+ * scale-about-pivot scales position, orientation untouched. */
42
+ const pivotalImpl = {
43
+ rotateAbout: (v, p, dθ) => {
44
+ const cos = Math.cos(dθ);
45
+ const sin = Math.sin(dθ);
46
+ const dx = v.x - p.x;
47
+ const dy = v.y - p.y;
48
+ return {
49
+ x: p.x + cos * dx - sin * dy,
50
+ y: p.y + sin * dx + cos * dy,
51
+ theta: v.theta + dθ,
52
+ };
53
+ },
54
+ scaleAbout: (v, p, k) => ({
55
+ x: p.x + k * (v.x - p.x),
56
+ y: p.y + k * (v.y - p.y),
57
+ theta: v.theta,
58
+ }),
59
+ };
60
+ export class Pose extends Cell {
61
+ static traits = {
62
+ linear: linearImpl,
63
+ lerp,
64
+ metric,
65
+ equals,
66
+ pack: packImpl,
67
+ pivotal: pivotalImpl,
68
+ };
69
+ constructor(v = { x: 0, y: 0, theta: 0 }) {
70
+ super(v, { equals });
71
+ }
72
+ }
73
+ /** Writable `Pose`. Literal seeds a fresh cell; existing `Pose` passes
74
+ * through by identity. RO sources are rejected at the type level — use
75
+ * `Pose.derive(...)` for reactive RO tracking, or `cell.value` to
76
+ * snapshot. */
77
+ export function pose(v = { x: 0, y: 0, theta: 0 }) {
78
+ if (v instanceof Pose)
79
+ return v;
80
+ const p = new Pose();
81
+ p.value = v;
82
+ return p;
83
+ }
@@ -0,0 +1,83 @@
1
+ import type { Easing } from "../../animation/index.js";
2
+ import { type Tween } from "../anim.js";
3
+ import { Cell, type Init, type Val, type Writable, type WritableBrand } from "../signal.js";
4
+ import type { Linear, Pack } from "../traits.js";
5
+ import { Bool } from "./bool.js";
6
+ import { Num } from "./num.js";
7
+ type V = {
8
+ lo: number;
9
+ hi: number;
10
+ };
11
+ export declare const add: (a: V, b: V) => V;
12
+ export declare const sub: (a: V, b: V) => V;
13
+ export declare const scale: (a: V, k: number) => V;
14
+ export declare const lerp: (a: V, b: V, t: number) => V;
15
+ export declare const equals: (a: V, b: V) => boolean;
16
+ /** L2 distance over (lo, hi). Treats a range as a point in 2-space. */
17
+ export declare const metric: (a: V, b: V) => number;
18
+ export declare const width: (r: V) => number;
19
+ export declare const center: (r: V) => number;
20
+ export declare const contains: (r: V, v: number) => boolean;
21
+ export declare const clamp: (r: V, v: number) => number;
22
+ /** Closest value STRICTLY outside `[lo, hi]`, displaced past the
23
+ * nearest endpoint by `eps`. Used by `Range#contains` as the bwd's
24
+ * false-side policy. */
25
+ export declare const eject: (r: V, v: number, eps?: number) => number;
26
+ /** Sample at parameter `t`: `lo + t·(hi - lo)`. `t ∈ [0, 1]` stays
27
+ * inside the range; values outside extrapolate linearly. */
28
+ export declare const sample: (r: V, t: number) => number;
29
+ /** Inverse of `sample`: given a value, recover the `t` that would
30
+ * produce it. Degenerate (zero-width) ranges return 0. */
31
+ export declare const paramOf: (r: V, v: number) => number;
32
+ export declare class Range extends Cell<V> {
33
+ static traits: {
34
+ linear: Linear<V>;
35
+ lerp: (a: V, b: V, t: number) => V;
36
+ metric: (a: V, b: V) => number;
37
+ equals: (a: V, b: V) => boolean;
38
+ pack: Pack<V>;
39
+ };
40
+ readonly _t: typeof Range.traits;
41
+ constructor(v?: V);
42
+ /** Start endpoint. Writes preserve `hi` (start-knob semantics). */
43
+ get lo(): this extends WritableBrand ? Writable<Num> : Num;
44
+ /** End endpoint. Writes preserve `lo` (end-knob semantics). */
45
+ get hi(): this extends WritableBrand ? Writable<Num> : Num;
46
+ get width(): Num;
47
+ get center(): Num;
48
+ /** Translate by `by`. Reads shift the interval; writes shift back. */
49
+ shift(by: Val<number>): this;
50
+ /** Scale uniformly about the origin. Iso for `k ≠ 0`. */
51
+ scale(k: Val<number>): this;
52
+ /** Body-drag handle: read returns `lo`; write shifts the range so `lo`
53
+ * matches (width preserved). For start-knob editing use `.lo`. */
54
+ get start(): Writable<Num>;
55
+ /** RO sample at `t`. `t ∈ [0, 1]` stays inside; outside extrapolates. */
56
+ sample(t: Val<number>): Num;
57
+ /** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
58
+ * solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
59
+ slider(t: Writable<Num>): Writable<Num>;
60
+ /** Membership predicate. Conditional return type: a writable `Num`
61
+ * yields `Writable<Bool>` and flipping the view bumps the source
62
+ * (`true` clamps into `[lo, hi]`, `false` ejects past the nearest
63
+ * endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
64
+ contains<P extends Val<number>>(v: P): P extends WritableBrand ? Writable<Bool> : Bool;
65
+ /** RO clamp: read `v` into `[lo, hi]`. For a writable clamping lens
66
+ * on a single Num, see `Num#clamp(lo, hi)`. */
67
+ clampedRead(v: Val<number>): Num;
68
+ /** Inverse of `sample`: derive the `t` that would produce `v`. */
69
+ paramOf(v: Val<number>): Num;
70
+ /** Tween-builder; animates `{lo, hi}` jointly. */
71
+ to(this: Writable<Range>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
72
+ }
73
+ /** Range over `[at, at + dur]`, parameterised by start + duration. The
74
+ * timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
75
+ * body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
76
+ export declare function span(at: Writable<Num>, dur: Writable<Num>): Writable<Range>;
77
+ /** Writable `Range` over `[lo, hi]`. Each endpoint is a literal `number`
78
+ * (lifted to a fresh seed) or an existing `Writable<Num>` (identity
79
+ * passthrough). RO sources are rejected at the type level — use
80
+ * `Range.derive(...)` for reactive RO tracking, or `cell.value` to
81
+ * snapshot. Lock an endpoint with `Num.pin(c)`. */
82
+ export declare function range(lo?: Init<Num>, hi?: Init<Num>): Writable<Range>;
83
+ export {};