@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/CLAUDE.md +87 -0
  2. package/LICENSE.md +664 -0
  3. package/README.md +84 -2
  4. package/dist/dist-manifest.json +218 -0
  5. package/dist/eyes/index.js +109 -0
  6. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  7. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  8. package/dist/fat/pkg-node/package.json +12 -0
  9. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  10. package/dist/frqtl/compat/boot-node.js +34 -0
  11. package/dist/frqtl/compat/engine.js +127 -0
  12. package/dist/frqtl/compat/package-root.js +24 -0
  13. package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
  14. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  15. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  16. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  17. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  18. package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
  19. package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
  20. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  21. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  22. package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
  23. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  24. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  25. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  26. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  27. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  28. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  30. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  31. package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
  32. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  33. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  34. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  35. package/dist/frqtl/compat/vendor.js +196 -0
  36. package/dist/frqtl/node-entry.js +17 -0
  37. package/dist/frqtl/objects/FrqtlApp.js +2920 -0
  38. package/dist/frqtl/objects/ObjectFactory.js +296 -0
  39. package/dist/frqtl/objects/RecipeContext.js +322 -0
  40. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  41. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  42. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  43. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  44. package/dist/frqtl/objects/instruments/capture.js +710 -0
  45. package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
  46. package/dist/frqtl/objects/kinds-entry.js +14 -0
  47. package/dist/frqtl/objects/kinds.js +190 -0
  48. package/dist/frqtl/objects/objects/Box.js +273 -0
  49. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  50. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  51. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  52. package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
  53. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  54. package/dist/frqtl/objects/objects/LightSource.js +351 -0
  55. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  56. package/dist/frqtl/objects/objects/Tube.js +471 -0
  57. package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
  58. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  59. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  60. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  61. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  62. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  66. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  67. package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
  68. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  69. package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
  70. package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
  71. package/dist/frqtl/objects/objects/tagger.js +228 -0
  72. package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
  73. package/dist/frqtl/objects/schema/index.js +256 -0
  74. package/dist/frqtl/objects/schema/scopes.js +457 -0
  75. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  76. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  77. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  78. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  79. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  80. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  81. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  82. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  83. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
  85. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  86. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  87. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  88. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  89. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  90. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  91. package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
  92. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  94. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  95. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
  96. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  97. package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
  98. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  99. package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
  100. package/dist/frqtl/objects/v1-replacements.js +29 -0
  101. package/dist/frqtl/package-core.js +23 -0
  102. package/dist/frqtl/parts/CameraControls.js +643 -0
  103. package/dist/frqtl/parts/CameraPath.js +575 -0
  104. package/dist/frqtl/parts/CollisionActions.js +34 -0
  105. package/dist/frqtl/parts/CollisionTypes.js +30 -0
  106. package/dist/frqtl/parts/DetectorWall.js +496 -0
  107. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  108. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  109. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  110. package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
  111. package/dist/frqtl/parts/LargeObject.js +405 -0
  112. package/dist/frqtl/parts/LightColor.js +255 -0
  113. package/dist/frqtl/parts/LiveChart.js +285 -0
  114. package/dist/frqtl/parts/LogicGate.js +717 -0
  115. package/dist/frqtl/parts/OvenBox.js +611 -0
  116. package/dist/frqtl/parts/PointSource.js +762 -0
  117. package/dist/frqtl/parts/Rectangle.js +200 -0
  118. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  119. package/dist/frqtl/parts/SlitScreen.js +628 -0
  120. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  121. package/dist/frqtl/parts/Trace.js +508 -0
  122. package/dist/frqtl/parts/Tunnel.js +1057 -0
  123. package/dist/frqtl/parts/Wall.js +1211 -0
  124. package/dist/frqtl/parts/index.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  126. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  127. package/dist/frqtl/scenes/atomic-model.js +88 -0
  128. package/dist/frqtl/scenes/big-bang.js +46 -0
  129. package/dist/frqtl/scenes/blackbody.js +81 -0
  130. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  131. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  132. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  133. package/dist/frqtl/scenes/counting-wall.js +38 -0
  134. package/dist/frqtl/scenes/double-slit.js +31 -0
  135. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  136. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  137. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  138. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
  139. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  140. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  141. package/dist/frqtl/scenes/empty.js +14 -0
  142. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  143. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  144. package/dist/frqtl/scenes/mirror-route.js +140 -0
  145. package/dist/frqtl/scenes/mirror.js +38 -0
  146. package/dist/frqtl/scenes/omni-3d.js +27 -0
  147. package/dist/frqtl/scenes/omni-source.js +22 -0
  148. package/dist/frqtl/scenes/self-interference.js +39 -0
  149. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  150. package/dist/frqtl/scenes/straight-walk.js +22 -0
  151. package/dist/frqtl/scenes/sun.js +39 -0
  152. package/dist/frqtl/scenes/two-source.js +37 -0
  153. package/dist/frqtl/scenes/wave.js +26 -0
  154. package/dist/shim/bundle/frqnb.js +112 -0
  155. package/dist/shim/bundle/index.js +121 -0
  156. package/dist/shim/bundle/indexHtml.js +107 -0
  157. package/dist/shim/bundle/manifest.js +95 -0
  158. package/dist/shim/bundle/zip.js +243 -0
  159. package/dist/shim/bus.js +67 -0
  160. package/dist/shim/controls/index.js +627 -0
  161. package/dist/shim/controls/registry.js +274 -0
  162. package/dist/shim/engine/artifact-shell.js +211 -0
  163. package/dist/shim/engine/boot-phases.js +44 -0
  164. package/dist/shim/engine/engine-stack-boot.js +59 -0
  165. package/dist/shim/engine/router.js +690 -0
  166. package/dist/shim/experiment-frame/index.js +296 -0
  167. package/dist/shim/expr/ast-schema.js +369 -0
  168. package/dist/shim/field-descriptor/index.js +325 -0
  169. package/dist/shim/frame-data/index.js +224 -0
  170. package/dist/shim/index.js +67 -0
  171. package/dist/shim/notebook/canvas-guard.js +82 -0
  172. package/dist/shim/notebook/catalog.js +35 -0
  173. package/dist/shim/notebook/format.js +736 -0
  174. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  175. package/dist/shim/notebook/kernel.js +671 -0
  176. package/dist/shim/notebook/knob.js +150 -0
  177. package/dist/shim/notebook/module-resolver.js +260 -0
  178. package/dist/shim/notebook/notebook-knobs.js +65 -0
  179. package/dist/shim/notebook/produced-bindings.js +67 -0
  180. package/dist/shim/notebook/realm-registry.js +121 -0
  181. package/dist/shim/notebook/realm-transport.js +73 -0
  182. package/dist/shim/notebook/setup-builder.js +507 -0
  183. package/dist/shim/notebook/store.js +159 -0
  184. package/dist/shim/notebook/vendor-importmap.js +134 -0
  185. package/dist/shim/notebook/view-builder.js +189 -0
  186. package/dist/shim/notebook/view-ctx.js +100 -0
  187. package/dist/shim/notebook/view-handle.js +202 -0
  188. package/dist/shim/notebook/warmup.js +102 -0
  189. package/dist/shim/package.json +4 -0
  190. package/dist/shim/page-params/index.js +213 -0
  191. package/dist/shim/project/identity.js +112 -0
  192. package/dist/shim/project/index.js +19 -0
  193. package/dist/shim/project/link.js +402 -0
  194. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  195. package/dist/shim/project/register-node.mjs +17 -0
  196. package/dist/shim/project/specifiers.js +106 -0
  197. package/dist/shim/registry/copy-gate.js +110 -0
  198. package/dist/shim/registry/exhibits.js +455 -0
  199. package/dist/shim/registry/experiments.js +215 -0
  200. package/dist/shim/registry/index.js +19 -0
  201. package/dist/shim/registry/scene-source.js +157 -0
  202. package/dist/shim/registry/walkthrough.js +804 -0
  203. package/dist/shim/render/capture-envelope.js +279 -0
  204. package/dist/shim/render/heat-colormap.js +92 -0
  205. package/dist/shim/render/render-stages.js +855 -0
  206. package/dist/shim/render/sun-look.js +33 -0
  207. package/dist/shim/render-state/apply.js +262 -0
  208. package/dist/shim/render-state/index.js +74 -0
  209. package/dist/shim/rpc/index.js +207 -0
  210. package/dist/shim/runtime/index.js +1023 -0
  211. package/dist/shim/storage/boot.js +69 -0
  212. package/dist/shim/storage/index.js +169 -0
  213. package/dist/shim/storage/local-storage-provider.js +230 -0
  214. package/dist/shim/storage/make-namespace-store.js +132 -0
  215. package/dist/shim/storage/modules.js +249 -0
  216. package/dist/shim/storage/projects.js +150 -0
  217. package/llms.txt +12 -0
  218. package/package.json +63 -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.
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
+