@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +87 -0
- package/LICENSE.md +664 -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 +2499 -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 +84 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +99 -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 +258 -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 +2470 -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 +2920 -0
- package/dist/frqtl/objects/ObjectFactory.js +296 -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 +710 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -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 +411 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +351 -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 +309 -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 +820 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +468 -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 +250 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -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 +277 -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 +1258 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
- package/dist/frqtl/objects/v1-replacements.js +29 -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 +34 -0
- package/dist/frqtl/parts/CollisionTypes.js +30 -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 +489 -0
- package/dist/frqtl/parts/LargeObject.js +405 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +717 -0
- package/dist/frqtl/parts/OvenBox.js +611 -0
- package/dist/frqtl/parts/PointSource.js +762 -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 +243 -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 +211 -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 +690 -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 +325 -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 +671 -0
- package/dist/shim/notebook/knob.js +150 -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 +507 -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 +213 -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 +804 -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 +63 -4
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — Double slit (the SDK's artifact page)
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world (its
|
|
7
|
+
* co-init call, `exp.createCanvas` — the one place its size is written), the parts and their order
|
|
8
|
+
* are all here; the sugar (SlitScreen, SlitLightSource, DetectorWall, LargeObject), the renderer
|
|
9
|
+
* and the engine are imports. Shipped as `dist/artifact/double-slit.html`: the generator
|
|
10
|
+
* (lib/shim/engine/artifact-shell.js, ARTIFACT_SCENE) embeds THIS file's text verbatim.
|
|
11
|
+
*
|
|
12
|
+
* The parts are setupDoubleSlit's at its defaults (slit screen at 1.5 λ, an absorbing start wall,
|
|
13
|
+
* the slit light source at a 10° step, the detector 3 λ past the screen), so the seeded run is
|
|
14
|
+
* that factory's, tick for tick (lib/shim/engine/test/artifact-shell-sketch.test.mjs holds it on
|
|
15
|
+
* the WASM engine). Zero new physics.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { CollisionTypes, DetectorWall, LargeObject, SlitLightSource, SlitScreen } from '@descix/frqtl-web/parts';
|
|
19
|
+
|
|
20
|
+
export default function buildExperiment(exp) {
|
|
21
|
+
var S = 480, WAVELENGTH = 64; // THE WORLD this scene states (its co-init call below) and its one geometry knob
|
|
22
|
+
|
|
23
|
+
exp.createCanvas(S, S, { dimension: 2, wavelengthScale: Math.floor(WAVELENGTH / 16) }); // a 2D canvas + universe, S x S
|
|
24
|
+
var u = exp.universe;
|
|
25
|
+
|
|
26
|
+
var slits = new SlitScreen(u, WAVELENGTH * 1.5, WAVELENGTH, S, WAVELENGTH, true, 1);
|
|
27
|
+
new LargeObject(u, [0, 0, 10, S], CollisionTypes.ABSORB, [], 'start_wall', 'closest', null, 1, [120, 120, 120]);
|
|
28
|
+
var light = new SlitLightSource(u, slits, 10);
|
|
29
|
+
new DetectorWall(u, u.universe_rect, slits.wall1.rect.left + slits.wall1.rect.w + 3 * WAVELENGTH);
|
|
30
|
+
u.experimentTickFunctions.push(light.getTickFunction());
|
|
31
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — One Emitter at the Canonical Leg
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung dse-one-emitter).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ── H1 AT THE CANONICAL DETECTOR LEG ─────────────────────────────────────────
|
|
14
|
+
//
|
|
15
|
+
// The competence baseline: the most basic thing, done right, seen with eyes. One
|
|
16
|
+
// emitter, then two, with the plate PRE-COMPUTED at the leg the ring formula was
|
|
17
|
+
// written for and the count-by-y chart registered to that plate. No barrier, no
|
|
18
|
+
// slits, no tunnels — nothing between the source and the plate, so a band on the
|
|
19
|
+
// chart can have come from nothing but the walk.
|
|
20
|
+
//
|
|
21
|
+
// WHY THESE EXIST BESIDE `dse-tunnel-rebuild`. That rung places its plate at
|
|
22
|
+
// D = 155 px against λ_eff = 128 px — 4.84× the reference leg — which pushes the
|
|
23
|
+
// low orders inside the leg where they never reach the plate at all, so only high-m
|
|
24
|
+
// rings can land. These two rungs put the plate ON the reference leg instead.
|
|
25
|
+
//
|
|
26
|
+
// THE GEOMETRY, and it is not re-derived here. λ is the ONLY length; every other
|
|
27
|
+
// length below is a dimensionless constant × λ (the `published/dse/scene.js`
|
|
28
|
+
// discipline). The ring ladder quoted in CLAUDE.md,
|
|
29
|
+
//
|
|
30
|
+
// r_m = (λ/4)·√((2m+1)² + 1) [slant, from the emission point]
|
|
31
|
+
//
|
|
32
|
+
// is the hypotenuse of a right triangle whose TRANSVERSE leg is ρ_m = (2m+1)·λ/4
|
|
33
|
+
// and whose AXIAL leg is λ/4 — the "+1" under the root IS the (λ/4)² axial term.
|
|
34
|
+
// Solve for the axial leg order by order and every order returns λ/4, so the leg is
|
|
35
|
+
// not fitted per-order: it is the one leg the formula was written for. At that leg
|
|
36
|
+
// the on-plate bands are ρ_m = (2m+1)·λ/4 from the source's own y, spacing λ/2.
|
|
37
|
+
//
|
|
38
|
+
// MEASURED, not assumed. `published/dse/h1/` ran exactly this apparatus (bare
|
|
39
|
+
// source, plate at D = λ/4 from the emitter, seed 1, detector 'hits') at λ = 160 /
|
|
40
|
+
// 320 / 640 and found a symmetric feature on the plate at |Δy| = λ/4 − 4 px — 36 /
|
|
41
|
+
// 76 / 156 px — holding that offset to the pixel across a 4× sweep while moving 120
|
|
42
|
+
// px in absolute terms. The raw histogram's shape is: a flat PLATEAU across the
|
|
43
|
+
// centre, a sharp MINIMUM just inside ±λ/4, and a PEAK riding immediately outside it
|
|
44
|
+
// at ±λ/4. At λ=640, counts by Δy/λ: −0.250 → 1107 | −0.225 → 306 | 0.000 → 1011 |
|
|
45
|
+
// +0.225 → 297 | +0.250 → 1125. At λ=320 the same shape, shallower. TWO points on
|
|
46
|
+
// the wall at ¼ λ_eff, with a plateau — not a hole — between them.
|
|
47
|
+
//
|
|
48
|
+
// AND THAT IS WHY λ IS 320 HERE. The dip-to-peak separation is 0.025λ: 16 px at
|
|
49
|
+
// λ=640, 8 px at λ=320, 4 px at λ=160 — which is why the λ=160 run reads as a broad
|
|
50
|
+
// hump with the feature only recoverable by a windowed search on a smoothed profile.
|
|
51
|
+
// Below ~λ=300 this stage cannot resolve the thing it exists to show. So this rung
|
|
52
|
+
// shows ONE well-resolved band pair per source rather than three mushy ones. The 4 px
|
|
53
|
+
// pedestal does NOT scale with λ (it is engine scale — a quantum steps ≤2 px/axis/
|
|
54
|
+
// tick) and no mechanism is offered for it here. The m ≥ 1 rungs of the ladder are
|
|
55
|
+
// NOT established: of the H1 runs' 8 predicted positions, 3 matched, and the m=1
|
|
56
|
+
// positions carried minima.
|
|
57
|
+
//
|
|
58
|
+
// THE LEG IS ONE NAMED CONSTANT (`legFrac`), and so is the plate's DETECTOR MODE —
|
|
59
|
+
// both so they can be moved deliberately from a one-line edit and the consequence
|
|
60
|
+
// re-derived, rather than being literals nobody chose.
|
|
61
|
+
//
|
|
62
|
+
// The beam is the `dse-tunnel-rebuild` beam and the H1 beam, unchanged: wavelength
|
|
63
|
+
// arg 3.2 → mass 5 / capacity 16 → fullness 0.3125, which LightColor renders yellow,
|
|
64
|
+
// and λ_eff = 3.2 × wavelengthScale. The leaves are emitted COLOURLESS; the colour
|
|
65
|
+
// is DERIVED from fullness, never forced.
|
|
66
|
+
//
|
|
67
|
+
// 2D ONLY, deliberately. There is no `modes` strip and no `exp.is3D` guard: these
|
|
68
|
+
// rungs declare one world each. A second dimensional variant would be a separate
|
|
69
|
+
// recipe declaration, not a mode.
|
|
70
|
+
// THE PAIR. `dse-two-emitters.js` is the SAME apparatus with NSRC = 2. The two files differ ONLY in
|
|
71
|
+
// the title lines and the `var NSRC` line — lib/frqtl/scenes/test/scene-files.test.mjs
|
|
72
|
+
// fails if any other line diverges, so the A/B the pair exists to make stays an A/B.
|
|
73
|
+
|
|
74
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
75
|
+
|
|
76
|
+
export default function buildExperiment(exp) {
|
|
77
|
+
var NSRC = 1;
|
|
78
|
+
var CW = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
79
|
+
// ── THE ONLY LENGTH ─────────────────────────────────────────────────────
|
|
80
|
+
// lamPx is lambda_eff in pixels. 320 is chosen for RESOLUTION, not for how many
|
|
81
|
+
// orders fit: the feature this rung exists to show (the minimum just inside the
|
|
82
|
+
// band and the peak riding outside it) is 0.025*lambda wide, which is 8 px at
|
|
83
|
+
// lambda 320 and 3 px at lambda 128. A stage that cannot separate them shows a
|
|
84
|
+
// smooth hump and leaves you unable to say whether the physics or the sampling
|
|
85
|
+
// was at fault. One well-resolved pair beats three mushy ones.
|
|
86
|
+
var lamPx = 320;
|
|
87
|
+
var lamArg = 3.2; // capacity/mass -> fullness 1/3.2 = 0.3125 (yellow)
|
|
88
|
+
var fwl = 4 * (lamPx / lamArg); // 400 -> wavelengthScale floor(400/4) = 100
|
|
89
|
+
var wlScale = Math.floor(fwl / 4); // 100
|
|
90
|
+
if (Math.abs(lamArg * wlScale - lamPx) > 1e-9) {
|
|
91
|
+
throw new Error('[dse-h1] lamPx ' + lamPx + ' is not realisable as lambda_eff: the engine ' +
|
|
92
|
+
'can only produce wavelengthArg x floor(fwl/4) = ' + (lamArg * wlScale) + '. (FAIL-LOUD.)');
|
|
93
|
+
}
|
|
94
|
+
// ── THE DETECTOR LEG — ONE NAMED CONSTANT ───────────────────────────────
|
|
95
|
+
// D = legFrac * lambda, measured from the SOURCE (there is no barrier, so the
|
|
96
|
+
// emitter itself is the reference point of the ladder). legFrac = 1/4 is the
|
|
97
|
+
// leg the ring formula was written for; see the block comment above.
|
|
98
|
+
var legFrac = 1 / 4;
|
|
99
|
+
var D = Math.round(lamPx * legFrac); // 80
|
|
100
|
+
var srcX = Math.round(lamPx / 16); // 20 — launch room; the canvas edge is the back sink
|
|
101
|
+
var cy = Math.round(H / 2); // 200 — the source plane's midline
|
|
102
|
+
var detX = srcX + D; // 100 — the plate's FRONT FACE
|
|
103
|
+
// PLATE THICKNESS IS AN OPTICAL CHOICE UNDER A FLUX DETECTOR, not trim. A flux
|
|
104
|
+
// probe counts every tick a quantum overlaps it, so it integrates the density
|
|
105
|
+
// ACROSS its thickness — and the ring crest is a CIRCLE about the source, not a
|
|
106
|
+
// plane. At the m=0 band the crest's slope is dy/dx = -D/y = -1, so a slab t px
|
|
107
|
+
// thick smears the band over ~t px of y. Against an 8 px feature a lambda/8 (40
|
|
108
|
+
// px) plate would guarantee mush. lambda/64 = 5 px keeps the smear sub-feature
|
|
109
|
+
// and is still unjumpable (a quantum steps at most 2 px/axis/tick). A 'hits'
|
|
110
|
+
// plate would not care — it counts at first contact, i.e. at the front face —
|
|
111
|
+
// which is exactly why the reference H1 runs could afford a lambda/8 slab.
|
|
112
|
+
var detW = Math.round(lamPx / 64); // 5 — plate thickness
|
|
113
|
+
var sinkW = Math.round(lamPx / 16); // 20 — far-edge sink thickness
|
|
114
|
+
// TWO-SOURCE SEPARATION. Two sources reinforce when their separation is a
|
|
115
|
+
// multiple of lambda/2, and the coupling falls as (lambda/d)^2, so lambda/2 is
|
|
116
|
+
// the strongest case. At this leg it also puts a band of EACH source on the
|
|
117
|
+
// midline y = H/2 (source 1's lower band and source 2's upper band coincide
|
|
118
|
+
// there) — the doubly-reinforced central peak, equidistant from both emitters,
|
|
119
|
+
// exactly where the one-source rung has no peak at all.
|
|
120
|
+
var sep = lamPx / 2; // 160 -> sources at y 120 and 280
|
|
121
|
+
var srcYs = (NSRC === 2) ? [cy - sep / 2, cy + sep / 2] : [cy];
|
|
122
|
+
// ── THE LIGHT SOURCE ────────────────────────────────────────────────────
|
|
123
|
+
// preset 'hemisphere' = 180 degrees of FORWARD (+X) emission: the SAME
|
|
124
|
+
// ball-rejected sampler as the omni source with the forward axis' range halved,
|
|
125
|
+
// so nothing about the walk changes, it is simply pointed at the plate. The
|
|
126
|
+
// default emission box is 0 for this preset, so the source is a single PIXEL —
|
|
127
|
+
// load-bearing, because any spread at birth blurs every band by that spread.
|
|
128
|
+
// NO START WALL: the backward population is empty by construction, and `wrap` is
|
|
129
|
+
// false, so the canvas edge kills whatever transverse wiggle out-runs its
|
|
130
|
+
// forward component. One sink, not two.
|
|
131
|
+
var SRC = {
|
|
132
|
+
preset: 'hemisphere', is2D: true, chargeMode: 'random',
|
|
133
|
+
wavelength: lamArg, fundamentalDimension: 2, fundamentalWaveLength: fwl,
|
|
134
|
+
burstSize: 8, emitEveryTicks: 1
|
|
135
|
+
};
|
|
136
|
+
exp.createCanvas(CW, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
137
|
+
for (var si = 0; si < srcYs.length; si++) {
|
|
138
|
+
exp.addObject('SphereEmitter', Object.assign({}, SRC, { center: [srcX, Math.round(srcYs[si])] }));
|
|
139
|
+
}
|
|
140
|
+
// FAR-EDGE SINK. The plate below counts WITHOUT removing, so quanta fly on past
|
|
141
|
+
// it; this is the experiment-boundary sink downstream of the plate. It is not
|
|
142
|
+
// load-bearing (wrap is false, so the canvas edge would remove them anyway) — it
|
|
143
|
+
// is where the population bound is STATED rather than inferred.
|
|
144
|
+
exp.addObject('Wall', { rect: [CW - sinkW, 0, sinkW, H], collisionBehavior: 'destroy',
|
|
145
|
+
name: 'far_sink', color: [120, 120, 120], alpha: 40 });
|
|
146
|
+
// ── THE PLATE — A COUNTING WALL THAT DOES NOT ABSORB ────────────────────
|
|
147
|
+
// ONE NAMED CONSTANT, so the reading can be swapped in a single edit and the two
|
|
148
|
+
// instruments compared rather than argued about:
|
|
149
|
+
// 'transparent-probe' = collisionBehavior 'nothing' + mark/count. FLUX. The
|
|
150
|
+
// quantum is counted on every tick it overlaps the plate and then flies on,
|
|
151
|
+
// so the reading is the OCCUPANCY INTEGRAL at the plate plane — the density
|
|
152
|
+
// at that plane. This is the shipped choice.
|
|
153
|
+
// 'detector-screen' = destroy + count. HITS. Each quantum is counted once, at
|
|
154
|
+
// arrival, and removed: a transit census, the headcount a photographic plate
|
|
155
|
+
// would report. It is what the H1 reference runs used.
|
|
156
|
+
// NEITHER ABSORBS. 'absorb' stores the arriving mass and re-radiates it — that is
|
|
157
|
+
// the photoelectric experiment, and it saturates.
|
|
158
|
+
var detectorPreset = 'transparent-probe';
|
|
159
|
+
// markTag — the plate's PROVENANCE stamp. The mark hook fires ONLY when the
|
|
160
|
+
// collision trigger does (genuine overlap), whereas the engine buffers every
|
|
161
|
+
// frame inside the LO's mb_w-PADDED search rect whether it triggered or not. So a
|
|
162
|
+
// raw frame count off the buffer is a padded-occupancy reading and the
|
|
163
|
+
// mark-filtered count is the trigger-gated census: two different instruments, and
|
|
164
|
+
// an offline reader needs the tag to tell them apart.
|
|
165
|
+
var screen = exp.addObject('Wall', { rect: [detX, 0, detW, H],
|
|
166
|
+
preset: detectorPreset, markTag: 'plate', name: 'screen', color: [120, 200, 120] });
|
|
167
|
+
// ── THE INSTRUMENT — the live count-by-y histogram, REGISTERED TO THE PLATE ──
|
|
168
|
+
// The panel takes the plate's OWN rect y-origin (0) and height (H), so the two
|
|
169
|
+
// are co-extensive on the bin axis at any canvas size, and it starts just past
|
|
170
|
+
// the plate's far face so it reads as sitting behind it. orientation 90 = bars
|
|
171
|
+
// stacked along y; the chart contract reverses the y scale when horizontal, so
|
|
172
|
+
// bin 0 (the SMALLEST y) sits at the TOP, matching the y-DOWN world.
|
|
173
|
+
// HONEST LIMIT (inherited from the shipped 2D block, and it is real): the bin
|
|
174
|
+
// axis is a CATEGORY axis over the bins that actually received hits, not a linear
|
|
175
|
+
// 0..H axis. Registration is exact in ORDER and DIRECTION always, and exact in
|
|
176
|
+
// absolute screen height only insofar as the hits span the wall. Here the plate
|
|
177
|
+
// is a full-height flux probe under a 180-degree source, so the occupied span
|
|
178
|
+
// fills the wall within a few hundred ticks and the axis is very nearly linear —
|
|
179
|
+
// but the ladder positions below must be read off the raw census, never measured
|
|
180
|
+
// with a ruler on the panel.
|
|
181
|
+
// SERIES 'line', NOT 'bar', AND THAT IS A MEASUREMENT NOT A TASTE. The bins are
|
|
182
|
+
// per-pixel y, so a full-height plate produces ~400 categories in a ~240 px plot:
|
|
183
|
+
// each bar is sub-pixel TALL, its 0.55 fill alpha is diluted by the coverage, and
|
|
184
|
+
// the profile collapses into a faint sliver hugging the axis. Measured identically
|
|
185
|
+
// on the shipped 1C Counting Wall rung, so it is the chart contract's behaviour at
|
|
186
|
+
// this bin count and not something these rungs introduced. A line joins the same 400
|
|
187
|
+
// points into one continuous curve, which reads as the profile it is. CAVEAT, stated
|
|
188
|
+
// because it bites: Chart.js does not begin a LINE value axis at zero, so once the
|
|
189
|
+
// profile's floor rises the curve compresses toward the left edge. Read positions off
|
|
190
|
+
// it, not magnitudes; magnitudes come from getCollisionHistogram().
|
|
191
|
+
exp.addObject('LiveChart', {
|
|
192
|
+
source: screen, orientation: 90,
|
|
193
|
+
rect: [detX + detW + 8, 0, Math.max(60, CW - (detX + detW + 8) - 8), H],
|
|
194
|
+
series: 'line', label: 'count by y', refreshMs: 100, color: [24, 32, 46], alpha: 225
|
|
195
|
+
});
|
|
196
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — One Slit at the Canonical Leg
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung dse-one-slit).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ── ONE SLIT AT THE CANONICAL LEG ────────────────────────────────────────────
|
|
14
|
+
//
|
|
15
|
+
// The companion arm to `dse-two-slits`: the IDENTICAL apparatus (same emitter, same
|
|
16
|
+
// barrier x-position, same slit-1 aperture at y=120, same leg D, same plate, same
|
|
17
|
+
// chart) with slit 2 (the y=280 aperture) WALLED OFF — a single opaque barrier
|
|
18
|
+
// segment stands where `wall2` + the slit-2 `Tunnel` + `wall3` stood in
|
|
19
|
+
// `dse-two-slits`. Nothing else moves: this rung and `dse-two-slits` differ by
|
|
20
|
+
// exactly one declared thing — how many apertures the barrier has.
|
|
21
|
+
//
|
|
22
|
+
// PRE-FLIGHT PREDICTION (recorded before capture; see the ring ladder in
|
|
23
|
+
// the header comment of scenes/dse-one-emitter.js, and `dse-one-emitter`'s use of the same
|
|
24
|
+
// ladder off-centre here): a slit's exit is a phase-locked point source at its own
|
|
25
|
+
// y (the `dse-tunnel-rebuild` composition), so with ONLY slit 1 open the surviving
|
|
26
|
+
// population is governed by ONE point source at y=120, D=lambda_eff/4=80 past the
|
|
27
|
+
// exit plane. Its own ladder (y0 +/- lambda/4, +/- 3*lambda/4, ...) places bands at
|
|
28
|
+
// y = 120-80=40, 120+80=200, and (the m=1 term) 120+240=360 -- the m=1 LOWER term
|
|
29
|
+
// (120-240=-120) falls off the 400px canvas and does not exist.
|
|
30
|
+
// PREDICTED: three bands at y=40, y=200, y=360; a dip/minimum on either side of
|
|
31
|
+
// each; NO claim about height relative to `dse-two-slits` — degree of
|
|
32
|
+
// reinforcement is a different question this scene does not ask. The claim under
|
|
33
|
+
// test is PRESENCE AND POSITION: does structure appear at all with one aperture,
|
|
34
|
+
// and does it land on this specific off-centre ladder rather than smearing into
|
|
35
|
+
// a single broad structureless lobe centred on y=120 (the naive "single-slit
|
|
36
|
+
// diffraction hump" expectation, which this engine's per-source ring mechanism
|
|
37
|
+
// does NOT predict — if that is what appears instead, it is a real null.)
|
|
38
|
+
|
|
39
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
40
|
+
|
|
41
|
+
export default function buildExperiment(exp) {
|
|
42
|
+
// One Slit -- the SAME apparatus as dse-two-slits with slit 2 WALLED OFF. Barrier =
|
|
43
|
+
// Wall segments (destroy); the surviving slit 1 = a directed Tunnel with the exit
|
|
44
|
+
// gate collapsed + phase-reset (identical to dse-two-slits' slit1_channel).
|
|
45
|
+
var CW = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
46
|
+
// ── THE ONLY LENGTH — IDENTICAL to the canonical-leg pair and dse-two-slits ─────
|
|
47
|
+
var lamPx = 320;
|
|
48
|
+
var lamArg = 3.2; // fullness 1/3.2 = 0.3125 -> yellow
|
|
49
|
+
var fwl = 4 * (lamPx / lamArg); // 400 -> wavelengthScale floor(400/4) = 100
|
|
50
|
+
var wlScale = Math.floor(fwl / 4); // 100
|
|
51
|
+
if (Math.abs(lamArg * wlScale - lamPx) > 1e-9) {
|
|
52
|
+
throw new Error('[dse-one-slit] lamPx ' + lamPx + ' is not realisable as lambda_eff: the ' +
|
|
53
|
+
'engine can only produce wavelengthArg x floor(fwl/4) = ' + (lamArg * wlScale) + '. (FAIL-LOUD.)');
|
|
54
|
+
}
|
|
55
|
+
// ── SLIT 1's POSITION — IDENTICAL to dse-two-slits' slit-1 aperture ─────────────
|
|
56
|
+
// sep is kept only to DERIVE slitCY1 at the exact same y dse-two-slits used (120) —
|
|
57
|
+
// there is no slit 2 in this rung, so it is never used to place a second aperture.
|
|
58
|
+
var legFrac = 1 / 4;
|
|
59
|
+
var D = Math.round(lamPx * legFrac); // 80 — the SAME leg, measured from the
|
|
60
|
+
// (single) slit EXIT plane
|
|
61
|
+
var sep = lamPx / 2; // 160 — dse-two-slits' source separation,
|
|
62
|
+
// reused ONLY to derive slit 1's y below
|
|
63
|
+
var cy = Math.round(H / 2); // 200
|
|
64
|
+
var slitCY1 = cy - sep / 2; // 120 — IDENTICAL y to dse-two-slits' slit 1
|
|
65
|
+
// ── THE BARRIER — one opaque plate, ONE slit aperture (slit 2 walled off) ───────
|
|
66
|
+
var barrierW = Math.round(lamPx / 8); // 40 — plate thickness
|
|
67
|
+
var slitH = Math.round(lamPx / 8); // 40 — slit aperture height (before liners)
|
|
68
|
+
var linerH = Math.round(lamPx / 64); // 5 — liner thickness (unjumpable: max step 2px/tick)
|
|
69
|
+
var bore = slitH - 2 * linerH; // 30 — the clear aperture between the liners
|
|
70
|
+
var srcX = Math.round(lamPx / 16); // 20 — the emitter, well behind the barrier
|
|
71
|
+
var barrierL = srcX + Math.round(lamPx / 2); // 180 — half a wavelength of travel room
|
|
72
|
+
var slit1Top = slitCY1 - slitH / 2; // 100 -- slit 1 spans y 100..140
|
|
73
|
+
var exitX = barrierL + barrierW; // 220 — the slit EXIT plane (barrier back face)
|
|
74
|
+
var detX = exitX + D; // 300 — SAME leg as dse-two-slits
|
|
75
|
+
var detW = Math.round(lamPx / 64); // 5 — plate thickness (flux probe, sub-feature)
|
|
76
|
+
var sinkW = Math.round(lamPx / 16); // 20 — far-edge sink thickness
|
|
77
|
+
// ── THE LIGHT SOURCE — ONE emitter, behind the barrier, lighting the one slit ───
|
|
78
|
+
var SRC = {
|
|
79
|
+
preset: 'hemisphere', is2D: true, chargeMode: 'random', center: [srcX, cy],
|
|
80
|
+
wavelength: lamArg, fundamentalDimension: 2, fundamentalWaveLength: fwl,
|
|
81
|
+
burstSize: 8, emitEveryTicks: 1
|
|
82
|
+
};
|
|
83
|
+
exp.createCanvas(CW, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
84
|
+
exp.addObject('SphereEmitter', SRC);
|
|
85
|
+
// Barrier segments — opaque destroy sinks, leaving ONE slit gap (slit 1 only). Where
|
|
86
|
+
// dse-two-slits had wall2 (a short strip between the two slits) + a slit-2 Tunnel +
|
|
87
|
+
// wall3 (below slit 2), this rung has ONE solid wall from slit 1's bottom liner all
|
|
88
|
+
// the way to the far edge — slit 2's aperture is WALLED, not narrowed or moved.
|
|
89
|
+
exp.addObject('Wall', { rect: [barrierL, 0, barrierW, slit1Top],
|
|
90
|
+
collisionBehavior: 'destroy', name: 'wall1', color: [200, 60, 60] });
|
|
91
|
+
exp.addObject('Wall', { rect: [barrierL, slit1Top + slitH, barrierW, H - (slit1Top + slitH)],
|
|
92
|
+
collisionBehavior: 'destroy', name: 'wall2_blocked_slit2', color: [150, 40, 40] });
|
|
93
|
+
// The one surviving slit channel — directed Tunnel, exit gate COLLAPSED + PHASE-RESET
|
|
94
|
+
// (the dse-tunnel-rebuild composition): its own mouth stands in for a point source at
|
|
95
|
+
// y=120, phase-locked — what a SphereEmitter would do at that pixel.
|
|
96
|
+
exp.addObject('Tunnel', {
|
|
97
|
+
left: barrierL, top: slit1Top + linerH, length: barrierW, bore: bore,
|
|
98
|
+
wallThickness: linerH, axis: 'x', mode: 'directed',
|
|
99
|
+
exitCollapse: true, exitRecenter: true, exitPhaseReset: true,
|
|
100
|
+
name: 'slit1_channel', color: [80, 140, 220]
|
|
101
|
+
});
|
|
102
|
+
// FAR-EDGE SINK — the same experiment-boundary sink the canonical-leg pair uses.
|
|
103
|
+
exp.addObject('Wall', { rect: [CW - sinkW, 0, sinkW, H], collisionBehavior: 'destroy',
|
|
104
|
+
name: 'far_sink', color: [120, 120, 120], alpha: 40 });
|
|
105
|
+
// ── THE PLATE — SAME instrument as dse-two-slits: a counting wall that does NOT
|
|
106
|
+
// absorb, at the SAME leg D past the new source plane (the one slit exit).
|
|
107
|
+
var screen = exp.addObject('Wall', { rect: [detX, 0, detW, H],
|
|
108
|
+
preset: 'transparent-probe', markTag: 'plate', name: 'screen', color: [120, 200, 120] });
|
|
109
|
+
exp.addObject('LiveChart', {
|
|
110
|
+
source: screen, orientation: 90,
|
|
111
|
+
rect: [detX + detW + 8, 0, Math.max(60, CW - (detX + detW + 8) - 8), H],
|
|
112
|
+
series: 'line', label: 'count by y', refreshMs: 100, color: [24, 32, 46], alpha: 225
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — Photoelectric
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung dse-photoelectric).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ── DSE Lab
|
|
14
|
+
// — the photoelectric experiment + THE double-slit, built on the full new
|
|
15
|
+
// component set (SphereEmitter chargeMode random, v2 Walls + presets, in-scene
|
|
16
|
+
// LiveChart). The single canonical double-slit is 2C (on the Tunnel object); the
|
|
17
|
+
// old Light-Lab r9 and DSE 2A double-slit rungs were retired.
|
|
18
|
+
// 2B — the photoelectric effect on the new component set (the CURRENT absorb/radiate
|
|
19
|
+
// drip, NO workFunction threshold).
|
|
20
|
+
// An incoming light stream charges an ABSORBING Wall (ABSORB v2 real backing-frame mass:
|
|
21
|
+
// fills to capacity, then per-tick radiates lower-dimension leaves out its back with
|
|
22
|
+
// p=1/capacity, mass conserved). A LiveChart on the absorber's absorb-stats series shows
|
|
23
|
+
// stored/absorbed/radiated as the wall charges; a second Detector Screen BEHIND the wall
|
|
24
|
+
// counts the RADIATED quanta (the photoelectron detector) — and its own live arrivals chart
|
|
25
|
+
// shows them being counted. SU1 — the LO5 absorb/radiate golden, composed, zero new physics.
|
|
26
|
+
|
|
27
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
28
|
+
|
|
29
|
+
export default function buildExperiment(exp) {
|
|
30
|
+
// 2B (Photoelectric) — light stream → ABSORB wall (charges + radiates) → a Detector
|
|
31
|
+
// Screen BEHIND counting the radiated photoelectrons. Two live charts: the wall's
|
|
32
|
+
// charge ledger (absorberStats) and the rear detector's arrivals.
|
|
33
|
+
var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
34
|
+
var cy = Math.round(H / 2);
|
|
35
|
+
// The incoming light stream: a thin +x beam of oppositely-charged dim-2 leaves. A
|
|
36
|
+
// SPARSE flux (1 leaf / 2 ticks) keeps the slow absorb/radiate drip radiation-dominated
|
|
37
|
+
// (a dense beam saturates the wall, which then leaks past it — the deferred emergent-
|
|
38
|
+
// probability threshold would gate that; see 2B prose).
|
|
39
|
+
var SRC = {
|
|
40
|
+
preset: 'particle-walk', is2D: true, center: [Math.round(W * 0.1), cy],
|
|
41
|
+
chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
|
|
42
|
+
burstSize: 1, emitEveryTicks: 2
|
|
43
|
+
};
|
|
44
|
+
exp.createCanvas(W, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
45
|
+
exp.addObject('SphereEmitter', SRC);
|
|
46
|
+
// The absorbing wall (ABSORB v2): charges up to capacity, then radiates lower-dim
|
|
47
|
+
// leaves out its back (mass conserved) — the photocathode.
|
|
48
|
+
var absorber = exp.addObject('Wall', {
|
|
49
|
+
rect: [Math.round(W * 0.45), Math.round(H * 0.25), 20, Math.round(H * 0.5)],
|
|
50
|
+
collisionBehavior: 'absorb', capacity: 64, name: 'absorber', color: [150, 90, 210]
|
|
51
|
+
});
|
|
52
|
+
// The photoelectron detector: a Detector Screen (destroy + count) BEHIND the absorber,
|
|
53
|
+
// counting the radiated quanta that emerge past it.
|
|
54
|
+
var photodetector = exp.addObject('Wall', {
|
|
55
|
+
rect: [Math.round(W * 0.72), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
|
|
56
|
+
preset: 'detector-screen', name: 'photodetector', color: [120, 200, 120]
|
|
57
|
+
});
|
|
58
|
+
// The wall's charge ledger (stored / absorbed / radiated) — the absorb-stats series.
|
|
59
|
+
exp.addObject('LiveChart', {
|
|
60
|
+
source: absorber, projector: 'absorberStats',
|
|
61
|
+
rect: [Math.round(W * 0.04), Math.round(H * 0.03), Math.round(W * 0.32), Math.round(H * 0.28)],
|
|
62
|
+
series: 'bar', label: 'wall charge (stored/absorbed/radiated)', refreshMs: 100, color: [30, 22, 44], alpha: 230
|
|
63
|
+
});
|
|
64
|
+
// The photoelectron count beyond the wall — the rear detector's live arrivals.
|
|
65
|
+
exp.addObject('LiveChart', {
|
|
66
|
+
source: photodetector,
|
|
67
|
+
rect: [Math.round(W * 0.63), Math.round(H * 0.03), Math.round(W * 0.32), Math.round(H * 0.28)],
|
|
68
|
+
series: 'bar', label: 'photoelectrons counted', refreshMs: 100, color: [22, 40, 30], alpha: 230
|
|
69
|
+
});
|
|
70
|
+
}
|