@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.1
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/CLAUDE.md +87 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2501 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +85 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +100 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +259 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2474 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2922 -0
- package/dist/frqtl/objects/ObjectFactory.js +297 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +711 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +985 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +412 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +354 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +310 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +824 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +263 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +299 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +469 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +251 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +327 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +280 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1259 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +242 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +178 -0
- package/dist/frqtl/objects/v1-replacements.js +30 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +35 -0
- package/dist/frqtl/parts/CollisionTypes.js +31 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +490 -0
- package/dist/frqtl/parts/LargeObject.js +406 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +720 -0
- package/dist/frqtl/parts/OvenBox.js +612 -0
- package/dist/frqtl/parts/PointSource.js +763 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +244 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +212 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +692 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +326 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +672 -0
- package/dist/shim/notebook/knob.js +151 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +510 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +214 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +807 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +62 -4
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* _worldInit — the ONE world-init contract: the world-cfg vocabulary (`INIT_CFG`) a scene
|
|
5
|
+
* hands its co-init call (`exp.createCanvas(w, h, cfg)` / `exp.createScene(w, h, d, cfg)`),
|
|
6
|
+
* its validator (`assertInitCfg`) and its mapping onto `universe.init(fundamentalDimension, options)`
|
|
7
|
+
* (`initArgs`). The SIZE and the AXES are not in it: the co-init call owns both (2D ⇒ the
|
|
8
|
+
* engine's {x,y} default, 3D ⇒ {x,y,z} + depth).
|
|
9
|
+
*
|
|
10
|
+
* A LEAF contract module, the `_setupOptions.js` class: it depends on nothing but the
|
|
11
|
+
* universe it is handed; a setup factory imports it. Consumers:
|
|
12
|
+
* `RecipeContext.createCanvas/createScene`, `setupSphereEmitter3D.initWorld`.
|
|
13
|
+
*
|
|
14
|
+
* An ES module: `export { FrqtlWorldInit }`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* INIT_CFG — every key a world cfg accepts (the `cfg` of `exp.createCanvas(w, h, cfg)` /
|
|
19
|
+
* `exp.createScene(w, h, d, cfg)`), and what each one does.
|
|
20
|
+
*
|
|
21
|
+
* THE contract, exported as `FrqtlWorldInit.INIT_CFG` so no consumer re-enumerates
|
|
22
|
+
* it — RecipeContext and the setup factories' world steps both read it.
|
|
23
|
+
*
|
|
24
|
+
* The list is derived from what `initArgs()` below ACTUALLY reads. `interQuantumCollisions`
|
|
25
|
+
* is in it because it is still ACCEPTED (inert — it fed a removed positional slot) and
|
|
26
|
+
* STUDIO codegen prints it into every saved recipe: dropping it from the vocabulary
|
|
27
|
+
* would make every saved scene throw.
|
|
28
|
+
*
|
|
29
|
+
* @type {Array<{key:string, effect:string}>}
|
|
30
|
+
*/
|
|
31
|
+
var INIT_CFG = [
|
|
32
|
+
{ key: 'dimension', effect: 'the fundamental (base) dimension layer — universe.init()\'s 1st argument. default 2.' },
|
|
33
|
+
{ key: 'wavelengthScale', effect: '→ universe.init options.wavelengthScale — the GLOBAL λ multiplier. default 8.' },
|
|
34
|
+
{ key: 'interQuantumCollisions', effect: 'ACCEPTED BUT INERT — it fed a removed positional slot the engine never read.' },
|
|
35
|
+
{ key: 'bonding', effect: '→ universe.init options.noEscape (the names do NOT line up 1:1). default false.' },
|
|
36
|
+
{ key: 'frames', effect: "BOOLEAN → universe.init options.frameGenMode: true → 'topDown', false → 'groundUp'. default false." },
|
|
37
|
+
{ key: 'stepping', effect: "'fixed' | 'average' — the wave-walk sampler, ferried VERBATIM (never validated or defaulted here). Omitted ⇒ the engine's default." },
|
|
38
|
+
{ key: 'iframeIntervalSeconds', effect: "the parent-generation interval (s) — applied through the universe's own PRE-INIT setter (`universe.iframe_interval_seconds`) by applyPreInit(), immediately before universe.init; the engine freezes it at init. Omitted ⇒ untouched (the engine's default)." },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
function _initCfgKeyList() {
|
|
42
|
+
return INIT_CFG.map(function (r) { return r.key; }).join(', ');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* THROW-OR-APPLY on the cfg bag. An unknown key is a hard error naming the legal
|
|
47
|
+
* vocabulary — never a silent drop.
|
|
48
|
+
*
|
|
49
|
+
* `universe.init()` throws on retired names, but a recipe bag must fail HERE, before
|
|
50
|
+
* the mapping could drop a key it does not know.
|
|
51
|
+
*
|
|
52
|
+
* RETIREMENT ENRICHMENT: the universe's own class publishes the retirement ledger
|
|
53
|
+
* (`EGPTUniverse.RETIRED_CONFIG_KEYS`), so a retired name gets the SAME migration
|
|
54
|
+
* sentence the engine throws — one ledger, never a second copy here. The THROW is
|
|
55
|
+
* unconditional; only the extra sentence depends on the ledger being reachable (a
|
|
56
|
+
* bare test double may not publish it), so a missing ledger can never weaken the gate.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} where — the call shape to quote back ('RecipeContext.init(cfg)')
|
|
59
|
+
* @param {Object} cfg
|
|
60
|
+
* @param {Object} universe — the live universe, for its class's retirement ledger
|
|
61
|
+
*/
|
|
62
|
+
// Keys the co-init call owns: a cfg carrying one is a SECOND declaration of a fact the
|
|
63
|
+
// call already states (the size, the axes) — it throws naming the owner.
|
|
64
|
+
var OWNED_BY_COINIT = {
|
|
65
|
+
activeAxes: 'the axes are the method: exp.createCanvas(w, h, cfg) is 2D, exp.createScene(w, h, d, cfg) is 3D',
|
|
66
|
+
depth: 'the depth is exp.createScene(w, h, d, cfg)\'s d',
|
|
67
|
+
canvasW: 'the size is the co-init call\'s w, h (exp.createCanvas(w, h, cfg) / exp.createScene(w, h, d, cfg))',
|
|
68
|
+
canvasH: 'the size is the co-init call\'s w, h (exp.createCanvas(w, h, cfg) / exp.createScene(w, h, d, cfg))',
|
|
69
|
+
stage: 'the size is the co-init call\'s w, h (exp.createCanvas(w, h, cfg) / exp.createScene(w, h, d, cfg))'
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function assertInitCfg(where, cfg, universe) {
|
|
73
|
+
Object.keys(cfg).forEach(function (k) {
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(OWNED_BY_COINIT, k)) {
|
|
75
|
+
throw new Error(where + ": '" + k + "' is not a world-cfg key — " + OWNED_BY_COINIT[k] +
|
|
76
|
+
'. (FAIL-LOUD: one declaration per fact.)');
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
var Ctor = universe ? universe.constructor : null;
|
|
80
|
+
var retired = (Ctor && Ctor.RETIRED_CONFIG_KEYS && typeof Ctor.RETIRED_CONFIG_KEYS === 'object')
|
|
81
|
+
? Ctor.RETIRED_CONFIG_KEYS : null;
|
|
82
|
+
Object.keys(cfg).forEach(function (k) {
|
|
83
|
+
var known = INIT_CFG.some(function (r) { return r.key === k; });
|
|
84
|
+
if (known) { return; }
|
|
85
|
+
var msg = where + ": unknown option '" + k + "'. Legal options: " + _initCfgKeyList() + '.';
|
|
86
|
+
if (retired && Object.prototype.hasOwnProperty.call(retired, k)) {
|
|
87
|
+
msg += " That name is RETIRED at the engine — '" + k + "' " + retired[k] +
|
|
88
|
+
'. Note this is the RECIPE vocabulary, not the engine option list: ' +
|
|
89
|
+
'migrate to one of the legal options above.';
|
|
90
|
+
}
|
|
91
|
+
throw new Error(msg);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* initArgs(cfg) — THE cfg-bag → `universe.init(fundamentalDimension, options)` mapping,
|
|
97
|
+
* as a pure function returning the argument list. The ONE owner: the RecipeContext co-init
|
|
98
|
+
* call and every setup factory's world step (`setupSphereEmitter3D.initWorld`) apply it, so one cfg bag boots one world whichever surface the author used.
|
|
99
|
+
*
|
|
100
|
+
* The cfg key names predate the engine option names and do NOT line up 1:1:
|
|
101
|
+
* cfg.dimension → universe.init()'s 1st argument (default 2)
|
|
102
|
+
* cfg.wavelengthScale → options.wavelengthScale (default 8)
|
|
103
|
+
* cfg.bonding → options.noEscape (default false)
|
|
104
|
+
* cfg.frames → options.frameGenMode (true → 'topDown', false → 'groundUp')
|
|
105
|
+
* cfg.stepping → options.stepping (1:1, attached ONLY when declared)
|
|
106
|
+
* cfg.interQuantumCollisions is accepted but inert (it fed a removed positional slot).
|
|
107
|
+
*
|
|
108
|
+
* Omit-when-undeclared keeps the ENGINE the one authority for those defaults (serde
|
|
109
|
+
* `active_axes` {x,y}, `stepping` 'fixed'); `stepping` is ferried VERBATIM so
|
|
110
|
+
* `universe.init()` is its one validator. `brownianMotion` is never sent: under
|
|
111
|
+
* frameGenMode 'groundUp' the engine resolves it to false (`universe.rs`, GroundUp ⇒
|
|
112
|
+
* `brownian_motion.unwrap_or(false)`), and 'topDown' forces it true.
|
|
113
|
+
*
|
|
114
|
+
* No rect: the world rect is `EGPTUniverse.create({ rect })`'s, and `universe.init` reads it.
|
|
115
|
+
*
|
|
116
|
+
* THROW-OR-APPLY: an unknown cfg key throws naming the vocabulary (INIT_CFG); a second
|
|
117
|
+
* argument is the deleted rect parameter and throws naming the signature.
|
|
118
|
+
*
|
|
119
|
+
* @param {Object} cfg — a world cfg bag (INIT_CFG keys)
|
|
120
|
+
* @returns {Array} [dimension, options] — spread into `universe.init`
|
|
121
|
+
*/
|
|
122
|
+
function initArgs(cfg) {
|
|
123
|
+
if (arguments.length > 1) {
|
|
124
|
+
throw new Error('FrqtlWorldInit.initArgs(cfg) takes no rect — the world rect is written ONCE, ' +
|
|
125
|
+
'by EGPTUniverse.create({ rect, seed, … }), and universe.init(fundamentalDimension, options) ' +
|
|
126
|
+
'reads it. (FAIL-LOUD.)');
|
|
127
|
+
}
|
|
128
|
+
cfg = cfg || {};
|
|
129
|
+
assertInitCfg('FrqtlWorldInit.initArgs(cfg)', cfg, null);
|
|
130
|
+
var dimension = cfg.dimension !== undefined ? cfg.dimension : 2;
|
|
131
|
+
var options = {
|
|
132
|
+
wavelengthScale: cfg.wavelengthScale !== undefined ? cfg.wavelengthScale : 8,
|
|
133
|
+
noEscape: cfg.bonding !== undefined ? cfg.bonding : false,
|
|
134
|
+
// cfg.frames is a BOOLEAN in the recipe vocabulary; the engine option is the
|
|
135
|
+
// ENUM frameGenMode. The translation lives here, once.
|
|
136
|
+
frameGenMode: (cfg.frames !== undefined ? cfg.frames : false) ? 'topDown' : 'groundUp',
|
|
137
|
+
};
|
|
138
|
+
if (cfg.stepping !== undefined && cfg.stepping !== null) {
|
|
139
|
+
options.stepping = cfg.stepping;
|
|
140
|
+
}
|
|
141
|
+
return [dimension, options];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* applyPreInit(cfg, universe) — the cfg keys that must reach the universe BEFORE
|
|
146
|
+
* `universe.init` (the engine reads them while initializing and freezes them): today
|
|
147
|
+
* `iframeIntervalSeconds`, through the universe's own pre-init setter. Omitted ⇒ nothing is
|
|
148
|
+
* written. Every world step that calls `universe.init(...initArgs(cfg))` calls this
|
|
149
|
+
* first.
|
|
150
|
+
* @param {Object} cfg
|
|
151
|
+
* @param {Object} universe
|
|
152
|
+
*/
|
|
153
|
+
function applyPreInit(cfg, universe) {
|
|
154
|
+
if (cfg && cfg.iframeIntervalSeconds !== undefined && cfg.iframeIntervalSeconds !== null) {
|
|
155
|
+
universe.iframe_interval_seconds = cfg.iframeIntervalSeconds;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
var FrqtlWorldInit = { INIT_CFG: INIT_CFG, assertInitCfg: assertInitCfg, initArgs: initArgs, applyPreInit: applyPreInit };
|
|
160
|
+
|
|
161
|
+
export { FrqtlWorldInit };
|
|
162
|
+
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* setupBlackbodySubstrate — Blackbody experiment reconstructed under the
|
|
5
|
+
* as-intended control loop (docs/internal/engine/ENGINE_CONTRACT.md).
|
|
6
|
+
*
|
|
7
|
+
* ## One-line OvenBox difference from `lib/frqtl/scenes/blackbody.js` (config a vs config b)
|
|
8
|
+
*
|
|
9
|
+
* Both configs use the SAME revised OvenBox sugar (walls = NONE + eject op-programs,
|
|
10
|
+
* emitters, slit preserved). Apart from the `temperature` VALUE (the scene's
|
|
11
|
+
* TEMPERATURE = 1500, this factory's default 300), the ONLY OvenBox option that differs
|
|
12
|
+
* is the thermostat count source:
|
|
13
|
+
*
|
|
14
|
+
* config a (scenes/blackbody.js) : OvenBox default → countSource = 'active-quanta'
|
|
15
|
+
* config b (THIS FILE) : ONE extra option → countSource = 'oven-rect'
|
|
16
|
+
*
|
|
17
|
+
* Outside the OvenBox: the scene seats its sink at the canvas edge (30 px); this factory
|
|
18
|
+
* seats it flush against the outer wall unless `radiationSinkDepth` is given.
|
|
19
|
+
*
|
|
20
|
+
* With `countSource:'oven-rect'`, OvenBox internally registers an oven-interior
|
|
21
|
+
* counter-LO whose `on:'collision'` op-program increments `self.count` for every
|
|
22
|
+
* frame inside the cavity each tick. The between-tick `thermostatStep(tickData)`
|
|
23
|
+
* method reads the count, resets it, and CRUDs emitters (as-intended §2.1).
|
|
24
|
+
*
|
|
25
|
+
* ## Layer flags (test isolation)
|
|
26
|
+
*
|
|
27
|
+
* These flags are forwarded to OvenBox and control which op-programs are applied:
|
|
28
|
+
*
|
|
29
|
+
* - **`options.layer3=true`** (default): cavity walls carry eject op-programs
|
|
30
|
+
* (L3 block — §10.3 expressiveness proof, conformance EP22–EP25).
|
|
31
|
+
* - **`options.layer4=true`** (default, implies layer3): adds velocity-flip ops
|
|
32
|
+
* (L4 bounce = L3 eject + flip, conformance EP27–EP28).
|
|
33
|
+
* - **`options.layer5=true`** (default): enables `universe.withInterQuantumCollisions=true`
|
|
34
|
+
* — as `lib/frqtl/scenes/blackbody.js` sets it. ENGINE physics; no new
|
|
35
|
+
* op-programs. Expected JS↔WASM divergence on parent-frame count (deferred §6).
|
|
36
|
+
*
|
|
37
|
+
* Set any flag to `false` only for progressive test isolation (L1/L2/L3 runs in
|
|
38
|
+
* `experiments/blackbody-substrate/run.js`). The default (all true) runs the
|
|
39
|
+
* scene's flags (brownian on, inter-quantum collisions on).
|
|
40
|
+
*
|
|
41
|
+
* ## Thermostat control loop (as-intended §2.1, Dispatch C2)
|
|
42
|
+
*
|
|
43
|
+
* ① IN-TICK (native, collision-driven):
|
|
44
|
+
* OvenBox registers an oven-interior LargeObject (rect = cavity interior,
|
|
45
|
+
* collision_type NONE) with on:'collision' op-program:
|
|
46
|
+
* set self.count = self.count + 1
|
|
47
|
+
* This counts cavity occupancy IN-TICK, bit-identical across JS↔WASM.
|
|
48
|
+
*
|
|
49
|
+
* ② doTick() MARSHALS: TickData.objects[ovenHandle].count = frames-in-cavity.
|
|
50
|
+
*
|
|
51
|
+
* ③ BETWEEN TICKS (user-space, single-source — OvenBox.thermostatStep):
|
|
52
|
+
* a. Reset count = 0 via updateObjectProperty.
|
|
53
|
+
* b. Read cavityCount = tickData.objects[ovenHandle].count (B5.1, B2.1).
|
|
54
|
+
* c. burst = cavityCount < target ? max(1, floor((target-cavityCount)/N)) : 0
|
|
55
|
+
* d. CRUD emitters via updateObjectProperty.
|
|
56
|
+
*
|
|
57
|
+
* ④ Loop.
|
|
58
|
+
*
|
|
59
|
+
* B2.1: cavityCount is read ONLY from tickData.objects[ovenHandle].count.
|
|
60
|
+
* getLargeObjectCount is DELETED (see wasm-engine.js) — no parallel read path.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} universe - The engine universe instance (JS or WASM).
|
|
63
|
+
* @param {Object} [options]
|
|
64
|
+
* @param {number} [options.temperature=300] - Target cavity quanta count.
|
|
65
|
+
* @param {number} [options.boxFraction=1/3]
|
|
66
|
+
* @param {number} [options.wallThickness=10]
|
|
67
|
+
* @param {number} [options.slitHeight=60]
|
|
68
|
+
* @param {string} [options.slitWall='right']
|
|
69
|
+
* @param {number} [options.radiationSinkDepth] - If provided, places sink at canvas
|
|
70
|
+
* edge at this depth. If omitted, sink is placed flush against the outer box wall
|
|
71
|
+
* for immediate absorption of escaped radiation (ratio → 1.0).
|
|
72
|
+
* @param {number} [options.fundamental_dim=0]
|
|
73
|
+
* @param {number} [options.quantum_dim=4]
|
|
74
|
+
* @param {boolean} [options.layer3=true] - Eject op-programs on walls (L3 block).
|
|
75
|
+
* @param {boolean} [options.layer4=true] - Velocity-flip ops (L4 bounce, implies L3).
|
|
76
|
+
* @param {boolean} [options.layer5=true] - Enable withInterQuantumCollisions=true.
|
|
77
|
+
*
|
|
78
|
+
* @returns {{
|
|
79
|
+
* box: OvenBox,
|
|
80
|
+
* emitters: QuantumEmitter[],
|
|
81
|
+
* ovenHandle: number,
|
|
82
|
+
* target: number,
|
|
83
|
+
* thermostatStep: function(tickData:object):void,
|
|
84
|
+
* setTemperature: function(n:number):void,
|
|
85
|
+
* getTemperature: function():number,
|
|
86
|
+
* radiationSink: LargeObject|null,
|
|
87
|
+
* largeObjects: LargeObject[]
|
|
88
|
+
* }}
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
import { assertSetupOptions as _assertSetupOptions, requireWorldRect as _requireWorldRect } from './_setupOptions.js';
|
|
92
|
+
import { OvenBox } from '../../parts/OvenBox.js';
|
|
93
|
+
import { Rectangle } from '../../parts/Rectangle.js';
|
|
94
|
+
import { LargeObject } from '../../parts/LargeObject.js';
|
|
95
|
+
import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
96
|
+
import { CollisionActions } from '../../parts/CollisionActions.js';
|
|
97
|
+
|
|
98
|
+
// The ONE unknown-option-key validator (objects/setup/_setupOptions.js). Resolved
|
|
99
|
+
// LAZILY in the browser: the game layer defines window.assertSetupOptions before any
|
|
100
|
+
// page script runs, but reading it at eval time would bake in a load-order dependency.
|
|
101
|
+
|
|
102
|
+
// ── Imports ─────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
// ── Setup Factory ───────────────────────────────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* OPTIONS — this factory's own option vocabulary: EXACTLY the keys it reads.
|
|
107
|
+
* Exported as `setupBlackbodySubstrate.OPTIONS` so a composing caller consumes it instead of
|
|
108
|
+
* retyping it. `setup-factory-option-vocabulary.test.mjs` re-derives the
|
|
109
|
+
* read-set by static trace and fails if this list drifts from the code.
|
|
110
|
+
*/
|
|
111
|
+
var OPTIONS = [
|
|
112
|
+
'boxFraction', 'fundamental_dim', 'layer3', 'layer4',
|
|
113
|
+
'layer5', 'quantum_dim', 'radiationSinkDepth', 'slitHeight',
|
|
114
|
+
'slitWall', 'temperature', 'wallThickness',
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
function setupBlackbodySubstrate(universe, options) {
|
|
118
|
+
options = _assertSetupOptions('setupBlackbodySubstrate', options, OPTIONS);
|
|
119
|
+
|
|
120
|
+
// The world rect the co-init call wrote (EGPTUniverse.create({rect})) — the ONE size.
|
|
121
|
+
var canvas_rect = universe.universe_rect;
|
|
122
|
+
_requireWorldRect('setupBlackbodySubstrate universe.universe_rect', canvas_rect);
|
|
123
|
+
|
|
124
|
+
var fundamental_dim = options.fundamental_dim !== undefined ? options.fundamental_dim : 0;
|
|
125
|
+
var quantum_dim = options.quantum_dim !== undefined ? options.quantum_dim : 4;
|
|
126
|
+
var temperature = options.temperature !== undefined ? options.temperature : 300;
|
|
127
|
+
var boxFraction = options.boxFraction !== undefined ? options.boxFraction : 1 / 3;
|
|
128
|
+
var wallThickness = options.wallThickness !== undefined ? options.wallThickness : 10;
|
|
129
|
+
var slitHeight = options.slitHeight !== undefined ? options.slitHeight : 60;
|
|
130
|
+
var slitWall = options.slitWall !== undefined ? options.slitWall : 'right';
|
|
131
|
+
|
|
132
|
+
// ── L5: parenting / promotion flag ──────────────────────────────
|
|
133
|
+
// L5 is on by default (matching scenes/blackbody.js). Set layer5:false
|
|
134
|
+
// to run without inter-quantum collisions (L1/L2/L3/L4 only).
|
|
135
|
+
var USE_L5 = options.layer5 !== undefined ? options.layer5 : true;
|
|
136
|
+
|
|
137
|
+
// ── Universe init ─────────────────────────────────────────────────
|
|
138
|
+
universe.init(fundamental_dim, { wavelengthScale: 1 });
|
|
139
|
+
universe.brownianMotion = true;
|
|
140
|
+
// L5: enable inter-quantum collisions → merge → promotion (compound/parent frames).
|
|
141
|
+
// Matches scenes/blackbody.js. Diverges JS↔WASM on parent count
|
|
142
|
+
// — expected (deferred quadtree/promotion workstream, handoff §6).
|
|
143
|
+
universe.withInterQuantumCollisions = USE_L5 ? true : false;
|
|
144
|
+
|
|
145
|
+
if (quantum_dim && quantum_dim > fundamental_dim) {
|
|
146
|
+
universe.addDimension(quantum_dim);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var screenCenterX = Math.round(canvas_rect.w / 2);
|
|
150
|
+
var screenCenterY = Math.round(canvas_rect.h / 2);
|
|
151
|
+
var boxW = Math.round(canvas_rect.w * boxFraction);
|
|
152
|
+
var boxH = Math.round(canvas_rect.h * boxFraction);
|
|
153
|
+
|
|
154
|
+
var outerRect = new Rectangle(screenCenterX, screenCenterY, boxW, boxH, null, true);
|
|
155
|
+
var innerLeft = outerRect.left + wallThickness;
|
|
156
|
+
var innerTop = outerRect.top + wallThickness;
|
|
157
|
+
var innerW = outerRect.w - 2 * wallThickness;
|
|
158
|
+
var innerH = outerRect.h - 2 * wallThickness;
|
|
159
|
+
|
|
160
|
+
// ── OvenBox (config b — the ONE distinguishing config line) ──────────
|
|
161
|
+
//
|
|
162
|
+
// countSource: 'oven-rect' is the ONLY difference from config a.
|
|
163
|
+
// Config a (scenes/blackbody.js) passes no countSource → OvenBox defaults
|
|
164
|
+
// to 'active-quanta'. Config b passes countSource: 'oven-rect'.
|
|
165
|
+
//
|
|
166
|
+
// OvenBox handles internally:
|
|
167
|
+
// - walls: NONE + eject op-programs (L3/L4 per layer3/layer4 flags)
|
|
168
|
+
// - emitters: 4 inward-firing (auto_fire=false)
|
|
169
|
+
// - oven-rect counter-LO registration (countSource='oven-rect')
|
|
170
|
+
// - thermostatStep(tickData) method (countSource='oven-rect')
|
|
171
|
+
var oven = new OvenBox(universe, {
|
|
172
|
+
left: innerLeft,
|
|
173
|
+
top: innerTop,
|
|
174
|
+
width: innerW,
|
|
175
|
+
height: innerH,
|
|
176
|
+
wallThickness: wallThickness,
|
|
177
|
+
temperature: temperature,
|
|
178
|
+
slitHeight: slitHeight,
|
|
179
|
+
slitWall: slitWall,
|
|
180
|
+
color: [120, 120, 120],
|
|
181
|
+
countSource: 'oven-rect', // ← the ONE config line (b vs a)
|
|
182
|
+
layer3: options.layer3 !== undefined ? options.layer3 : true,
|
|
183
|
+
layer4: options.layer4 !== undefined ? options.layer4 : true
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// ── Radiation sink ─────────────────────────────────────────────────
|
|
187
|
+
// Absorb escaped quanta to prevent them inflating activeQuanta without bound.
|
|
188
|
+
//
|
|
189
|
+
// Sink is placed flush against the outer box wall (not at the canvas edge) so
|
|
190
|
+
// that escaped frames are absorbed within 1 tick, keeping aq ≈ cavity count
|
|
191
|
+
// (ratio → 1.0). Default: flush against outer wall. Callers may override via
|
|
192
|
+
// options.radiationSinkDepth (places sink at canvas edge at given depth).
|
|
193
|
+
var radiationSink = null;
|
|
194
|
+
var sinkRect = null;
|
|
195
|
+
if (slitWall === 'right') {
|
|
196
|
+
var sinkLeft = options.radiationSinkDepth !== undefined
|
|
197
|
+
? canvas_rect.w - options.radiationSinkDepth
|
|
198
|
+
: outerRect.right;
|
|
199
|
+
sinkRect = [sinkLeft, 0, canvas_rect.w - sinkLeft, canvas_rect.h];
|
|
200
|
+
} else if (slitWall === 'left') {
|
|
201
|
+
var sinkRight = options.radiationSinkDepth !== undefined
|
|
202
|
+
? options.radiationSinkDepth
|
|
203
|
+
: outerRect.left;
|
|
204
|
+
sinkRect = [0, 0, sinkRight, canvas_rect.h];
|
|
205
|
+
} else if (slitWall === 'top') {
|
|
206
|
+
var sinkBottom = options.radiationSinkDepth !== undefined
|
|
207
|
+
? options.radiationSinkDepth
|
|
208
|
+
: outerRect.top;
|
|
209
|
+
sinkRect = [0, 0, canvas_rect.w, sinkBottom];
|
|
210
|
+
} else if (slitWall === 'bottom') {
|
|
211
|
+
var sinkTop = options.radiationSinkDepth !== undefined
|
|
212
|
+
? canvas_rect.h - options.radiationSinkDepth
|
|
213
|
+
: outerRect.bottom;
|
|
214
|
+
sinkRect = [0, sinkTop, canvas_rect.w, canvas_rect.h - sinkTop];
|
|
215
|
+
}
|
|
216
|
+
if (sinkRect && LargeObject && CollisionTypes && CollisionActions) {
|
|
217
|
+
radiationSink = new LargeObject(
|
|
218
|
+
universe, sinkRect,
|
|
219
|
+
CollisionTypes.NONE, [CollisionActions.ABSORB],
|
|
220
|
+
'radiation_sink', 'closest', null, 1, [0, 0, 0]
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
box: oven,
|
|
226
|
+
emitters: oven.emitters,
|
|
227
|
+
ovenHandle: oven.ovenHandle,
|
|
228
|
+
target: temperature,
|
|
229
|
+
thermostatStep: oven.thermostatStep,
|
|
230
|
+
setTemperature: function (newTarget) { oven.setTemperature(newTarget); },
|
|
231
|
+
getTemperature: function () { return oven.getTemperature(); },
|
|
232
|
+
radiationSink: radiationSink,
|
|
233
|
+
largeObjects: oven.walls
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
setupBlackbodySubstrate.OPTIONS = OPTIONS;
|
|
240
|
+
|
|
241
|
+
export { setupBlackbodySubstrate };
|
|
242
|
+
|