@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,354 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LightSource — thin wrapper over DirectedTunnel (T2, 2026-07-13,
|
|
5
|
+
* "the laser story — nearly free"; refactored I2, 2026-07-13, onto the
|
|
6
|
+
* I1-PROVEN tunnel semantics — the DSE-scale sugar-scale bore replica).
|
|
7
|
+
*
|
|
8
|
+
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* A LightSource is a `DirectedTunnel` (never re-derived — REPLICA DISCIPLINE:
|
|
11
|
+
* zero LightSource-special tunnel behavior, the stock object unchanged) PLUS one
|
|
12
|
+
* of two source mechanisms populating its bore — mirroring the classic
|
|
13
|
+
* side-by-side `this.bulletHandle` (hand-placed) / `this.emitter` (auto-wired)
|
|
14
|
+
* duality:
|
|
15
|
+
*
|
|
16
|
+
* - `options.source = 'particleWalk'` (DEFAULT, I2 — the CEO's PRIMARY
|
|
17
|
+
* REFERENCE source) — one or more HAND-PLACED leaf frames in the bore (the
|
|
18
|
+
* hand-placed-projectile pattern, generalized to
|
|
19
|
+
* `options.particleCount` quanta — I1's own scale-sweep validated technique
|
|
20
|
+
* for building a stochastic ensemble from IDENTICAL start conditions:
|
|
21
|
+
* `docs/internal/physics-lab/directed-tunnel-scale-sweep/sweep.cjs` places its whole
|
|
22
|
+
* N-quantum population at ONE shared entrance pixel per seed and still
|
|
23
|
+
* measures genuine per-quantum variance at exit — each frame's OWN
|
|
24
|
+
* move-gate/wavelength-oscillator RNG draws diverge it from its siblings
|
|
25
|
+
* over time, Injection-time same-cell overlap does not merge them). Velocity
|
|
26
|
+
* derived from `orientation`+`speed` exactly like the emitter case,
|
|
27
|
+
* positioned at the bore's centreline just inside the breech (or the bore's
|
|
28
|
+
* geometric centre when `capNegX` is false — no breech to anchor to). No
|
|
29
|
+
* `QuantumEmitter`, no continuous firing — a SNAPSHOT placement (all
|
|
30
|
+
* `particleCount` quanta exist from tick 1), matching `particle-walk`-family
|
|
31
|
+
* experiments' own "hand-placed, not emitted" convention.
|
|
32
|
+
* - `options.source = 'emitter'` — the tunnel's own `options.emitter` (Tube's
|
|
33
|
+
* opt-in auto-wired `QuantumEmitter` at the breech, DirectedTunnel's
|
|
34
|
+
* `orientation`-derived `vx/vy`). Requires `capNegX` (defaults to `true` for
|
|
35
|
+
* THIS source type — a LightSource with no wall to emit from is meaningless).
|
|
36
|
+
*
|
|
37
|
+
* **I2 default flip (CEO ruling, directed-tunnel composition model §3 row 2):
|
|
38
|
+
* `options.source` now DEFAULTS to `'particleWalk'`** (was `'emitter'` in T2) —
|
|
39
|
+
* "the PRIMARY reference source." The one production caller
|
|
40
|
+
* (the DSE-lab tunnels) always passes `source:'emitter'` explicitly, so this
|
|
41
|
+
* default flip is byte-identical for every existing caller in the tree; it only
|
|
42
|
+
* changes behavior for a hypothetical caller that OMITTED `source` entirely (none
|
|
43
|
+
* exist yet). `docs/internal/physics-lab/directed-tunnel-scale-sweep/README.md`'s companion
|
|
44
|
+
* source-invariance harness (I2) quantifies particleWalk vs emitter exit-ensemble
|
|
45
|
+
* equivalence — the empirical justification for treating them as interchangeable
|
|
46
|
+
* defaults at all.
|
|
47
|
+
*
|
|
48
|
+
* **DSE-scale sugar defaults (I2, directed-tunnel composition model §2.3: "the
|
|
49
|
+
* Gun Chamber object set, scaled by sugar params — DSE example ~8px bore ×
|
|
50
|
+
* ~16px length"):** `options.tunnel.width`/`options.tunnel.length` now default to
|
|
51
|
+
* `8`/`16` when the caller omits them (previously REQUIRED — `DirectedTunnel`
|
|
52
|
+
* itself still throws on non-positive values, so a caller could not omit them
|
|
53
|
+
* before this bite). `left`/`top`/`z` remain caller-required (position is always
|
|
54
|
+
* scene-specific) — DIRECTLY passed through, byte-identical.
|
|
55
|
+
*
|
|
56
|
+
* `options.polarization` ('random'|'positive'|'negative', default 'random') —
|
|
57
|
+
* "lasers are just light sources which are polarized and have smaller widths"
|
|
58
|
+
* (CEO). Maps DIRECTLY onto the SAME charge vocabulary `PointSource`/
|
|
59
|
+
* `QuantumEmitter`'s own `chargeMode` already use — no new charge concept:
|
|
60
|
+
* 'random' -> null (charge draw per particle — the DEFAULT, matches every
|
|
61
|
+
* existing DSE/WI emitter's un-polarized behavior)
|
|
62
|
+
* 'positive' -> 0 ('POSITIVE'), 'negative' -> 1 ('NEGATIVE') (a FIXED charge —
|
|
63
|
+
* every emitted/placed quantum gets the SAME charge: a polarized beam).
|
|
64
|
+
*
|
|
65
|
+
* **`LightSource.laser(universe, options)`** — the Laser preset/factory (CEO:
|
|
66
|
+
* "a Laser is a LightSource preset... not a separate class" — report §4.4 point
|
|
67
|
+
* 3: "A thin Laser wrapper is legitimately separate... mirroring
|
|
68
|
+
* the fx-* factories layering thin convenience over
|
|
69
|
+
* Tube.js"). A STATIC FACTORY, not a subclass: pre-fills `polarization` (default
|
|
70
|
+
* `'positive'` — a FIXED, not random, charge: the report's own "collimated,
|
|
71
|
+
* phase-locked beam... every emitted quantum gets identical vx,vy" framing
|
|
72
|
+
* extends naturally to "and identical charge") and a NARROWER default `width`
|
|
73
|
+
* (report §4.3: "A tunnel + a directed emitter is a collimated, phase-locked
|
|
74
|
+
* beam with zero new physics" — narrowness is a geometry convenience, not a new
|
|
75
|
+
* mechanism), then constructs a plain `LightSource`. Every field is still
|
|
76
|
+
* caller-overridable (`LightSource.laser(universe, {width: 40, polarization:
|
|
77
|
+
* 'random', ...})` is a legal, narrower-but-unpolarized "laser").
|
|
78
|
+
*
|
|
79
|
+
* @param {Object} universe - Already-init'd universe (JS or WASM).
|
|
80
|
+
* @param {Object} options
|
|
81
|
+
* @param {'particleWalk'|'emitter'} [options.source='particleWalk'] - see above
|
|
82
|
+
* (I2: default flipped from 'emitter').
|
|
83
|
+
* @param {'random'|'positive'|'negative'} [options.polarization='random'] - see above.
|
|
84
|
+
* @param {number} [options.speed=1] - initial speed magnitude (both source types);
|
|
85
|
+
* direction is ALWAYS `options.tunnel.orientation` (see `DirectedTunnel`).
|
|
86
|
+
* @param {number} [options.particleCount=1] - `options.source==='particleWalk'`
|
|
87
|
+
* only: how many hand-placed quanta to snapshot-place (I2 generalization from
|
|
88
|
+
* T2's fixed "exactly one"; `1` is byte-identical to T2's behavior).
|
|
89
|
+
* @param {Object} [options.particle] - `options.source==='particleWalk'` only:
|
|
90
|
+
* `{wavelength, mass, level}` (all optional — `makeLeafFrame` defaults apply).
|
|
91
|
+
* @param {Object} [options.tunnel={}] - passed straight through to
|
|
92
|
+
* `DirectedTunnel`'s own constructor (`left,top,z,length,width,depth,
|
|
93
|
+
* orientation,behavior,resetLocalTime,redirectSpread,redirectSpeed,capNegX,
|
|
94
|
+
* capPosX,wallThickness,color,wallOpacity,sinkOpenEnds,collectCollided`) —
|
|
95
|
+
* `options.emitter`/`options.tunnel.emitter` is OWNED by this file (derived
|
|
96
|
+
* from `source`+`polarization`+`speed`), never set directly by the caller.
|
|
97
|
+
* `length`/`width` DEFAULT to 16/8 (I2 DSE-scale sugar default) when omitted.
|
|
98
|
+
*
|
|
99
|
+
* @property {Object} tunnel - the underlying `DirectedTunnel` instance (a STOCK
|
|
100
|
+
* instance — replica discipline; LightSource never subclasses or monkeypatches it).
|
|
101
|
+
* @property {Array<LargeObject>} walls - alias for `this.tunnel.walls`.
|
|
102
|
+
* @property {Object|null} emitter - alias for `this.tunnel.emitter` (`source:
|
|
103
|
+
* 'emitter'` only; `null` for `'particleWalk'`).
|
|
104
|
+
* @property {Array<number>} particleHandles - the hand-placed leaves' FrameIds
|
|
105
|
+
* (`source:'particleWalk'` only; `[]` for `'emitter'`). Length ===
|
|
106
|
+
* `options.particleCount`.
|
|
107
|
+
* @property {Array<number>} particleQids - `particleHandles`' quantum_ids
|
|
108
|
+
* (writable via `setFrameProperty`; `[]` for `'emitter'`).
|
|
109
|
+
* @property {number|null} particleHandle - DEPRECATED alias for
|
|
110
|
+
* `particleHandles[0]` (T2 single-quantum API, kept for byte-identical reads
|
|
111
|
+
* when `particleCount` is omitted/1); `null` for `'emitter'` or an empty count.
|
|
112
|
+
* @property {number|null} particleQid - DEPRECATED alias for `particleQids[0]`
|
|
113
|
+
* (see above).
|
|
114
|
+
* @property {Function} applyRedirect - alias for `this.tunnel.applyRedirect`,
|
|
115
|
+
* ALWAYS present (C1 fix, 2026-07-13, ruling_directed_tunnel_lightsource.md)
|
|
116
|
+
* — prefer `this.tunnel.applyRedirectIfActive(tickData)` over calling this
|
|
117
|
+
* directly (self-gates on the tunnel's current `behavior`).
|
|
118
|
+
*
|
|
119
|
+
* ── Layer 2: physics behavior ────────────────────────────────────────────────
|
|
120
|
+
*
|
|
121
|
+
* LightSource adds ZERO new physics (canonical-bench SU1): it is a `DirectedTunnel`
|
|
122
|
+
* (itself a `Tube` — zero new physics, per Tube's own docstring) plus either
|
|
123
|
+
* `QuantumEmitter` (existing engine-agnostic sugar) or one-or-more hand-placed
|
|
124
|
+
* leaves (the SAME `makeLeafFrame`/`injectFrame`/`setFrameProperty` primitives
|
|
125
|
+
* every particle-walk/Gun-bullet experiment already uses, looped). The laser's
|
|
126
|
+
* "collimated, phase-locked beam" IS free (report §4.3): skipping the
|
|
127
|
+
* random-direction draw means every quantum gets IDENTICAL `vx,vy`, and a fresh
|
|
128
|
+
* emission is already phase-locked at `local_time=0` (theorist-established) — no
|
|
129
|
+
* new mechanism. Source-invariance (I2, `docs/internal/physics-lab/directed-tunnel-scale-
|
|
130
|
+
* sweep/source-invariance.cjs`): particleWalk and emitter sourcing at IDENTICAL
|
|
131
|
+
* tunnel params produce statistically comparable exit-face ensembles (see that
|
|
132
|
+
* script's README section for the measured numbers and the metric used).
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
import { DirectedTunnel } from './DirectedTunnel.js';
|
|
136
|
+
import * as SustainedSource from './sustainedSource.js';
|
|
137
|
+
|
|
138
|
+
// ── Imports ─────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
var VALID_SOURCES = ['emitter', 'particleWalk'];
|
|
141
|
+
var VALID_POLARIZATIONS = ['random', 'positive', 'negative'];
|
|
142
|
+
|
|
143
|
+
function _polarizationToCharge(polarization) {
|
|
144
|
+
if (polarization === 'positive') { return 0; }
|
|
145
|
+
if (polarization === 'negative') { return 1; }
|
|
146
|
+
return null; // 'random'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @param {Object} universe
|
|
151
|
+
* @param {Object} options - see module docstring above.
|
|
152
|
+
*/
|
|
153
|
+
function LightSource(universe, options) {
|
|
154
|
+
options = options || {};
|
|
155
|
+
|
|
156
|
+
this.universe = universe;
|
|
157
|
+
this._schemaKind = 'LightSource';
|
|
158
|
+
|
|
159
|
+
// I2 default flip (directed-tunnel composition model §3 row 2, CEO
|
|
160
|
+
// ruling): 'particleWalk' is now the DEFAULT ("the PRIMARY reference
|
|
161
|
+
// source") — was 'emitter' in T2. The one production caller
|
|
162
|
+
// (the DSE-lab tunnels) always passes source:'emitter' explicitly, so
|
|
163
|
+
// this is byte-identical for every existing caller in the tree.
|
|
164
|
+
var source = options.source !== undefined ? options.source : 'particleWalk';
|
|
165
|
+
if (VALID_SOURCES.indexOf(source) === -1) {
|
|
166
|
+
throw new Error('[LightSource] unknown options.source "' + source + '". Expected: ' + VALID_SOURCES.join(' | '));
|
|
167
|
+
}
|
|
168
|
+
var polarization = options.polarization !== undefined ? options.polarization : 'random';
|
|
169
|
+
if (VALID_POLARIZATIONS.indexOf(polarization) === -1) {
|
|
170
|
+
throw new Error('[LightSource] unknown options.polarization "' + polarization + '". Expected: ' + VALID_POLARIZATIONS.join(' | '));
|
|
171
|
+
}
|
|
172
|
+
var speed = options.speed !== undefined ? options.speed : 1;
|
|
173
|
+
var particleCount = options.particleCount !== undefined ? options.particleCount : 1;
|
|
174
|
+
if (!(particleCount >= 1) || Math.floor(particleCount) !== particleCount) {
|
|
175
|
+
throw new Error('[LightSource] options.particleCount must be a positive integer (got ' + particleCount + ').');
|
|
176
|
+
}
|
|
177
|
+
this.source = source;
|
|
178
|
+
this.polarization = polarization;
|
|
179
|
+
|
|
180
|
+
var tunnelOpts = {};
|
|
181
|
+
var passthroughKeys = [
|
|
182
|
+
'left', 'top', 'z', 'length', 'width', 'depth', 'orientation', 'behavior',
|
|
183
|
+
'resetLocalTime', 'redirectSpread', 'redirectSpeed', 'capNegX', 'capPosX',
|
|
184
|
+
'wallThickness', 'color', 'wallOpacity', 'sinkOpenEnds', 'collectCollided',
|
|
185
|
+
];
|
|
186
|
+
var srcTunnelOpts = options.tunnel || {};
|
|
187
|
+
for (var i = 0; i < passthroughKeys.length; i++) {
|
|
188
|
+
var key = passthroughKeys[i];
|
|
189
|
+
if (srcTunnelOpts[key] !== undefined) { tunnelOpts[key] = srcTunnelOpts[key]; }
|
|
190
|
+
}
|
|
191
|
+
// I2 DSE-scale sugar defaults (directed-tunnel composition model §2.3):
|
|
192
|
+
// "the Gun Chamber object set, scaled by sugar params — DSE example ~8px
|
|
193
|
+
// bore x ~16px length". T2 required length/width (DirectedTunnel throws
|
|
194
|
+
// on omission); I2 defaults them here so a bare `new LightSource(universe,
|
|
195
|
+
// {left,top})` is legal at the composition's actual sugar scale.
|
|
196
|
+
if (tunnelOpts.length === undefined) { tunnelOpts.length = 16; }
|
|
197
|
+
if (tunnelOpts.width === undefined) { tunnelOpts.width = 8; }
|
|
198
|
+
|
|
199
|
+
var orientationDeg = tunnelOpts.orientation !== undefined ? tunnelOpts.orientation : 0;
|
|
200
|
+
var orientationRad = orientationDeg * Math.PI / 180;
|
|
201
|
+
var charge = _polarizationToCharge(polarization);
|
|
202
|
+
|
|
203
|
+
this.emitter = null;
|
|
204
|
+
this.particleHandles = [];
|
|
205
|
+
this.particleQids = [];
|
|
206
|
+
this.particleHandle = null; // deprecated T2 single-quantum alias (see docstring)
|
|
207
|
+
this.particleQid = null;
|
|
208
|
+
|
|
209
|
+
if (source === 'emitter') {
|
|
210
|
+
// A LightSource emitter needs a breech to emit from — default capNegX:true
|
|
211
|
+
// for THIS source type (byte-identical is not possible here since the
|
|
212
|
+
// caller is EXPLICITLY asking for an emitter; a LightSource with no wall to
|
|
213
|
+
// emit from is a caller error class, matched by DirectedTunnel/Tube's own
|
|
214
|
+
// FAIL-LOUD for options.emitter without capNegX).
|
|
215
|
+
if (tunnelOpts.capNegX === undefined) { tunnelOpts.capNegX = true; }
|
|
216
|
+
tunnelOpts.emitter = Object.assign({ speed: speed, charge: charge }, options.emitterSpec || {});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
this.tunnel = new DirectedTunnel(universe, tunnelOpts);
|
|
220
|
+
this.walls = this.tunnel.walls;
|
|
221
|
+
this.sinks = this.tunnel.sinks;
|
|
222
|
+
this.innerRect = this.tunnel.innerRect;
|
|
223
|
+
this.largeObjects = this.tunnel.largeObjects;
|
|
224
|
+
this.applyRedirect = this.tunnel.applyRedirect;
|
|
225
|
+
|
|
226
|
+
this.sustainedSource = null;
|
|
227
|
+
|
|
228
|
+
if (source === 'emitter') {
|
|
229
|
+
this.emitter = this.tunnel.emitter;
|
|
230
|
+
} else {
|
|
231
|
+
// 'particleWalk' — one or more hand-placed leaf frames (the classic
|
|
232
|
+
// pattern, generalized I2), positioned at the bore's Y (and Z, when
|
|
233
|
+
// zContain) centreline, just inside the breech when capped (mirrors
|
|
234
|
+
// the emitter's own aperture placement), or at the bore's geometric
|
|
235
|
+
// X-centre when open on both ends (no breech to anchor to). ALL
|
|
236
|
+
// `particleCount` quanta share the SAME entrance pixel — the I1
|
|
237
|
+
// scale-sweep's own validated ensemble-building technique
|
|
238
|
+
// (docs/internal/physics-lab/directed-tunnel-scale-sweep/sweep.cjs): a shared
|
|
239
|
+
// injection-time position does not merge same-tick frames, and each
|
|
240
|
+
// leaf's OWN move-gate/wavelength-oscillator RNG draws diverge it
|
|
241
|
+
// from its siblings over subsequent ticks — a genuine stochastic
|
|
242
|
+
// ensemble from an identical start, not degenerate.
|
|
243
|
+
//
|
|
244
|
+
// ws-dse-3d (CEO ruling 2026-07-14, "one source surface"): this burst
|
|
245
|
+
// now routes through `sustainedSource.js`'s ONE `placeSourceQuanta`
|
|
246
|
+
// primitive — the SAME entrance-formula/velocity-draw/z-centring code
|
|
247
|
+
// `setupTunnelLab.js`'s and `setupLightSourceLab.js`'s own independently
|
|
248
|
+
// hand-rolled injectors used to duplicate. `velocityProfile:'beam'` +
|
|
249
|
+
// `entranceBias:'center'` reproduce this constructor's PRE-EXISTING
|
|
250
|
+
// formula EXACTLY (cos/sin(orientationRad)*speed, inner.top+inner.h/2) —
|
|
251
|
+
// byte-identical for every existing caller (DSE Lab, this file's own
|
|
252
|
+
// tests).
|
|
253
|
+
var particleSpec = options.particle || {};
|
|
254
|
+
var hasRealDepth = (tunnelOpts.depth !== undefined && tunnelOpts.depth !== null);
|
|
255
|
+
var burstSpec = SustainedSource.normalizeSourceSpec({
|
|
256
|
+
wavelength: particleSpec.wavelength !== undefined ? particleSpec.wavelength : null,
|
|
257
|
+
charge: charge,
|
|
258
|
+
velocityProfile: 'beam',
|
|
259
|
+
speed: speed,
|
|
260
|
+
orientationRad: orientationRad,
|
|
261
|
+
entranceBias: 'center',
|
|
262
|
+
level: particleSpec.level !== undefined ? particleSpec.level : null,
|
|
263
|
+
mass: particleSpec.mass,
|
|
264
|
+
});
|
|
265
|
+
var placed = SustainedSource.placeSourceQuanta(universe, this.tunnel.innerRect, !!tunnelOpts.capNegX, hasRealDepth, burstSpec, particleCount);
|
|
266
|
+
for (var pi = 0; pi < placed.length; pi++) {
|
|
267
|
+
this.particleHandles.push(placed[pi].handle);
|
|
268
|
+
this.particleQids.push(placed[pi].qid);
|
|
269
|
+
}
|
|
270
|
+
// Deprecated T2 single-quantum aliases — byte-identical reads for any
|
|
271
|
+
// caller written against the old ONE-quantum API when particleCount is
|
|
272
|
+
// omitted/1.
|
|
273
|
+
this.particleHandle = this.particleHandles.length ? this.particleHandles[0] : null;
|
|
274
|
+
this.particleQid = this.particleQids.length ? this.particleQids[0] : null;
|
|
275
|
+
|
|
276
|
+
// ws-dse-3d AC1 (CEO ruling, "LightSource itself should gain the
|
|
277
|
+
// sustain capability through the SAME single authority"): OPT-IN —
|
|
278
|
+
// `options.sustain` omitted (default) is byte-identical to every
|
|
279
|
+
// pre-existing caller (a genuine one-shot construction, DSE Lab's own
|
|
280
|
+
// usage untouched). When supplied (truthy — `true` for "use the SAME
|
|
281
|
+
// wavelength/charge/velocity convention as the burst above, just
|
|
282
|
+
// recurring", or a partial spec object to override individual
|
|
283
|
+
// fields), builds ONE recurring `sustainedSource` tick-fn replicating
|
|
284
|
+
// this burst's own entrance/velocity/charge convention forever —
|
|
285
|
+
// the SAME mechanism `setupTunnelLab.js`/`setupLightSourceLab.js` now
|
|
286
|
+
// both delegate to, rather than each hand-rolling their own copy
|
|
287
|
+
// (see `sustainedSource.js`'s module docstring).
|
|
288
|
+
if (options.sustain) {
|
|
289
|
+
var sustainOverrides = (options.sustain === true) ? {} : options.sustain;
|
|
290
|
+
var sustainSpec = Object.assign({}, burstSpec, sustainOverrides);
|
|
291
|
+
this.sustainedSource = SustainedSource.buildSustainedSource(
|
|
292
|
+
universe, this.tunnel.innerRect, !!tunnelOpts.capNegX, hasRealDepth, sustainSpec
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Tear down every disposable resource THIS LightSource registered — the
|
|
300
|
+
* SAME idiom `QuantumEmitter.prototype.dispose` and every setup factory's
|
|
301
|
+
* own `dispose()` already use (tag-then-splice by identity, idempotent).
|
|
302
|
+
* A `source:'particleWalk'` LightSource with no `options.sustain` (the
|
|
303
|
+
* default — every pre-existing caller) has nothing to dispose; calling this
|
|
304
|
+
* is always a safe no-op in that case. Callers building a page-level
|
|
305
|
+
* `setupResult.dispose()` should delegate here rather than re-deriving the
|
|
306
|
+
* teardown for `this.sustainedSource`/`this.emitter` a second time.
|
|
307
|
+
*/
|
|
308
|
+
LightSource.prototype.dispose = function () {
|
|
309
|
+
if (this.sustainedSource) { this.sustainedSource.dispose(); }
|
|
310
|
+
if (this.emitter && typeof this.emitter.dispose === 'function') { this.emitter.dispose(); }
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* The Laser preset/factory (CEO: "a Laser is a LightSource preset ... not a
|
|
315
|
+
* separate class"). Pre-fills a FIXED polarization (default 'positive' — a
|
|
316
|
+
* polarized beam, matching "lasers are ... polarized"), a NARROWER default
|
|
317
|
+
* width (report §4.3 — collimation/geometry convenience, zero new physics),
|
|
318
|
+
* and `source:'emitter'` (I2: a laser is "a collimated, phase-locked BEAM" —
|
|
319
|
+
* an ONGOING stream, not LightSource's own new I2 default of a single
|
|
320
|
+
* snapshot-placed particleWalk quantum; `laser()` keeps T2's original
|
|
321
|
+
* emitter-sourced behavior explicitly rather than silently inheriting
|
|
322
|
+
* LightSource's now-different bare default); every field is still
|
|
323
|
+
* caller-overridable (a `source:'particleWalk'` laser is legal, if unusual).
|
|
324
|
+
*
|
|
325
|
+
* **I2 re-baseline (width default 20 -> 4):** T2's "narrower" framing was
|
|
326
|
+
* relative to the THEN-current world where `width` had no LightSource
|
|
327
|
+
* default at all (callers always supplied one, historically ~40px) — 20px
|
|
328
|
+
* was narrower than that. I2 gave a bare LightSource its OWN DSE-scale
|
|
329
|
+
* default (`width:8`), which made the OLD laser default (20px) WIDER than
|
|
330
|
+
* the new baseline — breaking the "narrower" invariant this preset exists to
|
|
331
|
+
* express. Re-baselined to 4px (narrower than the new 8px DSE-scale
|
|
332
|
+
* default) so "a laser is narrower" stays TRUE, not just documented.
|
|
333
|
+
*
|
|
334
|
+
* @param {Object} universe
|
|
335
|
+
* @param {Object} [options] - SAME shape as `LightSource`'s own options; any
|
|
336
|
+
* field provided here overrides the laser defaults below.
|
|
337
|
+
* @returns {LightSource}
|
|
338
|
+
*/
|
|
339
|
+
LightSource.laser = function (universe, options) {
|
|
340
|
+
options = options || {};
|
|
341
|
+
var tunnelDefaults = { width: 4 };
|
|
342
|
+
var tunnelOpts = Object.assign({}, tunnelDefaults, options.tunnel || {});
|
|
343
|
+
var laserOpts = Object.assign(
|
|
344
|
+
{ polarization: 'positive', source: 'emitter' },
|
|
345
|
+
options,
|
|
346
|
+
{ tunnel: tunnelOpts }
|
|
347
|
+
);
|
|
348
|
+
return new LightSource(universe, laserOpts);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
export { LightSource };
|
|
354
|
+
|