@descix/frqtl-node 0.6.0-p0.1 → 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.
- package/CLAUDE.md +1 -1
- package/LICENSE.md +664 -0
- package/README.md +1 -1
- package/dist/dist-manifest.json +125 -125
- package/dist/fat/pkg-node/egpt_wasm.js +7 -7
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +1 -1
- package/dist/frqtl/compat/providers/wasm-engine.js +29 -31
- package/dist/frqtl/compat/providers/wasm-load-browser.js +2 -2
- package/dist/frqtl/compat/renderer/RenderFamily.js +1 -2
- package/dist/frqtl/compat/renderer/RenderMesh.js +2 -3
- package/dist/frqtl/compat/renderer/RenderShape.js +1 -1
- package/dist/frqtl/compat/renderer/captureStill-common.js +1 -2
- package/dist/frqtl/compat/renderer/display-slot.js +1 -1
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +5 -5
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +1 -1
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +4 -4
- package/dist/frqtl/compat/renderer/providers/three-provider.js +23 -27
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +2 -2
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +1 -1
- package/dist/frqtl/compat/vendor.js +2 -2
- package/dist/frqtl/objects/FrqtlApp.js +22 -24
- package/dist/frqtl/objects/ObjectFactory.js +1 -2
- package/dist/frqtl/objects/constants/WallBehaviors.js +5 -5
- package/dist/frqtl/objects/geometry/SlabFace.js +6 -6
- package/dist/frqtl/objects/instruments/capture-host.js +2 -2
- package/dist/frqtl/objects/instruments/capture.js +4 -5
- package/dist/frqtl/objects/instruments/hosted-eyes.js +3 -4
- package/dist/frqtl/objects/objects/DirectedTunnel.js +10 -11
- package/dist/frqtl/objects/objects/LightSource.js +22 -25
- package/dist/frqtl/objects/objects/QuantumEmitter.js +14 -14
- package/dist/frqtl/objects/objects/Tube.js +14 -14
- package/dist/frqtl/objects/objects/absorbRadiation.js +12 -13
- package/dist/frqtl/objects/objects/actionCompiler.js +52 -52
- package/dist/frqtl/objects/objects/controlAction.js +2 -2
- package/dist/frqtl/objects/objects/controlBind.js +2 -2
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +3 -3
- package/dist/frqtl/objects/objects/emitterProgram.js +9 -9
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +3 -3
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +9 -9
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +2 -2
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +2 -2
- package/dist/frqtl/objects/objects/makeControlsForObject.js +25 -29
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +2 -2
- package/dist/frqtl/objects/objects/slitLinerScatter.js +8 -10
- package/dist/frqtl/objects/objects/sustainedSource.js +15 -16
- package/dist/frqtl/objects/objects/tagger.js +2 -2
- package/dist/frqtl/objects/schema/engine-scopes.js +11 -12
- package/dist/frqtl/objects/schema/index.js +6 -6
- package/dist/frqtl/objects/schema/scopes.js +10 -10
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +1 -1
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +1 -1
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +3 -3
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +7 -8
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +20 -21
- package/dist/frqtl/objects/setup/setupFxSmoke.js +1 -1
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +23 -26
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +39 -40
- package/dist/frqtl/objects/setup/setupTunnelLab.js +17 -19
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +1 -1
- package/dist/frqtl/objects/setup/setupWaveInterference.js +4 -5
- package/dist/frqtl/objects/v1-replacements.js +0 -1
- package/dist/frqtl/parts/CameraControls.js +5 -5
- package/dist/frqtl/parts/CameraPath.js +1 -1
- package/dist/frqtl/parts/CollisionActions.js +2 -3
- package/dist/frqtl/parts/CollisionTypes.js +1 -2
- package/dist/frqtl/parts/DetectorWall.js +10 -10
- package/dist/frqtl/parts/FrqtlControlsPanel.js +11 -11
- package/dist/frqtl/parts/FrqtlVision.js +1 -1
- package/dist/frqtl/parts/GraphingDetectorWall.js +3 -4
- package/dist/frqtl/parts/LargeObject.js +7 -8
- package/dist/frqtl/parts/LightColor.js +2 -2
- package/dist/frqtl/parts/LiveChart.js +1 -1
- package/dist/frqtl/parts/LogicGate.js +4 -7
- package/dist/frqtl/parts/OvenBox.js +1 -2
- package/dist/frqtl/parts/PointSource.js +10 -11
- package/dist/frqtl/parts/SlitLightSource.js +6 -6
- package/dist/frqtl/parts/SlitScreen.js +14 -14
- package/dist/frqtl/parts/SphereEmitter.js +24 -24
- package/dist/frqtl/parts/Tunnel.js +17 -17
- package/dist/frqtl/parts/Wall.js +17 -17
- package/dist/frqtl/scenes/dse-one-emitter.js +1 -1
- package/dist/frqtl/scenes/dse-one-slit.js +2 -2
- package/dist/frqtl/scenes/dse-photoelectric.js +3 -3
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +5 -6
- package/dist/frqtl/scenes/dse-two-emitters.js +1 -1
- package/dist/frqtl/scenes/dse-two-slits.js +2 -2
- package/dist/frqtl/scenes/laser-buildup.js +1 -1
- package/dist/frqtl/scenes/mirror-3d.js +4 -4
- package/dist/frqtl/scenes/mirror-route.js +9 -9
- package/dist/frqtl/scenes/mirror.js +1 -1
- package/dist/frqtl/scenes/self-interference.js +1 -1
- package/dist/frqtl/scenes/smoking-gun.js +1 -1
- package/dist/frqtl/scenes/sun.js +2 -2
- package/dist/shim/bundle/frqnb.js +3 -3
- package/dist/shim/bundle/index.js +1 -1
- package/dist/shim/bundle/manifest.js +2 -2
- package/dist/shim/bundle/zip.js +1 -1
- package/dist/shim/controls/index.js +14 -14
- package/dist/shim/controls/registry.js +3 -3
- package/dist/shim/engine/artifact-shell.js +1 -2
- package/dist/shim/engine/router.js +15 -17
- package/dist/shim/expr/ast-schema.js +5 -5
- package/dist/shim/field-descriptor/index.js +7 -8
- package/dist/shim/frame-data/index.js +3 -3
- package/dist/shim/index.js +4 -4
- package/dist/shim/notebook/canvas-guard.js +1 -1
- package/dist/shim/notebook/catalog.js +2 -2
- package/dist/shim/notebook/format.js +26 -26
- package/dist/shim/notebook/kernel.js +3 -4
- package/dist/shim/notebook/knob.js +0 -1
- package/dist/shim/notebook/module-resolver.js +6 -6
- package/dist/shim/notebook/produced-bindings.js +1 -1
- package/dist/shim/notebook/setup-builder.js +9 -12
- package/dist/shim/notebook/vendor-importmap.js +1 -1
- package/dist/shim/notebook/view-builder.js +1 -1
- package/dist/shim/notebook/view-ctx.js +1 -1
- package/dist/shim/notebook/warmup.js +2 -2
- package/dist/shim/page-params/index.js +8 -9
- package/dist/shim/registry/copy-gate.js +1 -1
- package/dist/shim/registry/exhibits.js +4 -4
- package/dist/shim/registry/experiments.js +3 -3
- package/dist/shim/registry/walkthrough.js +21 -24
- package/dist/shim/render/render-stages.js +2 -2
- package/dist/shim/render/sun-look.js +2 -2
- package/dist/shim/rpc/index.js +2 -2
- package/dist/shim/runtime/index.js +12 -12
- package/dist/shim/storage/modules.js +8 -8
- package/llms.txt +2 -2
- package/package.json +4 -3
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* setupSphereEmitter3D — a spherical "sun" emitter: uniform-DIRECTION emission
|
|
5
|
-
* via ball-rejected integer velocity sampling (
|
|
6
|
-
*
|
|
5
|
+
* via ball-rejected integer velocity sampling (the model: speed is
|
|
6
|
+
* ALWAYS 1px/tick (bench M1) — (vx,vy,vz) is
|
|
7
7
|
* DIRECTION ONLY, never a speed knob; only the ratio vx:vy:vz matters,
|
|
8
8
|
* (3,4,0)≡(6,8,0)). Models on `setupConeEmitter3D.js` (same live-knob,
|
|
9
9
|
* host-tick-fn, thin-sugar pattern).
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
* enough that a SINGLE BURST (before engine-tick temporal averaging kicks
|
|
72
72
|
* in) doesn't itself read lumpy: a static (no-tick) per-burst octant CV is
|
|
73
73
|
* ~0.16 at R=9, ~0.09 at R=20, ~0.07 at R=24-25, flattening to ~0.05-0.06 by
|
|
74
|
-
* R=30-40 (
|
|
74
|
+
* R=30-40 (host-RNG measurement) — R=24
|
|
75
75
|
* is the new DEFAULT, comfortably past the R=20 knee with headroom below the
|
|
76
76
|
* R=40 slider ceiling (so the R=9-ish "lumpy" end of the range stays
|
|
77
77
|
* explorable too).
|
|
@@ -90,8 +90,8 @@
|
|
|
90
90
|
* ball's 0.082 at R=20,N=4000) because it drops the coarse near-origin
|
|
91
91
|
* lattice points; optional refinement, not the default.
|
|
92
92
|
* @param {'spherical'|'hemisphere'|'laser'|'drum'|'particle-walk'} [options.preset='spherical'] - Sets the
|
|
93
|
-
* per-axis velocity RANGES the sampler draws from — the unification
|
|
94
|
-
*
|
|
93
|
+
* per-axis velocity RANGES the sampler draws from — the unification:
|
|
94
|
+
* sun/laser/drum are the SAME sampler with different ranges.
|
|
95
95
|
* 'spherical' (DEFAULT, the sun): vx,vy,vz each in [-R,R] -> full ball ->
|
|
96
96
|
* uniform spherical direction distribution.
|
|
97
97
|
* 'hemisphere' (FORWARD 180° emission — the directed sun): the SAME ball-
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
* vy,vz in [-R,R]. Conditioning a uniform ball on vx>=0 leaves a uniform
|
|
100
100
|
* HALF-ball, whose direction marginal is EXACTLY the restriction of
|
|
101
101
|
* 'spherical''s to the forward hemisphere: same physics, pointed one way
|
|
102
|
-
* (
|
|
102
|
+
* (measured over 2x10^6 MC samples, 2026-07-28). BALL
|
|
103
103
|
* REJECTION IS RETAINED and that is load-bearing — a raw box draw
|
|
104
104
|
* (vx in [0,R], vy in [-R,R], no magnitude clip) measures 1.83:1
|
|
105
105
|
* anisotropy with 1.45x peaks at +/-45deg, which is exactly the diagonal
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
* component (this is the historical concentric-ring-emitter-vs-cone
|
|
131
131
|
* construction's "collimated cylinder" case generalized: here the
|
|
132
132
|
* DISK is of DIRECTIONS at the source, not ring positions).
|
|
133
|
-
* 'particle-walk' (
|
|
133
|
+
* 'particle-walk' (a
|
|
134
134
|
* FAITHFUL reproduction, not just the primitive, GROUND-TRUTHED against
|
|
135
135
|
* the actual `setupParticleWalk.js`/`setupParticleWalk3D.js` source):
|
|
136
136
|
* vx PINNED to `[R,R]` (deterministic +X drift, ALWAYS the forward
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
* `control.setAxisRange('vx', lo, hi)` etc. for custom directional shapes.
|
|
163
163
|
* @param {{x:number,y:number,z:number}} [options.emissionBox] - OPT-IN per-axis
|
|
164
164
|
* emission-position box (extends the scalar `emissionBoxSide` cube into an
|
|
165
|
-
* independent size per axis
|
|
165
|
+
* independent size per axis). Omitted (default) ->
|
|
166
166
|
* `emissionBox` is DERIVED from `emissionBoxSide` + the current preset
|
|
167
167
|
* (uniform cube for spherical/laser/drum — BYTE-IDENTICAL to every
|
|
168
168
|
* pre-existing caller; the flat line shape above for 'particle-walk',
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
* SOURCE POSITION in ABSOLUTE canvas px — WHERE this emitter seeds its leaves
|
|
244
244
|
* (the center `cx0/cy0/cz0` that BOTH the volume box AND the surface ring
|
|
245
245
|
* launch are measured relative to). Wins over `options.offset` when both are
|
|
246
|
-
* present. z (3rd element) defaults to 0. Pure sugar
|
|
246
|
+
* present. z (3rd element) defaults to 0. Pure sugar: it
|
|
247
247
|
* ONLY moves the `makeLeafFrame` seed pixel — the velocity-aligned ring launch
|
|
248
248
|
* (`start = center + innerR×unit(v)`) stays relative to THIS center; zero
|
|
249
249
|
* engine, no golden moves (SU1). Needed for TWO-SOURCE interference (two
|
|
@@ -299,8 +299,7 @@
|
|
|
299
299
|
* fewer axis; `is2D` simply narrows `activeAxes` at init and drops vz
|
|
300
300
|
* from the sampler.
|
|
301
301
|
*
|
|
302
|
-
* MEASURED engine-realized finding (canonical-bench S4/S5,
|
|
303
|
-
* `docs/internal/physics-lab/canonical-bench/bench.cjs`, pkg-node WASM, R=20 N=2000 T=30
|
|
302
|
+
* MEASURED engine-realized finding (canonical-bench S4/S5, pkg-node WASM, R=20 N=2000 T=30
|
|
304
303
|
* seed=42, bit-reproducible across runs): angular uniformity (equal-solid-
|
|
305
304
|
* angle octant CV + cos(polar) marginal-flatness CV) is NOT distorted toward
|
|
306
305
|
* axes or diagonals by the ±1px/tick per-axis speed cap — S4 measures
|
|
@@ -330,10 +329,10 @@
|
|
|
330
329
|
* is a property of the ALREADY-GOLDENED move-gate rule, not new sugar logic —
|
|
331
330
|
* composing it here (dial mass via the `wavelength` arg, read the resulting
|
|
332
331
|
* oscillation) is exactly the r0/r1 "unique rational wavelength, unchanged
|
|
333
|
-
* speed" demo the intuition guide
|
|
332
|
+
* speed" demo the intuition guide names.
|
|
334
333
|
*
|
|
335
334
|
* Sustained emission is a HOST TICK-FN pushed to
|
|
336
|
-
* `universe.experimentTickFunctions` (
|
|
335
|
+
* `universe.experimentTickFunctions` (guardrail: no new engine
|
|
337
336
|
* hook, no native emit-spec). Fast-path regime: no
|
|
338
337
|
* `withInterQuantumCollisions`, no `brownianMotion`, no top-down frame-gen
|
|
339
338
|
* auto-spawn ladder. No sink — leaves drift forever (M5) until they exit
|
|
@@ -376,9 +375,9 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
376
375
|
* Three names for three layers of one quantity — author knob, universe field,
|
|
377
376
|
* emergent wavelength — so a reader who sees only one of them can find the others.
|
|
378
377
|
* Confirmed by measurement at both sweep points: fWL 64 -> 256, fWL 32 -> 128
|
|
379
|
-
* (
|
|
378
|
+
* (the intuition record r3-radius-stall-shells).
|
|
380
379
|
*
|
|
381
|
-
* ── `stepping`
|
|
380
|
+
* ── `stepping` — a WORLD field, PASSED THROUGH ───────────────────────────────
|
|
382
381
|
* The wave-walk STEPPING SAMPLER ('fixed' | 'average') is a per-universe init
|
|
383
382
|
* option, reinit-class exactly like `is2D`/`fundamentalDimension`, so it belongs
|
|
384
383
|
* to the world step rather than to the part. This factory NEITHER validates NOR
|
|
@@ -554,7 +553,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
554
553
|
: (declaresDimension ? 0 : _worldFundamentalDimension(universe));
|
|
555
554
|
|
|
556
555
|
// `leafDimension` = the dimension THIS emitter births its leaves at (per-emitter
|
|
557
|
-
// — the
|
|
556
|
+
// — the corrected model: dimension is an ADDABLE scale layer, and
|
|
558
557
|
// an emitter creates leaves AT a chosen dimension via makeLeafFrame's explicit
|
|
559
558
|
// `level` + `addDimension` (the SAME pattern scenes/blackbody.js uses: init at a base
|
|
560
559
|
// fundamental, then addDimension for the higher scale layer). Default =
|
|
@@ -593,7 +592,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
593
592
|
universe.addDimension(leafDimension);
|
|
594
593
|
}
|
|
595
594
|
|
|
596
|
-
// ── Emission SOURCE POSITION (per-emitter
|
|
595
|
+
// ── Emission SOURCE POSITION (per-emitter) ───────────────────────────────
|
|
597
596
|
// cx0/cy0/cz0 = the center this emitter seeds its leaves around (BOTH the
|
|
598
597
|
// volume box AND the surface ring launch are relative to it). Resolved from
|
|
599
598
|
// options.center (ABSOLUTE canvas px, wins) / options.offset (RELATIVE to
|
|
@@ -642,7 +641,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
642
641
|
// = per-leaf ±, exactly reproducing PointSource/emitterProgram.js's
|
|
643
642
|
// `chargeInt = universe.random() < 0.5 ? 0 : 1` (the ONE opt-in extra
|
|
644
643
|
// draw). Required to reproduce the Wave-Interference look from a single
|
|
645
|
-
// omni source
|
|
644
|
+
// omni source.
|
|
646
645
|
chargeMode: (options.chargeMode === 'random' || options.chargeMode === 'negative'
|
|
647
646
|
|| options.chargeMode === 'positive') ? options.chargeMode : 'positive',
|
|
648
647
|
// Emission START-POSITION topology (WHERE a leaf is seeded, independent
|
|
@@ -652,14 +651,14 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
652
651
|
// CIRCLE in 2D / a SPHERE SHELL in 3D). Surface mode reproduces the old
|
|
653
652
|
// PointSource "ring launch": single-occupancy forbids many quanta from
|
|
654
653
|
// starting on the EXACT same pixel, so PointSource seeds them around a
|
|
655
|
-
// ring — the correct single-source start topology
|
|
654
|
+
// ring — the correct single-source start topology.
|
|
656
655
|
emissionShell: (options.emissionShell === 'surface') ? 'surface' : 'volume',
|
|
657
656
|
// Shell radius (px) for emissionShell:'surface'. Non-negative integer;
|
|
658
657
|
// 0 collapses the shell back to the center point (so 'surface' with
|
|
659
658
|
// innerR 0 == 'volume' with box 0 — a single pixel). Ignored entirely
|
|
660
659
|
// in 'volume' mode (no draw).
|
|
661
660
|
innerR: (options.innerR !== undefined) ? Math.max(0, Math.round(options.innerR)) : 0,
|
|
662
|
-
// Per-leaf RENDER COLOR OVERRIDE (render-intent DATA, NOT physics
|
|
661
|
+
// Per-leaf RENDER COLOR OVERRIDE (render-intent DATA, NOT physics).
|
|
663
662
|
// null (DEFAULT) → leaves are emitted COLORLESS (byte-identical to every pre-existing
|
|
664
663
|
// caller) so the renderer's colorScheme derives color from fullness (LightColor). An
|
|
665
664
|
// [r,g,b] / [r,g,b,a] array → FORCES that color: it is passed to makeLeafFrame's `color`
|
|
@@ -670,7 +669,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
670
669
|
// wavelength→LightColor mapping with ZERO engine field touched. LIVE via
|
|
671
670
|
// control.setRenderColor (affects the NEXT emitted leaf, M5).
|
|
672
671
|
renderColor: Array.isArray(options.renderColor) ? options.renderColor.slice() : null,
|
|
673
|
-
// Per-leaf RENDER SHAPE OVERRIDE (render-intent DATA, NOT physics
|
|
672
|
+
// Per-leaf RENDER SHAPE OVERRIDE (render-intent DATA, NOT physics).
|
|
674
673
|
// null (DEFAULT) → leaves emit with NO render.shape key (byte-identical to every
|
|
675
674
|
// pre-existing caller); the renderer falls back to the render-level "Default Shape"
|
|
676
675
|
// (setRenderOption) then 'ball'. A ball|box|circle|cube|sphere string is stamped as
|
|
@@ -693,7 +692,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
693
692
|
active: true
|
|
694
693
|
};
|
|
695
694
|
// GROUND-TRUTHED against setupParticleWalk.js / setupParticleWalk3D.js
|
|
696
|
-
// (
|
|
695
|
+
// (read the actual source, not a
|
|
697
696
|
// paraphrase): both historical experiments use IDENTICAL construction —
|
|
698
697
|
// `sourceRect=[cx,cy,1,10]` (a height-10 vertical LINE, x-width=1, in
|
|
699
698
|
// BOTH 2D and 3D — there is no disk anywhere in the historical code;
|
|
@@ -705,7 +704,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
705
704
|
// canvas, wrap=false so quanta die at the far wall): alive count
|
|
706
705
|
// climbs 1000/2000/4000 at ticks 50/100/200 then PLATEAUS at exactly
|
|
707
706
|
// 5960 by tick~300 (little's-law steady state: 20/tick x ~298-tick
|
|
708
|
-
// transit time from x~302 to the x=599 wall) — this IS the
|
|
707
|
+
// transit time from x~302 to the x=599 wall) — this IS the
|
|
709
708
|
// "~5960 alive, the fuller sheet/beam" density target, reproduced
|
|
710
709
|
// here by matching the SAME three numbers, not by tuning against the
|
|
711
710
|
// number directly.
|
|
@@ -719,7 +718,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
719
718
|
state.burstSize = options.burstSize !== undefined ? options.burstSize
|
|
720
719
|
: (state.preset === 'particle-walk' ? PARTICLE_WALK_DEFAULT_BURST_SIZE : 40);
|
|
721
720
|
|
|
722
|
-
// ── Per-axis emission SOURCE SHAPE
|
|
721
|
+
// ── Per-axis emission SOURCE SHAPE
|
|
723
722
|
// — the "drum emitter" shape: a flat line source, extending the
|
|
724
723
|
// scalar emissionBoxSide cube into an opt-in per-axis box).
|
|
725
724
|
// `explicitEmissionBox`/`explicitSourceShape` (options, FROZEN at
|
|
@@ -788,7 +787,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
788
787
|
ranges.vz = fwd;
|
|
789
788
|
}
|
|
790
789
|
} else if (name === 'hemisphere') {
|
|
791
|
-
// FORWARD 180° emission (D1
|
|
790
|
+
// FORWARD 180° emission (D1) — the ONE line that
|
|
792
791
|
// differs from 'spherical': the forward axis' range is HALVED to
|
|
793
792
|
// [0,R]. Everything else (the ball rejection below, the isotropy
|
|
794
793
|
// correction) is shared, which is the whole point: a uniform ball
|
|
@@ -805,7 +804,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
805
804
|
ranges.vy = [-R, R];
|
|
806
805
|
ranges.vz = [0, 0];
|
|
807
806
|
} else if (name === 'particle-walk') {
|
|
808
|
-
// Faithful reproduction
|
|
807
|
+
// Faithful reproduction: PURE-FORWARD
|
|
809
808
|
// +X momentum, ALWAYS (not is2D-branched like 'laser' — both
|
|
810
809
|
// historical experiments, 2D and 3D, used vx=1,vy=0 literally).
|
|
811
810
|
// vy=vz PINNED to 0 — the waviness comes entirely from the
|
|
@@ -829,7 +828,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
829
828
|
// Mulberry32 stream (G-EMIT primitive) — same stream physics draws from, so a
|
|
830
829
|
// fixed seed is fully reproducible.
|
|
831
830
|
//
|
|
832
|
-
// REJECTION IS PRESET-AWARE
|
|
831
|
+
// REJECTION IS PRESET-AWARE — the rejection criterion
|
|
833
832
|
// must match each preset's INTENDED SHAPE, not a single one-size ball:
|
|
834
833
|
// 'spherical': reject to the BALL |v|<=R (rounds the sampled cube into a
|
|
835
834
|
// sphere of directions — the mandatory isotropy correction, S4/S5).
|
|
@@ -916,8 +915,8 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
916
915
|
}
|
|
917
916
|
|
|
918
917
|
// Circular rejection in the Y-Z plane (the transverse cross-section
|
|
919
|
-
// perpendicular to 'particle-walk''s +X forward axis — the
|
|
920
|
-
//
|
|
918
|
+
// perpendicular to 'particle-walk''s +X forward axis — the
|
|
919
|
+
// circular disk source (3D)). Mirrors sampleVelocity's own
|
|
921
920
|
// ball-rejection idiom, applied to POSITION instead of direction.
|
|
922
921
|
// Only ever called when state.sourceShape==='disk' (opt-in — never
|
|
923
922
|
// the default path for spherical/laser/drum).
|
|
@@ -933,11 +932,11 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
933
932
|
return [0, 0]; // safety net (misconfigured box)
|
|
934
933
|
}
|
|
935
934
|
|
|
936
|
-
// Emission SHELL SURFACE start-position — VELOCITY-ALIGNED
|
|
935
|
+
// Emission SHELL SURFACE start-position — VELOCITY-ALIGNED.
|
|
937
936
|
// The old-PointSource "ring launch" seeds each particle ON the ring AT THE
|
|
938
937
|
// ANGLE OF ITS OWN VELOCITY, so its start pixel is COLLINEAR with its outward
|
|
939
|
-
// trajectory (the
|
|
940
|
-
// vector
|
|
938
|
+
// trajectory (the pixel position is the integer-scaled momentum
|
|
939
|
+
// vector). This is what gives a single source an EMPTY CENTER and razor-
|
|
941
940
|
// sharp bands: every particle projects radially outward and NONE cuts across
|
|
942
941
|
// the inner disk. Draws NOTHING itself — it is a deterministic function of
|
|
943
942
|
// the ALREADY-SAMPLED velocity `v` (which is why emitOneLeaf samples velocity
|
|
@@ -999,7 +998,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
999
998
|
// wavelength=state.wavelength -- null by default (mass=capacity, byte-identical);
|
|
1000
999
|
// a number sets mass=max(1,capacity/wavelength) for the NEXT emitted leaf (M5:
|
|
1001
1000
|
// already-emitted leaves keep whatever mass they were stamped with).
|
|
1002
|
-
// 10th arg
|
|
1001
|
+
// 10th arg = per-leaf render.shape override (null default → no key →
|
|
1003
1002
|
// byte-identical). Rides the SAME provider render-intent MERGE seam as color.
|
|
1004
1003
|
var h = universe.makeLeafFrame(lx, ly, leafDimension, state.wavelength, v[0], v[1], false, chg, state.renderColor, state.renderShape);
|
|
1005
1004
|
if (h === null) { return; } // liveQuantaCap refused the add: no leaf, nothing to stamp
|
|
@@ -1031,7 +1030,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
1031
1030
|
marker.setRect([cx0 - D / 2, cy0 - D / 2, cz0 - D / 2, D, D, D]);
|
|
1032
1031
|
}
|
|
1033
1032
|
var control = {
|
|
1034
|
-
// INVARIANT (
|
|
1033
|
+
// INVARIANT ("the clamp bug"): emissionBoxSide is
|
|
1035
1034
|
// NEVER larger than the CURRENT R. Construction clamps it once; setR()
|
|
1036
1035
|
// is the ONLY runtime path that can widen the gap (R shrinking below an
|
|
1037
1036
|
// already-set box), so it MUST re-clamp every time — this is the single
|
|
@@ -1067,7 +1066,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
1067
1066
|
getEmitEveryTicks: function () { return emitEveryTicks; },
|
|
1068
1067
|
setActive: function (v) { state.active = !!v; },
|
|
1069
1068
|
getIs2D: function () { return is2D; },
|
|
1070
|
-
// Emission SOURCE POSITION
|
|
1069
|
+
// Emission SOURCE POSITION — reposition this emitter's
|
|
1071
1070
|
// center LIVE (affects the NEXT emitted leaf, M5). Absolute canvas px. z
|
|
1072
1071
|
// optional (retains the current z when omitted). Pure sugar — only the
|
|
1073
1072
|
// makeLeafFrame seed pixel moves; the ring launch stays relative to it.
|
|
@@ -1113,11 +1112,11 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
1113
1112
|
getWavelength: function () { return state.wavelength !== null ? state.wavelength : 1; },
|
|
1114
1113
|
getMaxWavelength: function () { return maxWavelength; },
|
|
1115
1114
|
getFundamentalDimension: function () { return fundamentalDimension; },
|
|
1116
|
-
// The dimension THIS emitter births leaves at (per-emitter
|
|
1115
|
+
// The dimension THIS emitter births leaves at (per-emitter).
|
|
1117
1116
|
getLeafDimension: function () { return leafDimension; },
|
|
1118
|
-
// LIVE "Emit Dimension"
|
|
1117
|
+
// LIVE "Emit Dimension". Host-side addDimension (idempotent) +
|
|
1119
1118
|
// closure-var flip: the NEXT emitted leaf is born at the new dimension (M5, exactly
|
|
1120
|
-
// like wavelength/charge).
|
|
1119
|
+
// like wavelength/charge). Mid-run addDimension is SAFE (cleared 2026-07-22):
|
|
1121
1120
|
// do_tick snapshots the dim key set fresh each tick, so a between-ticks add is atomic;
|
|
1122
1121
|
// promotion routes live. SIDE EFFECT (S1): a value BELOW the current fundamental
|
|
1123
1122
|
// self-lowers the floor UNIVERSE-GLOBALLY and is a ONE-WAY RATCHET (no removeDimension)
|
|
@@ -1164,7 +1163,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
1164
1163
|
|
|
1165
1164
|
// Standardized live knobs (runner.experimentControls-compatible — the SAME
|
|
1166
1165
|
// shape coneEmitterControl.controls uses). Every
|
|
1167
|
-
// descriptor carries a `get()` accessor
|
|
1166
|
+
// descriptor carries a `get()` accessor so a page can
|
|
1168
1167
|
// generically RE-SYNC every slider's displayed value/label from authoritative
|
|
1169
1168
|
// state after ANY control fires — not just the one the user dragged. This is
|
|
1170
1169
|
// what makes the emissionBoxSide<=R clamp VISIBLE live: `emissionBoxSide`
|
|
@@ -1225,7 +1224,7 @@ import { FrqtlWorldInit } from './_worldInit.js';
|
|
|
1225
1224
|
return {
|
|
1226
1225
|
sphereEmitterControl: control,
|
|
1227
1226
|
largeObjects: [],
|
|
1228
|
-
// TOP-LEVEL dispose (STACKING-BUG FIX
|
|
1227
|
+
// TOP-LEVEL dispose (STACKING-BUG FIX): FrqtlApp.setup()
|
|
1229
1228
|
// tears down the PREVIOUS setupResult by calling `setupResult.dispose()` IF it
|
|
1230
1229
|
// exists (canonical teardown, FrqtlApp.js) — but this factory previously
|
|
1231
1230
|
// exposed dispose ONLY on `sphereEmitterControl`, so the runner's teardown check
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* setupTunnelLab — DirectedTunnel ISOLATION (I1, the directed-tunnel composition model
|
|
5
5
|
* §3): ONE `DirectedTunnel`, gun-chamber mechanics (fill / escape / dispersion) at a
|
|
6
|
-
* live-tunable scale — the visual half of the scale-sweep measurement
|
|
7
|
-
*
|
|
6
|
+
* live-tunable scale — the visual half of the scale-sweep measurement, same methodology,
|
|
7
|
+
* same source shape.
|
|
8
8
|
*
|
|
9
9
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
10
10
|
*
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* `QuantumEmitter` has no wavelength field, which is exactly why this factory hand-
|
|
23
23
|
* places quanta via a local host tick-fn instead of `DirectedTunnel`'s `options.emitter`
|
|
24
24
|
* — see the Layer 2 note below). LIVE (`setupResult.sourceState.wavelength`).
|
|
25
|
-
* **CEILING (
|
|
25
|
+
* **CEILING ("the wavelength slider has no effect"
|
|
26
26
|
* root cause):** Engine Invariant #6 is `mass = max(1, capacity/wavelength)` —
|
|
27
27
|
* at this factory's `FUNDAMENTAL_DIMENSION=2`, `capacity = (2^2)^2 = 16`
|
|
28
28
|
* (`setupTunnelLab.CAPACITY`, exported below), so ANY `wavelength >= capacity`
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* @param {number} [opts.sourceDensity=5] - Quanta hand-placed per burst. LIVE
|
|
44
44
|
* (`setupResult.sourceState.density`). NOTE: this is the FACTORY's own
|
|
45
45
|
* omitted-opt default (unchanged, for any headless caller) — `tunnel-lab
|
|
46
|
-
* /index.html`'s own UI default is 10 (
|
|
46
|
+
* /index.html`'s own UI default is 10 (the
|
|
47
47
|
* population-density fix: the page ALWAYS passes an explicit value read
|
|
48
48
|
* from its own `<input>` default, so this factory default is never
|
|
49
49
|
* actually reached by the page — see that file's own docstring for the
|
|
@@ -56,15 +56,14 @@
|
|
|
56
56
|
* for the same reason.
|
|
57
57
|
* @param {boolean} [opts.sourceActive=true] - Master on/off for the burst source. LIVE
|
|
58
58
|
* (`setupResult.sourceState.active`).
|
|
59
|
-
* @param {number} [opts.depth] -
|
|
59
|
+
* @param {number} [opts.depth] - 3D toggle. OMITTED
|
|
60
60
|
* (default) -> 2D (byte-identical to every pre-existing caller). A positive
|
|
61
61
|
* value -> real z-containment (`activeAxes:{x,y,z}` on `universe.init()`,
|
|
62
62
|
* `DirectedTunnel`'s own `depth`/`z=-depth/2`, hand-placed quanta z-centred)
|
|
63
63
|
* — the standard `options.depth` recipe. REINIT-CLASS
|
|
64
64
|
* (only meaningful at construction, like bore/length).
|
|
65
|
-
* @param {'spray'|'beam'} [opts.velocityProfile='spray'] -
|
|
66
|
-
*
|
|
67
|
-
* the shared surface"): the SAME `SustainedSource.SOURCE_SPEC_FIELDS.
|
|
65
|
+
* @param {'spray'|'beam'} [opts.velocityProfile='spray'] - tunnel-lab has the
|
|
66
|
+
* source-profile selection off the shared surface: the SAME `SustainedSource.SOURCE_SPEC_FIELDS.
|
|
68
67
|
* velocityProfile` axis `LightSource`'s own `particleWalk` burst always uses
|
|
69
68
|
* (`'beam'`, hardcoded there) — exposed here so tunnel-lab can be set to
|
|
70
69
|
* `'beam'` and produce a directly comparable collimated source to
|
|
@@ -98,12 +97,11 @@
|
|
|
98
97
|
*
|
|
99
98
|
* **Gun-chamber convention**: `capNegX:true, capPosX:false` — a CLOSED breech (source
|
|
100
99
|
* fires INTO the bore, quanta bounce/fill) and an OPEN muzzle (quanta escape, then
|
|
101
|
-
* disperse freely with no walls) — the
|
|
102
|
-
* in a tube, escape the open end, spread comes from dispersion AFTER escape
|
|
100
|
+
* disperse freely with no walls) — the Gun-Chamber model (particles ping-pong
|
|
101
|
+
* in a tube, escape the open end, spread comes from dispersion AFTER escape).
|
|
103
102
|
*
|
|
104
103
|
* **Fundamental dimension = 2** (matches `setupDoubleSlit.js`'s default AND the scale-
|
|
105
|
-
* sweep's own convention —
|
|
106
|
-
* "level 0 pins wavelength to a no-op" note): a level-0 leaf has `capacity=1`, so
|
|
104
|
+
* sweep's own convention — level 0 pins wavelength to a no-op): a level-0 leaf has `capacity=1`, so
|
|
107
105
|
* `mass=max(1,capacity/wavelength)` is PINNED at 1 for any `wavelength>=2` — the
|
|
108
106
|
* wavelength slider would be a silent no-op at level 0.
|
|
109
107
|
*
|
|
@@ -126,14 +124,14 @@ import * as SustainedSource from '../objects/sustainedSource.js';
|
|
|
126
124
|
|
|
127
125
|
var FUNDAMENTAL_DIMENSION = 2; // matches setupDoubleSlit.js + the scale-sweep script
|
|
128
126
|
|
|
129
|
-
//
|
|
127
|
+
// The ONE authority for "what's the largest
|
|
130
128
|
// wavelength value that still produces a DISTINCT mass from any larger one"
|
|
131
129
|
// at this factory's FUNDAMENTAL_DIMENSION. Engine Invariant #6:
|
|
132
130
|
// `mass = max(1, capacity/wavelength)` — the floor (mass=1) is reached
|
|
133
131
|
// exactly at `wavelength === capacity`; every wavelength beyond that is
|
|
134
132
|
// indistinguishable. `tunnel-lab/index.html` reads this (never a hand-typed
|
|
135
133
|
// "16" literal) to clamp its wavelength slider's max to the achievable range.
|
|
136
|
-
//
|
|
134
|
+
// Derived from `SustainedSource.capacityAtDimension` — the SAME
|
|
137
135
|
// authority `setupLightSourceLab.js` now also derives its own
|
|
138
136
|
// MAX_USEFUL_WAVELENGTH from (AC4: "same MAX_USEFUL_WAVELENGTH clamp
|
|
139
137
|
// authority" for both labs).
|
|
@@ -164,7 +162,7 @@ import * as SustainedSource from '../objects/sustainedSource.js';
|
|
|
164
162
|
var sourceDensity = opts.sourceDensity !== undefined ? opts.sourceDensity : 5;
|
|
165
163
|
var sourceRate = opts.sourceRate !== undefined ? opts.sourceRate : 10;
|
|
166
164
|
var sourceActive = opts.sourceActive !== undefined ? !!opts.sourceActive : true;
|
|
167
|
-
//
|
|
165
|
+
// OPT-IN 3D, OMISSION-preserves-2D
|
|
168
166
|
// (the "before adding a creation-method parameter" discipline): mirrors
|
|
169
167
|
// the standard `options.depth` recipe — omitted/null ->
|
|
170
168
|
// 2D (zContain:false, byte-identical to every existing caller); a
|
|
@@ -172,7 +170,7 @@ import * as SustainedSource from '../objects/sustainedSource.js';
|
|
|
172
170
|
// `options.depth` already existed on DirectedTunnel, T1; this factory
|
|
173
171
|
// simply now forwards it + arms activeAxes).
|
|
174
172
|
var depth = (opts.depth !== undefined && opts.depth !== null) ? opts.depth : null;
|
|
175
|
-
//
|
|
173
|
+
// Default 'spray' is byte-identical to every pre-existing
|
|
176
174
|
// caller (this factory's own original hardcoded value below).
|
|
177
175
|
var velocityProfile = opts.velocityProfile !== undefined ? opts.velocityProfile : 'spray';
|
|
178
176
|
|
|
@@ -191,7 +189,7 @@ import * as SustainedSource from '../objects/sustainedSource.js';
|
|
|
191
189
|
if (depth !== null) { tunnelOpts.depth = depth; tunnelOpts.z = -depth / 2; }
|
|
192
190
|
var tunnel = new DirectedTunnel(universe, tunnelOpts);
|
|
193
191
|
|
|
194
|
-
//
|
|
192
|
+
// One source surface: the recurring
|
|
195
193
|
// burst injector now routes through `sustainedSource.js`'s ONE
|
|
196
194
|
// `buildSustainedSource` authority — the SAME surface
|
|
197
195
|
// `setupLightSourceLab.js` (via `LightSource`'s own opt-in
|
|
@@ -199,7 +197,7 @@ import * as SustainedSource from '../objects/sustainedSource.js';
|
|
|
199
197
|
// `entranceBias:'top'` reproduce this factory's PRE-EXISTING biased-
|
|
200
198
|
// random-fan shape EXACTLY (the "uniformization visible" demo shape —
|
|
201
199
|
// see the module docstring's "Biased entrance" note); `zIsotropy:true`
|
|
202
|
-
// (the default) is the 3D-SYMMETRIC EMISSION fix (
|
|
200
|
+
// (the default) is the 3D-SYMMETRIC EMISSION fix (§4
|
|
203
201
|
// option (a)) — when `depth` is armed, every burst now ALSO draws a `vz`
|
|
204
202
|
// symmetric to the `vy` spread, instead of pinning z-momentum at zero
|
|
205
203
|
// forever (the tester-measured z/y=0.23 flatness root cause).
|
|
@@ -220,7 +218,7 @@ import * as SustainedSource from '../objects/sustainedSource.js';
|
|
|
220
218
|
// FrqtlApp.setup() (FrqtlApp.js:528) auto-wires
|
|
221
219
|
// the render list; today the world's own census carries them by construction.
|
|
222
220
|
largeObjects: tunnel.largeObjects,
|
|
223
|
-
// CANONICAL TEARDOWN (
|
|
221
|
+
// CANONICAL TEARDOWN (the reinit-
|
|
224
222
|
// compounding fix): `FrqtlApp.prototype.setup()` calls THIS
|
|
225
223
|
// automatically before installing the NEXT setupResult. Delegates to
|
|
226
224
|
// `src.dispose()` (the ONE `sustainedSource.js` teardown mechanism)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* setupTunnelPointExit — the TUNNEL POINT-EXIT LAB (D3
|
|
4
|
+
* setupTunnelPointExit — the TUNNEL POINT-EXIT LAB (D3).
|
|
5
5
|
*
|
|
6
6
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
7
7
|
*
|
|
@@ -26,8 +26,7 @@ import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
|
26
26
|
* Builds two {@link PointSource} emitters separated by `sourceSeparation`, each
|
|
27
27
|
* firing 1px/tick leaf Frames radially, plus two visual-only source circles.
|
|
28
28
|
* The fringe pattern is EMERGENT — no wave equation; it appears where the two
|
|
29
|
-
* particle streams overlap.
|
|
30
|
-
* `docs/internal/records/physics-lab/2026-06-16-wave-interference-wavelengthScale.md`.)
|
|
29
|
+
* particle streams overlap.
|
|
31
30
|
*
|
|
32
31
|
* Two independent "wavelength" knobs — keep them distinct:
|
|
33
32
|
* - `options.fundamentalWaveLength` → each emitter's `opts.wavelength` → particle
|
|
@@ -51,14 +50,14 @@ import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
|
51
50
|
* @param {number} [options.sourceDiameter] - Source circle diameter (default = wavelength).
|
|
52
51
|
* @param {number} [options.quantumLimit] - Max quanta emitted per angle (default: velocity);
|
|
53
52
|
* the steady-state density ceiling.
|
|
54
|
-
* @param {number} [options.depth] -
|
|
53
|
+
* @param {number} [options.depth] - total z-extent for a 3D run. OMITTED
|
|
55
54
|
* (default) → today's 2D behavior, byte-identical (no `activeAxes`/z key touched at all).
|
|
56
55
|
* When provided: `universe.init()` gains `activeAxes:{x:true,y:true,z:true}` (mirrors
|
|
57
56
|
* `setupParticleWalk3D`) and BOTH PointSources emit with a uniform z-spread centered on
|
|
58
57
|
* z=0 (`zMin=-depth/2, zDepth=depth`) — every z-slice sees the same radial illumination,
|
|
59
58
|
* so the emergent fringe pattern is genuinely 3D rather than a dark volume around one
|
|
60
59
|
* lit plane (design §2.3/§4.4). This is the SAME emitter option `SlitLightSource`
|
|
61
|
-
* consumes
|
|
60
|
+
* consumes — the z-spread lives in `PointSource`/
|
|
62
61
|
* `emitterProgram.js`, not here, precisely so both experiments share it.
|
|
63
62
|
* @returns {Object} `{ sources:{source1,source2}, source1Circle, source2Circle,
|
|
64
63
|
* fundamentalDimension, desiredWaveLength, largeObjects }`.
|
|
@@ -84,7 +83,7 @@ import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
|
84
83
|
var fundamentalDimension = options.fundamentalDimension !== undefined ? options.fundamentalDimension : 2;
|
|
85
84
|
var desiredWaveLength = options.fundamentalWaveLength !== undefined ? options.fundamentalWaveLength : 32;
|
|
86
85
|
var wavelengthScale = options.wavelengthScale !== undefined ? options.wavelengthScale : 8;
|
|
87
|
-
//
|
|
86
|
+
// Opt-in 3D depth. undefined/null → 2D, byte-identical.
|
|
88
87
|
var depth = (options.depth !== undefined && options.depth !== null) ? options.depth : null;
|
|
89
88
|
|
|
90
89
|
// Init universe: ground-up, brownianMotion:false (the wave-walk). activeAxes is ONLY
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* unknown-kind throw TELLS the author what to build instead of leaving them to guess. A LEAF (no
|
|
6
6
|
* imports): ObjectFactory reads it for its unknown-kind refusals, SlitLightSource for its own, and
|
|
7
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
8
|
*/
|
|
10
9
|
|
|
11
10
|
export const V1_REPLACEMENTS = Object.freeze({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* CameraControls.js — a REUSABLE first-person-game-style camera-controls sugar
|
|
5
|
-
* object
|
|
5
|
+
* object. ANY 3D experiment page drops
|
|
6
6
|
* it in with ONE call; it drives the existing `runner.cameraControl` capability
|
|
7
7
|
* (the camera-as-sugar / BYO-renderer pattern — `getCameraControl()` →
|
|
8
8
|
* `look({spin,pitch})` ROTATES, `move({forward,strafe})` TRANSLATES) with two
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
* all methods safe) is returned — every caller, including an unconditional
|
|
83
83
|
* walkthrough-rung attach, is right in 2D by construction.
|
|
84
84
|
*
|
|
85
|
-
* ── Input mapping — the STANDARD FPS SPLIT
|
|
85
|
+
* ── Input mapping — the STANDARD FPS SPLIT ───────────────────────────────────
|
|
86
86
|
* The two pads are the two halves of a first-person camera and they do NOT
|
|
87
87
|
* overlap: LOOK rotates, MOVE translates. MOVE never rotates.
|
|
88
88
|
* LOOK pad (bottom-RIGHT) — ROTATION ONLY, and it is SCREEN-RELATIVE.
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
* grabbed object follows the camera because the runner carries it on the camera's
|
|
107
107
|
* own pose notice (`addPoseListener`), not because a stick is routed to it.
|
|
108
108
|
* Space → `runner.grabToggle()` — the aim's grab/place key (STUDIO's
|
|
109
|
-
* stationary reticle
|
|
109
|
+
* stationary reticle).
|
|
110
110
|
* Ignored while typing (INPUT/TEXTAREA/SELECT/contenteditable) and
|
|
111
111
|
* on key auto-repeat; a no-op while the runner's aim is off.
|
|
112
112
|
* A state label above the pads (`data-role="aim-state"`) reads the runner's aim
|
|
@@ -166,7 +166,7 @@ import * as OrbitBag from '../compat/renderer/orbit-bag.js';
|
|
|
166
166
|
'(RENDERER_CONTRACT §1a.2). (FAIL-LOUD.)');
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
// ── IDEMPOTENT per mountEl (F4
|
|
169
|
+
// ── IDEMPOTENT per mountEl (F4) ──────────────────────────────────────
|
|
170
170
|
// A second attach() on the SAME container returns the LIVE instance instead of
|
|
171
171
|
// stacking a second pad set + a second window keydown listener. This kills the
|
|
172
172
|
// double-mount race for EVERY caller — a page that wires its own joysticks
|
|
@@ -286,7 +286,7 @@ import * as OrbitBag from '../compat/renderer/orbit-bag.js';
|
|
|
286
286
|
var moveY = clamp(jMove.y + km.y, -1, 1); // walk (translation, into the picture)
|
|
287
287
|
|
|
288
288
|
var o = cam.getOrbit();
|
|
289
|
-
// ── THE FPS SPLIT (
|
|
289
|
+
// ── THE FPS SPLIT (screen-relative) ──────────────────────────────
|
|
290
290
|
// LOOK = rotation ONLY, and the widget COMPOSES NOTHING: it hands the
|
|
291
291
|
// provider a screen-relative DELTA (spin about the view axis,
|
|
292
292
|
// pitch about screen-right) and the ONE camera authority does
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* CameraPath.js — a declarative, REPRODUCIBLE camera path
|
|
4
|
+
* CameraPath.js — a declarative, REPRODUCIBLE camera path.
|
|
5
5
|
*
|
|
6
6
|
* ONE object for both modes. It drives `runner.cameraControl` — the single camera write path —
|
|
7
7
|
* with ABSOLUTE poses derived from waypoints, so the same waypoints and the same tick always
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* CollisionActions — Canonical collision action enum for EGPT FRQTL VM.
|
|
5
5
|
* Values are SCREAMING_SNAKE (the JS engine's native vocabulary).
|
|
6
6
|
*
|
|
7
|
-
* Canonical set — exactly what
|
|
8
|
-
*
|
|
9
|
-
* ACTIVE_ACTIONS = [ABSORB, COUNT, MARK, DETECT, LOG] (EGPTfrqtl.js line 987).
|
|
7
|
+
* Canonical set — exactly what the JS reference engine's lo.collide() action dispatch has.
|
|
8
|
+
* ACTIVE_ACTIONS = [ABSORB, COUNT, MARK, DETECT, LOG].
|
|
10
9
|
* DO NOT add actions not present in the JS reference collide() without first
|
|
11
10
|
* adding them there and updating the conformance suite (§11.3).
|
|
12
11
|
*
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* CollisionTypes — Canonical collision type enum for EGPT FRQTL VM.
|
|
5
5
|
* Values are SCREAMING_SNAKE (the JS engine's native vocabulary).
|
|
6
6
|
*
|
|
7
|
-
* Canonical set — exactly what
|
|
8
|
-
* Reference:docs/internal/engine/ENGINE_CONTRACT.md §2.2.
|
|
7
|
+
* Canonical set — exactly what the JS reference engine's lo.collide() type switch has.
|
|
9
8
|
* DO NOT add types not present in the JS reference collide() without first
|
|
10
9
|
* adding them there and updating the conformance suite (§11.3).
|
|
11
10
|
*
|