@fundamental-engine/core 0.4.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 +128 -0
- package/dist/agents/element-agent.d.ts +38 -0
- package/dist/agents/element-agent.d.ts.map +1 -0
- package/dist/agents/element-agent.js +70 -0
- package/dist/agents/element-agent.js.map +1 -0
- package/dist/agents/event-agent.d.ts +47 -0
- package/dist/agents/event-agent.d.ts.map +1 -0
- package/dist/agents/event-agent.js +82 -0
- package/dist/agents/event-agent.js.map +1 -0
- package/dist/agents/index.d.ts +17 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +57 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/region-agents.d.ts +40 -0
- package/dist/agents/region-agents.d.ts.map +1 -0
- package/dist/agents/region-agents.js +22 -0
- package/dist/agents/region-agents.js.map +1 -0
- package/dist/agents/relationship.d.ts +55 -0
- package/dist/agents/relationship.d.ts.map +1 -0
- package/dist/agents/relationship.js +40 -0
- package/dist/agents/relationship.js.map +1 -0
- package/dist/agents/user-agent.d.ts +57 -0
- package/dist/agents/user-agent.d.ts.map +1 -0
- package/dist/agents/user-agent.js +45 -0
- package/dist/agents/user-agent.js.map +1 -0
- package/dist/config/forces.config.d.ts +101 -0
- package/dist/config/forces.config.d.ts.map +1 -0
- package/dist/config/forces.config.js +239 -0
- package/dist/config/forces.config.js.map +1 -0
- package/dist/config/manual.d.ts +134 -0
- package/dist/config/manual.d.ts.map +1 -0
- package/dist/config/manual.js +604 -0
- package/dist/config/manual.js.map +1 -0
- package/dist/config/palettes.d.ts +18 -0
- package/dist/config/palettes.d.ts.map +1 -0
- package/dist/config/palettes.js +34 -0
- package/dist/config/palettes.js.map +1 -0
- package/dist/config/presets.d.ts +48 -0
- package/dist/config/presets.d.ts.map +1 -0
- package/dist/config/presets.js +87 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/tokens.d.ts +3 -0
- package/dist/config/tokens.d.ts.map +1 -0
- package/dist/config/tokens.js +16 -0
- package/dist/config/tokens.js.map +1 -0
- package/dist/conformance/expectations.d.ts +40 -0
- package/dist/conformance/expectations.d.ts.map +1 -0
- package/dist/conformance/expectations.js +347 -0
- package/dist/conformance/expectations.js.map +1 -0
- package/dist/conformance/experiments.d.ts +17 -0
- package/dist/conformance/experiments.d.ts.map +1 -0
- package/dist/conformance/experiments.js +875 -0
- package/dist/conformance/experiments.js.map +1 -0
- package/dist/conformance/run.d.ts +18 -0
- package/dist/conformance/run.d.ts.map +1 -0
- package/dist/conformance/run.js +240 -0
- package/dist/conformance/run.js.map +1 -0
- package/dist/conformance/types.d.ts +100 -0
- package/dist/conformance/types.d.ts.map +1 -0
- package/dist/conformance/types.js +2 -0
- package/dist/conformance/types.js.map +1 -0
- package/dist/contracts/guards.d.ts +51 -0
- package/dist/contracts/guards.d.ts.map +1 -0
- package/dist/contracts/guards.js +100 -0
- package/dist/contracts/guards.js.map +1 -0
- package/dist/contracts/index.d.ts +18 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +107 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/passport.d.ts +88 -0
- package/dist/contracts/passport.d.ts.map +1 -0
- package/dist/contracts/passport.js +135 -0
- package/dist/contracts/passport.js.map +1 -0
- package/dist/contracts/types.d.ts +120 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +24 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/core/accretion.d.ts +50 -0
- package/dist/core/accretion.d.ts.map +1 -0
- package/dist/core/accretion.js +98 -0
- package/dist/core/accretion.js.map +1 -0
- package/dist/core/agents.d.ts +31 -0
- package/dist/core/agents.d.ts.map +1 -0
- package/dist/core/agents.js +51 -0
- package/dist/core/agents.js.map +1 -0
- package/dist/core/attention.d.ts +72 -0
- package/dist/core/attention.d.ts.map +1 -0
- package/dist/core/attention.js +122 -0
- package/dist/core/attention.js.map +1 -0
- package/dist/core/causality.d.ts +38 -0
- package/dist/core/causality.d.ts.map +1 -0
- package/dist/core/causality.js +64 -0
- package/dist/core/causality.js.map +1 -0
- package/dist/core/conditions.d.ts +10 -0
- package/dist/core/conditions.d.ts.map +1 -0
- package/dist/core/conditions.js +22 -0
- package/dist/core/conditions.js.map +1 -0
- package/dist/core/currents.d.ts +53 -0
- package/dist/core/currents.d.ts.map +1 -0
- package/dist/core/currents.js +65 -0
- package/dist/core/currents.js.map +1 -0
- package/dist/core/dock.d.ts +35 -0
- package/dist/core/dock.d.ts.map +1 -0
- package/dist/core/dock.js +39 -0
- package/dist/core/dock.js.map +1 -0
- package/dist/core/events.d.ts +23 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +34 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/feedback-sink.d.ts +32 -0
- package/dist/core/feedback-sink.d.ts.map +1 -0
- package/dist/core/feedback-sink.js +53 -0
- package/dist/core/feedback-sink.js.map +1 -0
- package/dist/core/feedback.d.ts +11 -0
- package/dist/core/feedback.d.ts.map +1 -0
- package/dist/core/feedback.js +16 -0
- package/dist/core/feedback.js.map +1 -0
- package/dist/core/field-store.d.ts +26 -0
- package/dist/core/field-store.d.ts.map +1 -0
- package/dist/core/field-store.js +54 -0
- package/dist/core/field-store.js.map +1 -0
- package/dist/core/field.d.ts +18 -0
- package/dist/core/field.d.ts.map +1 -0
- package/dist/core/field.js +1943 -0
- package/dist/core/field.js.map +1 -0
- package/dist/core/fieldline-seeds.d.ts +25 -0
- package/dist/core/fieldline-seeds.d.ts.map +1 -0
- package/dist/core/fieldline-seeds.js +32 -0
- package/dist/core/fieldline-seeds.js.map +1 -0
- package/dist/core/fieldlines.d.ts +75 -0
- package/dist/core/fieldlines.d.ts.map +1 -0
- package/dist/core/fieldlines.js +111 -0
- package/dist/core/fieldlines.js.map +1 -0
- package/dist/core/flow.d.ts +38 -0
- package/dist/core/flow.d.ts.map +1 -0
- package/dist/core/flow.js +27 -0
- package/dist/core/flow.js.map +1 -0
- package/dist/core/formations.d.ts +11 -0
- package/dist/core/formations.d.ts.map +1 -0
- package/dist/core/formations.js +22 -0
- package/dist/core/formations.js.map +1 -0
- package/dist/core/geometry.d.ts +67 -0
- package/dist/core/geometry.d.ts.map +1 -0
- package/dist/core/geometry.js +68 -0
- package/dist/core/geometry.js.map +1 -0
- package/dist/core/heatmap.d.ts +22 -0
- package/dist/core/heatmap.d.ts.map +1 -0
- package/dist/core/heatmap.js +55 -0
- package/dist/core/heatmap.js.map +1 -0
- package/dist/core/host.d.ts +46 -0
- package/dist/core/host.d.ts.map +1 -0
- package/dist/core/host.js +11 -0
- package/dist/core/host.js.map +1 -0
- package/dist/core/integrator.d.ts +24 -0
- package/dist/core/integrator.d.ts.map +1 -0
- package/dist/core/integrator.js +375 -0
- package/dist/core/integrator.js.map +1 -0
- package/dist/core/math.d.ts +37 -0
- package/dist/core/math.d.ts.map +1 -0
- package/dist/core/math.js +77 -0
- package/dist/core/math.js.map +1 -0
- package/dist/core/reactions.d.ts +32 -0
- package/dist/core/reactions.d.ts.map +1 -0
- package/dist/core/reactions.js +45 -0
- package/dist/core/reactions.js.map +1 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +20 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/render-backend.d.ts +46 -0
- package/dist/core/render-backend.d.ts.map +1 -0
- package/dist/core/render-backend.js +75 -0
- package/dist/core/render-backend.js.map +1 -0
- package/dist/core/render-modes.d.ts +42 -0
- package/dist/core/render-modes.d.ts.map +1 -0
- package/dist/core/render-modes.js +141 -0
- package/dist/core/render-modes.js.map +1 -0
- package/dist/core/reservoir.d.ts +43 -0
- package/dist/core/reservoir.d.ts.map +1 -0
- package/dist/core/reservoir.js +207 -0
- package/dist/core/reservoir.js.map +1 -0
- package/dist/core/scalar-grid.d.ts +51 -0
- package/dist/core/scalar-grid.d.ts.map +1 -0
- package/dist/core/scalar-grid.js +146 -0
- package/dist/core/scalar-grid.js.map +1 -0
- package/dist/core/scanner.d.ts +59 -0
- package/dist/core/scanner.d.ts.map +1 -0
- package/dist/core/scanner.js +260 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/shadow.d.ts +69 -0
- package/dist/core/shadow.d.ts.map +1 -0
- package/dist/core/shadow.js +84 -0
- package/dist/core/shadow.js.map +1 -0
- package/dist/core/spatial-hash.d.ts +30 -0
- package/dist/core/spatial-hash.d.ts.map +1 -0
- package/dist/core/spatial-hash.js +64 -0
- package/dist/core/spatial-hash.js.map +1 -0
- package/dist/core/streamlines.d.ts +29 -0
- package/dist/core/streamlines.d.ts.map +1 -0
- package/dist/core/streamlines.js +70 -0
- package/dist/core/streamlines.js.map +1 -0
- package/dist/core/surface.d.ts +19 -0
- package/dist/core/surface.d.ts.map +1 -0
- package/dist/core/surface.js +21 -0
- package/dist/core/surface.js.map +1 -0
- package/dist/core/temporal.d.ts +110 -0
- package/dist/core/temporal.d.ts.map +1 -0
- package/dist/core/temporal.js +139 -0
- package/dist/core/temporal.js.map +1 -0
- package/dist/core/thermo.d.ts +48 -0
- package/dist/core/thermo.d.ts.map +1 -0
- package/dist/core/thermo.js +48 -0
- package/dist/core/thermo.js.map +1 -0
- package/dist/core/types.d.ts +610 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/weights.d.ts +111 -0
- package/dist/core/weights.d.ts.map +1 -0
- package/dist/core/weights.js +128 -0
- package/dist/core/weights.js.map +1 -0
- package/dist/diagnostics/energy.d.ts +21 -0
- package/dist/diagnostics/energy.d.ts.map +1 -0
- package/dist/diagnostics/energy.js +27 -0
- package/dist/diagnostics/energy.js.map +1 -0
- package/dist/diagnostics/fields.d.ts +23 -0
- package/dist/diagnostics/fields.d.ts.map +1 -0
- package/dist/diagnostics/fields.js +30 -0
- package/dist/diagnostics/fields.js.map +1 -0
- package/dist/diagnostics/index.d.ts +46 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +23 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/diagnostics/modes.d.ts +108 -0
- package/dist/diagnostics/modes.d.ts.map +1 -0
- package/dist/diagnostics/modes.js +181 -0
- package/dist/diagnostics/modes.js.map +1 -0
- package/dist/diagnostics/potential.d.ts +30 -0
- package/dist/diagnostics/potential.d.ts.map +1 -0
- package/dist/diagnostics/potential.js +43 -0
- package/dist/diagnostics/potential.js.map +1 -0
- package/dist/diagnostics/probes.d.ts +31 -0
- package/dist/diagnostics/probes.d.ts.map +1 -0
- package/dist/diagnostics/probes.js +61 -0
- package/dist/diagnostics/probes.js.map +1 -0
- package/dist/diagnostics/render.d.ts +49 -0
- package/dist/diagnostics/render.d.ts.map +1 -0
- package/dist/diagnostics/render.js +132 -0
- package/dist/diagnostics/render.js.map +1 -0
- package/dist/export.d.ts +18 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +17 -0
- package/dist/export.js.map +1 -0
- package/dist/forces/extended.d.ts +121 -0
- package/dist/forces/extended.d.ts.map +1 -0
- package/dist/forces/extended.js +674 -0
- package/dist/forces/extended.js.map +1 -0
- package/dist/forces/index.d.ts +33 -0
- package/dist/forces/index.d.ts.map +1 -0
- package/dist/forces/index.js +237 -0
- package/dist/forces/index.js.map +1 -0
- package/dist/forces/natural.d.ts +106 -0
- package/dist/forces/natural.d.ts.map +1 -0
- package/dist/forces/natural.js +385 -0
- package/dist/forces/natural.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect/budget.d.ts +17 -0
- package/dist/inspect/budget.d.ts.map +1 -0
- package/dist/inspect/budget.js +19 -0
- package/dist/inspect/budget.js.map +1 -0
- package/dist/inspect/index.d.ts +10 -0
- package/dist/inspect/index.d.ts.map +1 -0
- package/dist/inspect/index.js +10 -0
- package/dist/inspect/index.js.map +1 -0
- package/dist/inspect/report.d.ts +17 -0
- package/dist/inspect/report.d.ts.map +1 -0
- package/dist/inspect/report.js +44 -0
- package/dist/inspect/report.js.map +1 -0
- package/dist/inspect/snapshot.d.ts +21 -0
- package/dist/inspect/snapshot.d.ts.map +1 -0
- package/dist/inspect/snapshot.js +30 -0
- package/dist/inspect/snapshot.js.map +1 -0
- package/dist/recipes/catalog.d.ts +51 -0
- package/dist/recipes/catalog.d.ts.map +1 -0
- package/dist/recipes/catalog.js +1496 -0
- package/dist/recipes/catalog.js.map +1 -0
- package/dist/recipes/charge.d.ts +18 -0
- package/dist/recipes/charge.d.ts.map +1 -0
- package/dist/recipes/charge.js +27 -0
- package/dist/recipes/charge.js.map +1 -0
- package/dist/recipes/compile.d.ts +93 -0
- package/dist/recipes/compile.d.ts.map +1 -0
- package/dist/recipes/compile.js +113 -0
- package/dist/recipes/compile.js.map +1 -0
- package/dist/recipes/explain.d.ts +8 -0
- package/dist/recipes/explain.d.ts.map +1 -0
- package/dist/recipes/explain.js +46 -0
- package/dist/recipes/explain.js.map +1 -0
- package/dist/recipes/gallery.d.ts +6 -0
- package/dist/recipes/gallery.d.ts.map +1 -0
- package/dist/recipes/gallery.js +6 -0
- package/dist/recipes/gallery.js.map +1 -0
- package/dist/recipes/gravity.d.ts +16 -0
- package/dist/recipes/gravity.d.ts.map +1 -0
- package/dist/recipes/gravity.js +27 -0
- package/dist/recipes/gravity.js.map +1 -0
- package/dist/recipes/index.d.ts +18 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +36 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/recipes/intent.d.ts +44 -0
- package/dist/recipes/intent.d.ts.map +1 -0
- package/dist/recipes/intent.js +46 -0
- package/dist/recipes/intent.js.map +1 -0
- package/dist/recipes/schema.d.ts +103 -0
- package/dist/recipes/schema.d.ts.map +1 -0
- package/dist/recipes/schema.js +123 -0
- package/dist/recipes/schema.js.map +1 -0
- package/dist/recipes/wayfinding.d.ts +39 -0
- package/dist/recipes/wayfinding.d.ts.map +1 -0
- package/dist/recipes/wayfinding.js +77 -0
- package/dist/recipes/wayfinding.js.map +1 -0
- package/dist/semantic/index.d.ts +13 -0
- package/dist/semantic/index.d.ts.map +1 -0
- package/dist/semantic/index.js +31 -0
- package/dist/semantic/index.js.map +1 -0
- package/dist/semantic/layers.d.ts +24 -0
- package/dist/semantic/layers.d.ts.map +1 -0
- package/dist/semantic/layers.js +27 -0
- package/dist/semantic/layers.js.map +1 -0
- package/dist/semantic/materials.d.ts +20 -0
- package/dist/semantic/materials.d.ts.map +1 -0
- package/dist/semantic/materials.js +17 -0
- package/dist/semantic/materials.js.map +1 -0
- package/dist/semantic/states.d.ts +11 -0
- package/dist/semantic/states.d.ts.map +1 -0
- package/dist/semantic/states.js +26 -0
- package/dist/semantic/states.js.map +1 -0
- package/dist/visual/channels.d.ts +71 -0
- package/dist/visual/channels.d.ts.map +1 -0
- package/dist/visual/channels.js +70 -0
- package/dist/visual/channels.js.map +1 -0
- package/dist/visual/index.d.ts +39 -0
- package/dist/visual/index.d.ts.map +1 -0
- package/dist/visual/index.js +30 -0
- package/dist/visual/index.js.map +1 -0
- package/dist/visual/lint.d.ts +41 -0
- package/dist/visual/lint.d.ts.map +1 -0
- package/dist/visual/lint.js +58 -0
- package/dist/visual/lint.js.map +1 -0
- package/dist/visual/mapping.d.ts +13 -0
- package/dist/visual/mapping.d.ts.map +1 -0
- package/dist/visual/mapping.js +43 -0
- package/dist/visual/mapping.js.map +1 -0
- package/dist/visual/semantic-text.d.ts +28 -0
- package/dist/visual/semantic-text.d.ts.map +1 -0
- package/dist/visual/semantic-text.js +36 -0
- package/dist/visual/semantic-text.js.map +1 -0
- package/dist/visual/tokens.d.ts +23 -0
- package/dist/visual/tokens.d.ts.map +1 -0
- package/dist/visual/tokens.js +54 -0
- package/dist/visual/tokens.js.map +1 -0
- package/dist/visual/visualization.d.ts +31 -0
- package/dist/visual/visualization.d.ts.map +1 -0
- package/dist/visual/visualization.js +47 -0
- package/dist/visual/visualization.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scalar field on a uniform grid — the backing store for field-buffer forces
|
|
3
|
+
* (§20.1 class [C]): `diffuse` (heat/concentration, `∂φ/∂t = D∇²φ`) and `propagate`
|
|
4
|
+
* (a travelling wave, `∂²φ/∂t² = c²∇²φ`). Particles `deposit` into it and read its
|
|
5
|
+
* `gradient`; the engine advances it once per frame with `step()`.
|
|
6
|
+
*
|
|
7
|
+
* Two stepping modes, chosen at construction (the field picks by grid name):
|
|
8
|
+
* - `diffuse` — explicit heat equation with a decay term; D is clamped to the
|
|
9
|
+
* stable range for the forward scheme.
|
|
10
|
+
* - `wave` — second-order leapfrog using a previous buffer; c² clamped for the
|
|
11
|
+
* CFL limit, with light damping.
|
|
12
|
+
*
|
|
13
|
+
* Pure (no DOM), so every operation is golden-tested.
|
|
14
|
+
*/
|
|
15
|
+
import type { ScalarGrid, Vec2 } from './types.ts';
|
|
16
|
+
export type GridMode = 'diffuse' | 'wave' | 'memory';
|
|
17
|
+
export declare class ScalarGridImpl implements ScalarGrid {
|
|
18
|
+
readonly mode: GridMode;
|
|
19
|
+
readonly cell: number;
|
|
20
|
+
private W;
|
|
21
|
+
private H;
|
|
22
|
+
private cols;
|
|
23
|
+
private rows;
|
|
24
|
+
private cur;
|
|
25
|
+
private nxt;
|
|
26
|
+
private prev;
|
|
27
|
+
constructor(W: number, H: number, mode?: GridMode, cell?: number);
|
|
28
|
+
private clampCol;
|
|
29
|
+
private clampRow;
|
|
30
|
+
/** the current value at a clamped (Neumann boundary) cell. */
|
|
31
|
+
private at;
|
|
32
|
+
/** bilinear sample of the field in pixel space. */
|
|
33
|
+
sample(x: number, y: number): number;
|
|
34
|
+
/** add `amount` to the nearest cell. */
|
|
35
|
+
deposit(x: number, y: number, amount: number): void;
|
|
36
|
+
/** the current peak value across the field — for normalizing a heatmap to [0, 1]. */
|
|
37
|
+
max(): number;
|
|
38
|
+
/** central-difference gradient ∇φ in pixel space (points up-slope). */
|
|
39
|
+
gradient(x: number, y: number): Vec2;
|
|
40
|
+
/** advance one frame in the grid's mode. */
|
|
41
|
+
step(): void;
|
|
42
|
+
/** explicit heat equation `φ' = (φ + D·∇²φ)·(1 − decay)` (§20.10). */
|
|
43
|
+
stepDiffuse(D?: number, decay?: number): void;
|
|
44
|
+
/** leapfrog wave `φ' = 2φ − φ_prev + c²·∇²φ`, lightly damped (§20.10). */
|
|
45
|
+
stepWave(c2?: number, damping?: number): void;
|
|
46
|
+
/** Zero every cell in all internal buffers (cur, nxt, prev). */
|
|
47
|
+
clear(): void;
|
|
48
|
+
/** resize to a new viewport, preserving nothing (rebuilds the buffers). */
|
|
49
|
+
resize(W: number, H: number): void;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=scalar-grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scalar-grid.d.ts","sourceRoot":"","sources":["../../src/core/scalar-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAErD,qBAAa,cAAe,YAAW,UAAU;IAC/C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,CAAC,CAAS;IAClB,OAAO,CAAC,CAAC,CAAS;IAClB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,GAAG,CAAe;IAC1B,OAAO,CAAC,GAAG,CAAe;IAC1B,OAAO,CAAC,IAAI,CAAe;gBAIf,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,QAAoB,EAAE,IAAI,SAAK;IAavE,OAAO,CAAC,QAAQ;IAGhB,OAAO,CAAC,QAAQ;IAGhB,8DAA8D;IAC9D,OAAO,CAAC,EAAE;IAIV,mDAAmD;IACnD,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAYpC,wCAAwC;IACxC,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMnD,qFAAqF;IACrF,GAAG,IAAI,MAAM;IAMb,uEAAuE;IACvE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAQpC,4CAA4C;IAC5C,IAAI,IAAI,IAAI;IAMZ,sEAAsE;IACtE,WAAW,CAAC,CAAC,SAAO,EAAE,KAAK,SAAO,GAAG,IAAI;IAiBzC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,SAAO,EAAE,OAAO,SAAQ,GAAG,IAAI;IAmB1C,gEAAgE;IAChE,KAAK,IAAI,IAAI;IAMb,2EAA2E;IAC3E,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;CAWnC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scalar field on a uniform grid — the backing store for field-buffer forces
|
|
3
|
+
* (§20.1 class [C]): `diffuse` (heat/concentration, `∂φ/∂t = D∇²φ`) and `propagate`
|
|
4
|
+
* (a travelling wave, `∂²φ/∂t² = c²∇²φ`). Particles `deposit` into it and read its
|
|
5
|
+
* `gradient`; the engine advances it once per frame with `step()`.
|
|
6
|
+
*
|
|
7
|
+
* Two stepping modes, chosen at construction (the field picks by grid name):
|
|
8
|
+
* - `diffuse` — explicit heat equation with a decay term; D is clamped to the
|
|
9
|
+
* stable range for the forward scheme.
|
|
10
|
+
* - `wave` — second-order leapfrog using a previous buffer; c² clamped for the
|
|
11
|
+
* CFL limit, with light damping.
|
|
12
|
+
*
|
|
13
|
+
* Pure (no DOM), so every operation is golden-tested.
|
|
14
|
+
*/
|
|
15
|
+
export class ScalarGridImpl {
|
|
16
|
+
mode;
|
|
17
|
+
cell;
|
|
18
|
+
W;
|
|
19
|
+
H;
|
|
20
|
+
cols;
|
|
21
|
+
rows;
|
|
22
|
+
cur;
|
|
23
|
+
nxt;
|
|
24
|
+
prev; // previous frame, for the wave scheme
|
|
25
|
+
// NB: explicit field assignment, not constructor parameter properties — Node's
|
|
26
|
+
// strip-only type-stripping (which runs node:test) rejects the latter.
|
|
27
|
+
constructor(W, H, mode = 'diffuse', cell = 32) {
|
|
28
|
+
this.W = W;
|
|
29
|
+
this.H = H;
|
|
30
|
+
this.mode = mode;
|
|
31
|
+
this.cell = cell;
|
|
32
|
+
this.cols = Math.max(2, Math.ceil(W / cell) + 1);
|
|
33
|
+
this.rows = Math.max(2, Math.ceil(H / cell) + 1);
|
|
34
|
+
const n = this.cols * this.rows;
|
|
35
|
+
this.cur = new Float32Array(n);
|
|
36
|
+
this.nxt = new Float32Array(n);
|
|
37
|
+
this.prev = new Float32Array(n);
|
|
38
|
+
}
|
|
39
|
+
clampCol(ix) {
|
|
40
|
+
return ix < 0 ? 0 : ix >= this.cols ? this.cols - 1 : ix;
|
|
41
|
+
}
|
|
42
|
+
clampRow(iy) {
|
|
43
|
+
return iy < 0 ? 0 : iy >= this.rows ? this.rows - 1 : iy;
|
|
44
|
+
}
|
|
45
|
+
/** the current value at a clamped (Neumann boundary) cell. */
|
|
46
|
+
at(ix, iy) {
|
|
47
|
+
return this.cur[this.clampRow(iy) * this.cols + this.clampCol(ix)];
|
|
48
|
+
}
|
|
49
|
+
/** bilinear sample of the field in pixel space. */
|
|
50
|
+
sample(x, y) {
|
|
51
|
+
const gx = x / this.cell;
|
|
52
|
+
const gy = y / this.cell;
|
|
53
|
+
const ix = Math.floor(gx);
|
|
54
|
+
const iy = Math.floor(gy);
|
|
55
|
+
const fx = gx - ix;
|
|
56
|
+
const fy = gy - iy;
|
|
57
|
+
const top = this.at(ix, iy) * (1 - fx) + this.at(ix + 1, iy) * fx;
|
|
58
|
+
const bot = this.at(ix, iy + 1) * (1 - fx) + this.at(ix + 1, iy + 1) * fx;
|
|
59
|
+
return top * (1 - fy) + bot * fy;
|
|
60
|
+
}
|
|
61
|
+
/** add `amount` to the nearest cell. */
|
|
62
|
+
deposit(x, y, amount) {
|
|
63
|
+
const ix = this.clampCol(Math.round(x / this.cell));
|
|
64
|
+
const iy = this.clampRow(Math.round(y / this.cell));
|
|
65
|
+
this.cur[iy * this.cols + ix] += amount;
|
|
66
|
+
}
|
|
67
|
+
/** the current peak value across the field — for normalizing a heatmap to [0, 1]. */
|
|
68
|
+
max() {
|
|
69
|
+
let m = 0;
|
|
70
|
+
for (let i = 0; i < this.cur.length; i++)
|
|
71
|
+
if (this.cur[i] > m)
|
|
72
|
+
m = this.cur[i];
|
|
73
|
+
return m;
|
|
74
|
+
}
|
|
75
|
+
/** central-difference gradient ∇φ in pixel space (points up-slope). */
|
|
76
|
+
gradient(x, y) {
|
|
77
|
+
const h = this.cell;
|
|
78
|
+
return {
|
|
79
|
+
x: (this.sample(x + h, y) - this.sample(x - h, y)) / (2 * h),
|
|
80
|
+
y: (this.sample(x, y + h) - this.sample(x, y - h)) / (2 * h),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** advance one frame in the grid's mode. */
|
|
84
|
+
step() {
|
|
85
|
+
if (this.mode === 'wave')
|
|
86
|
+
this.stepWave();
|
|
87
|
+
else if (this.mode === 'memory')
|
|
88
|
+
this.stepDiffuse(0.03, 0.004); // barely blur, fade slowly
|
|
89
|
+
else
|
|
90
|
+
this.stepDiffuse();
|
|
91
|
+
}
|
|
92
|
+
/** explicit heat equation `φ' = (φ + D·∇²φ)·(1 − decay)` (§20.10). */
|
|
93
|
+
stepDiffuse(D = 0.18, decay = 0.01) {
|
|
94
|
+
const Dc = D < 0 ? 0 : D > 0.24 ? 0.24 : D; // forward-scheme stability
|
|
95
|
+
const keep = 1 - decay;
|
|
96
|
+
const { cols, rows, cur, nxt } = this;
|
|
97
|
+
for (let iy = 0; iy < rows; iy++) {
|
|
98
|
+
for (let ix = 0; ix < cols; ix++) {
|
|
99
|
+
const i = iy * cols + ix;
|
|
100
|
+
const lap = this.at(ix - 1, iy) + this.at(ix + 1, iy) + this.at(ix, iy - 1) + this.at(ix, iy + 1) -
|
|
101
|
+
4 * cur[i];
|
|
102
|
+
nxt[i] = (cur[i] + Dc * lap) * keep;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.cur = nxt;
|
|
106
|
+
this.nxt = cur;
|
|
107
|
+
}
|
|
108
|
+
/** leapfrog wave `φ' = 2φ − φ_prev + c²·∇²φ`, lightly damped (§20.10). */
|
|
109
|
+
stepWave(c2 = 0.25, damping = 0.002) {
|
|
110
|
+
const cc = c2 < 0 ? 0 : c2 > 0.5 ? 0.5 : c2; // CFL limit
|
|
111
|
+
const keep = 1 - damping;
|
|
112
|
+
const { cols, rows, cur, prev, nxt } = this;
|
|
113
|
+
for (let iy = 0; iy < rows; iy++) {
|
|
114
|
+
for (let ix = 0; ix < cols; ix++) {
|
|
115
|
+
const i = iy * cols + ix;
|
|
116
|
+
const lap = this.at(ix - 1, iy) + this.at(ix + 1, iy) + this.at(ix, iy - 1) + this.at(ix, iy + 1) -
|
|
117
|
+
4 * cur[i];
|
|
118
|
+
nxt[i] = (2 * cur[i] - prev[i] + cc * lap) * keep;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// rotate buffers: prev ← cur, cur ← nxt, reuse old prev as next scratch
|
|
122
|
+
this.prev = cur;
|
|
123
|
+
this.cur = nxt;
|
|
124
|
+
this.nxt = prev;
|
|
125
|
+
}
|
|
126
|
+
/** Zero every cell in all internal buffers (cur, nxt, prev). */
|
|
127
|
+
clear() {
|
|
128
|
+
this.cur.fill(0);
|
|
129
|
+
this.nxt.fill(0);
|
|
130
|
+
this.prev.fill(0);
|
|
131
|
+
}
|
|
132
|
+
/** resize to a new viewport, preserving nothing (rebuilds the buffers). */
|
|
133
|
+
resize(W, H) {
|
|
134
|
+
if (W === this.W && H === this.H)
|
|
135
|
+
return;
|
|
136
|
+
this.W = W;
|
|
137
|
+
this.H = H;
|
|
138
|
+
this.cols = Math.max(2, Math.ceil(W / this.cell) + 1);
|
|
139
|
+
this.rows = Math.max(2, Math.ceil(H / this.cell) + 1);
|
|
140
|
+
const n = this.cols * this.rows;
|
|
141
|
+
this.cur = new Float32Array(n);
|
|
142
|
+
this.nxt = new Float32Array(n);
|
|
143
|
+
this.prev = new Float32Array(n);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=scalar-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scalar-grid.js","sourceRoot":"","sources":["../../src/core/scalar-grid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DOM scanner — turns `[data-body]` elements into bodies the engine reads
|
|
3
|
+
* (§2.1, §3.1). The *parsing* is a pure function (testable without a DOM); the
|
|
4
|
+
* *measurement* (`getBoundingClientRect`) is thin glue.
|
|
5
|
+
*/
|
|
6
|
+
import type { Body } from './types.ts';
|
|
7
|
+
/** A minimal attribute accessor — `name` is the suffix after `data-`. */
|
|
8
|
+
export interface BodyAttrs {
|
|
9
|
+
get(name: string): string | null | undefined;
|
|
10
|
+
has(name: string): boolean;
|
|
11
|
+
}
|
|
12
|
+
/** The static (non-runtime) fields of a Body, parsed from attributes. */
|
|
13
|
+
export type StaticBody = Pick<Body, 'tokens' | 'strength' | 'range' | 'absorbR' | 'capacity' | 'spin' | 'angle' | 'ux' | 'uy' | 'when' | 'feedback' | 'shaped' | 'fmin' | 'fmax' | 'opsz' | 'pair' | 'twist' | 'warpScale' | 'life' | 'cap' | 'budgeted' | 'screenMin' | 'classified' | 'M'>;
|
|
14
|
+
/** Parse a body's static parameters from its `data-*` attributes (pure). */
|
|
15
|
+
export declare function parseBodyParams(a: BodyAttrs): StaticBody;
|
|
16
|
+
/** Expand a preset name into the static params of its virtual bodies (pure, §20.9).
|
|
17
|
+
* An unknown preset yields `[]` — the element simply contributes nothing. */
|
|
18
|
+
export declare function expandPreset(name: string): StaticBody[];
|
|
19
|
+
/**
|
|
20
|
+
* The source-budget guard (workover v0.3, §"Source and sink rules"): a class-[S] source body
|
|
21
|
+
* (`spawn` — see `SOURCE_TOKENS`) must declare at least one of `data-life` / `data-cap` /
|
|
22
|
+
* `data-budget` / `data-sink`. An unbudgeted source gets the safe default budget
|
|
23
|
+
* (`data-life="300"`, `data-cap="120"`) so its live emission is bounded at ~cap regardless of
|
|
24
|
+
* the pool ceiling — and, in dev (the same flag that gates the contract guards), a LOUD
|
|
25
|
+
* `console.warn` naming the element and the missing attributes. Mutates `sb` in place; pure
|
|
26
|
+
* over its inputs otherwise (no DOM reads).
|
|
27
|
+
*/
|
|
28
|
+
export declare function guardSourceBudget(sb: StaticBody, name?: string): void;
|
|
29
|
+
/** Build a single body from an element, parsing its `data-*` (or an explicit `attrs` view,
|
|
30
|
+
* for event-registered shadow-DOM hosts whose attrs may be supplied out of band). */
|
|
31
|
+
export declare function bodyFromElement(el: HTMLElement, attrs?: BodyAttrs): Body;
|
|
32
|
+
/**
|
|
33
|
+
* A `BodyAttrs` for an element authored via `data-intent` or `data-field-role` instead of a raw
|
|
34
|
+
* `data-body`. The intent is compiled (authoring §4) or the role mapped to a default token; explicit
|
|
35
|
+
* `data-*` on the element still wins over the compiled defaults (precedence §3 — component props beat
|
|
36
|
+
* intent defaults). Returns null when the element carries neither an intent nor a known role.
|
|
37
|
+
*/
|
|
38
|
+
export declare function authoredAttrs(el: HTMLElement): BodyAttrs | null;
|
|
39
|
+
/**
|
|
40
|
+
* The selector matching every element the scanner turns into one or more bodies: a raw `data-body`,
|
|
41
|
+
* a `data-preset`, or an element authored via `data-intent` / `data-field-role`. This is the single
|
|
42
|
+
* source of truth shared with the platform's MeasurementRegistry (Phase D) so the two never drift.
|
|
43
|
+
* `querySelectorAll` returns each element once, even if it matches several clauses.
|
|
44
|
+
*/
|
|
45
|
+
export declare const BODY_SELECTOR = "[data-body], [data-preset], [data-intent]:not([data-body]), [data-field-role]:not([data-body]):not([data-intent])";
|
|
46
|
+
/**
|
|
47
|
+
* Every element in `root` that contributes a body, deduped and in document order. The geometry
|
|
48
|
+
* source for the platform MeasurementRegistry; pure over the passed root (no globals). A
|
|
49
|
+
* `data-preset` element still expands to several virtual bodies in `scanBodies`, but it is one
|
|
50
|
+
* element with one rect — which is exactly what measurement registers.
|
|
51
|
+
*/
|
|
52
|
+
export declare function bodyElements(root: ParentNode): Element[];
|
|
53
|
+
/** Scan a DOM subtree for `[data-body]` and `[data-preset]` elements → bodies (§2.1,
|
|
54
|
+
* §20.9). A preset element emits one virtual body per entry, all sharing its rect;
|
|
55
|
+
* the plain `data-body` path is unchanged. */
|
|
56
|
+
export declare function scanBodies(root: ParentNode): Body[];
|
|
57
|
+
/** Refresh each body's measured rect, visibility, and engaged state (§2.1). */
|
|
58
|
+
export declare function measureBodies(bodies: readonly Body[], W: number, H: number): void;
|
|
59
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAUvC,yEAAyE;AACzE,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC7C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAAG,IAAI,CAC3B,IAAI,EACF,QAAQ,GACR,UAAU,GACV,OAAO,GACP,SAAS,GACT,UAAU,GACV,MAAM,GACN,OAAO,GACP,IAAI,GACJ,IAAI,GACJ,MAAM,GACN,UAAU,GACV,QAAQ,GACR,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,WAAW,GACX,MAAM,GACN,KAAK,GACL,UAAU,GACV,WAAW,GACX,YAAY,GACZ,GAAG,CACN,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,GAAG,UAAU,CAoDxD;AAqBD;8EAC8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAGvD;AAUD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,SAAW,GAAG,IAAI,CAYvE;AA8BD;sFACsF;AACtF,wBAAgB,eAAe,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAExE;AAgBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CA6B/D;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,sHAC2F,CAAC;AAEtH;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,CAExD;AAED;;+CAE+C;AAC/C,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,CAkBnD;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAcjF"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DOM scanner — turns `[data-body]` elements into bodies the engine reads
|
|
3
|
+
* (§2.1, §3.1). The *parsing* is a pure function (testable without a DOM); the
|
|
4
|
+
* *measurement* (`getBoundingClientRect`) is thin glue.
|
|
5
|
+
*/
|
|
6
|
+
import { PRESETS } from "../config/presets.js";
|
|
7
|
+
import { classifyBodyTokens, SOURCE_DEFAULT_CAP, SOURCE_DEFAULT_LIFE, } from "../config/forces.config.js";
|
|
8
|
+
import { contractChecksEnabled } from "../contracts/guards.js";
|
|
9
|
+
import { compileIntent } from "../recipes/intent.js";
|
|
10
|
+
/** Parse a body's static parameters from its `data-*` attributes (pure). */
|
|
11
|
+
export function parseBodyParams(a) {
|
|
12
|
+
const num = (name, def) => {
|
|
13
|
+
const v = Number.parseFloat(a.get(name) ?? '');
|
|
14
|
+
return Number.isFinite(v) ? v : def;
|
|
15
|
+
};
|
|
16
|
+
const strength = num('strength', 0.5);
|
|
17
|
+
const angle = (num('angle', 0) * Math.PI) / 180;
|
|
18
|
+
const spinRaw = a.get('spin');
|
|
19
|
+
const spin = spinRaw == null ? 1 : Number.isFinite(Number.parseFloat(spinRaw)) ? Number.parseFloat(spinRaw) : 0;
|
|
20
|
+
const tokens = (a.get('body') ?? '').split(/\s+/).filter(Boolean);
|
|
21
|
+
// the modifier contract (workover v0.3): classify tokens into {modifiers, forces, sources}
|
|
22
|
+
// at parse time, so the integrator consumes the classified sets in the documented order.
|
|
23
|
+
const classified = classifyBodyTokens(tokens);
|
|
24
|
+
// the source budget contract: an [S] body declares at least one of
|
|
25
|
+
// data-life / data-cap / data-budget / data-sink (guarded in makeBody/guardSourceBudget).
|
|
26
|
+
const lifeRaw = a.get('life');
|
|
27
|
+
const capRaw = a.get('cap');
|
|
28
|
+
const life = Number.parseFloat(lifeRaw ?? '');
|
|
29
|
+
const cap = Number.parseFloat(capRaw ?? '');
|
|
30
|
+
const budgeted = lifeRaw != null || capRaw != null || a.has('budget') || a.has('sink');
|
|
31
|
+
return {
|
|
32
|
+
tokens,
|
|
33
|
+
classified,
|
|
34
|
+
...(Number.isFinite(life) && life > 0 ? { life } : {}),
|
|
35
|
+
...(Number.isFinite(cap) && cap > 0 ? { cap } : {}),
|
|
36
|
+
budgeted,
|
|
37
|
+
strength,
|
|
38
|
+
range: num('range', 280),
|
|
39
|
+
absorbR: num('absorb', 64),
|
|
40
|
+
capacity: num('max', 60),
|
|
41
|
+
spin,
|
|
42
|
+
angle,
|
|
43
|
+
ux: Math.cos(angle),
|
|
44
|
+
uy: Math.sin(angle),
|
|
45
|
+
when: a.get('when') ?? '',
|
|
46
|
+
feedback: a.has('feedback'),
|
|
47
|
+
shaped: a.has('shaped'), // data-shaped → forces sample the element's box surface (Stage C)
|
|
48
|
+
fmin: num('fmin', 0),
|
|
49
|
+
fmax: num('fmax', 0),
|
|
50
|
+
opsz: a.get('opsz') ?? '',
|
|
51
|
+
// `warp` relocate pairing (§22.3): the paired body's selector + the twist/scale applied to
|
|
52
|
+
// matter that crosses the throat. Undefined `pair` ⇒ not a wormhole (the force no-ops).
|
|
53
|
+
pair: a.get('pair') || undefined,
|
|
54
|
+
twist: (num('twist', 0) * Math.PI) / 180,
|
|
55
|
+
warpScale: num('scale', 1),
|
|
56
|
+
// `screen` quiet zones (workover v0.3): the attenuation floor (0 = full cancellation allowed).
|
|
57
|
+
screenMin: num('screen-min', 0),
|
|
58
|
+
// source mass M = strength · k_g (k_g = 1 for now, §20.10).
|
|
59
|
+
M: strength,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** A `BodyAttrs` view over one preset entry, so it parses through the very same
|
|
63
|
+
* defaults as a real `data-*` element (single source of truth in `parseBodyParams`).
|
|
64
|
+
* The entry's keys map to the attribute suffixes the parser already reads. */
|
|
65
|
+
function entryAttrs(e) {
|
|
66
|
+
const map = { body: e.body };
|
|
67
|
+
if (e.strength != null)
|
|
68
|
+
map.strength = String(e.strength);
|
|
69
|
+
if (e.range != null)
|
|
70
|
+
map.range = String(e.range);
|
|
71
|
+
if (e.spin != null)
|
|
72
|
+
map.spin = String(e.spin);
|
|
73
|
+
if (e.angle != null)
|
|
74
|
+
map.angle = String(e.angle);
|
|
75
|
+
if (e.absorb != null)
|
|
76
|
+
map.absorb = String(e.absorb);
|
|
77
|
+
if (e.max != null)
|
|
78
|
+
map.max = String(e.max);
|
|
79
|
+
if (e.life != null)
|
|
80
|
+
map.life = String(e.life);
|
|
81
|
+
if (e.cap != null)
|
|
82
|
+
map.cap = String(e.cap);
|
|
83
|
+
return {
|
|
84
|
+
get: (name) => map[name] ?? null,
|
|
85
|
+
has: (name) => name in map,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Expand a preset name into the static params of its virtual bodies (pure, §20.9).
|
|
89
|
+
* An unknown preset yields `[]` — the element simply contributes nothing. */
|
|
90
|
+
export function expandPreset(name) {
|
|
91
|
+
const entries = PRESETS[name];
|
|
92
|
+
return entries ? entries.map((e) => parseBodyParams(entryAttrs(e))) : [];
|
|
93
|
+
}
|
|
94
|
+
/** A compact element description for guard messages: `tag#id.class` (best effort). */
|
|
95
|
+
function describeEl(el) {
|
|
96
|
+
const tag = (el.tagName ?? 'element').toLowerCase();
|
|
97
|
+
const id = el.id ? `#${el.id}` : '';
|
|
98
|
+
const cls = typeof el.className === 'string' && el.className ? `.${el.className.split(/\s+/)[0]}` : '';
|
|
99
|
+
return `<${tag}${id}${cls}>`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The source-budget guard (workover v0.3, §"Source and sink rules"): a class-[S] source body
|
|
103
|
+
* (`spawn` — see `SOURCE_TOKENS`) must declare at least one of `data-life` / `data-cap` /
|
|
104
|
+
* `data-budget` / `data-sink`. An unbudgeted source gets the safe default budget
|
|
105
|
+
* (`data-life="300"`, `data-cap="120"`) so its live emission is bounded at ~cap regardless of
|
|
106
|
+
* the pool ceiling — and, in dev (the same flag that gates the contract guards), a LOUD
|
|
107
|
+
* `console.warn` naming the element and the missing attributes. Mutates `sb` in place; pure
|
|
108
|
+
* over its inputs otherwise (no DOM reads).
|
|
109
|
+
*/
|
|
110
|
+
export function guardSourceBudget(sb, name = '<body>') {
|
|
111
|
+
if (!sb.classified || sb.classified.sources.length === 0 || sb.budgeted)
|
|
112
|
+
return;
|
|
113
|
+
sb.life = SOURCE_DEFAULT_LIFE;
|
|
114
|
+
sb.cap = SOURCE_DEFAULT_CAP;
|
|
115
|
+
if (contractChecksEnabled()) {
|
|
116
|
+
console.warn(`[field-ui:UNBUDGETED_SOURCE] ${name} runs the source force "${sb.classified.sources.join(' ')}" ` +
|
|
117
|
+
`with none of data-life / data-cap / data-budget / data-sink. Applying the safe default budget ` +
|
|
118
|
+
`(data-life="${SOURCE_DEFAULT_LIFE}", data-cap="${SOURCE_DEFAULT_CAP}") — declare one to make ` +
|
|
119
|
+
`the source's budget explicit (workover v0.3 source rules).`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** A fresh Body from static params, all runtime fields zeroed — shared by both paths. */
|
|
123
|
+
function makeBody(el, sb) {
|
|
124
|
+
guardSourceBudget(sb, describeEl(el));
|
|
125
|
+
return {
|
|
126
|
+
el,
|
|
127
|
+
...sb,
|
|
128
|
+
tint: el.dataset.color, // data-color → pigment tint (§20.8); undefined if absent
|
|
129
|
+
cx: 0,
|
|
130
|
+
cy: 0,
|
|
131
|
+
hw: 0,
|
|
132
|
+
hh: 0,
|
|
133
|
+
on: false,
|
|
134
|
+
vis: true,
|
|
135
|
+
accreted: 0,
|
|
136
|
+
count: 0,
|
|
137
|
+
d: 0,
|
|
138
|
+
attn: 1,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** Read an element's own `data-*` attributes as a `BodyAttrs` view. */
|
|
142
|
+
function elementAttrs(el) {
|
|
143
|
+
return {
|
|
144
|
+
get: (name) => el.getAttribute('data-' + name),
|
|
145
|
+
has: (name) => el.hasAttribute('data-' + name),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Build a single body from an element, parsing its `data-*` (or an explicit `attrs` view,
|
|
149
|
+
* for event-registered shadow-DOM hosts whose attrs may be supplied out of band). */
|
|
150
|
+
export function bodyFromElement(el, attrs) {
|
|
151
|
+
return makeBody(el, parseBodyParams(attrs ?? elementAttrs(el)));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Default force tokens per `data-field-role` (worldclass §12). Force-like roles map to a token;
|
|
155
|
+
* `sensor`/`display` are feedback-only responders (no token, density still sampled). Used only when
|
|
156
|
+
* a role element has no explicit `data-body`/`data-intent`.
|
|
157
|
+
*/
|
|
158
|
+
const FIELD_ROLE_TOKENS = {
|
|
159
|
+
source: 'jet',
|
|
160
|
+
sink: 'sink',
|
|
161
|
+
anchor: 'tether',
|
|
162
|
+
boundary: 'wall',
|
|
163
|
+
sensor: '', // feedback-only
|
|
164
|
+
display: '', // feedback-only
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* A `BodyAttrs` for an element authored via `data-intent` or `data-field-role` instead of a raw
|
|
168
|
+
* `data-body`. The intent is compiled (authoring §4) or the role mapped to a default token; explicit
|
|
169
|
+
* `data-*` on the element still wins over the compiled defaults (precedence §3 — component props beat
|
|
170
|
+
* intent defaults). Returns null when the element carries neither an intent nor a known role.
|
|
171
|
+
*/
|
|
172
|
+
export function authoredAttrs(el) {
|
|
173
|
+
let compiled = null;
|
|
174
|
+
const intent = el.getAttribute('data-intent');
|
|
175
|
+
if (intent) {
|
|
176
|
+
const intensityAttr = el.getAttribute('data-intensity');
|
|
177
|
+
const c = compileIntent(intent, {
|
|
178
|
+
intensity: intensityAttr != null ? Number(intensityAttr) : undefined,
|
|
179
|
+
risk: el.getAttribute('data-risk') ?? undefined,
|
|
180
|
+
});
|
|
181
|
+
if (c) {
|
|
182
|
+
compiled = { body: c.body };
|
|
183
|
+
if (c.strength != null)
|
|
184
|
+
compiled.strength = String(c.strength);
|
|
185
|
+
if (c.range != null)
|
|
186
|
+
compiled.range = String(c.range);
|
|
187
|
+
if (c.feedback)
|
|
188
|
+
compiled.feedback = '';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!compiled) {
|
|
192
|
+
const role = el.getAttribute('data-field-role');
|
|
193
|
+
if (role != null && role in FIELD_ROLE_TOKENS)
|
|
194
|
+
compiled = { body: FIELD_ROLE_TOKENS[role], feedback: '' };
|
|
195
|
+
}
|
|
196
|
+
if (!compiled)
|
|
197
|
+
return null;
|
|
198
|
+
const defaults = compiled;
|
|
199
|
+
return {
|
|
200
|
+
get: (name) => el.getAttribute('data-' + name) ?? defaults[name] ?? null,
|
|
201
|
+
has: (name) => el.hasAttribute('data-' + name) || name in defaults,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The selector matching every element the scanner turns into one or more bodies: a raw `data-body`,
|
|
206
|
+
* a `data-preset`, or an element authored via `data-intent` / `data-field-role`. This is the single
|
|
207
|
+
* source of truth shared with the platform's MeasurementRegistry (Phase D) so the two never drift.
|
|
208
|
+
* `querySelectorAll` returns each element once, even if it matches several clauses.
|
|
209
|
+
*/
|
|
210
|
+
export const BODY_SELECTOR = '[data-body], [data-preset], [data-intent]:not([data-body]), [data-field-role]:not([data-body]):not([data-intent])';
|
|
211
|
+
/**
|
|
212
|
+
* Every element in `root` that contributes a body, deduped and in document order. The geometry
|
|
213
|
+
* source for the platform MeasurementRegistry; pure over the passed root (no globals). A
|
|
214
|
+
* `data-preset` element still expands to several virtual bodies in `scanBodies`, but it is one
|
|
215
|
+
* element with one rect — which is exactly what measurement registers.
|
|
216
|
+
*/
|
|
217
|
+
export function bodyElements(root) {
|
|
218
|
+
return [...root.querySelectorAll(BODY_SELECTOR)];
|
|
219
|
+
}
|
|
220
|
+
/** Scan a DOM subtree for `[data-body]` and `[data-preset]` elements → bodies (§2.1,
|
|
221
|
+
* §20.9). A preset element emits one virtual body per entry, all sharing its rect;
|
|
222
|
+
* the plain `data-body` path is unchanged. */
|
|
223
|
+
export function scanBodies(root) {
|
|
224
|
+
const bodies = [];
|
|
225
|
+
root.querySelectorAll('[data-body]').forEach((node) => {
|
|
226
|
+
bodies.push(bodyFromElement(node));
|
|
227
|
+
});
|
|
228
|
+
root.querySelectorAll('[data-preset]').forEach((node) => {
|
|
229
|
+
const el = node;
|
|
230
|
+
for (const sb of expandPreset(el.dataset.preset ?? ''))
|
|
231
|
+
bodies.push(makeBody(el, sb));
|
|
232
|
+
});
|
|
233
|
+
// authored via intent/role rather than a raw data-body (authoring §4, worldclass §12). An
|
|
234
|
+
// explicit data-body always takes the plain path above, so these never double-register.
|
|
235
|
+
root
|
|
236
|
+
.querySelectorAll('[data-intent]:not([data-body]), [data-field-role]:not([data-body]):not([data-intent])')
|
|
237
|
+
.forEach((node) => {
|
|
238
|
+
const a = authoredAttrs(node);
|
|
239
|
+
if (a)
|
|
240
|
+
bodies.push(bodyFromElement(node, a));
|
|
241
|
+
});
|
|
242
|
+
return bodies;
|
|
243
|
+
}
|
|
244
|
+
/** Refresh each body's measured rect, visibility, and engaged state (§2.1). */
|
|
245
|
+
export function measureBodies(bodies, W, H) {
|
|
246
|
+
const margin = H * 0.15;
|
|
247
|
+
for (const b of bodies) {
|
|
248
|
+
// a shadow-DOM body may carry a custom rect provider (closed root, internal core); the
|
|
249
|
+
// host's own box is the default (shadow-dom.md §10/§16).
|
|
250
|
+
const r = b.rect ? b.rect() : b.el.getBoundingClientRect();
|
|
251
|
+
b.cx = r.left + r.width / 2;
|
|
252
|
+
b.cy = r.top + r.height / 2;
|
|
253
|
+
b.hw = r.width / 2;
|
|
254
|
+
b.hh = r.height / 2;
|
|
255
|
+
b.on = b.el.dataset.active === '1';
|
|
256
|
+
b.vis =
|
|
257
|
+
r.bottom > -margin && r.top < H + margin && r.right > -margin && r.left < W + margin;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow-DOM participation (docs/engine-reference/shadow-dom.md) — the host-first, event-driven body
|
|
3
|
+
* registration model. A component encapsulates its rendering but exposes a public physical
|
|
4
|
+
* body: it dispatches a `composed` registration event, the field registers the HOST (never
|
|
5
|
+
* inspecting the shadow tree), measures it by `getBoundingClientRect` or an optional
|
|
6
|
+
* `getRect`, and writes field state back as CSS variables on the host (or a write target).
|
|
7
|
+
*
|
|
8
|
+
* Two halves, deliberately split so the engine logic is testable without a DOM event system:
|
|
9
|
+
* - `FieldController` — the component side: dispatches register / unregister / update.
|
|
10
|
+
* - `ShadowRegistry` — the engine side: holds registered hosts, prunes the disconnected,
|
|
11
|
+
* and builds bodies. The DOM-event wiring lives in `field.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import type { Body } from './types.ts';
|
|
14
|
+
import type { BodyAttrs } from './scanner.ts';
|
|
15
|
+
/** The registration event names. `composed: true` lets them cross the shadow boundary. */
|
|
16
|
+
export declare const REGISTER_BODY = "field:register-body";
|
|
17
|
+
export declare const UNREGISTER_BODY = "field:unregister-body";
|
|
18
|
+
export declare const UPDATE_BODY = "field:update-body";
|
|
19
|
+
/** Payload of a `field:register-body` event (shadow-dom.md §7). */
|
|
20
|
+
export interface RegisterBodyDetail {
|
|
21
|
+
/** the public physical element — usually the custom-element host. */
|
|
22
|
+
element: HTMLElement;
|
|
23
|
+
/** optional rectangle provider, when the body's box is not the host box (closed roots). */
|
|
24
|
+
getRect?: () => DOMRect;
|
|
25
|
+
/** explicit body attributes (suffix → value, e.g. `{ body: 'attract', strength: '0.9' }`);
|
|
26
|
+
* when omitted the engine reads the host's own `data-*`. */
|
|
27
|
+
attrs?: Record<string, string>;
|
|
28
|
+
/** element that receives the CSS-variable write-back; defaults to `element`. */
|
|
29
|
+
writeTarget?: HTMLElement;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Component-side helper (shadow-dom.md §31.1) so a custom element joins the field without
|
|
33
|
+
* repeating event boilerplate. Construct with the host (and optional extra detail), then
|
|
34
|
+
* call `connect()` / `disconnect()` / `update()` from the element's lifecycle callbacks.
|
|
35
|
+
*/
|
|
36
|
+
export declare class FieldController {
|
|
37
|
+
private readonly host;
|
|
38
|
+
private readonly detail;
|
|
39
|
+
constructor(host: HTMLElement, detail?: Omit<Partial<RegisterBodyDetail>, 'element'>);
|
|
40
|
+
/** register the host as a body — call from `connectedCallback`. */
|
|
41
|
+
connect(): void;
|
|
42
|
+
/** remove the body — call from `disconnectedCallback`. */
|
|
43
|
+
disconnect(): void;
|
|
44
|
+
/** refresh attrs/geometry — call from `attributeChangedCallback`. */
|
|
45
|
+
update(): void;
|
|
46
|
+
private emit;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Engine-side registry of event-registered hosts. Pure of the DOM event system: the field
|
|
50
|
+
* feeds it details and asks for bodies each scan. It prunes hosts that have left the document
|
|
51
|
+
* (do not rely on `disconnectedCallback` alone, §15) and never touches a shadow root.
|
|
52
|
+
*/
|
|
53
|
+
export declare class ShadowRegistry {
|
|
54
|
+
private readonly hosts;
|
|
55
|
+
/** Register (or, idempotently, refresh) a host. */
|
|
56
|
+
register(detail: RegisterBodyDetail): void;
|
|
57
|
+
/** Drop a host. */
|
|
58
|
+
unregister(element: HTMLElement): void;
|
|
59
|
+
/** how many hosts are currently registered (post-prune count is via `bodies`). */
|
|
60
|
+
get size(): number;
|
|
61
|
+
/**
|
|
62
|
+
* Build a `Body` per live registered host, pruning any that have disconnected. `build` is
|
|
63
|
+
* the scanner's `bodyFromElement`; `attrs` (if supplied at registration) override the
|
|
64
|
+
* host's own `data-*`, else the host is read directly. A custom `getRect` and `writeTarget`
|
|
65
|
+
* are attached to the resulting body.
|
|
66
|
+
*/
|
|
67
|
+
bodies(build: (el: HTMLElement, attrs?: BodyAttrs) => Body): Body[];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=shadow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../src/core/shadow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,0FAA0F;AAC1F,eAAO,MAAM,aAAa,wBAAwB,CAAC;AACnD,eAAO,MAAM,eAAe,0BAA0B,CAAC;AACvD,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,OAAO,EAAE,WAAW,CAAC;IACrB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACxB;iEAC6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,gFAAgF;IAChF,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+C;gBAE1D,IAAI,EAAE,WAAW,EAAE,MAAM,GAAE,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,SAAS,CAAM;IAKxF,mEAAmE;IACnE,OAAO,IAAI,IAAI;IAGf,0DAA0D;IAC1D,UAAU,IAAI,IAAI;IAGlB,qEAAqE;IACrE,MAAM,IAAI,IAAI;IAId,OAAO,CAAC,IAAI;CAIb;AAOD;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8C;IAEpE,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAI1C,mBAAmB;IACnB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAItC,kFAAkF;IAClF,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI,EAAE;CAepE"}
|