@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,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color templates for the field's travelling accent (§9) and particle tint. The
|
|
3
|
+
* accent journeys through these stops as you scroll; `setPalette` / the `palette`
|
|
4
|
+
* option swap the whole set live.
|
|
5
|
+
*
|
|
6
|
+
* `ours` is the canonical forces journey; the others are recognizable scientific
|
|
7
|
+
* ramps. Stops are ordered cool → hot (or by spectrum), chosen to read as accents
|
|
8
|
+
* on the dark substrate.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PALETTES: Record<string, readonly string[]>;
|
|
11
|
+
/** Names of the built-in palettes, in display order. */
|
|
12
|
+
export declare const PALETTE_NAMES: readonly ["ours", "heatmap", "infrared", "spectrum"];
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a palette option to its color stops: a name looks up `PALETTES`, an array
|
|
15
|
+
* is used as-is, anything unknown or empty falls back to the canonical journey.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolvePalette(p?: string | readonly string[]): readonly string[];
|
|
18
|
+
//# sourceMappingURL=palettes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palettes.d.ts","sourceRoot":"","sources":["../../src/config/palettes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAStD,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,aAAa,sDAAuD,CAAC;AAElF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAIhF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color templates for the field's travelling accent (§9) and particle tint. The
|
|
3
|
+
* accent journeys through these stops as you scroll; `setPalette` / the `palette`
|
|
4
|
+
* option swap the whole set live.
|
|
5
|
+
*
|
|
6
|
+
* `ours` is the canonical forces journey; the others are recognizable scientific
|
|
7
|
+
* ramps. Stops are ordered cool → hot (or by spectrum), chosen to read as accents
|
|
8
|
+
* on the dark substrate.
|
|
9
|
+
*/
|
|
10
|
+
import { ACCENT_JOURNEY } from "./forces.config.js";
|
|
11
|
+
export const PALETTES = {
|
|
12
|
+
/** the canonical forces accent journey. */
|
|
13
|
+
ours: ACCENT_JOURNEY,
|
|
14
|
+
/** classic heatmap: cool blue → cyan → green → yellow → orange → red. */
|
|
15
|
+
heatmap: ['#2b3a8c', '#2d8fd4', '#3fd07a', '#ffe14d', '#ff8a3d', '#e23b3b'],
|
|
16
|
+
/** thermal "ironbow": deep purple → magenta → red → amber → pale yellow. */
|
|
17
|
+
infrared: ['#2a0a4a', '#8e24aa', '#e53935', '#ff8f00', '#ffd54f', '#fffde7'],
|
|
18
|
+
/** the visible spectrum: red → orange → yellow → green → teal → blue → violet. */
|
|
19
|
+
spectrum: ['#ff3b30', '#ff9500', '#ffcc00', '#34c759', '#00c7be', '#007aff', '#af52de'],
|
|
20
|
+
};
|
|
21
|
+
/** Names of the built-in palettes, in display order. */
|
|
22
|
+
export const PALETTE_NAMES = ['ours', 'heatmap', 'infrared', 'spectrum'];
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a palette option to its color stops: a name looks up `PALETTES`, an array
|
|
25
|
+
* is used as-is, anything unknown or empty falls back to the canonical journey.
|
|
26
|
+
*/
|
|
27
|
+
export function resolvePalette(p) {
|
|
28
|
+
if (Array.isArray(p))
|
|
29
|
+
return p.length > 0 ? p : ACCENT_JOURNEY;
|
|
30
|
+
if (typeof p === 'string')
|
|
31
|
+
return PALETTES[p] ?? ACCENT_JOURNEY;
|
|
32
|
+
return ACCENT_JOURNEY;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=palettes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palettes.js","sourceRoot":"","sources":["../../src/config/palettes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,CAAC,MAAM,QAAQ,GAAsC;IACzD,2CAA2C;IAC3C,IAAI,EAAE,cAAc;IACpB,yEAAyE;IACzE,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;IAC3E,4EAA4E;IAC5E,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;IAC5E,kFAAkF;IAClF,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;CACxF,CAAC;AAEF,wDAAwD;AACxD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,CAAU,CAAC;AAElF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,CAA8B;IAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IAC/D,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC;IAChE,OAAO,cAAc,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preset layer (§20.9) — cosmology as *named arrangements of primitives*, not
|
|
3
|
+
* new force modules.
|
|
4
|
+
*
|
|
5
|
+
* An element with `data-preset="blackhole"` expands into several **co-located virtual
|
|
6
|
+
* bodies**, each a single primitive with its *own* attrs, all bound to the same rect.
|
|
7
|
+
* This solves the one blocker the spec calls out: a plain `data-body="attract swirl
|
|
8
|
+
* sink"` shares one strength/range across every token, so the horizon (`sink`)
|
|
9
|
+
* can't be tuned independently of the well (`attract`). A preset gives each primitive
|
|
10
|
+
* its own parameters.
|
|
11
|
+
*
|
|
12
|
+
* The force loop is unchanged — it already iterates `bodies × tokens` (§4); the
|
|
13
|
+
* scanner just emits more bodies (`scanner.ts` → `expandPreset`). Presets are opt-in:
|
|
14
|
+
* a page using `data-body` is entirely unaffected.
|
|
15
|
+
*
|
|
16
|
+
* This file is pure data (no imports), so it can't create an import cycle with the
|
|
17
|
+
* scanner that consumes it. Entries use only *implemented* tokens — the canonical nine
|
|
18
|
+
* (§6), the natural primitives (§20.10), and the designed-extended forces (§20.3, e.g.
|
|
19
|
+
* `lens`, `buoyancy`, `spawn`). A composite that needs a still-unbuilt atom (e.g. `warp`,
|
|
20
|
+
* §20.6) waits until it lands.
|
|
21
|
+
*/
|
|
22
|
+
/** One virtual body in a preset — a single primitive with its own parameters (§20.9). */
|
|
23
|
+
export interface PresetEntry {
|
|
24
|
+
/** the primitive token this virtual body runs (a single force). */
|
|
25
|
+
body: string;
|
|
26
|
+
/** force magnitude S (and, for gravity/charge, the source scalar GM). */
|
|
27
|
+
strength?: number;
|
|
28
|
+
/** influence radius, px. */
|
|
29
|
+
range?: number;
|
|
30
|
+
/** swirl/charge/magnetism sign or spin (±). */
|
|
31
|
+
spin?: number;
|
|
32
|
+
/** heading in degrees, for stream/jet. */
|
|
33
|
+
angle?: number;
|
|
34
|
+
/** capture radius, for sink. */
|
|
35
|
+
absorb?: number;
|
|
36
|
+
/** load at which a sink supernovas. */
|
|
37
|
+
max?: number;
|
|
38
|
+
/** lifespan (frames) of matter a class-[S] source emits — the `data-life` budget. */
|
|
39
|
+
life?: number;
|
|
40
|
+
/** max live particles a class-[S] source sustains — the `data-cap` budget. */
|
|
41
|
+
cap?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The preset table (§20.9 composite map). Tunable starting points — refine when each
|
|
45
|
+
* is actually demoed, since these never touch the live site until opted into.
|
|
46
|
+
*/
|
|
47
|
+
export declare const PRESETS: Record<string, readonly PresetEntry[]>;
|
|
48
|
+
//# sourceMappingURL=presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/config/presets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CA4D1D,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The preset layer (§20.9) — cosmology as *named arrangements of primitives*, not
|
|
3
|
+
* new force modules.
|
|
4
|
+
*
|
|
5
|
+
* An element with `data-preset="blackhole"` expands into several **co-located virtual
|
|
6
|
+
* bodies**, each a single primitive with its *own* attrs, all bound to the same rect.
|
|
7
|
+
* This solves the one blocker the spec calls out: a plain `data-body="attract swirl
|
|
8
|
+
* sink"` shares one strength/range across every token, so the horizon (`sink`)
|
|
9
|
+
* can't be tuned independently of the well (`attract`). A preset gives each primitive
|
|
10
|
+
* its own parameters.
|
|
11
|
+
*
|
|
12
|
+
* The force loop is unchanged — it already iterates `bodies × tokens` (§4); the
|
|
13
|
+
* scanner just emits more bodies (`scanner.ts` → `expandPreset`). Presets are opt-in:
|
|
14
|
+
* a page using `data-body` is entirely unaffected.
|
|
15
|
+
*
|
|
16
|
+
* This file is pure data (no imports), so it can't create an import cycle with the
|
|
17
|
+
* scanner that consumes it. Entries use only *implemented* tokens — the canonical nine
|
|
18
|
+
* (§6), the natural primitives (§20.10), and the designed-extended forces (§20.3, e.g.
|
|
19
|
+
* `lens`, `buoyancy`, `spawn`). A composite that needs a still-unbuilt atom (e.g. `warp`,
|
|
20
|
+
* §20.6) waits until it lands.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The preset table (§20.9 composite map). Tunable starting points — refine when each
|
|
24
|
+
* is actually demoed, since these never touch the live site until opted into.
|
|
25
|
+
*/
|
|
26
|
+
export const PRESETS = {
|
|
27
|
+
// §20.9 designed composite — well + frame-drag + horizon + grazing-path bending.
|
|
28
|
+
blackhole: [
|
|
29
|
+
{ body: 'attract', strength: 1.4, range: 340 }, // the well
|
|
30
|
+
{ body: 'swirl', strength: 1.0, range: 300, spin: 1 }, // frame-drag → accretion disk
|
|
31
|
+
{ body: 'sink', absorb: 42, max: 60 }, // event horizon: capture → supernova
|
|
32
|
+
{ body: 'lens', strength: 0.5, range: 380 }, // gravitational lensing of passing matter
|
|
33
|
+
],
|
|
34
|
+
// §20.9 — the time-reverse: an emission horizon that throws matter out.
|
|
35
|
+
whitehole: [
|
|
36
|
+
{ body: 'repel', strength: 1.4, range: 340 },
|
|
37
|
+
{ body: 'stream', strength: 0.6, range: 300, angle: 0 }, // optional directed eject
|
|
38
|
+
],
|
|
39
|
+
// §20.10 — hydrostatic equilibrium: gravity's collapse balanced by thermal pressure.
|
|
40
|
+
// The same `gravity ⇄ thermal` fluctuation–dissipation balance that keeps the
|
|
41
|
+
// resting field calm, scaled up. GM is large because true 1/d² is weak at UI scale.
|
|
42
|
+
star: [
|
|
43
|
+
{ body: 'gravity', strength: 300, range: 320 }, // inward GM well
|
|
44
|
+
{ body: 'thermal', strength: 1.0, range: 220 }, // outward Brownian pressure
|
|
45
|
+
],
|
|
46
|
+
// §20.9 — an accreting black hole that also jets along its poles.
|
|
47
|
+
// attract is softened (0.7) so the jet nozzle speed (2.4 + 2.6·S) at S=2.5 → 8.9 px/frame
|
|
48
|
+
// exceeds the escape velocity from the weakened well (~7 px/frame at d=50) — jets escape.
|
|
49
|
+
quasar: [
|
|
50
|
+
{ body: 'attract', strength: 0.7, range: 340 }, // softened well — jets must escape it
|
|
51
|
+
{ body: 'swirl', strength: 1.2, range: 300, spin: 1 }, // accretion disk
|
|
52
|
+
{ body: 'sink', absorb: 40, max: 60 }, // horizon
|
|
53
|
+
{ body: 'lens', strength: 0.5, range: 380 }, // lensing
|
|
54
|
+
{ body: 'jet', strength: 2.5, range: 280, angle: -90 }, // north pole jet (fast enough to escape)
|
|
55
|
+
{ body: 'jet', strength: 2.5, range: 280, angle: 90 }, // south pole jet
|
|
56
|
+
],
|
|
57
|
+
// A spiral disk: a soft well, a swirl for the arms, viscosity to settle into a plane,
|
|
58
|
+
// and lensing at the rim. Attract is gentle (0.6) so swirl (1.3) dominates — spiral, not cluster.
|
|
59
|
+
galaxy: [
|
|
60
|
+
{ body: 'attract', strength: 0.6, range: 400 }, // soft bulge — swirl must dominate
|
|
61
|
+
{ body: 'swirl', strength: 1.3, range: 380, spin: 1 }, // spiral arms
|
|
62
|
+
{ body: 'viscosity', strength: 0.4, range: 400 }, // settle into the disk
|
|
63
|
+
{ body: 'lens', strength: 0.3, range: 420 }, // halo lensing
|
|
64
|
+
],
|
|
65
|
+
// A warm, slow cloud: thermal agitation churning in a viscous medium, light matter
|
|
66
|
+
// drifting up.
|
|
67
|
+
nebula: [
|
|
68
|
+
{ body: 'thermal', strength: 0.8, range: 320 }, // warm churn
|
|
69
|
+
{ body: 'viscosity', strength: 0.5, range: 320 }, // viscous medium
|
|
70
|
+
{ body: 'buoyancy', strength: 0.3, range: 0 }, // light wisps rise
|
|
71
|
+
],
|
|
72
|
+
// A swirl with an updraft along its axis, calmed at the edges by viscosity.
|
|
73
|
+
tornado: [
|
|
74
|
+
{ body: 'swirl', strength: 1.6, range: 300, spin: 1 }, // the funnel
|
|
75
|
+
{ body: 'stream', strength: 0.8, range: 280, angle: -90 }, // updraft
|
|
76
|
+
{ body: 'viscosity', strength: 0.3, range: 300 }, // ragged edge
|
|
77
|
+
],
|
|
78
|
+
// §20.2 — a continuous class-[S] source jetting matter upward, the spray pulled back
|
|
79
|
+
// down by a gentle global gravity well: a literal water fountain.
|
|
80
|
+
fountain: [
|
|
81
|
+
// the explicit budget (life 90 = the source's historical lifespan, kept so the look is
|
|
82
|
+
// unchanged) satisfies the [S] source-budget guard — presets always declare theirs.
|
|
83
|
+
{ body: 'spawn', strength: 1.2, angle: -90, life: 90 }, // jet up (−y); emits while on-screen
|
|
84
|
+
{ body: 'gravity', strength: 60, range: 0 }, // the arc home — global, weak
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/config/presets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/config/tokens.ts"],"names":[],"mappings":"AAQA,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,QAAQ,SAAU,GAAG,MAAM,CAMpD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design tokens (§25.2) — the force palette + coherence + ease as CSS custom
|
|
3
|
+
* properties, derived from the canonical catalog (the single source of truth).
|
|
4
|
+
* Consumers can inject `cssTokens()` into a <style> so CSS can reference
|
|
5
|
+
* `var(--f-attract)`, `var(--coherence)`, `var(--ease)`.
|
|
6
|
+
*/
|
|
7
|
+
import { FORCES } from "./forces.config.js";
|
|
8
|
+
/** A `selector { --f-*: …; --coherence: …; --ease: …; }` block (§25.2). */
|
|
9
|
+
export function cssTokens(selector = ':root') {
|
|
10
|
+
const lines = FORCES.map((f) => ` --f-${f.id}: ${f.color};`);
|
|
11
|
+
lines.push(' --f-condition: var(--f-jet);');
|
|
12
|
+
lines.push(' --coherence: #ffce6b;');
|
|
13
|
+
lines.push(' --ease: cubic-bezier(0.16, 1, 0.3, 1);');
|
|
14
|
+
return `${selector} {\n${lines.join('\n')}\n}`;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/config/tokens.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,2EAA2E;AAC3E,MAAM,UAAU,SAAS,CAAC,QAAQ,GAAG,OAAO;IAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACvD,OAAO,GAAG,QAAQ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Expectation } from './types.ts';
|
|
2
|
+
/** Δv on the test particle points toward the body. */
|
|
3
|
+
export declare function movesToward(): Expectation;
|
|
4
|
+
/** Δv on the test particle points away from the body. */
|
|
5
|
+
export declare function movesAway(): Expectation;
|
|
6
|
+
/** Over the whole run, the test particle ends farther from the body than it began — it rode
|
|
7
|
+
* an outgoing wavefront out. Trajectory-based (a wave needs frames to form), for class-[C]
|
|
8
|
+
* forces whose effect can't be read from a single frame-0 apply on an empty grid. */
|
|
9
|
+
export declare function endsFartherOut(minGain?: number): Expectation;
|
|
10
|
+
/** The exact frame-0 Δv matches the spec formula. */
|
|
11
|
+
export declare function exactDelta(dvx: number, dvy: number, tol?: number): Expectation;
|
|
12
|
+
/** Speed is preserved by the force (rotation only): |v+Δv| ≈ |v|. */
|
|
13
|
+
export declare function speedPreserved(tol?: number): Expectation;
|
|
14
|
+
/** The force bleeds speed without redirecting (drag): final speed < initial, same heading. */
|
|
15
|
+
export declare function speedReduced(): Expectation;
|
|
16
|
+
/** The force is perpendicular to velocity (does no work): Δv·v ≈ 0, Δv ≠ 0. */
|
|
17
|
+
export declare function perpendicularToVelocity(tol?: number): Expectation;
|
|
18
|
+
/** Total frame-0 momentum change across the particle set is ≈ 0 (e.g. an elastic collision). */
|
|
19
|
+
export declare function momentumConserved(tol?: number): Expectation;
|
|
20
|
+
/** Over the run, two particles end farther apart than they began (they repel/separate). */
|
|
21
|
+
export declare function separates(i?: number, j?: number): Expectation;
|
|
22
|
+
/** Over the run, the test particle ends closer to the body than it began. */
|
|
23
|
+
export declare function approachesBody(): Expectation;
|
|
24
|
+
/** Over the run, the test particle ends farther from the body than it began. */
|
|
25
|
+
export declare function recedesFromBody(): Expectation;
|
|
26
|
+
/** The test particle gains heat over the run. */
|
|
27
|
+
export declare function heats(): Expectation;
|
|
28
|
+
/** Beyond ~1.6× range the force does nothing (the integrator's cull / each force's cutoff). */
|
|
29
|
+
export declare function noEffectBeyondRange(): Expectation;
|
|
30
|
+
/** A field-buffer force deposits a mark and steers along the grid gradient (§20.1 [C]). */
|
|
31
|
+
export declare function followsGradient(gx: number, gy: number): Expectation;
|
|
32
|
+
/** A modifier gates its siblings outside a heading cone and lets them act inside it. */
|
|
33
|
+
export declare function gatesOutsideCone(insideAngle: number, outsideAngle: number): Expectation;
|
|
34
|
+
/** The force does nothing to neutral (charge-free) matter. */
|
|
35
|
+
export declare function unaffectedWhenNeutral(): Expectation;
|
|
36
|
+
/** The particle adopts and carries the body's tint on overlap (conserved color). */
|
|
37
|
+
export declare function adoptsTint(): Expectation;
|
|
38
|
+
/** A modifier scales sibling strength as 1 + sin(ω·t) over time. */
|
|
39
|
+
export declare function modulatesStrength(): Expectation;
|
|
40
|
+
//# sourceMappingURL=expectations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expectations.d.ts","sourceRoot":"","sources":["../../src/conformance/expectations.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAqC,MAAM,YAAY,CAAC;AAkBjF,sDAAsD;AACtD,wBAAgB,WAAW,IAAI,WAAW,CAWzC;AAED,yDAAyD;AACzD,wBAAgB,SAAS,IAAI,WAAW,CAWvC;AAED;;sFAEsF;AACtF,wBAAgB,cAAc,CAAC,OAAO,SAAO,GAAG,WAAW,CAa1D;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,SAAO,GAAG,WAAW,CAU5E;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,GAAG,SAAO,GAAG,WAAW,CAetD;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,IAAI,WAAW,CAY1C;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,GAAG,SAAO,GAAG,WAAW,CAY/D;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,GAAG,SAAO,GAAG,WAAW,CAczD;AAQD,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,CAAC,SAAI,EAAE,CAAC,SAAI,GAAG,WAAW,CAUnD;AAOD,6EAA6E;AAC7E,wBAAgB,cAAc,IAAI,WAAW,CAU5C;AAED,gFAAgF;AAChF,wBAAgB,eAAe,IAAI,WAAW,CAU7C;AAED,iDAAiD;AACjD,wBAAgB,KAAK,IAAI,WAAW,CASnC;AAyCD,+FAA+F;AAC/F,wBAAgB,mBAAmB,IAAI,WAAW,CAgBjD;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CA+BnE;AAED,wFAAwF;AACxF,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,WAAW,CAiBvF;AAED,8DAA8D;AAC9D,wBAAgB,qBAAqB,IAAI,WAAW,CAiBnD;AAED,oFAAoF;AACpF,wBAAgB,UAAU,IAAI,WAAW,CAaxC;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,IAAI,WAAW,CAoB/C"}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { allForces } from "./run.js";
|
|
2
|
+
const f3 = (n) => (Math.abs(n) < 1e-9 ? '0' : n.toFixed(3));
|
|
3
|
+
const ok = (pass, measured, expected) => ({
|
|
4
|
+
pass,
|
|
5
|
+
measured,
|
|
6
|
+
expected,
|
|
7
|
+
});
|
|
8
|
+
function toBodyUnit(r) {
|
|
9
|
+
const p = r.scenario.particles[0];
|
|
10
|
+
const dx = r.body.cx - p.x;
|
|
11
|
+
const dy = r.body.cy - p.y;
|
|
12
|
+
const d = Math.hypot(dx, dy) || 1;
|
|
13
|
+
return { ux: dx / d, uy: dy / d };
|
|
14
|
+
}
|
|
15
|
+
/** Δv on the test particle points toward the body. */
|
|
16
|
+
export function movesToward() {
|
|
17
|
+
return {
|
|
18
|
+
label: 'pulled toward the body',
|
|
19
|
+
kind: 'invariant',
|
|
20
|
+
check(r) {
|
|
21
|
+
const { ux, uy } = toBodyUnit(r);
|
|
22
|
+
const { dvx, dvy } = r.applyDelta[0];
|
|
23
|
+
const along = dvx * ux + dvy * uy;
|
|
24
|
+
return ok(along > 1e-6, `Δv·û = ${f3(along)}`, '> 0 (inward)');
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Δv on the test particle points away from the body. */
|
|
29
|
+
export function movesAway() {
|
|
30
|
+
return {
|
|
31
|
+
label: 'pushed away from the body',
|
|
32
|
+
kind: 'invariant',
|
|
33
|
+
check(r) {
|
|
34
|
+
const { ux, uy } = toBodyUnit(r);
|
|
35
|
+
const { dvx, dvy } = r.applyDelta[0];
|
|
36
|
+
const along = dvx * ux + dvy * uy;
|
|
37
|
+
return ok(along < -1e-6, `Δv·û = ${f3(along)}`, '< 0 (outward)');
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Over the whole run, the test particle ends farther from the body than it began — it rode
|
|
42
|
+
* an outgoing wavefront out. Trajectory-based (a wave needs frames to form), for class-[C]
|
|
43
|
+
* forces whose effect can't be read from a single frame-0 apply on an empty grid. */
|
|
44
|
+
export function endsFartherOut(minGain = 1.05) {
|
|
45
|
+
return {
|
|
46
|
+
label: 'rides the wavefront outward',
|
|
47
|
+
kind: 'invariant',
|
|
48
|
+
check(r) {
|
|
49
|
+
const b = r.body;
|
|
50
|
+
const first = r.trajectory[0][0];
|
|
51
|
+
const last = r.trajectory[r.trajectory.length - 1][0];
|
|
52
|
+
const r0 = Math.hypot(first.x - b.cx, first.y - b.cy) || 1;
|
|
53
|
+
const rN = Math.hypot(last.x - b.cx, last.y - b.cy);
|
|
54
|
+
return ok(rN > r0 * minGain, `r ${f3(r0)}→${f3(rN)}`, `> ${minGain}× (outward)`);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The exact frame-0 Δv matches the spec formula. */
|
|
59
|
+
export function exactDelta(dvx, dvy, tol = 2e-3) {
|
|
60
|
+
return {
|
|
61
|
+
label: `Δv = (${f3(dvx)}, ${f3(dvy)})`,
|
|
62
|
+
kind: 'exact',
|
|
63
|
+
check(r) {
|
|
64
|
+
const a = r.applyDelta[0];
|
|
65
|
+
const pass = Math.abs(a.dvx - dvx) < tol && Math.abs(a.dvy - dvy) < tol;
|
|
66
|
+
return ok(pass, `(${f3(a.dvx)}, ${f3(a.dvy)})`, `(${f3(dvx)}, ${f3(dvy)}) ±${tol}`);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Speed is preserved by the force (rotation only): |v+Δv| ≈ |v|. */
|
|
71
|
+
export function speedPreserved(tol = 0.02) {
|
|
72
|
+
return {
|
|
73
|
+
label: 'speed preserved (no work done)',
|
|
74
|
+
kind: 'invariant',
|
|
75
|
+
check(r) {
|
|
76
|
+
const p = r.scenario.particles[0];
|
|
77
|
+
const vx = p.vx ?? 0;
|
|
78
|
+
const vy = p.vy ?? 0;
|
|
79
|
+
const before = Math.hypot(vx, vy) || 1;
|
|
80
|
+
const a = r.applyDelta[0];
|
|
81
|
+
const after = Math.hypot(vx + a.dvx, vy + a.dvy);
|
|
82
|
+
const rel = Math.abs(after - before) / before;
|
|
83
|
+
return ok(rel < tol, `|v| ${f3(before)} → ${f3(after)}`, `±${(tol * 100).toFixed(0)}%`);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** The force bleeds speed without redirecting (drag): final speed < initial, same heading. */
|
|
88
|
+
export function speedReduced() {
|
|
89
|
+
return {
|
|
90
|
+
label: 'speed bled off',
|
|
91
|
+
kind: 'invariant',
|
|
92
|
+
check(r) {
|
|
93
|
+
const p = r.scenario.particles[0];
|
|
94
|
+
const before = Math.hypot(p.vx ?? 0, p.vy ?? 0) || 1;
|
|
95
|
+
const a = r.applyDelta[0];
|
|
96
|
+
const after = Math.hypot((p.vx ?? 0) + a.dvx, (p.vy ?? 0) + a.dvy);
|
|
97
|
+
return ok(after < before - 1e-6, `|v| ${f3(before)} → ${f3(after)}`, '< initial');
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** The force is perpendicular to velocity (does no work): Δv·v ≈ 0, Δv ≠ 0. */
|
|
102
|
+
export function perpendicularToVelocity(tol = 1e-3) {
|
|
103
|
+
return {
|
|
104
|
+
label: 'force ⟂ velocity (no work)',
|
|
105
|
+
kind: 'invariant',
|
|
106
|
+
check(r) {
|
|
107
|
+
const p = r.scenario.particles[0];
|
|
108
|
+
const a = r.applyDelta[0];
|
|
109
|
+
const dot = (p.vx ?? 0) * a.dvx + (p.vy ?? 0) * a.dvy;
|
|
110
|
+
const mag = Math.hypot(a.dvx, a.dvy);
|
|
111
|
+
return ok(mag > 1e-6 && Math.abs(dot) < tol, `Δv·v = ${f3(dot)}, |Δv| = ${f3(mag)}`, '≈ 0, |Δv| > 0');
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Total frame-0 momentum change across the particle set is ≈ 0 (e.g. an elastic collision). */
|
|
116
|
+
export function momentumConserved(tol = 1e-6) {
|
|
117
|
+
return {
|
|
118
|
+
label: 'momentum conserved across the pair',
|
|
119
|
+
kind: 'invariant',
|
|
120
|
+
check(r) {
|
|
121
|
+
let sx = 0;
|
|
122
|
+
let sy = 0;
|
|
123
|
+
for (const a of r.applyDelta) {
|
|
124
|
+
sx += a.dvx;
|
|
125
|
+
sy += a.dvy;
|
|
126
|
+
}
|
|
127
|
+
return ok(Math.hypot(sx, sy) < tol, `Σ Δp = ${f3(Math.hypot(sx, sy))}`, '≈ 0');
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const distAt = (r, frame, i, j) => {
|
|
132
|
+
const a = r.trajectory[frame][i];
|
|
133
|
+
const b = r.trajectory[frame][j];
|
|
134
|
+
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
135
|
+
};
|
|
136
|
+
/** Over the run, two particles end farther apart than they began (they repel/separate). */
|
|
137
|
+
export function separates(i = 0, j = 1) {
|
|
138
|
+
return {
|
|
139
|
+
label: 'the two particles separate',
|
|
140
|
+
kind: 'invariant',
|
|
141
|
+
check(r) {
|
|
142
|
+
const start = distAt(r, 0, i, j);
|
|
143
|
+
const end = distAt(r, r.trajectory.length - 1, i, j);
|
|
144
|
+
return ok(end > start + 1e-6, `gap ${f3(start)} → ${f3(end)}`, 'end > start');
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const distToBody = (r, frame, i = 0) => {
|
|
149
|
+
const p = r.trajectory[frame][i];
|
|
150
|
+
return Math.hypot(p.x - r.body.cx, p.y - r.body.cy);
|
|
151
|
+
};
|
|
152
|
+
/** Over the run, the test particle ends closer to the body than it began. */
|
|
153
|
+
export function approachesBody() {
|
|
154
|
+
return {
|
|
155
|
+
label: 'ends closer to the body',
|
|
156
|
+
kind: 'invariant',
|
|
157
|
+
check(r) {
|
|
158
|
+
const start = distToBody(r, 0);
|
|
159
|
+
const end = distToBody(r, r.trajectory.length - 1);
|
|
160
|
+
return ok(end < start - 1e-6, `dist ${f3(start)} → ${f3(end)}`, 'end < start');
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** Over the run, the test particle ends farther from the body than it began. */
|
|
165
|
+
export function recedesFromBody() {
|
|
166
|
+
return {
|
|
167
|
+
label: 'ends farther from the body',
|
|
168
|
+
kind: 'invariant',
|
|
169
|
+
check(r) {
|
|
170
|
+
const start = distToBody(r, 0);
|
|
171
|
+
const end = distToBody(r, r.trajectory.length - 1);
|
|
172
|
+
return ok(end > start + 1e-6, `dist ${f3(start)} → ${f3(end)}`, 'end > start');
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** The test particle gains heat over the run. */
|
|
177
|
+
export function heats() {
|
|
178
|
+
return {
|
|
179
|
+
label: 'particle gains heat',
|
|
180
|
+
kind: 'invariant',
|
|
181
|
+
check(r) {
|
|
182
|
+
const end = r.trajectory[r.trajectory.length - 1][0];
|
|
183
|
+
return ok(end.heat > 1e-4, `heat = ${f3(end.heat)}`, '> 0');
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// ── checks that do a controlled direct apply via the registry ────────────────────
|
|
188
|
+
function probeEnv(over = {}) {
|
|
189
|
+
return {
|
|
190
|
+
dx: 0,
|
|
191
|
+
dy: 0,
|
|
192
|
+
dist: 1,
|
|
193
|
+
form: { driftX: 0, wander: 0, orbit: 0, spread: 0, conv: 0 },
|
|
194
|
+
W: 1200,
|
|
195
|
+
H: 800,
|
|
196
|
+
t: 0,
|
|
197
|
+
frameN: 0,
|
|
198
|
+
dt: 1,
|
|
199
|
+
c: 12,
|
|
200
|
+
G: 1,
|
|
201
|
+
spark: () => { },
|
|
202
|
+
supernova: () => { },
|
|
203
|
+
spawn: () => { },
|
|
204
|
+
neighbors: () => [],
|
|
205
|
+
grid: () => ({ sample: () => 0, deposit: () => { }, gradient: () => ({ x: 0, y: 0 }) }),
|
|
206
|
+
...over,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function cloneParticle(p) {
|
|
210
|
+
return {
|
|
211
|
+
x: p.x,
|
|
212
|
+
y: p.y,
|
|
213
|
+
vx: p.vx ?? 0,
|
|
214
|
+
vy: p.vy ?? 0,
|
|
215
|
+
m: 1,
|
|
216
|
+
heat: p.heat ?? 0,
|
|
217
|
+
size: p.size ?? 1,
|
|
218
|
+
cap: null,
|
|
219
|
+
...(p.charge != null ? { charge: p.charge } : {}),
|
|
220
|
+
...(p.color != null ? { color: p.color } : {}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/** Beyond ~1.6× range the force does nothing (the integrator's cull / each force's cutoff). */
|
|
224
|
+
export function noEffectBeyondRange() {
|
|
225
|
+
return {
|
|
226
|
+
label: 'no effect beyond range',
|
|
227
|
+
kind: 'invariant',
|
|
228
|
+
check(r) {
|
|
229
|
+
const force = allForces()[r.scenario.force];
|
|
230
|
+
const range = r.body.range || 300;
|
|
231
|
+
const p = cloneParticle({ x: r.body.cx + range * 2, y: r.body.cy });
|
|
232
|
+
const env = probeEnv({ dx: r.body.cx - p.x, dy: r.body.cy - p.y, dist: range * 2 });
|
|
233
|
+
const vx0 = p.vx;
|
|
234
|
+
const vy0 = p.vy;
|
|
235
|
+
force.apply(r.body, p, env);
|
|
236
|
+
const mag = Math.hypot(p.vx - vx0, p.vy - vy0);
|
|
237
|
+
return ok(mag < 1e-9, `|Δv| = ${f3(mag)} at 2× range`, '≈ 0');
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** A field-buffer force deposits a mark and steers along the grid gradient (§20.1 [C]). */
|
|
242
|
+
export function followsGradient(gx, gy) {
|
|
243
|
+
return {
|
|
244
|
+
label: 'deposits + follows the grid gradient',
|
|
245
|
+
kind: 'invariant',
|
|
246
|
+
check(r) {
|
|
247
|
+
const force = allForces()[r.scenario.force];
|
|
248
|
+
const deposits = [];
|
|
249
|
+
const grid = {
|
|
250
|
+
sample: () => 0.5,
|
|
251
|
+
deposit: (x, y, a) => deposits.push([x, y, a]),
|
|
252
|
+
gradient: () => ({ x: gx, y: gy }),
|
|
253
|
+
};
|
|
254
|
+
const sp = r.scenario.particles[0];
|
|
255
|
+
const p = cloneParticle(sp);
|
|
256
|
+
const env = probeEnv({
|
|
257
|
+
dx: r.body.cx - p.x,
|
|
258
|
+
dy: r.body.cy - p.y,
|
|
259
|
+
dist: Math.hypot(r.body.cx - p.x, r.body.cy - p.y) || 1,
|
|
260
|
+
grid: () => grid,
|
|
261
|
+
});
|
|
262
|
+
const vx0 = p.vx;
|
|
263
|
+
force.apply(r.body, p, env);
|
|
264
|
+
const along = (p.vx - vx0) * gx + (p.vy - (sp.vy ?? 0)) * gy;
|
|
265
|
+
void vx0;
|
|
266
|
+
return ok(deposits.length > 0 && along > 0, `deposits ${deposits.length}, Δv·∇ = ${f3(along)}`, 'deposits > 0, follows up-gradient');
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/** A modifier gates its siblings outside a heading cone and lets them act inside it. */
|
|
271
|
+
export function gatesOutsideCone(insideAngle, outsideAngle) {
|
|
272
|
+
return {
|
|
273
|
+
label: 'gates outside the cone, acts inside',
|
|
274
|
+
kind: 'invariant',
|
|
275
|
+
check(r) {
|
|
276
|
+
const force = allForces()[r.scenario.force];
|
|
277
|
+
if (!force.modify)
|
|
278
|
+
return ok(false, 'no modify()', 'a modifier force');
|
|
279
|
+
const at = (ang) => {
|
|
280
|
+
const p = cloneParticle({ x: r.body.cx + Math.cos(ang) * 100, y: r.body.cy + Math.sin(ang) * 100 });
|
|
281
|
+
const env = probeEnv({ dx: r.body.cx - p.x, dy: r.body.cy - p.y, dist: 100 });
|
|
282
|
+
return force.modify(r.body, p, env).gate === true;
|
|
283
|
+
};
|
|
284
|
+
const outGated = at(outsideAngle);
|
|
285
|
+
const inGated = at(insideAngle);
|
|
286
|
+
return ok(outGated && !inGated, `out=${outGated} in=${inGated}`, 'out=true, in=false');
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/** The force does nothing to neutral (charge-free) matter. */
|
|
291
|
+
export function unaffectedWhenNeutral() {
|
|
292
|
+
return {
|
|
293
|
+
label: 'neutral matter is unaffected',
|
|
294
|
+
kind: 'invariant',
|
|
295
|
+
check(r) {
|
|
296
|
+
const force = allForces()[r.scenario.force];
|
|
297
|
+
const p = cloneParticle(r.scenario.particles[0]);
|
|
298
|
+
delete p.charge; // truly neutral
|
|
299
|
+
const d = Math.hypot(r.body.cx - p.x, r.body.cy - p.y) || 1;
|
|
300
|
+
const env = probeEnv({ dx: r.body.cx - p.x, dy: r.body.cy - p.y, dist: d });
|
|
301
|
+
const vx0 = p.vx;
|
|
302
|
+
const vy0 = p.vy;
|
|
303
|
+
force.apply(r.body, p, env);
|
|
304
|
+
const mag = Math.hypot(p.vx - vx0, p.vy - vy0);
|
|
305
|
+
return ok(mag < 1e-9, `|Δv| = ${f3(mag)} (q = 0)`, '≈ 0');
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/** The particle adopts and carries the body's tint on overlap (conserved color). */
|
|
310
|
+
export function adoptsTint() {
|
|
311
|
+
return {
|
|
312
|
+
label: 'adopts and carries the tint',
|
|
313
|
+
kind: 'invariant',
|
|
314
|
+
check(r) {
|
|
315
|
+
const force = allForces()[r.scenario.force];
|
|
316
|
+
const p = cloneParticle(r.scenario.particles[0]);
|
|
317
|
+
const d = Math.hypot(r.body.cx - p.x, r.body.cy - p.y) || 1;
|
|
318
|
+
const env = probeEnv({ dx: r.body.cx - p.x, dy: r.body.cy - p.y, dist: d });
|
|
319
|
+
force.apply(r.body, p, env);
|
|
320
|
+
return ok(!!p.color, `color = ${p.color ?? 'none'}`, 'set to the body tint');
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
/** A modifier scales sibling strength as 1 + sin(ω·t) over time. */
|
|
325
|
+
export function modulatesStrength() {
|
|
326
|
+
return {
|
|
327
|
+
label: 'strength oscillates as 1 + sin(ωt)',
|
|
328
|
+
kind: 'invariant',
|
|
329
|
+
check(r) {
|
|
330
|
+
const force = allForces()[r.scenario.force];
|
|
331
|
+
if (!force.modify)
|
|
332
|
+
return ok(false, 'no modify()', 'a modifier force');
|
|
333
|
+
const p = cloneParticle(r.scenario.particles[0]);
|
|
334
|
+
let min = Infinity;
|
|
335
|
+
let max = -Infinity;
|
|
336
|
+
for (let i = 0; i < 64; i++) {
|
|
337
|
+
const env = probeEnv({ t: i / 8, dx: 1, dy: 0, dist: 1 });
|
|
338
|
+
const s = force.modify(r.body, p, env).strength ?? 1;
|
|
339
|
+
min = Math.min(min, s);
|
|
340
|
+
max = Math.max(max, s);
|
|
341
|
+
}
|
|
342
|
+
// a clean sinusoid swings roughly [0, 2] (around 1)
|
|
343
|
+
return ok(min < 0.2 && max > 1.8, `range [${f3(min)}, ${f3(max)}]`, '≈ [0, 2]');
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=expectations.js.map
|