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,83 @@
1
+ import { type Cell, type Lifecycle, type Read } from "../core/index.js";
2
+ import { type Phase } from "./phases.js";
3
+ import { Solver, type SolverOpts } from "./solver.js";
4
+ /** A constraint relation. `bind(c)` sets up (cell binding, term
5
+ * registration, …) and returns a disposer. */
6
+ export interface Relation {
7
+ bind(c: Constraints): () => void;
8
+ }
9
+ export declare class Constraints {
10
+ /** The numerical solver underneath. Phases read/write its buffers. */
11
+ readonly solver: Solver;
12
+ /** Phases run on each `step(dt)`. Mutable — factories specialise by
13
+ * reassigning. Defaults to the reactive pipeline. */
14
+ pipeline: Phase[];
15
+ private readonly _sigToCell;
16
+ private readonly _bindings;
17
+ /** Active-relation disposers, keyed by relation reference. */
18
+ private readonly _disposers;
19
+ /** Add / remove hooks for factories tracking relation kinds (e.g.
20
+ * `world` tracks `Body` instances for the broadphase). */
21
+ private readonly _addHooks;
22
+ private readonly _removeHooks;
23
+ /** Bumped on structural change so the reactive driver re-fires. */
24
+ private readonly _gen;
25
+ /** Reactive driver: a network calling `step()` on signal change.
26
+ * Lazy-installed on first `_bind`; permanently silenced once
27
+ * `dispose()`d (e.g. when physics/world take the time loop). */
28
+ private _network?;
29
+ private _networkDisposed;
30
+ constructor(opts?: SolverOpts);
31
+ get iterations(): number;
32
+ set iterations(v: number);
33
+ get alpha(): number;
34
+ set alpha(v: number);
35
+ get beta(): number;
36
+ set beta(v: number);
37
+ get gamma(): number;
38
+ set gamma(v: number);
39
+ get postStabilize(): boolean;
40
+ set postStabilize(v: boolean);
41
+ /** Run the pipeline once. `dt` defaults to `1` (static-edit case);
42
+ * physics callers pass the real frame `dt`. */
43
+ step(dt?: number): void;
44
+ /** Add one or more relations. Single-arg returns the relation;
45
+ * multi-arg returns an array (destructure as needed). */
46
+ add<R extends Relation>(rel: R): R;
47
+ add<R extends Relation>(rel1: R, rel2: R, ...rest: R[]): R[];
48
+ private _addOne;
49
+ /** Add `rels` while `cond` is truthy; remove them when falsy. */
50
+ addWhile(cond: Read<unknown>, ...rels: Relation[]): Lifecycle;
51
+ /** Remove a relation. No-op if not previously added. */
52
+ remove(rel: Relation): void;
53
+ /** Subscribe to relation-add events. Returns an unsubscribe thunk.
54
+ * Called synchronously from `add` after the relation's `bind` runs. */
55
+ onAdd(fn: (rel: Relation) => void): () => void;
56
+ /** Subscribe to relation-remove events. Returns an unsubscribe thunk. */
57
+ onRemove(fn: (rel: Relation) => void): () => void;
58
+ /** Tear down the reactive driver, permanently — later `add`/`_bind`
59
+ * won't re-install it. Bound signals keep their values but stop
60
+ * being constraint-driven; `step(dt)` is the only way to advance.
61
+ * Physics-flavored factories call this to take the time loop. */
62
+ dispose(): void;
63
+ /** Number of bound signal cells (= solver cell count). */
64
+ get cellCount(): number;
65
+ /** @internal — bind a signal as a cell. Idempotent (same signal →
66
+ * same id); cells are append-only for the cluster's lifetime. */
67
+ _bind(sig: Cell<any>): number;
68
+ /** @internal — subscribe a reactive Term parameter. Without this,
69
+ * mutating the param wouldn't fire the network (body reads don't
70
+ * auto-track). Called from relations with reactive params. */
71
+ _trackParam(sig: Cell<any>): void;
72
+ /** Params bound before the network existed; folded in at install. */
73
+ private _pendingParamDeps;
74
+ private _installReactiveDriver;
75
+ }
76
+ /** Build a reactive `Constraints` (sketchpad / IK / layout, no time
77
+ * integration). For physics use `physics(opts)` or `world(opts)`.
78
+ *
79
+ * const c = constraints({ iterations: 24 });
80
+ * c.add(distance(a, b, 100));
81
+ * c.iterations = 30;
82
+ */
83
+ export declare function constraints(opts?: SolverOpts): Constraints;
@@ -0,0 +1,213 @@
1
+ // cluster.ts — the `Constraints` holder.
2
+ //
3
+ // Holds a `Solver`, a registry of relations, and a `pipeline` of
4
+ // phases run on every `step(dt)`. The default reactive pipeline plus
5
+ // a driver that fires on bound-signal changes make a freshly
6
+ // constructed `Constraints` a sketchpad-style reactive solver with
7
+ // no further setup.
8
+ //
9
+ // Specialised factories (`physics`, `world`, …) overwrite the
10
+ // pipeline, allocate their own per-cell state, and dispose the
11
+ // reactive driver to take over the time loop.
12
+ //
13
+ // Relation contract: `bind(c)` registers what the relation needs and
14
+ // returns a disposer. `c.add(rel)` calls `bind`; `c.remove(rel)`
15
+ // calls the disposer.
16
+ import { cell, network, requirePack, } from "../core/index.js";
17
+ import { when } from "../core/network-utils.js";
18
+ import { reactivePipeline } from "./phases.js";
19
+ import { Solver } from "./solver.js";
20
+ export class Constraints {
21
+ /** The numerical solver underneath. Phases read/write its buffers. */
22
+ solver;
23
+ /** Phases run on each `step(dt)`. Mutable — factories specialise by
24
+ * reassigning. Defaults to the reactive pipeline. */
25
+ pipeline;
26
+ // biome-ignore lint/suspicious/noExplicitAny: heterogeneous binding registry
27
+ _sigToCell = new Map();
28
+ _bindings = [];
29
+ /** Active-relation disposers, keyed by relation reference. */
30
+ _disposers = new Map();
31
+ /** Add / remove hooks for factories tracking relation kinds (e.g.
32
+ * `world` tracks `Body` instances for the broadphase). */
33
+ _addHooks = new Set();
34
+ _removeHooks = new Set();
35
+ /** Bumped on structural change so the reactive driver re-fires. */
36
+ _gen;
37
+ /** Reactive driver: a network calling `step()` on signal change.
38
+ * Lazy-installed on first `_bind`; permanently silenced once
39
+ * `dispose()`d (e.g. when physics/world take the time loop). */
40
+ _network;
41
+ _networkDisposed = false;
42
+ constructor(opts = {}) {
43
+ this.solver = new Solver(opts);
44
+ this._gen = cell(0);
45
+ this.pipeline = reactivePipeline.slice();
46
+ }
47
+ get iterations() {
48
+ return this.solver.iterations;
49
+ }
50
+ set iterations(v) {
51
+ this.solver.iterations = v;
52
+ }
53
+ get alpha() {
54
+ return this.solver.alpha;
55
+ }
56
+ set alpha(v) {
57
+ this.solver.alpha = v;
58
+ }
59
+ get beta() {
60
+ return this.solver.beta;
61
+ }
62
+ set beta(v) {
63
+ this.solver.beta = v;
64
+ }
65
+ get gamma() {
66
+ return this.solver.gamma;
67
+ }
68
+ set gamma(v) {
69
+ this.solver.gamma = v;
70
+ }
71
+ get postStabilize() {
72
+ return this.solver.postStabilize;
73
+ }
74
+ set postStabilize(v) {
75
+ this.solver.postStabilize = v;
76
+ }
77
+ /** Run the pipeline once. `dt` defaults to `1` (static-edit case);
78
+ * physics callers pass the real frame `dt`. */
79
+ step(dt = 1) {
80
+ const p = this.pipeline;
81
+ for (let i = 0; i < p.length; i++)
82
+ p[i](this, dt);
83
+ }
84
+ add(...rels) {
85
+ for (const rel of rels)
86
+ this._addOne(rel);
87
+ return rels.length === 1 ? rels[0] : rels;
88
+ }
89
+ _addOne(rel) {
90
+ if (this._disposers.has(rel))
91
+ return;
92
+ const dispose = rel.bind(this);
93
+ this._disposers.set(rel, dispose);
94
+ for (const fn of this._addHooks)
95
+ fn(rel);
96
+ }
97
+ /** Add `rels` while `cond` is truthy; remove them when falsy. */
98
+ addWhile(cond, ...rels) {
99
+ return when(cond, () => {
100
+ for (const r of rels)
101
+ this._addOne(r);
102
+ return () => {
103
+ for (const r of rels) {
104
+ const dispose = this._disposers.get(r);
105
+ if (dispose === undefined)
106
+ continue;
107
+ dispose();
108
+ this._disposers.delete(r);
109
+ for (const fn of this._removeHooks)
110
+ fn(r);
111
+ }
112
+ this._gen.value += 1;
113
+ };
114
+ });
115
+ }
116
+ /** Remove a relation. No-op if not previously added. */
117
+ remove(rel) {
118
+ const dispose = this._disposers.get(rel);
119
+ if (dispose === undefined)
120
+ return;
121
+ dispose();
122
+ this._disposers.delete(rel);
123
+ for (const fn of this._removeHooks)
124
+ fn(rel);
125
+ this._gen.value += 1;
126
+ }
127
+ /** Subscribe to relation-add events. Returns an unsubscribe thunk.
128
+ * Called synchronously from `add` after the relation's `bind` runs. */
129
+ onAdd(fn) {
130
+ this._addHooks.add(fn);
131
+ return () => this._addHooks.delete(fn);
132
+ }
133
+ /** Subscribe to relation-remove events. Returns an unsubscribe thunk. */
134
+ onRemove(fn) {
135
+ this._removeHooks.add(fn);
136
+ return () => this._removeHooks.delete(fn);
137
+ }
138
+ /** Tear down the reactive driver, permanently — later `add`/`_bind`
139
+ * won't re-install it. Bound signals keep their values but stop
140
+ * being constraint-driven; `step(dt)` is the only way to advance.
141
+ * Physics-flavored factories call this to take the time loop. */
142
+ dispose() {
143
+ if (this._network !== undefined) {
144
+ this._network.dispose();
145
+ this._network = undefined;
146
+ }
147
+ this._networkDisposed = true;
148
+ }
149
+ /** Number of bound signal cells (= solver cell count). */
150
+ get cellCount() {
151
+ return this._sigToCell.size;
152
+ }
153
+ /** @internal — bind a signal as a cell. Idempotent (same signal →
154
+ * same id); cells are append-only for the cluster's lifetime. */
155
+ // biome-ignore lint/suspicious/noExplicitAny: see header
156
+ _bind(sig) {
157
+ const existing = this._sigToCell.get(sig);
158
+ if (existing !== undefined)
159
+ return existing;
160
+ const pack = requirePack(sig);
161
+ const id = this.solver.addCell(pack.dim);
162
+ pack.read(sig.peek(), this.solver.positions, this.solver.offsets[id]);
163
+ this._sigToCell.set(sig, id);
164
+ this._bindings[id] = { sig, pack };
165
+ if (this._network === undefined && !this._networkDisposed) {
166
+ this._installReactiveDriver();
167
+ }
168
+ else if (this._network !== undefined) {
169
+ // Network already running — subscribe the new cell so its later
170
+ // `.value` mutations fire the body and trigger a solve.
171
+ this._network.subscribe(sig);
172
+ }
173
+ this._gen.value += 1;
174
+ return id;
175
+ }
176
+ /** @internal — subscribe a reactive Term parameter. Without this,
177
+ * mutating the param wouldn't fire the network (body reads don't
178
+ * auto-track). Called from relations with reactive params. */
179
+ // biome-ignore lint/suspicious/noExplicitAny: heterogeneous params
180
+ _trackParam(sig) {
181
+ if (this._network !== undefined)
182
+ this._network.subscribe(sig);
183
+ else
184
+ this._pendingParamDeps.push(sig);
185
+ }
186
+ /** Params bound before the network existed; folded in at install. */
187
+ // biome-ignore lint/suspicious/noExplicitAny: same
188
+ _pendingParamDeps = [];
189
+ _installReactiveDriver() {
190
+ const gen = this._gen;
191
+ const initialDeps = [gen];
192
+ for (const [sig] of this._sigToCell)
193
+ initialDeps.push(sig);
194
+ for (const sig of this._pendingParamDeps)
195
+ initialDeps.push(sig);
196
+ this._pendingParamDeps.length = 0;
197
+ this._network = network(initialDeps, () => {
198
+ // Explicit-deps mode: body reads don't subscribe; deps come from
199
+ // the initial array + later `subscribe(...)` in `_bind`/`_trackParam`.
200
+ this.step();
201
+ });
202
+ }
203
+ }
204
+ /** Build a reactive `Constraints` (sketchpad / IK / layout, no time
205
+ * integration). For physics use `physics(opts)` or `world(opts)`.
206
+ *
207
+ * const c = constraints({ iterations: 24 });
208
+ * c.add(distance(a, b, 100));
209
+ * c.iterations = 30;
210
+ */
211
+ export function constraints(opts = {}) {
212
+ return new Constraints(opts);
213
+ }
@@ -0,0 +1,15 @@
1
+ import type { Tick } from "../animation/anim.js";
2
+ import type { Constraints } from "./cluster.js";
3
+ /** Real-time driver: advance by the actual frame `dt`. Mainly for
4
+ * cloth / particle physics (reactive scenes self-fire). */
5
+ export declare function animate(c: Constraints): Generator<undefined, never, Tick>;
6
+ /** Fixed-`dt` sub-stepping: accumulate frame time and fire as many
7
+ * fixed steps as fit (capped at `maxSubSteps` against the
8
+ * spiral-of-death). Fixed `dt` avoids the jitter variable `dt`
9
+ * causes in warm-start / extrapolation / velocity. Defaults
10
+ * (1/60s, 4) match the AVBD demo. */
11
+ export declare function fixedStep(c: Constraints, fixedDt: number, maxSubSteps?: number): Generator<undefined, never, Tick>;
12
+ /** Time-dilated driver: scale the wall-clock dt by `factor()` each
13
+ * frame. `factor` is a thunk so callers can flip it live (slow-mo
14
+ * toggles, pause via `factor: () => 0`, scrubbing, etc.). */
15
+ export declare function dilated(c: Constraints, factor: () => number): Generator<undefined, never, Tick>;
@@ -0,0 +1,40 @@
1
+ // drivers.ts — animation drivers for `Constraints.step(dt)`.
2
+ //
3
+ // Generators that wrap `c.step(dt)` with different rhythms. Driven by
4
+ // any `Tick`-yielding animation loop (`anim.start(...)`). Custom
5
+ // drivers (scrubbing, pause/resume) just yield then call `c.step`.
6
+ /** Real-time driver: advance by the actual frame `dt`. Mainly for
7
+ * cloth / particle physics (reactive scenes self-fire). */
8
+ export function* animate(c) {
9
+ for (;;) {
10
+ const tick = yield;
11
+ c.step(tick.dt);
12
+ }
13
+ }
14
+ /** Fixed-`dt` sub-stepping: accumulate frame time and fire as many
15
+ * fixed steps as fit (capped at `maxSubSteps` against the
16
+ * spiral-of-death). Fixed `dt` avoids the jitter variable `dt`
17
+ * causes in warm-start / extrapolation / velocity. Defaults
18
+ * (1/60s, 4) match the AVBD demo. */
19
+ export function* fixedStep(c, fixedDt, maxSubSteps = 4) {
20
+ let acc = 0;
21
+ for (;;) {
22
+ const tick = yield;
23
+ acc += Math.min(tick.dt, fixedDt * maxSubSteps);
24
+ let n = 0;
25
+ while (acc >= fixedDt && n < maxSubSteps) {
26
+ c.step(fixedDt);
27
+ acc -= fixedDt;
28
+ n++;
29
+ }
30
+ }
31
+ }
32
+ /** Time-dilated driver: scale the wall-clock dt by `factor()` each
33
+ * frame. `factor` is a thunk so callers can flip it live (slow-mo
34
+ * toggles, pause via `factor: () => 0`, scrubbing, etc.). */
35
+ export function* dilated(c, factor) {
36
+ for (;;) {
37
+ const tick = yield;
38
+ c.step(tick.dt * factor());
39
+ }
40
+ }
@@ -0,0 +1,73 @@
1
+ import { type Cell, type Writable } from "../core/index.js";
2
+ import type { Relation } from "./cluster.js";
3
+ import { type ResidualFn, Strength } from "./terms.js";
4
+ export { Strength };
5
+ type S = Cell<any>;
6
+ /** Pin a signal in place: while attached, its solver cell has mass 0
7
+ * (kinematic). Removing the relation restores the prior mass.
8
+ *
9
+ * c.add(pin(O1)); // static pin
10
+ * c.addWhile(h.dragging, pin(sig)); // conditional pin */
11
+ export declare function pin(sig: S): Relation;
12
+ /** Hard equality `a = b`. Cell dims must match. */
13
+ export declare function eq(a: S, b: S): Relation;
14
+ /** Distance constraint `‖b − a‖ = rest`. Hard by default; pass
15
+ * `stiffness` for a Hooke spring. `rest` and `stiffness` are mutable
16
+ * signals on the returned relation. */
17
+ export interface DistanceRelation extends Relation {
18
+ readonly rest: Writable<Cell<number>>;
19
+ /** Present only for the spring (finite-stiffness) variant. */
20
+ readonly stiffness?: Writable<Cell<number>>;
21
+ }
22
+ export declare function distance(a: S, b: S, rest: number | Writable<Cell<number>>, opts?: {
23
+ stiffness?: number | Writable<Cell<number>>;
24
+ }): DistanceRelation;
25
+ /** Soft distance constraint — alias for `distance(a, b, rest, { stiffness })`. */
26
+ export declare function spring(a: S, b: S, rest: number | Writable<Cell<number>>, stiffness: number | Writable<Cell<number>>): DistanceRelation;
27
+ /** Scalar relation `b = fwd(a)` between two `Num` signals. */
28
+ export declare function lensNum(a: S, b: S, fwd: (x: number) => number): Relation;
29
+ /** Hard 1D range `lo ≤ x ≤ hi`. `r.lo` / `r.hi` are mutable signals. */
30
+ export declare function clamp(x: S, lo: number | Writable<Cell<number>>, hi: number | Writable<Cell<number>>): Relation & {
31
+ lo: Writable<Cell<number>>;
32
+ hi: Writable<Cell<number>>;
33
+ };
34
+ /** Hard minimum distance: `‖b − a‖ ≥ minDist`. */
35
+ export declare function gap(a: S, b: S, minDist: number): Relation;
36
+ /** Soft long-range repulsion: pushes two points apart with force
37
+ * `stiffness · (range − ‖b − a‖)` while they're closer than `range`,
38
+ * dropping to zero outside. */
39
+ export declare function repel(a: S, b: S, range: number, stiffness: number): Relation;
40
+ /** Hard rectangular containment: keep a `Vec` inside the AABB
41
+ * `[xLo, xHi] × [yLo, yHi]`. */
42
+ export declare function inside(P: S, xLo: number, yLo: number, xHi: number, yHi: number): Relation;
43
+ /** Hard inequality `a ≤ b`. */
44
+ export declare function leq(a: S, b: S): Relation;
45
+ /** Hard inequality `a ≥ b`. */
46
+ export declare function geq(a: S, b: S): Relation;
47
+ /** Pull `cell` toward `target` with finite stiffness. */
48
+ export declare function softTarget(cell: S, target: ArrayLike<number>, stiffness: number): Relation;
49
+ /** Custom constraint with `rows` residual outputs computed by `fn`. */
50
+ export declare function generic(cells: readonly S[], rows: number, fn: ResidualFn, opts?: {
51
+ fdStep?: number;
52
+ hard?: boolean;
53
+ stiffness?: number;
54
+ lambdaMax?: readonly number[];
55
+ }): Relation;
56
+ /** Interior angle ABC = θ. */
57
+ export declare function angle(A: S, B: S, C: S, theta: number): Relation;
58
+ /** Lines AB ∥ CD: cross product of direction vectors = 0. */
59
+ export declare function parallel(A: S, B: S, C: S, D: S): Relation;
60
+ /** Lines AB ⟂ CD: dot product = 0. */
61
+ export declare function perpendicular(A: S, B: S, C: S, D: S): Relation;
62
+ /** Right angle at B between segments AB and BC. */
63
+ export declare function rightAngle(A: S, B: S, C: S): Relation;
64
+ /** Soft 3-point bending resistance at vertex B. */
65
+ export declare function bend(A: S, B: S, C: S, stiffness?: number): Relation;
66
+ /** Point P on line AB. */
67
+ export declare function collinear(P: S, A: S, B: S): Relation;
68
+ /** Point P on a circle of given center and radius. */
69
+ export declare function onCircle(P: S, center: S, radius: number): Relation;
70
+ /** Equal distance: ‖A − B‖ = ‖C − D‖. */
71
+ export declare function equalDist(A: S, B: S, C: S, D: S): Relation;
72
+ /** Midpoint: M = (A + B) / 2. */
73
+ export declare function midpoint(M: S, A: S, B: S): Relation;
@@ -0,0 +1,248 @@
1
+ // factories.ts — free constraint factories returning `Relation`s.
2
+ //
3
+ // Each returns a plain object with a `bind(c)` (→ disposer) plus
4
+ // `Cell` fields for mutable params. Pass a number (wrapped in a
5
+ // fresh signal) or your own signal (used directly, so UI bindings
6
+ // flow through):
7
+ //
8
+ // const len = signal(100);
9
+ // const r = c.add(distance(a, b, len));
10
+ // len.value = 50; // re-solves
11
+ // r.rest.value = 75; // same underlying signal
12
+ //
13
+ // Cell-signal args must declare the `pack` trait (checked in `_bind`).
14
+ //
15
+ // Caveats:
16
+ // - Multi-solution constraints can branch-flip under fast drags (no
17
+ // branch tracking).
18
+ // - Infeasible configs saturate, not explode (λ capped at LAMBDA_MAX).
19
+ // - Duplicate cells in `generic` (e.g. `[A, B, B, C]`) are treated as
20
+ // independent by the FD path — use `rightAngle(A, B, C)`, not
21
+ // `perpendicular(A, B, B, C)`.
22
+ import { cell } from "../core/index.js";
23
+ import { BoundsTerm, DistanceTerm, EqTerm, GenericTerm, LensNumTerm, SoftTargetTerm, Strength, } from "./terms.js";
24
+ export { Strength };
25
+ /** Pin a signal in place: while attached, its solver cell has mass 0
26
+ * (kinematic). Removing the relation restores the prior mass.
27
+ *
28
+ * c.add(pin(O1)); // static pin
29
+ * c.addWhile(h.dragging, pin(sig)); // conditional pin */
30
+ export function pin(sig) {
31
+ return {
32
+ bind(c) {
33
+ const id = c._bind(sig);
34
+ const prev = c.solver.massOf(id);
35
+ c.solver.setMass(id, 0);
36
+ return () => c.solver.setMass(id, prev);
37
+ },
38
+ };
39
+ }
40
+ /** Hard equality `a = b`. Cell dims must match. */
41
+ export function eq(a, b) {
42
+ return {
43
+ bind(c) {
44
+ const f = new EqTerm(c.solver, c._bind(a), c._bind(b));
45
+ c.solver.addTerm(f);
46
+ return () => c.solver.removeTerm(f);
47
+ },
48
+ };
49
+ }
50
+ export function distance(a, b, rest, opts) {
51
+ const rest_ = cell(rest);
52
+ const hard = opts?.stiffness === undefined;
53
+ const stiff_ = hard ? undefined : cell(opts.stiffness);
54
+ return {
55
+ rest: rest_,
56
+ stiffness: stiff_,
57
+ bind(c) {
58
+ const f = new DistanceTerm(c.solver, c._bind(a), c._bind(b), rest_, hard, stiff_);
59
+ c.solver.addTerm(f);
60
+ // Track params so mutating rest / stiffness fires the network.
61
+ c._trackParam(rest_);
62
+ if (stiff_ !== undefined)
63
+ c._trackParam(stiff_);
64
+ return () => c.solver.removeTerm(f);
65
+ },
66
+ };
67
+ }
68
+ /** Soft distance constraint — alias for `distance(a, b, rest, { stiffness })`. */
69
+ export function spring(a, b, rest, stiffness) {
70
+ return distance(a, b, rest, { stiffness });
71
+ }
72
+ /** Scalar relation `b = fwd(a)` between two `Num` signals. */
73
+ export function lensNum(a, b, fwd) {
74
+ return {
75
+ bind(c) {
76
+ const f = new LensNumTerm(c.solver, c._bind(a), c._bind(b), fwd);
77
+ c.solver.addTerm(f);
78
+ return () => c.solver.removeTerm(f);
79
+ },
80
+ };
81
+ }
82
+ /** Hard 1D range `lo ≤ x ≤ hi`. `r.lo` / `r.hi` are mutable signals. */
83
+ export function clamp(x, lo, hi) {
84
+ const lo_ = cell(lo);
85
+ const hi_ = cell(hi);
86
+ return {
87
+ lo: lo_,
88
+ hi: hi_,
89
+ bind(c) {
90
+ const f = new BoundsTerm(c.solver, c._bind(x), lo_, hi_);
91
+ c.solver.addTerm(f);
92
+ c._trackParam(lo_);
93
+ c._trackParam(hi_);
94
+ return () => c.solver.removeTerm(f);
95
+ },
96
+ };
97
+ }
98
+ /** Hard minimum distance: `‖b − a‖ ≥ minDist`. */
99
+ export function gap(a, b, minDist) {
100
+ return generic([a, b], 1, (pos, out) => {
101
+ const dx = pos[1][0] - pos[0][0];
102
+ const dy = pos[1][1] - pos[0][1];
103
+ out[0] = Math.hypot(dx, dy) - minDist;
104
+ }, { lambdaMax: [0] });
105
+ }
106
+ /** Soft long-range repulsion: pushes two points apart with force
107
+ * `stiffness · (range − ‖b − a‖)` while they're closer than `range`,
108
+ * dropping to zero outside. */
109
+ export function repel(a, b, range, stiffness) {
110
+ return generic([a, b], 1, (pos, out) => {
111
+ const dx = pos[1][0] - pos[0][0];
112
+ const dy = pos[1][1] - pos[0][1];
113
+ out[0] = Math.hypot(dx, dy) - range;
114
+ }, { hard: false, stiffness, lambdaMax: [0] });
115
+ }
116
+ /** Hard rectangular containment: keep a `Vec` inside the AABB
117
+ * `[xLo, xHi] × [yLo, yHi]`. */
118
+ export function inside(P, xLo, yLo, xHi, yHi) {
119
+ return generic([P], 4, (pos, out) => {
120
+ const p = pos[0];
121
+ out[0] = p[0] - xLo;
122
+ out[1] = xHi - p[0];
123
+ out[2] = p[1] - yLo;
124
+ out[3] = yHi - p[1];
125
+ }, { lambdaMax: [0, 0, 0, 0] });
126
+ }
127
+ /** Hard inequality `a ≤ b`. */
128
+ export function leq(a, b) {
129
+ return generic([a, b], 1, (pos, out) => {
130
+ out[0] = pos[1][0] - pos[0][0];
131
+ }, { lambdaMax: [0] });
132
+ }
133
+ /** Hard inequality `a ≥ b`. */
134
+ export function geq(a, b) {
135
+ return leq(b, a);
136
+ }
137
+ /** Pull `cell` toward `target` with finite stiffness. */
138
+ export function softTarget(cell, target, stiffness) {
139
+ return {
140
+ bind(c) {
141
+ const f = new SoftTargetTerm(c.solver, c._bind(cell), target, stiffness);
142
+ c.solver.addTerm(f);
143
+ return () => c.solver.removeTerm(f);
144
+ },
145
+ };
146
+ }
147
+ /** Custom constraint with `rows` residual outputs computed by `fn`. */
148
+ export function generic(cells, rows, fn, opts) {
149
+ return {
150
+ bind(c) {
151
+ const f = new GenericTerm(c.solver, cells.map(s => c._bind(s)), rows, fn, opts);
152
+ c.solver.addTerm(f);
153
+ if (opts?.lambdaMax) {
154
+ for (let i = 0; i < opts.lambdaMax.length && i < rows; i++) {
155
+ f.lambdaMax[i] = opts.lambdaMax[i];
156
+ }
157
+ }
158
+ return () => c.solver.removeTerm(f);
159
+ },
160
+ };
161
+ }
162
+ /** Interior angle ABC = θ. */
163
+ export function angle(A, B, C, theta) {
164
+ return generic([A, B, C], 1, (pos, out) => {
165
+ const a = pos[0], b = pos[1], cc = pos[2];
166
+ const ux = a[0] - b[0], uy = a[1] - b[1];
167
+ const vx = cc[0] - b[0], vy = cc[1] - b[1];
168
+ const lu = Math.hypot(ux, uy);
169
+ const lv = Math.hypot(vx, vy);
170
+ if (lu < 1e-12 || lv < 1e-12) {
171
+ out[0] = 0;
172
+ return;
173
+ }
174
+ const cosA = (ux * vx + uy * vy) / (lu * lv);
175
+ const cur = Math.acos(cosA < -1 ? -1 : cosA > 1 ? 1 : cosA);
176
+ out[0] = cur - theta;
177
+ });
178
+ }
179
+ /** Lines AB ∥ CD: cross product of direction vectors = 0. */
180
+ export function parallel(A, B, C, D) {
181
+ return generic([A, B, C, D], 1, (pos, out) => {
182
+ const a = pos[0], b = pos[1], cc = pos[2], d = pos[3];
183
+ const ux = b[0] - a[0], uy = b[1] - a[1];
184
+ const vx = d[0] - cc[0], vy = d[1] - cc[1];
185
+ out[0] = ux * vy - uy * vx;
186
+ });
187
+ }
188
+ /** Lines AB ⟂ CD: dot product = 0. */
189
+ export function perpendicular(A, B, C, D) {
190
+ return generic([A, B, C, D], 1, (pos, out) => {
191
+ const a = pos[0], b = pos[1], cc = pos[2], d = pos[3];
192
+ const ux = b[0] - a[0], uy = b[1] - a[1];
193
+ const vx = d[0] - cc[0], vy = d[1] - cc[1];
194
+ out[0] = ux * vx + uy * vy;
195
+ });
196
+ }
197
+ /** Right angle at B between segments AB and BC. */
198
+ export function rightAngle(A, B, C) {
199
+ return generic([A, B, C], 1, (pos, out) => {
200
+ const a = pos[0], b = pos[1], cc = pos[2];
201
+ const ux = b[0] - a[0], uy = b[1] - a[1];
202
+ const vx = cc[0] - b[0], vy = cc[1] - b[1];
203
+ out[0] = ux * vx + uy * vy;
204
+ });
205
+ }
206
+ /** Soft 3-point bending resistance at vertex B. */
207
+ export function bend(A, B, C, stiffness = Strength.MEDIUM) {
208
+ return generic([A, B, C], 1, (pos, out) => {
209
+ const a = pos[0], b = pos[1], cc = pos[2];
210
+ const ux = b[0] - a[0], uy = b[1] - a[1];
211
+ const vx = cc[0] - b[0], vy = cc[1] - b[1];
212
+ out[0] = ux * vy - uy * vx;
213
+ }, { hard: false, stiffness });
214
+ }
215
+ /** Point P on line AB. */
216
+ export function collinear(P, A, B) {
217
+ return generic([P, A, B], 1, (pos, out) => {
218
+ const p = pos[0], a = pos[1], b = pos[2];
219
+ const ux = p[0] - a[0], uy = p[1] - a[1];
220
+ const vx = b[0] - a[0], vy = b[1] - a[1];
221
+ out[0] = ux * vy - uy * vx;
222
+ });
223
+ }
224
+ /** Point P on a circle of given center and radius. */
225
+ export function onCircle(P, center, radius) {
226
+ return generic([P, center], 1, (pos, out) => {
227
+ const p = pos[0], cc = pos[1];
228
+ const dx = p[0] - cc[0], dy = p[1] - cc[1];
229
+ out[0] = Math.hypot(dx, dy) - radius;
230
+ });
231
+ }
232
+ /** Equal distance: ‖A − B‖ = ‖C − D‖. */
233
+ export function equalDist(A, B, C, D) {
234
+ return generic([A, B, C, D], 1, (pos, out) => {
235
+ const a = pos[0], b = pos[1], cc = pos[2], d = pos[3];
236
+ const ab = Math.hypot(a[0] - b[0], a[1] - b[1]);
237
+ const cd = Math.hypot(cc[0] - d[0], cc[1] - d[1]);
238
+ out[0] = ab - cd;
239
+ });
240
+ }
241
+ /** Midpoint: M = (A + B) / 2. */
242
+ export function midpoint(M, A, B) {
243
+ return generic([M, A, B], 2, (pos, out) => {
244
+ const m = pos[0], a = pos[1], b = pos[2];
245
+ out[0] = 2 * m[0] - a[0] - b[0];
246
+ out[1] = 2 * m[1] - a[1] - b[1];
247
+ });
248
+ }
@@ -0,0 +1,11 @@
1
+ export { Constraints, constraints, type Relation } from "./cluster.js";
2
+ export { animate, dilated, fixedStep, } from "./drivers.js";
3
+ export { angle, bend, clamp, collinear, distance, eq, equalDist, gap, generic, geq, inside, lensNum, leq, midpoint, onCircle, parallel, perpendicular, pin, repel, rightAngle, softTarget, spring, } from "./factories.js";
4
+ export { dragBody, dragBodyAnchored } from "./interaction.js";
5
+ export { type Phase, prepare, snapshot, solve, writeback } from "./phases.js";
6
+ export { type Physics, type PhysicsOpts, physics } from "./physics.js";
7
+ export { Body, BodyAnchor, BodyAnchorTerm, type BodyInit, type BodyOpts, BoxContact, body, bodyAnchor, Joint, type JointStiffness, JointTerm, joint, weld, } from "./rigid.js";
8
+ export { Solver, type SolverOpts } from "./solver.js";
9
+ export { LAMBDA_MAX, PENALTY_MAX, PENALTY_MIN, Term } from "./term.js";
10
+ export { BoundsTerm, DistanceTerm, EqTerm, GenericTerm, LensNumTerm, type ResidualFn, SoftTargetTerm, Strength, } from "./terms.js";
11
+ export { type World, type WorldOpts, world } from "./world.js";