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.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/animation/anim.d.ts +57 -0
- package/dist/animation/anim.js +318 -0
- package/dist/animation/combinators.d.ts +39 -0
- package/dist/animation/combinators.js +113 -0
- package/dist/animation/easings.d.ts +5 -0
- package/dist/animation/easings.js +5 -0
- package/dist/animation/index.d.ts +3 -0
- package/dist/animation/index.js +3 -0
- package/dist/assert/algebra.d.ts +20 -0
- package/dist/assert/algebra.js +79 -0
- package/dist/assert/claim.d.ts +40 -0
- package/dist/assert/claim.js +129 -0
- package/dist/assert/index.d.ts +7 -0
- package/dist/assert/index.js +19 -0
- package/dist/assert/predicates.d.ts +18 -0
- package/dist/assert/predicates.js +43 -0
- package/dist/assert/record.d.ts +20 -0
- package/dist/assert/record.js +78 -0
- package/dist/assert/scope.d.ts +42 -0
- package/dist/assert/scope.js +233 -0
- package/dist/assert/span.d.ts +37 -0
- package/dist/assert/span.js +68 -0
- package/dist/assert/tree.d.ts +22 -0
- package/dist/assert/tree.js +65 -0
- package/dist/code/code.d.ts +70 -0
- package/dist/code/code.js +361 -0
- package/dist/code/index.d.ts +2 -0
- package/dist/code/index.js +9 -0
- package/dist/code/morph.d.ts +5 -0
- package/dist/code/morph.js +194 -0
- package/dist/code/tokenize.d.ts +8 -0
- package/dist/code/tokenize.js +51 -0
- package/dist/constraints/cluster.d.ts +83 -0
- package/dist/constraints/cluster.js +213 -0
- package/dist/constraints/drivers.d.ts +15 -0
- package/dist/constraints/drivers.js +40 -0
- package/dist/constraints/factories.d.ts +73 -0
- package/dist/constraints/factories.js +248 -0
- package/dist/constraints/index.d.ts +11 -0
- package/dist/constraints/index.js +39 -0
- package/dist/constraints/interaction.d.ts +21 -0
- package/dist/constraints/interaction.js +148 -0
- package/dist/constraints/linalg.d.ts +18 -0
- package/dist/constraints/linalg.js +141 -0
- package/dist/constraints/phases.d.ts +21 -0
- package/dist/constraints/phases.js +60 -0
- package/dist/constraints/physics.d.ts +34 -0
- package/dist/constraints/physics.js +128 -0
- package/dist/constraints/rigid.d.ts +210 -0
- package/dist/constraints/rigid.js +835 -0
- package/dist/constraints/solver.d.ts +107 -0
- package/dist/constraints/solver.js +510 -0
- package/dist/constraints/term.d.ts +50 -0
- package/dist/constraints/term.js +80 -0
- package/dist/constraints/terms.d.ts +80 -0
- package/dist/constraints/terms.js +302 -0
- package/dist/constraints/world.d.ts +31 -0
- package/dist/constraints/world.js +245 -0
- package/dist/core/aggregates.d.ts +64 -0
- package/dist/core/aggregates.js +198 -0
- package/dist/core/anim.d.ts +84 -0
- package/dist/core/anim.js +301 -0
- package/dist/core/index.d.ts +38 -0
- package/dist/core/index.js +38 -0
- package/dist/core/introspect.d.ts +5 -0
- package/dist/core/introspect.js +31 -0
- package/dist/core/lenses/closed-form-policies.d.ts +64 -0
- package/dist/core/lenses/closed-form-policies.js +452 -0
- package/dist/core/lenses/domain-aggregates.d.ts +54 -0
- package/dist/core/lenses/domain-aggregates.js +259 -0
- package/dist/core/lenses/factor-lens.d.ts +42 -0
- package/dist/core/lenses/factor-lens.js +419 -0
- package/dist/core/lenses/index.d.ts +5 -0
- package/dist/core/lenses/index.js +16 -0
- package/dist/core/lenses/memory.d.ts +47 -0
- package/dist/core/lenses/memory.js +102 -0
- package/dist/core/lenses/typed-factor.d.ts +45 -0
- package/dist/core/lenses/typed-factor.js +376 -0
- package/dist/core/network-utils.d.ts +14 -0
- package/dist/core/network-utils.js +62 -0
- package/dist/core/new-primitives.d.ts +33 -0
- package/dist/core/new-primitives.js +113 -0
- package/dist/core/signal.d.ts +254 -0
- package/dist/core/signal.js +1349 -0
- package/dist/core/traits.d.ts +61 -0
- package/dist/core/traits.js +56 -0
- package/dist/core/tree.d.ts +23 -0
- package/dist/core/tree.js +62 -0
- package/dist/core/values/anchor.d.ts +23 -0
- package/dist/core/values/anchor.js +23 -0
- package/dist/core/values/audio.d.ts +33 -0
- package/dist/core/values/audio.js +107 -0
- package/dist/core/values/bool.d.ts +37 -0
- package/dist/core/values/bool.js +75 -0
- package/dist/core/values/box.d.ts +77 -0
- package/dist/core/values/box.js +211 -0
- package/dist/core/values/canvas.d.ts +71 -0
- package/dist/core/values/canvas.js +495 -0
- package/dist/core/values/color.d.ts +49 -0
- package/dist/core/values/color.js +106 -0
- package/dist/core/values/flags.d.ts +18 -0
- package/dist/core/values/flags.js +50 -0
- package/dist/core/values/gpu.d.ts +74 -0
- package/dist/core/values/gpu.js +426 -0
- package/dist/core/values/matrix.d.ts +53 -0
- package/dist/core/values/matrix.js +140 -0
- package/dist/core/values/num.d.ts +62 -0
- package/dist/core/values/num.js +166 -0
- package/dist/core/values/pose.d.ts +31 -0
- package/dist/core/values/pose.js +83 -0
- package/dist/core/values/range.d.ts +83 -0
- package/dist/core/values/range.js +167 -0
- package/dist/core/values/str.d.ts +76 -0
- package/dist/core/values/str.js +346 -0
- package/dist/core/values/template.d.ts +49 -0
- package/dist/core/values/template.js +148 -0
- package/dist/core/values/transform.d.ts +49 -0
- package/dist/core/values/transform.js +115 -0
- package/dist/core/values/tri.d.ts +31 -0
- package/dist/core/values/tri.js +95 -0
- package/dist/core/values/vec.d.ts +72 -0
- package/dist/core/values/vec.js +219 -0
- package/dist/core/writable.d.ts +15 -0
- package/dist/core/writable.js +29 -0
- package/dist/ext/events.d.ts +10 -0
- package/dist/ext/events.js +31 -0
- package/dist/ext/index.d.ts +4 -0
- package/dist/ext/index.js +4 -0
- package/dist/ext/snapshot.d.ts +8 -0
- package/dist/ext/snapshot.js +29 -0
- package/dist/ext/timeline.d.ts +56 -0
- package/dist/ext/timeline.js +94 -0
- package/dist/ext/waapi.d.ts +25 -0
- package/dist/ext/waapi.js +198 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/propagators/index.d.ts +6 -0
- package/dist/propagators/index.js +6 -0
- package/dist/propagators/layout.d.ts +68 -0
- package/dist/propagators/layout.js +336 -0
- package/dist/propagators/network.d.ts +52 -0
- package/dist/propagators/network.js +185 -0
- package/dist/propagators/propagator.d.ts +12 -0
- package/dist/propagators/propagator.js +16 -0
- package/dist/propagators/range.d.ts +45 -0
- package/dist/propagators/range.js +147 -0
- package/dist/propagators/relations.d.ts +60 -0
- package/dist/propagators/relations.js +343 -0
- package/dist/shapes/annular-sector.d.ts +15 -0
- package/dist/shapes/annular-sector.js +64 -0
- package/dist/shapes/button.d.ts +14 -0
- package/dist/shapes/button.js +31 -0
- package/dist/shapes/choreographers.d.ts +22 -0
- package/dist/shapes/choreographers.js +69 -0
- package/dist/shapes/circle.d.ts +17 -0
- package/dist/shapes/circle.js +57 -0
- package/dist/shapes/clip.d.ts +5 -0
- package/dist/shapes/clip.js +31 -0
- package/dist/shapes/connect.d.ts +16 -0
- package/dist/shapes/connect.js +70 -0
- package/dist/shapes/curve.d.ts +60 -0
- package/dist/shapes/curve.js +285 -0
- package/dist/shapes/dashed.d.ts +16 -0
- package/dist/shapes/dashed.js +142 -0
- package/dist/shapes/debug.d.ts +43 -0
- package/dist/shapes/debug.js +97 -0
- package/dist/shapes/group.d.ts +5 -0
- package/dist/shapes/group.js +10 -0
- package/dist/shapes/handle.d.ts +32 -0
- package/dist/shapes/handle.js +88 -0
- package/dist/shapes/index.d.ts +23 -0
- package/dist/shapes/index.js +23 -0
- package/dist/shapes/interaction.d.ts +32 -0
- package/dist/shapes/interaction.js +187 -0
- package/dist/shapes/label.d.ts +20 -0
- package/dist/shapes/label.js +42 -0
- package/dist/shapes/layout.d.ts +29 -0
- package/dist/shapes/layout.js +74 -0
- package/dist/shapes/line.d.ts +21 -0
- package/dist/shapes/line.js +79 -0
- package/dist/shapes/list.d.ts +18 -0
- package/dist/shapes/list.js +51 -0
- package/dist/shapes/mount.d.ts +7 -0
- package/dist/shapes/mount.js +10 -0
- package/dist/shapes/path.d.ts +77 -0
- package/dist/shapes/path.js +227 -0
- package/dist/shapes/rect.d.ts +30 -0
- package/dist/shapes/rect.js +131 -0
- package/dist/shapes/shape.d.ts +132 -0
- package/dist/shapes/shape.js +306 -0
- package/dist/shapes/text.d.ts +24 -0
- package/dist/shapes/text.js +53 -0
- package/dist/shapes/tokens.d.ts +28 -0
- package/dist/shapes/tokens.js +27 -0
- package/dist/shapes/transitions.d.ts +23 -0
- package/dist/shapes/transitions.js +62 -0
- package/dist/tex/decorations.d.ts +26 -0
- package/dist/tex/decorations.js +116 -0
- package/dist/tex/index.d.ts +5 -0
- package/dist/tex/index.js +5 -0
- package/dist/tex/marker.d.ts +17 -0
- package/dist/tex/marker.js +63 -0
- package/dist/tex/motion.d.ts +43 -0
- package/dist/tex/motion.js +290 -0
- package/dist/tex/parts.d.ts +65 -0
- package/dist/tex/parts.js +149 -0
- package/dist/tex/tex.d.ts +45 -0
- package/dist/tex/tex.js +244 -0
- package/dist/web/attr.d.ts +16 -0
- package/dist/web/attr.js +98 -0
- package/dist/web/diagram.d.ts +49 -0
- package/dist/web/diagram.js +260 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +6 -0
- package/dist/web/md-marker.d.ts +6 -0
- package/dist/web/md-marker.js +39 -0
- package/dist/web/md-tex.d.ts +6 -0
- package/dist/web/md-tex.js +61 -0
- package/dist/web/raf.d.ts +6 -0
- package/dist/web/raf.js +24 -0
- package/dist/web/viewport.d.ts +7 -0
- package/dist/web/viewport.js +13 -0
- package/package.json +87 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// term.ts — abstract Term: one augmented-Lagrangian constraint over
|
|
2
|
+
// cells, with residual `C(x)`, Jacobian `J = ∂C/∂x`, dual `λ`, penalty.
|
|
3
|
+
// Cells are integer ids into the solver's SOA buffers.
|
|
4
|
+
//
|
|
5
|
+
// AVBD inner loop, per term:
|
|
6
|
+
// - initialize() once per prepare(); `false` drops the term.
|
|
7
|
+
// - computeConstraint(alpha) writes `C`; hard constraints stabilise
|
|
8
|
+
// as `C(x) = C*(x) − α·C*(x⁻)`.
|
|
9
|
+
// - computeDerivatives(cellIdx) writes the Jacobian column and
|
|
10
|
+
// geometric-stiffness Hessian column-norms. `cellIdx` indexes
|
|
11
|
+
// `term.cells`, not the integer cell id.
|
|
12
|
+
export const PENALTY_MIN = 1.0;
|
|
13
|
+
export const PENALTY_MAX = 1e9;
|
|
14
|
+
/** Hard symmetric cap on the per-row multiplier `λ`. Without it the
|
|
15
|
+
* dual update grows unboundedly while a constraint stays infeasible
|
|
16
|
+
* (e.g. a joint dragged outside its workspace), blowing positions to
|
|
17
|
+
* infinity. Capping at `1e9` saturates instead of exploding;
|
|
18
|
+
* symmetric so equalities stay reachable from either side. */
|
|
19
|
+
export const LAMBDA_MAX = 1e9;
|
|
20
|
+
export class Term {
|
|
21
|
+
solver;
|
|
22
|
+
/** Bound cell ids in subclass order; `cells[ci]` is cell-index ci. */
|
|
23
|
+
cells;
|
|
24
|
+
/** Per-cell-index offset into `solver.positions`; cached (offsets are
|
|
25
|
+
* append-only). */
|
|
26
|
+
cellOffsets;
|
|
27
|
+
cellDims;
|
|
28
|
+
rows;
|
|
29
|
+
/** Current constraint values; filled by `computeConstraint`. */
|
|
30
|
+
C;
|
|
31
|
+
/** Constraint values at start of step (hard-constraint stabilisation). */
|
|
32
|
+
C0;
|
|
33
|
+
/** Per-row stiffness; `Infinity` = hard (augmented-Lagrangian path). */
|
|
34
|
+
stiffness;
|
|
35
|
+
/** Per-row lower bound on λ (default `-Infinity`); one-sided
|
|
36
|
+
* constraints and friction-cone clamps. */
|
|
37
|
+
lambdaMin;
|
|
38
|
+
lambdaMax;
|
|
39
|
+
/** Fracture threshold: `|λ| > fracture` disables the term. */
|
|
40
|
+
fracture;
|
|
41
|
+
/** Current penalty (warm-started, ramped via β). */
|
|
42
|
+
penalty;
|
|
43
|
+
/** Lagrange multiplier for hard constraints (soft uses 0). */
|
|
44
|
+
lambda;
|
|
45
|
+
/** Removal flag, honoured at the next `prepare()`. Set by
|
|
46
|
+
* `dispose()` or by fracture in `_dualPass`. */
|
|
47
|
+
disabled = false;
|
|
48
|
+
/** Per-cell-index Jacobian; `J[ci]` is `rows × dim` row-major. */
|
|
49
|
+
J;
|
|
50
|
+
/** Per-cell-index Hessian column norms; same shape as `J[ci]`. */
|
|
51
|
+
HCols;
|
|
52
|
+
constructor(solver, cells, rows) {
|
|
53
|
+
this.solver = solver;
|
|
54
|
+
this.cells = cells;
|
|
55
|
+
this.cellOffsets = cells.map(id => solver.offsets[id]);
|
|
56
|
+
this.cellDims = cells.map(id => solver.dims[id]);
|
|
57
|
+
this.rows = rows;
|
|
58
|
+
this.C = new Float64Array(rows);
|
|
59
|
+
this.C0 = new Float64Array(rows);
|
|
60
|
+
this.stiffness = new Float64Array(rows).fill(Number.POSITIVE_INFINITY);
|
|
61
|
+
this.lambdaMin = new Float64Array(rows).fill(Number.NEGATIVE_INFINITY);
|
|
62
|
+
this.lambdaMax = new Float64Array(rows).fill(Number.POSITIVE_INFINITY);
|
|
63
|
+
this.fracture = new Float64Array(rows).fill(Number.POSITIVE_INFINITY);
|
|
64
|
+
this.penalty = new Float64Array(rows).fill(PENALTY_MIN);
|
|
65
|
+
this.lambda = new Float64Array(rows);
|
|
66
|
+
this.J = this.cellDims.map(d => new Float64Array(rows * d));
|
|
67
|
+
this.HCols = this.cellDims.map(d => new Float64Array(rows * d));
|
|
68
|
+
for (let ci = 0; ci < cells.length; ci++) {
|
|
69
|
+
solver._connectTerm(this, cells[ci], ci);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Mark for removal; takes effect on the next solver pass. */
|
|
73
|
+
dispose() {
|
|
74
|
+
this.disabled = true;
|
|
75
|
+
}
|
|
76
|
+
/** True iff row is hard (`stiffness === Infinity`). */
|
|
77
|
+
isHard(row) {
|
|
78
|
+
return this.stiffness[row] === Number.POSITIVE_INFINITY;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Cell, type Writable } from "../core/index.js";
|
|
2
|
+
import type { Solver } from "./solver.js";
|
|
3
|
+
import { Term } from "./term.js";
|
|
4
|
+
export declare const Strength: {
|
|
5
|
+
readonly WEAK: 1;
|
|
6
|
+
readonly MEDIUM: 1000;
|
|
7
|
+
readonly STRONG: 1000000;
|
|
8
|
+
readonly REQUIRED: 1000000000;
|
|
9
|
+
/** True hard constraint (augmented-Lagrangian path); `*Term` default. */
|
|
10
|
+
readonly HARD: number;
|
|
11
|
+
};
|
|
12
|
+
export declare class EqTerm extends Term {
|
|
13
|
+
constructor(solver: Solver, a: number, b: number, hard?: boolean);
|
|
14
|
+
initialize(): boolean;
|
|
15
|
+
computeConstraint(alpha: number): void;
|
|
16
|
+
computeDerivatives(cellIdx: number): void;
|
|
17
|
+
}
|
|
18
|
+
export declare class LensNumTerm extends Term {
|
|
19
|
+
fwd: (a: number) => number;
|
|
20
|
+
private readonly fdStep;
|
|
21
|
+
private _cachedFwdA;
|
|
22
|
+
private _cachedA;
|
|
23
|
+
constructor(solver: Solver, a: number, b: number, fwd: (x: number) => number, fdStep?: number);
|
|
24
|
+
initialize(): boolean;
|
|
25
|
+
computeConstraint(alpha: number): void;
|
|
26
|
+
computeDerivatives(cellIdx: number): void;
|
|
27
|
+
}
|
|
28
|
+
export declare class DistanceTerm extends Term {
|
|
29
|
+
/** Rest-length signal; cached in `initialize()`. */
|
|
30
|
+
readonly rest: Cell<number>;
|
|
31
|
+
/** Optional mutable stiffness signal (only set when `hard=false`). */
|
|
32
|
+
readonly stiffnessSig?: Cell<number>;
|
|
33
|
+
private _restCached;
|
|
34
|
+
private _cachedNx;
|
|
35
|
+
private _cachedNy;
|
|
36
|
+
private _cachedInvD;
|
|
37
|
+
private _cachedDegenerate;
|
|
38
|
+
constructor(solver: Solver, a: number, b: number, rest: number | Writable<Cell<number>>, hard?: boolean, stiffness?: number | Writable<Cell<number>>);
|
|
39
|
+
initialize(): boolean;
|
|
40
|
+
computeConstraint(alpha: number): void;
|
|
41
|
+
computeDerivatives(cellIdx: number): void;
|
|
42
|
+
}
|
|
43
|
+
export declare class BoundsTerm extends Term {
|
|
44
|
+
readonly lo: Cell<number>;
|
|
45
|
+
readonly hi: Cell<number>;
|
|
46
|
+
private _loCached;
|
|
47
|
+
private _hiCached;
|
|
48
|
+
constructor(solver: Solver, cellIdx: number, lo: number | Writable<Cell<number>>, hi: number | Writable<Cell<number>>);
|
|
49
|
+
initialize(): boolean;
|
|
50
|
+
computeConstraint(alpha: number): void;
|
|
51
|
+
computeDerivatives(_cellIdx: number): void;
|
|
52
|
+
}
|
|
53
|
+
export declare class SoftTargetTerm extends Term {
|
|
54
|
+
target: Float64Array;
|
|
55
|
+
constructor(solver: Solver, cell: number, target: ArrayLike<number>, stiffness: number);
|
|
56
|
+
initialize(): boolean;
|
|
57
|
+
computeConstraint(_alpha: number): void;
|
|
58
|
+
computeDerivatives(_cellIdx: number): void;
|
|
59
|
+
}
|
|
60
|
+
export type ResidualFn = (
|
|
61
|
+
/** Per-cell position snapshots, in order (read-only). */
|
|
62
|
+
positions: readonly Float64Array[],
|
|
63
|
+
/** Output buffer of length `rows`; write residuals (0 = satisfied). */
|
|
64
|
+
out: Float64Array) => void;
|
|
65
|
+
export declare class GenericTerm extends Term {
|
|
66
|
+
private fn;
|
|
67
|
+
private fdStep;
|
|
68
|
+
private _fdPositions;
|
|
69
|
+
private _fdRawBase;
|
|
70
|
+
private _fdScratchPlus;
|
|
71
|
+
private _fdScratchMinus;
|
|
72
|
+
constructor(solver: Solver, cells: readonly number[], rows: number, fn: ResidualFn, opts?: {
|
|
73
|
+
fdStep?: number;
|
|
74
|
+
hard?: boolean;
|
|
75
|
+
stiffness?: number;
|
|
76
|
+
});
|
|
77
|
+
initialize(): boolean;
|
|
78
|
+
computeConstraint(alpha: number): void;
|
|
79
|
+
computeDerivatives(cellIdx: number): void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// terms.ts — concrete `Term` subclasses (numerical kernel).
|
|
2
|
+
//
|
|
3
|
+
// Each subclass reads positions via `solver.positions`/`offsets` and
|
|
4
|
+
// writes Jacobian / Hessian column norms into `J[ci]` / `HCols[ci]`.
|
|
5
|
+
//
|
|
6
|
+
// Mutable numeric parameters (rest lengths, bounds, …) are held as
|
|
7
|
+
// `Cell<number>` and cached in `initialize()` (called once per
|
|
8
|
+
// `solve()`), so the inner per-iteration methods stay signal-free.
|
|
9
|
+
// Subscription happens at the cluster layer: the network body reads
|
|
10
|
+
// each param signal, so mutating it triggers a re-solve.
|
|
11
|
+
import { cell } from "../core/index.js";
|
|
12
|
+
import { Term } from "./term.js";
|
|
13
|
+
export const Strength = {
|
|
14
|
+
WEAK: 1,
|
|
15
|
+
MEDIUM: 1e3,
|
|
16
|
+
STRONG: 1e6,
|
|
17
|
+
REQUIRED: 1e9,
|
|
18
|
+
/** True hard constraint (augmented-Lagrangian path); `*Term` default. */
|
|
19
|
+
HARD: Number.POSITIVE_INFINITY,
|
|
20
|
+
};
|
|
21
|
+
export class EqTerm extends Term {
|
|
22
|
+
constructor(solver, a, b, hard = true) {
|
|
23
|
+
if (solver.dims[a] !== solver.dims[b]) {
|
|
24
|
+
throw new Error("eq: cell dims must match");
|
|
25
|
+
}
|
|
26
|
+
super(solver, [a, b], solver.dims[a]);
|
|
27
|
+
if (!hard)
|
|
28
|
+
this.stiffness.fill(1e6);
|
|
29
|
+
}
|
|
30
|
+
initialize() {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
computeConstraint(alpha) {
|
|
34
|
+
const positions = this.solver.positions;
|
|
35
|
+
const aOff = this.cellOffsets[0];
|
|
36
|
+
const bOff = this.cellOffsets[1];
|
|
37
|
+
for (let k = 0; k < this.rows; k++) {
|
|
38
|
+
const Cn = positions[aOff + k] - positions[bOff + k];
|
|
39
|
+
this.C[k] = this.stiffness[k] === Number.POSITIVE_INFINITY ? Cn - alpha * this.C0[k] : Cn;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
computeDerivatives(cellIdx) {
|
|
43
|
+
const dim = this.rows;
|
|
44
|
+
const J = this.J[cellIdx];
|
|
45
|
+
const sign = cellIdx === 0 ? 1.0 : -1.0;
|
|
46
|
+
for (let r = 0; r < dim; r++) {
|
|
47
|
+
for (let k = 0; k < dim; k++)
|
|
48
|
+
J[r * dim + k] = r === k ? sign : 0.0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class LensNumTerm extends Term {
|
|
53
|
+
fwd;
|
|
54
|
+
fdStep;
|
|
55
|
+
_cachedFwdA = 0;
|
|
56
|
+
_cachedA = Number.NaN;
|
|
57
|
+
constructor(solver, a, b, fwd, fdStep = 1e-6) {
|
|
58
|
+
if (solver.dims[a] !== 1 || solver.dims[b] !== 1) {
|
|
59
|
+
throw new Error("lensNum: both cells must be Num (dim=1)");
|
|
60
|
+
}
|
|
61
|
+
super(solver, [a, b], 1);
|
|
62
|
+
this.fwd = fwd;
|
|
63
|
+
this.fdStep = fdStep;
|
|
64
|
+
}
|
|
65
|
+
initialize() {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
computeConstraint(alpha) {
|
|
69
|
+
const positions = this.solver.positions;
|
|
70
|
+
const aOff = this.cellOffsets[0];
|
|
71
|
+
const bOff = this.cellOffsets[1];
|
|
72
|
+
const a = positions[aOff];
|
|
73
|
+
const b = positions[bOff];
|
|
74
|
+
const fa = this.fwd(a);
|
|
75
|
+
this._cachedFwdA = fa;
|
|
76
|
+
this._cachedA = a;
|
|
77
|
+
const Cn = b - fa;
|
|
78
|
+
this.C[0] = this.stiffness[0] === Number.POSITIVE_INFINITY ? Cn - alpha * this.C0[0] : Cn;
|
|
79
|
+
}
|
|
80
|
+
computeDerivatives(cellIdx) {
|
|
81
|
+
const J = this.J[cellIdx];
|
|
82
|
+
if (cellIdx === 1) {
|
|
83
|
+
J[0] = 1.0;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const a = this._cachedA;
|
|
87
|
+
const f0 = this._cachedFwdA;
|
|
88
|
+
const f1 = this.fwd(a + this.fdStep);
|
|
89
|
+
J[0] = -(f1 - f0) / this.fdStep;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export class DistanceTerm extends Term {
|
|
93
|
+
/** Rest-length signal; cached in `initialize()`. */
|
|
94
|
+
rest;
|
|
95
|
+
/** Optional mutable stiffness signal (only set when `hard=false`). */
|
|
96
|
+
stiffnessSig;
|
|
97
|
+
_restCached = 0;
|
|
98
|
+
_cachedNx = 0;
|
|
99
|
+
_cachedNy = 0;
|
|
100
|
+
_cachedInvD = 0;
|
|
101
|
+
_cachedDegenerate = false;
|
|
102
|
+
constructor(solver, a, b, rest, hard = true, stiffness) {
|
|
103
|
+
if (solver.dims[a] !== 2 || solver.dims[b] !== 2) {
|
|
104
|
+
throw new Error("distance: both cells must be Vec (dim=2)");
|
|
105
|
+
}
|
|
106
|
+
super(solver, [a, b], 1);
|
|
107
|
+
this.rest = cell(rest);
|
|
108
|
+
this._restCached = this.rest.peek();
|
|
109
|
+
if (!hard) {
|
|
110
|
+
this.stiffnessSig = cell(stiffness ?? 1e6);
|
|
111
|
+
this.stiffness.fill(this.stiffnessSig.peek());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
initialize() {
|
|
115
|
+
// `.value` (not `.peek()`): runs in the network body, so the read
|
|
116
|
+
// both refreshes the cache and subscribes the param signal.
|
|
117
|
+
this._restCached = this.rest.value;
|
|
118
|
+
if (this.stiffnessSig !== undefined) {
|
|
119
|
+
const k = this.stiffnessSig.value;
|
|
120
|
+
this.stiffness[0] = k;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
computeConstraint(alpha) {
|
|
125
|
+
const positions = this.solver.positions;
|
|
126
|
+
const aOff = this.cellOffsets[0];
|
|
127
|
+
const bOff = this.cellOffsets[1];
|
|
128
|
+
const dx = positions[aOff] - positions[bOff];
|
|
129
|
+
const dy = positions[aOff + 1] - positions[bOff + 1];
|
|
130
|
+
const d2 = dx * dx + dy * dy;
|
|
131
|
+
const restCached = this._restCached;
|
|
132
|
+
if (d2 < 1e-24) {
|
|
133
|
+
this._cachedDegenerate = true;
|
|
134
|
+
this._cachedNx = 0;
|
|
135
|
+
this._cachedNy = 0;
|
|
136
|
+
this._cachedInvD = 0;
|
|
137
|
+
const Cn = -restCached;
|
|
138
|
+
this.C[0] = this.stiffness[0] === Number.POSITIVE_INFINITY ? Cn - alpha * this.C0[0] : Cn;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const d = Math.sqrt(d2);
|
|
142
|
+
const inv = 1 / d;
|
|
143
|
+
this._cachedDegenerate = false;
|
|
144
|
+
this._cachedNx = dx * inv;
|
|
145
|
+
this._cachedNy = dy * inv;
|
|
146
|
+
this._cachedInvD = inv;
|
|
147
|
+
const Cn = d - restCached;
|
|
148
|
+
this.C[0] = this.stiffness[0] === Number.POSITIVE_INFINITY ? Cn - alpha * this.C0[0] : Cn;
|
|
149
|
+
}
|
|
150
|
+
computeDerivatives(cellIdx) {
|
|
151
|
+
const J = this.J[cellIdx];
|
|
152
|
+
const Hcols = this.HCols[cellIdx];
|
|
153
|
+
if (this._cachedDegenerate) {
|
|
154
|
+
J[0] = cellIdx === 0 ? 1 : -1;
|
|
155
|
+
J[1] = 0;
|
|
156
|
+
Hcols[0] = 0;
|
|
157
|
+
Hcols[1] = 0;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const nx = this._cachedNx;
|
|
161
|
+
const ny = this._cachedNy;
|
|
162
|
+
const inv = this._cachedInvD;
|
|
163
|
+
const sign = cellIdx === 0 ? 1.0 : -1.0;
|
|
164
|
+
J[0] = sign * nx;
|
|
165
|
+
J[1] = sign * ny;
|
|
166
|
+
Hcols[0] = Math.sqrt(1 - nx * nx) * inv;
|
|
167
|
+
Hcols[1] = Math.sqrt(1 - ny * ny) * inv;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export class BoundsTerm extends Term {
|
|
171
|
+
lo;
|
|
172
|
+
hi;
|
|
173
|
+
_loCached = 0;
|
|
174
|
+
_hiCached = 0;
|
|
175
|
+
constructor(solver, cellIdx, lo, hi) {
|
|
176
|
+
if (solver.dims[cellIdx] !== 1)
|
|
177
|
+
throw new Error("clamp: cell must be Num (dim=1)");
|
|
178
|
+
super(solver, [cellIdx], 2);
|
|
179
|
+
this.lo = cell(lo);
|
|
180
|
+
this.hi = cell(hi);
|
|
181
|
+
this._loCached = this.lo.peek();
|
|
182
|
+
this._hiCached = this.hi.peek();
|
|
183
|
+
this.lambdaMax[0] = 0;
|
|
184
|
+
this.lambdaMax[1] = 0;
|
|
185
|
+
}
|
|
186
|
+
initialize() {
|
|
187
|
+
// `.value` to subscribe + refresh; see DistanceTerm note.
|
|
188
|
+
this._loCached = this.lo.value;
|
|
189
|
+
this._hiCached = this.hi.value;
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
computeConstraint(alpha) {
|
|
193
|
+
const positions = this.solver.positions;
|
|
194
|
+
const off = this.cellOffsets[0];
|
|
195
|
+
const x = positions[off];
|
|
196
|
+
const c0 = x - this._loCached;
|
|
197
|
+
const c1 = this._hiCached - x;
|
|
198
|
+
const stiff = this.stiffness;
|
|
199
|
+
this.C[0] = stiff[0] === Number.POSITIVE_INFINITY ? c0 - alpha * this.C0[0] : c0;
|
|
200
|
+
this.C[1] = stiff[1] === Number.POSITIVE_INFINITY ? c1 - alpha * this.C0[1] : c1;
|
|
201
|
+
}
|
|
202
|
+
computeDerivatives(_cellIdx) {
|
|
203
|
+
const J = this.J[0];
|
|
204
|
+
J[0] = 1;
|
|
205
|
+
J[1] = -1;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export class SoftTargetTerm extends Term {
|
|
209
|
+
target;
|
|
210
|
+
constructor(solver, cell, target, stiffness) {
|
|
211
|
+
const dim = solver.dims[cell];
|
|
212
|
+
super(solver, [cell], dim);
|
|
213
|
+
this.target = new Float64Array(dim);
|
|
214
|
+
for (let k = 0; k < dim; k++)
|
|
215
|
+
this.target[k] = target[k] ?? 0;
|
|
216
|
+
this.stiffness.fill(stiffness);
|
|
217
|
+
}
|
|
218
|
+
initialize() {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
computeConstraint(_alpha) {
|
|
222
|
+
const positions = this.solver.positions;
|
|
223
|
+
const off = this.cellOffsets[0];
|
|
224
|
+
const dim = this.rows;
|
|
225
|
+
for (let k = 0; k < dim; k++) {
|
|
226
|
+
this.C[k] = positions[off + k] - this.target[k];
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
computeDerivatives(_cellIdx) {
|
|
230
|
+
const dim = this.rows;
|
|
231
|
+
const J = this.J[0];
|
|
232
|
+
for (let r = 0; r < dim; r++) {
|
|
233
|
+
for (let k = 0; k < dim; k++)
|
|
234
|
+
J[r * dim + k] = r === k ? 1 : 0;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
export class GenericTerm extends Term {
|
|
239
|
+
fn;
|
|
240
|
+
fdStep;
|
|
241
|
+
_fdPositions;
|
|
242
|
+
_fdRawBase;
|
|
243
|
+
_fdScratchPlus;
|
|
244
|
+
_fdScratchMinus;
|
|
245
|
+
constructor(solver, cells, rows, fn, opts = {}) {
|
|
246
|
+
super(solver, cells, rows);
|
|
247
|
+
this.fn = fn;
|
|
248
|
+
this.fdStep = opts.fdStep ?? 1e-6;
|
|
249
|
+
this._fdScratchPlus = new Float64Array(rows);
|
|
250
|
+
this._fdScratchMinus = new Float64Array(rows);
|
|
251
|
+
this._fdRawBase = new Float64Array(rows);
|
|
252
|
+
this._fdPositions = this.cellDims.map(d => new Float64Array(d));
|
|
253
|
+
if (opts.hard === false)
|
|
254
|
+
this.stiffness.fill(opts.stiffness ?? 1e6);
|
|
255
|
+
}
|
|
256
|
+
initialize() {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
computeConstraint(alpha) {
|
|
260
|
+
const positions = this.solver.positions;
|
|
261
|
+
const offsets = this.cellOffsets;
|
|
262
|
+
const dims = this.cellDims;
|
|
263
|
+
for (let i = 0; i < this.cells.length; i++) {
|
|
264
|
+
const off = offsets[i];
|
|
265
|
+
const dim = dims[i];
|
|
266
|
+
const into = this._fdPositions[i];
|
|
267
|
+
for (let k = 0; k < dim; k++)
|
|
268
|
+
into[k] = positions[off + k];
|
|
269
|
+
}
|
|
270
|
+
this.fn(this._fdPositions, this._fdRawBase);
|
|
271
|
+
const stiff = this.stiffness;
|
|
272
|
+
for (let r = 0; r < this.rows; r++) {
|
|
273
|
+
const raw = this._fdRawBase[r];
|
|
274
|
+
this.C[r] = stiff[r] === Number.POSITIVE_INFINITY ? raw - alpha * this.C0[r] : raw;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
computeDerivatives(cellIdx) {
|
|
278
|
+
const dim = this.cellDims[cellIdx];
|
|
279
|
+
const J = this.J[cellIdx];
|
|
280
|
+
const Hcols = this.HCols[cellIdx];
|
|
281
|
+
const baseRaw = this._fdRawBase;
|
|
282
|
+
const Cplus = this._fdScratchPlus;
|
|
283
|
+
const Cminus = this._fdScratchMinus;
|
|
284
|
+
const fdPos = this._fdPositions[cellIdx];
|
|
285
|
+
const h = this.fdStep;
|
|
286
|
+
const invH = 1 / h;
|
|
287
|
+
const invH2 = 1 / (h * h);
|
|
288
|
+
for (let k = 0; k < dim; k++) {
|
|
289
|
+
const saved = fdPos[k];
|
|
290
|
+
fdPos[k] = saved + h;
|
|
291
|
+
this.fn(this._fdPositions, Cplus);
|
|
292
|
+
fdPos[k] = saved - h;
|
|
293
|
+
this.fn(this._fdPositions, Cminus);
|
|
294
|
+
fdPos[k] = saved;
|
|
295
|
+
for (let r = 0; r < this.rows; r++) {
|
|
296
|
+
J[r * dim + k] = (Cplus[r] - baseRaw[r]) * invH;
|
|
297
|
+
const d2 = (Cplus[r] - 2 * baseRaw[r] + Cminus[r]) * invH2;
|
|
298
|
+
Hcols[r * dim + k] = d2 < 0 ? -d2 : d2;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Constraints } from "./cluster.js";
|
|
2
|
+
import type { PhysicsOpts } from "./physics.js";
|
|
3
|
+
import { Body } from "./rigid.js";
|
|
4
|
+
export interface WorldOpts extends PhysicsOpts {
|
|
5
|
+
/** Cap on angular speed (rad/s), applied each sub-step before the
|
|
6
|
+
* inertial extrapolation. Stops a body that picked up spurious
|
|
7
|
+
* spin from running away. Default `50`. */
|
|
8
|
+
maxAngularSpeed?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface World extends Constraints {
|
|
11
|
+
/** External acceleration vector. */
|
|
12
|
+
readonly aExt: Float64Array;
|
|
13
|
+
/** Read velocity for cell `id`. */
|
|
14
|
+
velocity(id: number, out?: number[]): number[];
|
|
15
|
+
/** Write velocity for cell `id`. */
|
|
16
|
+
setVelocity(id: number, value: ArrayLike<number>): void;
|
|
17
|
+
/** Per-tick velocity damping. */
|
|
18
|
+
damping: number;
|
|
19
|
+
/** All `Body` instances currently registered. */
|
|
20
|
+
readonly bodies: readonly Body[];
|
|
21
|
+
}
|
|
22
|
+
/** Build a `Constraints` configured for 2D rigid-body physics:
|
|
23
|
+
* velocity + gravity + broadphase + contact manifolds.
|
|
24
|
+
*
|
|
25
|
+
* const w = world({ gravity: [0, 1500], iterations: 24 });
|
|
26
|
+
* const a = w.add(body({ size: { w: 40, h: 40 } }, { x: 0, y: 0 }));
|
|
27
|
+
* const b = w.add(body({ size: { w: 40, h: 40 } }, { x: 100, y: 0 }));
|
|
28
|
+
* w.add(joint(a, b, { x: 0, y: 0 }, { x: 0, y: 0 }));
|
|
29
|
+
* this.anim.start(fixedStep(w, 1/60));
|
|
30
|
+
*/
|
|
31
|
+
export declare function world(opts?: WorldOpts): World;
|