@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,306 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* setupTunnelPointExit — the TUNNEL POINT-EXIT LAB (D3, CEO 2026-07-29).
|
|
5
|
+
*
|
|
6
|
+
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
7
|
+
*
|
|
8
|
+
* Composes THREE already-committed pieces and adds no physics of its own (SU1):
|
|
9
|
+
* 1. a `SphereEmitter` on the `'hemisphere'` preset (D1), parked JUST INSIDE the
|
|
10
|
+
* corridor's input mouth and aimed down the bore, so the whole burst is fed into
|
|
11
|
+
* the tunnel instead of half of it flying back out of the entrance;
|
|
12
|
+
* 2. a `Tunnel` in `mode:'directed'` (D2) — each wall hit ejects the quantum back into
|
|
13
|
+
* the bore and re-emits it in a uniformly random direction within 180° of the axis;
|
|
14
|
+
* 3. that Tunnel's `exitCollapse` gate (D3) — every quantum crossing the output mouth is
|
|
15
|
+
* pinned to the bore mid-line, so the mouth becomes a one-pixel POINT SOURCE.
|
|
16
|
+
*
|
|
17
|
+
* The question the lab exists to answer VISUALLY: does a point source fed by tunnel
|
|
18
|
+
* transit produce the same semicircular banded self-interference as a fresh point emitter?
|
|
19
|
+
* Graded against the Single-Source Self-Interference experiment as the named exemplar.
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} universe - an EGPTUniverse (not yet init'd; the emitter inits it).
|
|
22
|
+
* @param {Object} [options]
|
|
23
|
+
* @param {number} [options.tunnelLength=240] - corridor travel-axis extent, px. REBUILD-class
|
|
24
|
+
* (it is wall geometry): the page re-runs `runner.setup()` to change it — the documented
|
|
25
|
+
* restart-in-place idiom, not a live knob. Deliberately NOT faked as live.
|
|
26
|
+
* @param {number} [options.bore=40] - clear aperture between the corridor walls, px.
|
|
27
|
+
* @param {number} [options.wavelengthScale=16] - the universe's GLOBAL `wavelengthScale`.
|
|
28
|
+
* LIVE (`universe.wavelengthScale`, consumed fresh each tick). Leaves are born at
|
|
29
|
+
* dimension 2 (capacity 16) with a `wavelength:32` arg (mass 1), so capacity/mass = 16 and
|
|
30
|
+
* the EFFECTIVE per-leaf transverse wavelength is `16 × scale` px. 16 ⇒ eff λ 256, the
|
|
31
|
+
* exemplar's own golden.
|
|
32
|
+
* @param {boolean} [options.exitPhaseReset=false] - arm the exit gate's phase reset. LIVE.
|
|
33
|
+
* @param {number} [options.burstSize=6] @param {number} [options.emitEveryTicks=1]
|
|
34
|
+
* @param {boolean} [options.active=true]
|
|
35
|
+
*
|
|
36
|
+
* @returns {{ tunnel, emitterControl, largeObjects, pointExitControl, dispose }}
|
|
37
|
+
* `pointExitControl` is the live sugar handle (the canonical knob pattern):
|
|
38
|
+
* `setWavelengthScale/getWavelengthScale/getEffectiveWavelength`,
|
|
39
|
+
* `setExitPhaseReset/getExitPhaseReset`, `setActive`, `getTunnelLength`, `controls`.
|
|
40
|
+
*
|
|
41
|
+
* ── Layer 2: physics behavior ────────────────────────────────────────────────
|
|
42
|
+
*
|
|
43
|
+
* Zero new physics. The banding in the exemplar comes from equal-radius quanta sharing a
|
|
44
|
+
* spin phase: births at `local_time=0` are phase-locked, fundamental leaves step P=1 so AGE
|
|
45
|
+
* = RADIUS (bench M1), and the λ coin adds a transverse kick whose bias flips per spin
|
|
46
|
+
* quadrant — every ray snakes with period λ_eff and the ensemble reads as concentric bands.
|
|
47
|
+
* The exit collapse supplies the "one origin" half of that mechanism.
|
|
48
|
+
*
|
|
49
|
+
* It does NOT supply the phase half, and that is the whole point of the lab. Quanta arriving
|
|
50
|
+
* at the mouth have different transit histories, so their phases are spread (measured on this
|
|
51
|
+
* construction: within-same-crossing-tick `local_time` std ≈ 45 ticks). Against a spin period
|
|
52
|
+
* of λ_eff that spread is either negligible or dominant:
|
|
53
|
+
*
|
|
54
|
+
* | wavelengthScale | eff λ | spin period | expected picture, phase reset OFF |
|
|
55
|
+
* |---|---|---|---|
|
|
56
|
+
* | 16 | 256 | 256 ≫ 45 | ~90% phase-aligned for free — BANDED |
|
|
57
|
+
* | ≤ 8 | ≤ 128 | ≤ 128 | phase-flat (8-bin CV ≈ 0.09) — SMOOTH, un-banded |
|
|
58
|
+
*
|
|
59
|
+
* With the reset ON the mouth is phase-locked at any λ, so the fan should band in both
|
|
60
|
+
* regimes. Sweeping λ against the toggle IS the experiment — nothing here is tuned to make
|
|
61
|
+
* the two regimes agree.
|
|
62
|
+
*
|
|
63
|
+
* Runs the fast path (`withInterQuantumCollisions:false`, no brownian). That is a hard
|
|
64
|
+
* precondition of the collapse, enforced FAIL-LOUD inside `Tunnel`: with collisions ON the
|
|
65
|
+
* collapse pixel serializes one frame per tick and kills contended quanta, so the scene would
|
|
66
|
+
* measure contention rather than a point source.
|
|
67
|
+
*
|
|
68
|
+
* The emitter's start pixel carries ZERO jitter, matching the exemplar: a SphereEmitter burst
|
|
69
|
+
* with emission box 0 starts every quantum on the SAME pixel and co-location is inert on this
|
|
70
|
+
* path. A transverse random jitter is exactly what an earlier self-interference seed did
|
|
71
|
+
* before it was replaced for blurring the bands.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
import { assertSetupOptions as _assertSetupOptions, requireWorldRect as _requireWorldRect } from './_setupOptions.js';
|
|
75
|
+
import { setupSphereEmitter3D } from './setupSphereEmitter3D.js';
|
|
76
|
+
import { kindNamed } from '../kinds.js';
|
|
77
|
+
|
|
78
|
+
// The ONE unknown-option-key validator (objects/setup/_setupOptions.js). Resolved
|
|
79
|
+
// LAZILY in the browser: the game layer defines window.assertSetupOptions before any
|
|
80
|
+
// page script runs, but reading it at eval time would bake in a load-order dependency.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// Leaves are born HERE (capacity = (2^2)² = 16) with a wavelength arg of 32 → mass 1, so
|
|
85
|
+
// capacity/mass = 16 and the global scale has real leverage. Both numbers are the
|
|
86
|
+
// Single-Source Self-Interference exemplar's own — parity of the per-leaf creation
|
|
87
|
+
// conditions is what makes the two pictures comparable at all.
|
|
88
|
+
var FUNDAMENTAL_DIMENSION = 2;
|
|
89
|
+
var LEAF_WAVELENGTH_ARG = 32;
|
|
90
|
+
var CAPACITY_OVER_MASS = 16; // (2^FUNDAMENTAL_DIMENSION)² / mass(=1)
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* THE ONE SCENE-SPEC AUTHORITY (D4). Pure function: options → the exact `SphereEmitter`
|
|
94
|
+
* and `Tunnel` construction configs for this scene, plus the derived geometry a readout
|
|
95
|
+
* wants. It draws nothing and touches no universe.
|
|
96
|
+
*
|
|
97
|
+
* It exists because this scene has TWO surfaces — the standalone page (which runs the
|
|
98
|
+
* factory below) and the Light Lab rung (which must be an `addObject` recipe so STUDIO can
|
|
99
|
+
* select and edit the objects). Without a shared authority those two would each hand-carry
|
|
100
|
+
* the same ten numbers and drift apart, which is the anti-pattern this repo keeps paying
|
|
101
|
+
* for. Both consume THIS; neither re-derives it.
|
|
102
|
+
*
|
|
103
|
+
* @param {Object} spec - `setupTunnelPointExit`'s own options (same names) PLUS the
|
|
104
|
+
* REQUIRED world size `canvasW`/`canvasH` — the caller ferries the size its co-init
|
|
105
|
+
* call declared (the rung: the sketch's own `W, H`). Absent THROWS; never a default.
|
|
106
|
+
* @returns {{emitter:Object, tunnel:Object, geom:Object}} the two addObject-ready configs.
|
|
107
|
+
*/
|
|
108
|
+
function sceneSpec(spec) {
|
|
109
|
+
// sceneSpec is EXPORTED (the Light Lab rung consumes it so the rung and the
|
|
110
|
+
// standalone page cannot drift), and it reads the SAME bag the factory does plus
|
|
111
|
+
// the world size — so it takes the SAME gate, widened by exactly those two keys.
|
|
112
|
+
spec = _assertSetupOptions('setupTunnelPointExit.sceneSpec', spec, SCENE_SPEC_OPTIONS);
|
|
113
|
+
return _sceneGeometry(spec, _requireWorldRect('setupTunnelPointExit.sceneSpec canvasW/canvasH',
|
|
114
|
+
{ w: spec.canvasW, h: spec.canvasH }));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// The geometry itself, from an options bag and the world rect. The factory calls this
|
|
118
|
+
// with `universe.universe_rect`, the rung (through sceneSpec) with its declared W, H.
|
|
119
|
+
function _sceneGeometry(o, world) {
|
|
120
|
+
var cw = world.w;
|
|
121
|
+
var ch = world.h;
|
|
122
|
+
var tunnelLength = o.tunnelLength !== undefined ? o.tunnelLength : 240;
|
|
123
|
+
var bore = o.bore !== undefined ? o.bore : 40;
|
|
124
|
+
var wallThickness = o.wallThickness !== undefined ? o.wallThickness : 10;
|
|
125
|
+
var wavelengthScale = o.wavelengthScale !== undefined ? o.wavelengthScale : 16;
|
|
126
|
+
var exitRecenter = (o.exitRecenter !== undefined) ? !!o.exitRecenter : true;
|
|
127
|
+
var exitPhaseReset = !!o.exitPhaseReset;
|
|
128
|
+
var burstSize = o.burstSize !== undefined ? o.burstSize : 6;
|
|
129
|
+
var emitEveryTicks = o.emitEveryTicks !== undefined ? o.emitEveryTicks : 1;
|
|
130
|
+
|
|
131
|
+
// The corridor sits in the LEFT portion so the exit mouth has open canvas to its right
|
|
132
|
+
// for the fan to develop — the fan is the observable, so it gets the room.
|
|
133
|
+
var left = Math.round(cw * 0.08);
|
|
134
|
+
var midY = Math.round(ch / 2);
|
|
135
|
+
var top = midY - Math.round(bore / 2);
|
|
136
|
+
var emitterX = left + Math.max(2, Math.round(wallThickness / 2)); // a few px inside the mouth
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
emitter: {
|
|
140
|
+
preset: 'hemisphere', // D1 — forward 180°, so the whole burst enters the bore
|
|
141
|
+
is2D: true,
|
|
142
|
+
center: [emitterX, midY],
|
|
143
|
+
emissionBoxSide: 0, // a literal one-pixel source — no jitter
|
|
144
|
+
chargeMode: 'random', // braiding — the self-interference exemplar's setting
|
|
145
|
+
fundamentalDimension: FUNDAMENTAL_DIMENSION,
|
|
146
|
+
wavelength: LEAF_WAVELENGTH_ARG,
|
|
147
|
+
fundamentalWaveLength: 4 * wavelengthScale, // → waveLengthMultiplier = wavelengthScale
|
|
148
|
+
burstSize: burstSize,
|
|
149
|
+
emitEveryTicks: emitEveryTicks
|
|
150
|
+
},
|
|
151
|
+
tunnel: {
|
|
152
|
+
left: left, top: top, length: tunnelLength, bore: bore,
|
|
153
|
+
wallThickness: wallThickness, axis: 'x',
|
|
154
|
+
mode: 'directed', // D2 — 180° forward re-emission on every wall hit
|
|
155
|
+
exitCollapse: true, // D3 — the gate at the output mouth
|
|
156
|
+
exitRecenter: exitRecenter, // D4 — the point-source collapse (default ON)
|
|
157
|
+
exitPhaseReset: exitPhaseReset,
|
|
158
|
+
name: 'point_exit_tunnel', color: [90, 120, 200]
|
|
159
|
+
},
|
|
160
|
+
geom: {
|
|
161
|
+
left: left, top: top, midY: midY, bore: bore,
|
|
162
|
+
exitX: left + tunnelLength, tunnelLength: tunnelLength,
|
|
163
|
+
wavelengthScale: wavelengthScale,
|
|
164
|
+
effectiveWavelength: Math.floor(CAPACITY_OVER_MASS * wavelengthScale)
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* OPTIONS — this factory's own option vocabulary: EXACTLY the keys it reads.
|
|
170
|
+
* Exported as `setupTunnelPointExit.OPTIONS` so a composing caller consumes it instead of
|
|
171
|
+
* retyping it. `setup-factory-option-vocabulary.test.mjs` re-derives the
|
|
172
|
+
* read-set by static trace and fails if this list drifts from the code.
|
|
173
|
+
*/
|
|
174
|
+
var OPTIONS = [
|
|
175
|
+
'active', 'bore', 'burstSize',
|
|
176
|
+
'emitEveryTicks', 'exitPhaseReset', 'exitRecenter',
|
|
177
|
+
'tunnelLength', 'wallThickness', 'wavelengthScale',
|
|
178
|
+
];
|
|
179
|
+
// sceneSpec's vocabulary: the factory's own, plus the world size it cannot read off a
|
|
180
|
+
// universe (it touches none). Composed from OPTIONS, never retyped.
|
|
181
|
+
var SCENE_SPEC_OPTIONS = OPTIONS.concat(['canvasH', 'canvasW']);
|
|
182
|
+
|
|
183
|
+
function setupTunnelPointExit(universe, options) {
|
|
184
|
+
options = _assertSetupOptions('setupTunnelPointExit', options, OPTIONS);
|
|
185
|
+
|
|
186
|
+
// The world rect the co-init call wrote (EGPTUniverse.create({rect})) — the ONE size.
|
|
187
|
+
var spec = _sceneGeometry(options, _requireWorldRect(
|
|
188
|
+
'setupTunnelPointExit universe.universe_rect', universe.universe_rect));
|
|
189
|
+
var tunnelLength = spec.geom.tunnelLength;
|
|
190
|
+
var bore = spec.geom.bore;
|
|
191
|
+
var wavelengthScale = spec.geom.wavelengthScale;
|
|
192
|
+
var midY = spec.geom.midY;
|
|
193
|
+
var exitX = spec.geom.exitX;
|
|
194
|
+
var active = options.active !== undefined ? !!options.active : true;
|
|
195
|
+
|
|
196
|
+
var emitterFactory = setupSphereEmitter3D;
|
|
197
|
+
// The corridor part by its kind: Tunnel's own module imports this file (its
|
|
198
|
+
// `Tunnel.pointExitSpec` facade delegates to sceneSpec), so an import back would be a cycle.
|
|
199
|
+
var TunnelCls = kindNamed('Tunnel');
|
|
200
|
+
|
|
201
|
+
// ── (0) THE WORLD — this factory IS the sketch: init FIRST, then parts ──
|
|
202
|
+
// (fast path, 2D, derived from the emitter spec). Part construction ORDER is
|
|
203
|
+
// irrelevant once the world is up.
|
|
204
|
+
emitterFactory.initWorld(universe, spec.emitter);
|
|
205
|
+
|
|
206
|
+
// ── (1) The feeder: a hemisphere emitter JUST INSIDE the input mouth ──
|
|
207
|
+
var emitterResult = emitterFactory(universe, spec.emitter);
|
|
208
|
+
var emitterControl = emitterResult.sphereEmitterControl;
|
|
209
|
+
emitterControl.setActive(active);
|
|
210
|
+
|
|
211
|
+
// ── (2)+(3) The directed corridor WITH the point-exit gate ───────────
|
|
212
|
+
var tunnel = new TunnelCls(universe, spec.tunnel);
|
|
213
|
+
|
|
214
|
+
// The global λ multiplier is a LIVE universe property (consumed fresh each tick),
|
|
215
|
+
// the same knob the self-interference page drives.
|
|
216
|
+
universe.wavelengthScale = wavelengthScale;
|
|
217
|
+
|
|
218
|
+
var control = {
|
|
219
|
+
// λ — LIVE. Effective per-leaf transverse wavelength = (capacity/mass) × scale.
|
|
220
|
+
setWavelengthScale: function (v) {
|
|
221
|
+
var s = Math.max(1, Math.round(v));
|
|
222
|
+
wavelengthScale = s;
|
|
223
|
+
universe.wavelengthScale = s;
|
|
224
|
+
return s;
|
|
225
|
+
},
|
|
226
|
+
getWavelengthScale: function () { return wavelengthScale; },
|
|
227
|
+
getEffectiveWavelength: function () { return Math.floor(CAPACITY_OVER_MASS * wavelengthScale); },
|
|
228
|
+
// The two exit flags — both LIVE (each recompiles + pushes the gate's op-program).
|
|
229
|
+
setExitPhaseReset: function (on) { return tunnel.setExitPhaseReset(on); },
|
|
230
|
+
getExitPhaseReset: function () { return !!tunnel.exitPhaseReset; },
|
|
231
|
+
setExitRecenter: function (on) { return tunnel.setExitRecenter(on); },
|
|
232
|
+
getExitRecenter: function () { return !!tunnel.exitRecenter; },
|
|
233
|
+
// True when the gate currently carries at least one op. With BOTH flags off the
|
|
234
|
+
// gate is a registered but DISARMED collector (no program at all).
|
|
235
|
+
isExitGateArmed: function () { return tunnel.isExitGateArmed(); },
|
|
236
|
+
setActive: function (v) { emitterControl.setActive(!!v); },
|
|
237
|
+
// REBUILD-class — exposed as a getter so the page can show it and re-run setup()
|
|
238
|
+
// with a new value. There is no live setter BY DESIGN: the corridor length is wall
|
|
239
|
+
// geometry, and pretending otherwise would be a lie in the control surface.
|
|
240
|
+
getTunnelLength: function () { return tunnelLength; },
|
|
241
|
+
getBore: function () { return bore; },
|
|
242
|
+
getExitX: function () { return exitX; },
|
|
243
|
+
getMidY: function () { return midY; }
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// The standardized descriptor array — declared ONCE and consumed by both the page's
|
|
247
|
+
// own sliders and any host control strip (the established `controls` idiom).
|
|
248
|
+
control.controls = [
|
|
249
|
+
{
|
|
250
|
+
id: 'wavelengthScale', label: 'λ scale (eff λ = 16 × scale)',
|
|
251
|
+
min: 1, max: 24, step: 1, value: wavelengthScale,
|
|
252
|
+
fmt: function (v) { return String(v) + ' (eff λ ' + Math.floor(CAPACITY_OVER_MASS * v) + 'px)'; },
|
|
253
|
+
get: function () { return control.getWavelengthScale(); },
|
|
254
|
+
onChange: function (v) { control.setWavelengthScale(v); }
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'exitRecenter', label: 'recenter at exit', kind: 'checkbox',
|
|
258
|
+
value: spec.tunnel.exitRecenter,
|
|
259
|
+
fmt: function (v) { return v ? 'on' : 'off'; },
|
|
260
|
+
get: function () { return control.getExitRecenter(); },
|
|
261
|
+
onChange: function (v) { control.setExitRecenter(v); }
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
id: 'exitPhaseReset', label: 'phase reset at exit', kind: 'checkbox',
|
|
265
|
+
value: spec.tunnel.exitPhaseReset,
|
|
266
|
+
fmt: function (v) { return v ? 'on' : 'off'; },
|
|
267
|
+
get: function () { return control.getExitPhaseReset(); },
|
|
268
|
+
onChange: function (v) { control.setExitPhaseReset(v); }
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
// REBUILD-class: the page re-runs setup() on change (restart-in-place).
|
|
272
|
+
id: 'tunnelLength', label: 'tunnel length (rebuild)',
|
|
273
|
+
min: 60, max: 420, step: 20, value: tunnelLength, rebuild: true,
|
|
274
|
+
fmt: function (v) { return String(v) + 'px'; },
|
|
275
|
+
get: function () { return control.getTunnelLength(); },
|
|
276
|
+
onChange: function () { /* the page owns the rebuild — see `rebuild:true` */ }
|
|
277
|
+
}
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
tunnel: tunnel,
|
|
282
|
+
emitterControl: emitterControl,
|
|
283
|
+
pointExitControl: control,
|
|
284
|
+
// FrqtlApp.setup() auto-wires this for rendering.
|
|
285
|
+
largeObjects: tunnel.largeObjects,
|
|
286
|
+
// CANONICAL TEARDOWN — the runner calls this before installing the next
|
|
287
|
+
// setupResult, so a rebuild (the length knob) never stacks emitters or walls.
|
|
288
|
+
dispose: function () {
|
|
289
|
+
if (emitterResult && typeof emitterResult.dispose === 'function') { emitterResult.dispose(); }
|
|
290
|
+
if (tunnel && typeof tunnel.dispose === 'function') { tunnel.dispose(); }
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ONE authority for the per-leaf creation constants the page/readouts quote, and for the
|
|
296
|
+
// SCENE SPEC both surfaces (this factory + the Light Lab rung recipe) consume.
|
|
297
|
+
setupTunnelPointExit.sceneSpec = sceneSpec;
|
|
298
|
+
setupTunnelPointExit.FUNDAMENTAL_DIMENSION = FUNDAMENTAL_DIMENSION;
|
|
299
|
+
setupTunnelPointExit.LEAF_WAVELENGTH_ARG = LEAF_WAVELENGTH_ARG;
|
|
300
|
+
setupTunnelPointExit.CAPACITY_OVER_MASS = CAPACITY_OVER_MASS;
|
|
301
|
+
|
|
302
|
+
setupTunnelPointExit.OPTIONS = OPTIONS;
|
|
303
|
+
setupTunnelPointExit.SCENE_SPEC_OPTIONS = SCENE_SPEC_OPTIONS;
|
|
304
|
+
|
|
305
|
+
export { setupTunnelPointExit };
|
|
306
|
+
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* setupWaveInterference — Engine-agnostic wave interference experiment factory.
|
|
5
|
+
* Two PointSources separated by 2*wavelength, emitting radially.
|
|
6
|
+
*
|
|
7
|
+
* Engine-agnostic: works with both the JS reference engine and the Rust/WASM engine.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { assertSetupOptions as _assertSetupOptions, requireWorldRect as _requireWorldRect } from './_setupOptions.js';
|
|
11
|
+
import { PointSource } from '../../parts/PointSource.js';
|
|
12
|
+
import { LargeObject } from '../../parts/LargeObject.js';
|
|
13
|
+
import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
14
|
+
|
|
15
|
+
// The ONE unknown-option-key validator (objects/setup/_setupOptions.js). Resolved
|
|
16
|
+
// LAZILY in the browser: the game layer defines window.assertSetupOptions before any
|
|
17
|
+
// page script runs, but reading it at eval time would bake in a load-order dependency.
|
|
18
|
+
|
|
19
|
+
// ── Imports ─────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
// ── Setup Factory ───────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Create a wave interference experiment with two radial point sources.
|
|
25
|
+
*
|
|
26
|
+
* Builds two {@link PointSource} emitters separated by `sourceSeparation`, each
|
|
27
|
+
* firing 1px/tick leaf Frames radially, plus two visual-only source circles.
|
|
28
|
+
* The fringe pattern is EMERGENT — no wave equation; it appears where the two
|
|
29
|
+
* particle streams overlap. (Figure + the knob sweep:
|
|
30
|
+
* `docs/internal/records/physics-lab/2026-06-16-wave-interference-wavelengthScale.md`.)
|
|
31
|
+
*
|
|
32
|
+
* Two independent "wavelength" knobs — keep them distinct:
|
|
33
|
+
* - `options.fundamentalWaveLength` → each emitter's `opts.wavelength` → particle
|
|
34
|
+
* MASS (`mass = capacity / wavelength`); 32 gives leaf mass ≈ 1 (clean fringes,
|
|
35
|
+
* matches WASM). It also sets the default source geometry below.
|
|
36
|
+
* - `options.wavelengthScale` → the universe-wide scale in
|
|
37
|
+
* `wavelength = floor((capacity/mass) × WAVELENGTH_CONSTANT)`. Raising it WIDENS
|
|
38
|
+
* the fringes (the live slider on the experiment page drives this).
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} universe - The engine universe instance (JS or WASM), not yet init'd.
|
|
41
|
+
* @param {Object} [options] - Configuration overrides.
|
|
42
|
+
* @param {number} [options.fundamentalDimension=2] - Fundamental (leaf) dimension layer.
|
|
43
|
+
* @param {number} [options.fundamentalWaveLength=32] - Per-emitter wavelength → particle
|
|
44
|
+
* mass; also the default for source separation (2×) and diameter (1×).
|
|
45
|
+
* @param {number} [options.wavelengthScale=8] - Universe-wide fringe-spacing scale
|
|
46
|
+
* (see above); higher = wider fringes.
|
|
47
|
+
* @param {number} [options.velocity=50000] - Default quantum-limit source (NOT particle
|
|
48
|
+
* speed; speed is fixed at 1px/tick by the Universal Speed Limit invariant).
|
|
49
|
+
* @param {number} [options.sourceSeparation] - Distance between the two sources (physics
|
|
50
|
+
* pixels; default 2×wavelength).
|
|
51
|
+
* @param {number} [options.sourceDiameter] - Source circle diameter (default = wavelength).
|
|
52
|
+
* @param {number} [options.quantumLimit] - Max quanta emitted per angle (default: velocity);
|
|
53
|
+
* the steady-state density ceiling.
|
|
54
|
+
* @param {number} [options.depth] - WS-DSE-3D Bite B0: total z-extent for a 3D run. OMITTED
|
|
55
|
+
* (default) → today's 2D behavior, byte-identical (no `activeAxes`/z key touched at all).
|
|
56
|
+
* When provided: `universe.init()` gains `activeAxes:{x:true,y:true,z:true}` (mirrors
|
|
57
|
+
* `setupParticleWalk3D`) and BOTH PointSources emit with a uniform z-spread centered on
|
|
58
|
+
* z=0 (`zMin=-depth/2, zDepth=depth`) — every z-slice sees the same radial illumination,
|
|
59
|
+
* so the emergent fringe pattern is genuinely 3D rather than a dark volume around one
|
|
60
|
+
* lit plane (design §2.3/§4.4). This is the SAME emitter option `SlitLightSource`
|
|
61
|
+
* consumes (WS-DSE-3D Bite B1) — the z-spread lives in `PointSource`/
|
|
62
|
+
* `emitterProgram.js`, not here, precisely so both experiments share it.
|
|
63
|
+
* @returns {Object} `{ sources:{source1,source2}, source1Circle, source2Circle,
|
|
64
|
+
* fundamentalDimension, desiredWaveLength, largeObjects }`.
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* OPTIONS — this factory's own option vocabulary: EXACTLY the keys it reads.
|
|
68
|
+
* Exported as `setupWaveInterference.OPTIONS` so a composing caller consumes it instead of
|
|
69
|
+
* retyping it. `setup-factory-option-vocabulary.test.mjs` re-derives the
|
|
70
|
+
* read-set by static trace and fails if this list drifts from the code.
|
|
71
|
+
*/
|
|
72
|
+
var OPTIONS = [
|
|
73
|
+
'depth', 'fundamentalDimension', 'fundamentalWaveLength', 'quantumLimit',
|
|
74
|
+
'sourceDiameter', 'sourceSeparation', 'velocity', 'wavelengthScale',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
function setupWaveInterference(universe, options) {
|
|
78
|
+
options = _assertSetupOptions('setupWaveInterference', options, OPTIONS);
|
|
79
|
+
|
|
80
|
+
// The world rect the co-init call wrote (EGPTUniverse.create({rect})) — the ONE size.
|
|
81
|
+
var canvas_rect = universe.universe_rect;
|
|
82
|
+
_requireWorldRect('setupWaveInterference universe.universe_rect', canvas_rect);
|
|
83
|
+
|
|
84
|
+
var fundamentalDimension = options.fundamentalDimension !== undefined ? options.fundamentalDimension : 2;
|
|
85
|
+
var desiredWaveLength = options.fundamentalWaveLength !== undefined ? options.fundamentalWaveLength : 32;
|
|
86
|
+
var wavelengthScale = options.wavelengthScale !== undefined ? options.wavelengthScale : 8;
|
|
87
|
+
// WS-DSE-3D Bite B0: opt-in 3D depth. undefined/null → 2D, byte-identical.
|
|
88
|
+
var depth = (options.depth !== undefined && options.depth !== null) ? options.depth : null;
|
|
89
|
+
|
|
90
|
+
// Init universe: ground-up, brownianMotion:false (the wave-walk). activeAxes is ONLY
|
|
91
|
+
// attached when a
|
|
92
|
+
// 3D depth was requested — omission keeps the exact init() call 2D callers already get.
|
|
93
|
+
var initOpts = { wavelengthScale: wavelengthScale, brownianMotion: false };
|
|
94
|
+
if (depth !== null) {
|
|
95
|
+
initOpts.activeAxes = { x: true, y: true, z: true };
|
|
96
|
+
}
|
|
97
|
+
universe.init(fundamentalDimension, initOpts);
|
|
98
|
+
|
|
99
|
+
// Centered uniform z-spread — the caller (this factory) decides centering;
|
|
100
|
+
// PointSource itself just forwards zMin/zDepth (see PointSource.js opts doc).
|
|
101
|
+
var zMin = depth !== null ? -depth / 2 : null;
|
|
102
|
+
var zDepth = depth !== null ? depth : null;
|
|
103
|
+
|
|
104
|
+
var light_source_center_y = Math.floor(canvas_rect.h / 2);
|
|
105
|
+
var light_source_center_x = Math.floor(canvas_rect.w / 2);
|
|
106
|
+
|
|
107
|
+
var sourceSeparation = options.sourceSeparation !== undefined ? options.sourceSeparation : 2 * desiredWaveLength;
|
|
108
|
+
var sourceOffset = sourceSeparation / 2;
|
|
109
|
+
var lightSourceDiameter = options.sourceDiameter !== undefined
|
|
110
|
+
? options.sourceDiameter
|
|
111
|
+
: desiredWaveLength;
|
|
112
|
+
|
|
113
|
+
var velocity = options.velocity !== undefined ? options.velocity : 50000;
|
|
114
|
+
var quantumLimit = options.quantumLimit !== undefined ? options.quantumLimit : velocity;
|
|
115
|
+
var charge = null; // random +/- per particle
|
|
116
|
+
|
|
117
|
+
var center_x1 = light_source_center_x - sourceOffset;
|
|
118
|
+
var center_x2 = light_source_center_x + sourceOffset;
|
|
119
|
+
var sourceRadius = lightSourceDiameter / 2;
|
|
120
|
+
|
|
121
|
+
// Create two radial point sources
|
|
122
|
+
var pointSource1 = new PointSource(
|
|
123
|
+
universe,
|
|
124
|
+
center_x1, light_source_center_y,
|
|
125
|
+
lightSourceDiameter,
|
|
126
|
+
sourceRadius, // speed = radius (matches WASM adapter velocity pattern)
|
|
127
|
+
charge,
|
|
128
|
+
true, // auto_fire
|
|
129
|
+
quantumLimit, // quantum_limit per angle
|
|
130
|
+
0, // delay
|
|
131
|
+
5, // angle_increment = 5 degrees
|
|
132
|
+
{ wavelength: desiredWaveLength, zMin: zMin, zDepth: zDepth } // JS leaf mass = capacity/wavelength → 1 (matches WASM; clean fringes)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
var pointSource2 = new PointSource(
|
|
136
|
+
universe,
|
|
137
|
+
center_x2, light_source_center_y,
|
|
138
|
+
lightSourceDiameter,
|
|
139
|
+
sourceRadius,
|
|
140
|
+
charge,
|
|
141
|
+
true,
|
|
142
|
+
quantumLimit,
|
|
143
|
+
0,
|
|
144
|
+
5,
|
|
145
|
+
{ wavelength: desiredWaveLength, zMin: zMin, zDepth: zDepth } // JS leaf mass = capacity/wavelength → 1 (matches WASM; clean fringes)
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Visual-only circles for the source bodies (black, no collision)
|
|
149
|
+
var source1Circle = new LargeObject(
|
|
150
|
+
universe,
|
|
151
|
+
[center_x1 - sourceRadius, light_source_center_y - sourceRadius, lightSourceDiameter, lightSourceDiameter],
|
|
152
|
+
CollisionTypes.NONE, [], 'source1', 'closest', null, 1, [0, 0, 0]
|
|
153
|
+
);
|
|
154
|
+
source1Circle.drawAsCircle = true;
|
|
155
|
+
|
|
156
|
+
var source2Circle = new LargeObject(
|
|
157
|
+
universe,
|
|
158
|
+
[center_x2 - sourceRadius, light_source_center_y - sourceRadius, lightSourceDiameter, lightSourceDiameter],
|
|
159
|
+
CollisionTypes.NONE, [], 'source2', 'closest', null, 1, [0, 0, 0]
|
|
160
|
+
);
|
|
161
|
+
source2Circle.drawAsCircle = true;
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
sources: { source1: pointSource1, source2: pointSource2 },
|
|
165
|
+
source1Circle: source1Circle,
|
|
166
|
+
source2Circle: source2Circle,
|
|
167
|
+
fundamentalDimension: fundamentalDimension,
|
|
168
|
+
desiredWaveLength: desiredWaveLength,
|
|
169
|
+
largeObjects: [source1Circle, source2Circle]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
setupWaveInterference.OPTIONS = OPTIONS;
|
|
176
|
+
|
|
177
|
+
export { setupWaveInterference };
|
|
178
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* v1-replacements.js — the V1 → V2 port map: every removed V1 kind names its V2 composition, so an
|
|
5
|
+
* unknown-kind throw TELLS the author what to build instead of leaving them to guess. A LEAF (no
|
|
6
|
+
* imports): ObjectFactory reads it for its unknown-kind refusals, SlitLightSource for its own, and
|
|
7
|
+
* the sketch rule (lib/frqtl/scenes/test/_sketch-rule.mjs) reads the names from here.
|
|
8
|
+
* Rulings: docs/internal/engine/V2_PARTS_CLASSIFICATION.md §4.3/§4.4.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const V1_REPLACEMENTS = Object.freeze({
|
|
12
|
+
DetectorWall: "Wall{ collisionBehavior:'destroy', hooks:['count'] } (preset 'detector-screen')",
|
|
13
|
+
SlitScreen: "Wall{ collisionBehavior:'destroy' } slabs + Tunnel{ mode:'directed', exitCollapse }",
|
|
14
|
+
DSEWall: "Wall{ collisionBehavior:'destroy' } slabs + one Tunnel{ mode:'directed' } per passage",
|
|
15
|
+
SlitLightSource: "SphereEmitter{ preset:'laser' }",
|
|
16
|
+
QuantumEmitter: "SphereEmitter",
|
|
17
|
+
PointSource: "SphereEmitter{ preset:'drum' | 'spherical' }",
|
|
18
|
+
DirectedTunnel: "Tunnel{ mode:'directed' }",
|
|
19
|
+
LightSource: "SphereEmitter{ preset:'hemisphere' | 'laser' } + Tunnel",
|
|
20
|
+
Tube: "Tunnel (with Wall end caps)",
|
|
21
|
+
ChamberEmitter: "SphereEmitter + Tunnel (with Wall end caps)",
|
|
22
|
+
Gun: "Tunnel + SphereEmitter, composed at the sketch level",
|
|
23
|
+
Bullet: "Wall{ collisionBehavior:'bounce' } moved by a sketch tick-fn through setRect",
|
|
24
|
+
Box: "four Wall{ collisionBehavior:'block' } slabs",
|
|
25
|
+
OvenBox: "Wall{ collisionBehavior:'absorb' } + SphereEmitter{ emissionCap, gateCounter }",
|
|
26
|
+
CircuitGate: "LogicGate (the stock V2 gate: Wall{'destroy',['count']} input ammeters + Tunnel{'mirror'} channel + SphereEmitter rail + Wall{'destroy'} shunt valve)",
|
|
27
|
+
CircuitProbe: "Wall preset 'transparent-probe'",
|
|
28
|
+
LeafField: "(archived — no V2 successor)",
|
|
29
|
+
EgptFractal: "(archived — no V2 successor)"
|
|
30
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* package-core.js — the members BOTH package entries carry beside the parts barrel and their own host
|
|
5
|
+
* door (frqtl/boot-entry.js for @descix/frqtl-web, frqtl/node-entry.js for @descix/frqtl-node): the
|
|
6
|
+
* engine accessors, the capture contracts and the renderer contract's validator. One module, so the two
|
|
7
|
+
* packages' `.` cannot disagree on them.
|
|
8
|
+
*
|
|
9
|
+
* engine, engineState the booted EngineHandle / the boot state (compat/engine.js)
|
|
10
|
+
* FrqtlCapture the render-pipeline tap (objects/instruments/capture.js)
|
|
11
|
+
* RenderStages, FieldBuilder the two contracts the instrument reads
|
|
12
|
+
* CaptureEnvelope the transport contract a recording is validated against
|
|
13
|
+
* validateRendererProvider, RENDERER_PROVIDER_REQUIRED, RENDERER_PROVIDER_OPTIONAL
|
|
14
|
+
* a BYO renderer's design aid (reports, never throws)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export { engine, engineState } from './compat/engine.js';
|
|
18
|
+
export { FrqtlCapture } from './objects/instruments/capture.js';
|
|
19
|
+
export * as RenderStages from '../shim/render/render-stages.js';
|
|
20
|
+
export { FieldBuilder } from './compat/renderer/FieldBuilder.js';
|
|
21
|
+
export * as CaptureEnvelope from '../shim/render/capture-envelope.js';
|
|
22
|
+
export { validateRendererProvider, RENDERER_PROVIDER_REQUIRED, RENDERER_PROVIDER_OPTIONAL }
|
|
23
|
+
from './compat/renderer/renderer-provider-contract.js';
|