@descix/frqtl-node 0.6.1-p3.3 → 0.7.0-p0
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 +3 -3
- package/README.md +4 -3
- package/dist/dist-manifest.json +26 -56
- package/dist/frqtl/experiments/_shared/empty-sketch.js +17 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +6 -6
- package/dist/frqtl/objects/setup/setupFxSmoke.js +2 -2
- package/dist/frqtl/objects/setup/setupG3Collide.js +1 -1
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +2 -2
- package/dist/frqtl/objects/v1-replacements.js +1 -1
- package/dist/frqtl/parts/OvenBox.js +2 -2
- package/dist/frqtl/parts/SphereEmitter.js +1 -1
- package/dist/shim/engine/router.js +18 -6
- package/dist/shim/engine/site-assemble.mjs +268 -0
- package/dist/shim/engine/site-graph.js +42 -14
- package/dist/shim/host/device.js +1 -1
- package/dist/shim/index.js +3 -7
- package/dist/shim/notebook/catalog.js +2 -2
- package/dist/shim/notebook/format.js +1 -1
- package/dist/shim/notebook/knob.js +1 -1
- package/dist/shim/notebook/notebook-knobs.js +1 -1
- package/dist/shim/project/specifiers.js +8 -8
- package/dist/shim/registry/copy-gate.js +3 -3
- package/dist/shim/registry/index.js +9 -12
- package/dist/shim/registry/roster-link.js +142 -0
- package/dist/shim/registry/roster.js +595 -0
- package/dist/shim/registry/scene-source.js +80 -105
- package/dist/shim/registry/studio-landing.js +15 -7
- package/dist/shim/render/sun-look.js +1 -2
- package/llms.txt +4 -2
- package/package.json +1 -1
- package/dist/frqtl/scenes/absorbing-wall-3d.js +0 -37
- package/dist/frqtl/scenes/absorbing-wall.js +0 -36
- package/dist/frqtl/scenes/atomic-model.js +0 -91
- package/dist/frqtl/scenes/big-bang.js +0 -49
- package/dist/frqtl/scenes/blackbody.js +0 -84
- package/dist/frqtl/scenes/circuit-sat.js +0 -131
- package/dist/frqtl/scenes/collision-matrix-3d.js +0 -110
- package/dist/frqtl/scenes/counting-wall-3d.js +0 -40
- package/dist/frqtl/scenes/counting-wall.js +0 -42
- package/dist/frqtl/scenes/double-slit.js +0 -34
- package/dist/frqtl/scenes/dse-one-emitter.js +0 -200
- package/dist/frqtl/scenes/dse-one-slit.js +0 -118
- package/dist/frqtl/scenes/dse-photoelectric.js +0 -74
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +0 -279
- package/dist/frqtl/scenes/dse-two-emitters.js +0 -201
- package/dist/frqtl/scenes/dse-two-slits.js +0 -136
- package/dist/frqtl/scenes/empty.js +0 -17
- package/dist/frqtl/scenes/laser-buildup.js +0 -56
- package/dist/frqtl/scenes/mirror-3d.js +0 -60
- package/dist/frqtl/scenes/mirror-route.js +0 -164
- package/dist/frqtl/scenes/mirror.js +0 -42
- package/dist/frqtl/scenes/omni-3d.js +0 -30
- package/dist/frqtl/scenes/omni-source.js +0 -25
- package/dist/frqtl/scenes/self-interference.js +0 -42
- package/dist/frqtl/scenes/smoking-gun.js +0 -126
- package/dist/frqtl/scenes/straight-walk.js +0 -25
- package/dist/frqtl/scenes/sun.js +0 -42
- package/dist/frqtl/scenes/two-source.js +0 -42
- package/dist/frqtl/scenes/wave.js +0 -29
- package/dist/shim/engine/artifact-shell.js +0 -211
- package/dist/shim/registry/exhibits.js +0 -436
- package/dist/shim/registry/scene-roster.js +0 -35
- package/dist/shim/registry/showcase-link.js +0 -61
- package/dist/shim/registry/walkthrough.js +0 -752
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SCENE — Two Emitters 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 (`export const parts` + `exp.declare`, or `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-two-emitters).
|
|
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-one-emitter.js` is the SAME apparatus with ONE source entry. The two files differ ONLY in the
|
|
71
|
-
// title lines, this note, and their `source*` entries of `parts` (with the world call naming the first
|
|
72
|
-
// of them) — lib/frqtl/scenes/test/scene-files.test.mjs fails if anything else diverges, so the A/B the
|
|
73
|
-
// pair exists to make stays an A/B.
|
|
74
|
-
|
|
75
|
-
import { SphereEmitter, Wall } from '@descix/frqtl-web/parts';
|
|
76
|
-
|
|
77
|
-
const CW = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
78
|
-
// ── THE ONLY LENGTH ─────────────────────────────────────────────────────
|
|
79
|
-
// lamPx is lambda_eff in pixels. 320 is chosen for RESOLUTION, not for how many
|
|
80
|
-
// orders fit: the feature this rung exists to show (the minimum just inside the
|
|
81
|
-
// band and the peak riding outside it) is 0.025*lambda wide, which is 8 px at
|
|
82
|
-
// lambda 320 and 3 px at lambda 128. A stage that cannot separate them shows a
|
|
83
|
-
// smooth hump and leaves you unable to say whether the physics or the sampling
|
|
84
|
-
// was at fault. One well-resolved pair beats three mushy ones.
|
|
85
|
-
const lamPx = 320;
|
|
86
|
-
const sinkW = Math.round(lamPx / 16); // 20 — far-edge sink thickness
|
|
87
|
-
|
|
88
|
-
// ── THE DETECTOR LEG — ONE NAMED CONSTANT ───────────────────────────────
|
|
89
|
-
// D = legFrac * lambda, measured from the SOURCE (there is no barrier, so the
|
|
90
|
-
// emitter itself is the reference point of the ladder). legFrac = 1/4 is the
|
|
91
|
-
// leg the ring formula was written for; see the block comment above.
|
|
92
|
-
const legFrac = 1 / 4;
|
|
93
|
-
const D = Math.round(lamPx * legFrac); // 80
|
|
94
|
-
const srcX = Math.round(lamPx / 16); // 20 — launch room; the canvas edge is the back sink
|
|
95
|
-
const detX = srcX + D; // 100 — the plate's FRONT FACE
|
|
96
|
-
// PLATE THICKNESS IS AN OPTICAL CHOICE UNDER A FLUX DETECTOR, not trim. A flux
|
|
97
|
-
// probe counts every tick a quantum overlaps it, so it integrates the density
|
|
98
|
-
// ACROSS its thickness — and the ring crest is a CIRCLE about the source, not a
|
|
99
|
-
// plane. At the m=0 band the crest's slope is dy/dx = -D/y = -1, so a slab t px
|
|
100
|
-
// thick smears the band over ~t px of y. Against an 8 px feature a lambda/8 (40
|
|
101
|
-
// px) plate would guarantee mush. lambda/64 = 5 px keeps the smear sub-feature
|
|
102
|
-
// and is still unjumpable (a quantum steps at most 2 px/axis/tick). A 'hits'
|
|
103
|
-
// plate would not care — it counts at first contact, i.e. at the front face —
|
|
104
|
-
// which is exactly why the reference H1 runs could afford a lambda/8 slab.
|
|
105
|
-
const detW = Math.round(lamPx / 64); // 5 — plate thickness
|
|
106
|
-
// ── THE PLATE — A COUNTING WALL THAT DOES NOT ABSORB ────────────────────
|
|
107
|
-
// ONE NAMED CONSTANT, so the reading can be swapped in a single edit and the two
|
|
108
|
-
// instruments compared rather than argued about:
|
|
109
|
-
// 'transparent-probe' = collisionBehavior 'nothing' + mark/count. FLUX. The
|
|
110
|
-
// quantum is counted on every tick it overlaps the plate and then flies on,
|
|
111
|
-
// so the reading is the OCCUPANCY INTEGRAL at the plate plane — the density
|
|
112
|
-
// at that plane. This is the shipped choice.
|
|
113
|
-
// 'detector-screen' = destroy + count. HITS. Each quantum is counted once, at
|
|
114
|
-
// arrival, and removed: a transit census, the headcount a photographic plate
|
|
115
|
-
// would report. It is what the H1 reference runs used.
|
|
116
|
-
// NEITHER ABSORBS. 'absorb' stores the arriving mass and re-radiates it — that is
|
|
117
|
-
// the photoelectric experiment, and it saturates.
|
|
118
|
-
const detectorPreset = 'transparent-probe';
|
|
119
|
-
|
|
120
|
-
const lamArg = 3.2; // capacity/mass -> fullness 1/3.2 = 0.3125 (yellow)
|
|
121
|
-
const fwl = 4 * (lamPx / lamArg); // 400 -> wavelengthScale floor(400/4) = 100
|
|
122
|
-
const wlScale = Math.floor(fwl / 4); // 100
|
|
123
|
-
const cy = Math.round(H / 2); // 200 — the source plane's midline
|
|
124
|
-
// TWO-SOURCE SEPARATION. Two sources reinforce when their separation is a
|
|
125
|
-
// multiple of lambda/2, and the coupling falls as (lambda/d)^2, so lambda/2 is
|
|
126
|
-
// the strongest case. At this leg it also puts a band of EACH source on the
|
|
127
|
-
// midline y = H/2 (source 1's lower band and source 2's upper band coincide
|
|
128
|
-
// there) — the doubly-reinforced central peak, equidistant from both emitters,
|
|
129
|
-
// exactly where the one-source rung has no peak at all.
|
|
130
|
-
const sep = lamPx / 2; // 160 -> sources at y 120 and 280
|
|
131
|
-
// ── THE LIGHT SOURCE ────────────────────────────────────────────────────
|
|
132
|
-
// preset 'hemisphere' = 180 degrees of FORWARD (+X) emission: the SAME
|
|
133
|
-
// ball-rejected sampler as the omni source with the forward axis' range halved,
|
|
134
|
-
// so nothing about the walk changes, it is simply pointed at the plate. The
|
|
135
|
-
// default emission box is 0 for this preset, so the source is a single PIXEL —
|
|
136
|
-
// load-bearing, because any spread at birth blurs every band by that spread.
|
|
137
|
-
// NO START WALL: the backward population is empty by construction, and `wrap` is
|
|
138
|
-
// false, so the canvas edge kills whatever transverse wiggle out-runs its
|
|
139
|
-
// forward component. One sink, not two.
|
|
140
|
-
const SRC = {
|
|
141
|
-
preset: 'hemisphere', is2D: true, chargeMode: 'random',
|
|
142
|
-
wavelength: lamArg, fundamentalDimension: 2, fundamentalWaveLength: fwl,
|
|
143
|
-
burstSize: 8, emitEveryTicks: 1
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export const parts = {
|
|
147
|
-
source1: [SphereEmitter, { ...SRC, center: [srcX, Math.round(cy - sep / 2)] }],
|
|
148
|
-
source2: [SphereEmitter, { ...SRC, center: [srcX, Math.round(cy + sep / 2)] }],
|
|
149
|
-
// FAR-EDGE SINK. The plate below counts WITHOUT removing, so quanta fly on past
|
|
150
|
-
// it; this is the experiment-boundary sink downstream of the plate. It is not
|
|
151
|
-
// load-bearing (wrap is false, so the canvas edge would remove them anyway) — it
|
|
152
|
-
// is where the population bound is STATED rather than inferred.
|
|
153
|
-
far_sink: [Wall, { rect: [CW - sinkW, 0, sinkW, H], collisionBehavior: 'destroy',
|
|
154
|
-
color: [120, 120, 120], alpha: 40 }],
|
|
155
|
-
// markTag — the plate's PROVENANCE stamp. The mark hook fires ONLY when the
|
|
156
|
-
// collision trigger does (genuine overlap), whereas the engine buffers every
|
|
157
|
-
// frame inside the LO's mb_w-PADDED search rect whether it triggered or not. So a
|
|
158
|
-
// raw frame count off the buffer is a padded-occupancy reading and the
|
|
159
|
-
// mark-filtered count is the trigger-gated census: two different instruments, and
|
|
160
|
-
// an offline reader needs the tag to tell them apart.
|
|
161
|
-
screen: [Wall, { rect: [detX, 0, detW, H],
|
|
162
|
-
preset: detectorPreset, markTag: 'plate', color: [120, 200, 120] }],
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
export default function buildExperiment(exp) {
|
|
166
|
-
if (Math.abs(lamArg * wlScale - lamPx) > 1e-9) {
|
|
167
|
-
throw new Error('[dse-h1] lamPx ' + lamPx + ' is not realisable as lambda_eff: the engine ' +
|
|
168
|
-
'can only produce wavelengthArg x floor(fwl/4) = ' + (lamArg * wlScale) + '. (FAIL-LOUD.)');
|
|
169
|
-
}
|
|
170
|
-
exp.createCanvas(CW, H, SphereEmitter.worldCfg(parts.source1[1])); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
171
|
-
const p = exp.declare(parts);
|
|
172
|
-
// ── THE INSTRUMENT — the live count-by-y histogram, REGISTERED TO THE PLATE ──
|
|
173
|
-
// The panel takes the plate's OWN rect y-origin (0) and height (H), so the two
|
|
174
|
-
// are co-extensive on the bin axis at any canvas size, and it starts just past
|
|
175
|
-
// the plate's far face so it reads as sitting behind it. orientation 90 = bars
|
|
176
|
-
// stacked along y; the chart contract reverses the y scale when horizontal, so
|
|
177
|
-
// bin 0 (the SMALLEST y) sits at the TOP, matching the y-DOWN world.
|
|
178
|
-
// HONEST LIMIT (inherited from the shipped 2D block, and it is real): the bin
|
|
179
|
-
// axis is a CATEGORY axis over the bins that actually received hits, not a linear
|
|
180
|
-
// 0..H axis. Registration is exact in ORDER and DIRECTION always, and exact in
|
|
181
|
-
// absolute screen height only insofar as the hits span the wall. Here the plate
|
|
182
|
-
// is a full-height flux probe under a 180-degree source, so the occupied span
|
|
183
|
-
// fills the wall within a few hundred ticks and the axis is very nearly linear —
|
|
184
|
-
// but the ladder positions below must be read off the raw census, never measured
|
|
185
|
-
// with a ruler on the panel.
|
|
186
|
-
// SERIES 'line', NOT 'bar', AND THAT IS A MEASUREMENT NOT A TASTE. The bins are
|
|
187
|
-
// per-pixel y, so a full-height plate produces ~400 categories in a ~240 px plot:
|
|
188
|
-
// each bar is sub-pixel TALL, its 0.55 fill alpha is diluted by the coverage, and
|
|
189
|
-
// the profile collapses into a faint sliver hugging the axis. Measured identically
|
|
190
|
-
// on the shipped 1C Counting Wall rung, so it is the chart contract's behaviour at
|
|
191
|
-
// this bin count and not something these rungs introduced. A line joins the same 400
|
|
192
|
-
// points into one continuous curve, which reads as the profile it is. CAVEAT, stated
|
|
193
|
-
// because it bites: Chart.js does not begin a LINE value axis at zero, so once the
|
|
194
|
-
// profile's floor rises the curve compresses toward the left edge. Read positions off
|
|
195
|
-
// it, not magnitudes; magnitudes come from getCollisionHistogram().
|
|
196
|
-
exp.addObject('LiveChart', {
|
|
197
|
-
source: p.screen, orientation: 90,
|
|
198
|
-
rect: [detX + detW + 8, 0, Math.max(60, CW - (detX + detW + 8) - 8), H],
|
|
199
|
-
series: 'line', label: 'count by y', refreshMs: 100, color: [24, 32, 46], alpha: 225
|
|
200
|
-
});
|
|
201
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SCENE — Two Slits 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 (`export const parts` + `exp.declare`, or `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-two-slits).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// ── TWO SLITS AT THE CANONICAL LEG ───────────────────────────────────────────
|
|
14
|
-
//
|
|
15
|
-
// The SAME apparatus as `dse-two-emitters` — same λ, same separation, same leg,
|
|
16
|
-
// same detector, same chart — but the two point sources are now SLIT EXITS in
|
|
17
|
-
// an opaque barrier rather than two free emitters. ONE emitter sits behind the
|
|
18
|
-
// barrier and feeds BOTH slit channels; each channel is a `directed` Tunnel
|
|
19
|
-
// with the exit gate COLLAPSED + PHASE-RESET, exactly the composition
|
|
20
|
-
// `dse-tunnel-rebuild` already uses to make a slit's own mouth stand in for a
|
|
21
|
-
// point source ("THE TUNNEL IS A CONFIGURED EMITTER", see that
|
|
22
|
-
// rung's source comment for the flag-by-flag mapping). Composed only: Wall +
|
|
23
|
-
// Tunnel, zero new parts, zero engine code.
|
|
24
|
-
//
|
|
25
|
-
// WHY THE NUMBERS ARE THE SAME AS `dse-two-emitters`. That rung places its two
|
|
26
|
-
// SOURCES at y = cy∓sep/2 (120, 280) and its plate at D = λ_eff/4 past the
|
|
27
|
-
// SOURCE plane (srcX). Here the slit CENTRES sit at the identical y = 120, 280
|
|
28
|
-
// (`sep` unchanged), and the plate sits at the identical D = λ_eff/4 — now
|
|
29
|
-
// measured past the SLIT EXIT plane (the barrier's back face), which is this
|
|
30
|
-
// apparatus's new "source plane". If the exit gate's point-source claim holds,
|
|
31
|
-
// the two apparatuses must draw the SAME picture: a central peak at y = 200
|
|
32
|
-
// (source 1's lower band and source 2's upper band coincide there) flanked by
|
|
33
|
-
// the outer bands at y = 40 and y = 360 — because nothing about the DERIVATION
|
|
34
|
-
// of those positions used "free emitter" anywhere; it only used "point source
|
|
35
|
-
// at (x0, y0), phase-locked, λ_eff". A slit exit under `exitPhaseReset` is
|
|
36
|
-
// exactly that.
|
|
37
|
-
//
|
|
38
|
-
// λ_eff, the leg, and the plate/instrument are the scenes/dse-one-emitter.js numbers,
|
|
39
|
-
// copied rather than referenced because this rung's body is shaped completely
|
|
40
|
-
// differently (a barrier + two Tunnels stand where two bare SphereEmitters
|
|
41
|
-
// stood) — there is no shared statement to factor without re-introducing the
|
|
42
|
-
// exact config-mirror risk the emitter pair's line-diff test guards elsewhere.
|
|
43
|
-
// The bare numbers (320 / 3.2 / 400 / 80 / 160) are the SAME numbers, and the
|
|
44
|
-
// fail-loud realisability check is repeated verbatim so a future λ edit here
|
|
45
|
-
// cannot silently drift from the pair it must match.
|
|
46
|
-
|
|
47
|
-
import { SphereEmitter, Tunnel, Wall } from '@descix/frqtl-web/parts';
|
|
48
|
-
|
|
49
|
-
// Two Slits — reproduces dse-two-emitters' picture with the two point sources built
|
|
50
|
-
// as SLIT EXITS in a barrier instead of two free emitters. Barrier = Wall segments
|
|
51
|
-
// (destroy); each slit = a directed Tunnel with the exit gate collapsed + phase-reset
|
|
52
|
-
// (the dse-tunnel-rebuild composition) so the mouth is a phase-locked point source.
|
|
53
|
-
const CW = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
54
|
-
// ── THE ONLY LENGTH — IDENTICAL to the canonical-leg pair ───────────────
|
|
55
|
-
const lamPx = 320;
|
|
56
|
-
const lamArg = 3.2; // fullness 1/3.2 = 0.3125 -> yellow
|
|
57
|
-
const fwl = 4 * (lamPx / lamArg); // 400 -> wavelengthScale floor(400/4) = 100
|
|
58
|
-
const wlScale = Math.floor(fwl / 4); // 100
|
|
59
|
-
// ── SLIT SEPARATION — IDENTICAL to dse-two-emitters' source separation ──
|
|
60
|
-
const legFrac = 1 / 4;
|
|
61
|
-
const D = Math.round(lamPx * legFrac); // 80 — the SAME leg, now measured from the
|
|
62
|
-
// slit EXIT plane, this apparatus's source plane
|
|
63
|
-
const sep = lamPx / 2; // 160 -> slit centres at y 120 and 280 — the
|
|
64
|
-
// EXACT y positions dse-two-emitters used
|
|
65
|
-
const cy = Math.round(H / 2); // 200
|
|
66
|
-
const slitCY1 = cy - sep / 2, slitCY2 = cy + sep / 2; // 120, 280
|
|
67
|
-
// ── THE BARRIER — one opaque plate, two slit apertures ──────────────────
|
|
68
|
-
const barrierW = Math.round(lamPx / 8); // 40 — plate thickness (dimensionless x lamPx,
|
|
69
|
-
// an independent dial from the transverse
|
|
70
|
-
// wavelength, same discipline as dse-tunnel-rebuild)
|
|
71
|
-
const slitH = Math.round(lamPx / 8); // 40 — slit aperture height (before liners)
|
|
72
|
-
const linerH = Math.round(lamPx / 64); // 5 — liner thickness (unjumpable: max step 2px/tick)
|
|
73
|
-
const bore = slitH - 2 * linerH; // 30 — the clear aperture between the liners
|
|
74
|
-
const srcX = Math.round(lamPx / 16); // 20 — the emitter, well behind the barrier
|
|
75
|
-
const barrierL = srcX + Math.round(lamPx / 2); // 180 — half a wavelength of travel room
|
|
76
|
-
const slit1Top = slitCY1 - slitH / 2, slit2Top = slitCY2 - slitH / 2; // 100, 260
|
|
77
|
-
const exitX = barrierL + barrierW; // 220 — the slit EXIT plane (barrier back face)
|
|
78
|
-
const detX = exitX + D; // 300 — SAME leg as dse-two-emitters, now
|
|
79
|
-
// measured from the slit exits
|
|
80
|
-
const detW = Math.round(lamPx / 64); // 5 — plate thickness (flux probe, sub-feature)
|
|
81
|
-
const sinkW = Math.round(lamPx / 16); // 20 — far-edge sink thickness
|
|
82
|
-
|
|
83
|
-
export const parts = {
|
|
84
|
-
source: [SphereEmitter, {
|
|
85
|
-
preset: 'hemisphere', is2D: true, chargeMode: 'random', center: [srcX, cy],
|
|
86
|
-
wavelength: lamArg, fundamentalDimension: 2, fundamentalWaveLength: fwl,
|
|
87
|
-
burstSize: 8, emitEveryTicks: 1
|
|
88
|
-
}],
|
|
89
|
-
// Barrier segments — opaque destroy sinks, leaving the two slit gaps.
|
|
90
|
-
wall1: [Wall, { rect: [barrierL, 0, barrierW, slit1Top],
|
|
91
|
-
collisionBehavior: 'destroy', color: [200, 60, 60] }],
|
|
92
|
-
wall2: [Wall, { rect: [barrierL, slit1Top + slitH, barrierW, slit2Top - (slit1Top + slitH)],
|
|
93
|
-
collisionBehavior: 'destroy', color: [150, 40, 40] }],
|
|
94
|
-
wall3: [Wall, { rect: [barrierL, slit2Top + slitH, barrierW, H - (slit2Top + slitH)],
|
|
95
|
-
collisionBehavior: 'destroy', color: [200, 60, 60] }],
|
|
96
|
-
// The two slit channels — directed Tunnels, exit gate COLLAPSED + PHASE-RESET (the
|
|
97
|
-
// dse-tunnel-rebuild composition): each channel's own mouth stands in for a point
|
|
98
|
-
// source at the slit's own y, phase-locked — what a SphereEmitter would do at that pixel.
|
|
99
|
-
slit1_channel: [Tunnel, {
|
|
100
|
-
left: barrierL, top: slit1Top + linerH, length: barrierW, bore: bore,
|
|
101
|
-
wallThickness: linerH, axis: 'x', mode: 'directed',
|
|
102
|
-
exitCollapse: true, exitRecenter: true, exitPhaseReset: true,
|
|
103
|
-
color: [80, 140, 220]
|
|
104
|
-
}],
|
|
105
|
-
slit2_channel: [Tunnel, {
|
|
106
|
-
left: barrierL, top: slit2Top + linerH, length: barrierW, bore: bore,
|
|
107
|
-
wallThickness: linerH, axis: 'x', mode: 'directed',
|
|
108
|
-
exitCollapse: true, exitRecenter: true, exitPhaseReset: true,
|
|
109
|
-
color: [80, 140, 220]
|
|
110
|
-
}],
|
|
111
|
-
// FAR-EDGE SINK — the same experiment-boundary sink the canonical-leg pair uses.
|
|
112
|
-
far_sink: [Wall, { rect: [CW - sinkW, 0, sinkW, H], collisionBehavior: 'destroy',
|
|
113
|
-
color: [120, 120, 120], alpha: 40 }],
|
|
114
|
-
// ── THE PLATE — SAME instrument as the canonical-leg pair: a counting wall that does
|
|
115
|
-
// NOT absorb, at the SAME leg D past the new source plane (the slit exits).
|
|
116
|
-
screen: [Wall, { rect: [detX, 0, detW, H],
|
|
117
|
-
preset: 'transparent-probe', markTag: 'plate', color: [120, 200, 120] }],
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
export default function buildExperiment(exp) {
|
|
121
|
-
if (Math.abs(lamArg * wlScale - lamPx) > 1e-9) {
|
|
122
|
-
throw new Error('[dse-two-slits] lamPx ' + lamPx + ' is not realisable as lambda_eff: the ' +
|
|
123
|
-
'engine can only produce wavelengthArg x floor(fwl/4) = ' + (lamArg * wlScale) + '. (FAIL-LOUD.)');
|
|
124
|
-
}
|
|
125
|
-
// ── THE LIGHT SOURCE — ONE emitter, behind the barrier, lighting BOTH slits ──
|
|
126
|
-
// preset 'hemisphere': 180 degrees of FORWARD emission, aimed at the barrier — the
|
|
127
|
-
// same walk-character (chargeMode/wavelength/fundamentalWaveLength) the slit exits must
|
|
128
|
-
// carry through so the exit's phase-locked point source is at the SAME λ_eff.
|
|
129
|
-
exp.createCanvas(CW, H, SphereEmitter.worldCfg(parts.source[1])); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
130
|
-
const p = exp.declare(parts);
|
|
131
|
-
exp.addObject('LiveChart', {
|
|
132
|
-
source: p.screen, orientation: 90,
|
|
133
|
-
rect: [detX + detW + 8, 0, Math.max(60, CW - (detX + detW + 8) - 8), H],
|
|
134
|
-
series: 'line', label: 'count by y', refreshMs: 100, color: [24, 32, 46], alpha: 225
|
|
135
|
-
});
|
|
136
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SCENE — Empty Universe
|
|
5
|
-
*
|
|
6
|
-
* STUDIO's blank world, a scene like every other: it states its world in its first (and only)
|
|
7
|
-
* statement and constructs nothing — the palette authors into it. Named by
|
|
8
|
-
* lib/shim/registry/scene-source.js::EMPTY_SCENE (not a walk-through rung).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export const parts = {};
|
|
12
|
-
|
|
13
|
-
export default function buildExperiment(exp) {
|
|
14
|
-
// THE WORLD: a 600x400 2D canvas + universe on the dim-2 fundamental, λ multiplier 8.
|
|
15
|
-
exp.createCanvas(600, 400, { dimension: 2, wavelengthScale: 8 });
|
|
16
|
-
exp.declare(parts);
|
|
17
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SCENE — 4B · Laser Build-Up
|
|
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 (`export const parts` + `exp.declare`, or `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 r4b-laser-buildup).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// LASER BUILD-UP (D4) — the three
|
|
14
|
-
// components of a laser assembled one at a time on top of the earlier Light Lab rungs:
|
|
15
|
-
// 1. a GAIN SOURCE — the hemisphere emitter (rung 3's omni source, restricted forward)
|
|
16
|
-
// 2. a RESONANT BORE — the directed corridor, which re-aims every wall hit forward
|
|
17
|
-
// 3. an OUTPUT COUPLER — the exit gate, optionally collapsing the mouth to a point and
|
|
18
|
-
// optionally re-phase-locking what leaves it
|
|
19
|
-
// The two exit flags are INDEPENDENT and both live, which is what lets the rung separate
|
|
20
|
-
// "does the point source matter?" from "does coherence matter?".
|
|
21
|
-
//
|
|
22
|
-
// SINGLE AUTHORITY: the emitter + tunnel configs come from `setupTunnelPointExit.sceneSpec()`
|
|
23
|
-
// — the SAME pure function the standalone `tunnel-point-exit` page's factory consumes. The
|
|
24
|
-
// rung is the addObject form (so STUDIO can select and edit the objects); the page is the
|
|
25
|
-
// slider-driven form. They share the numbers rather than each hand-carrying a copy.
|
|
26
|
-
|
|
27
|
-
import { SphereEmitter, Tunnel } from '@descix/frqtl-web/parts';
|
|
28
|
-
|
|
29
|
-
// Laser Build-Up — gain source + resonant bore + output coupler.
|
|
30
|
-
// Geometry and per-leaf creation conditions come from the ONE scene-spec authority,
|
|
31
|
-
// Tunnel.pointExitSpec() (setupTunnelPointExit.sceneSpec), so this rung and the standalone page cannot drift.
|
|
32
|
-
const W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
33
|
-
const SPEC = Tunnel.pointExitSpec({
|
|
34
|
-
canvasW: W, canvasH: H,
|
|
35
|
-
tunnelLength: 240, // REBUILD-class: corridor geometry, re-authored not dragged
|
|
36
|
-
wavelengthScale: 16, // eff lambda 256px — the self-interference golden's own regime
|
|
37
|
-
exitRecenter: true, // output coupler collapses the mouth to a point
|
|
38
|
-
exitPhaseReset: false // coherence OFF by default — turning it ON is the experiment
|
|
39
|
-
});
|
|
40
|
-
// The spec names its tunnel; in `parts` the key is the name.
|
|
41
|
-
const { name: _tunnelName, ...TUNNEL } = SPEC.tunnel;
|
|
42
|
-
|
|
43
|
-
export const parts = {
|
|
44
|
-
// 1. THE GAIN SOURCE — a hemisphere emitter just inside the input mouth, so the whole
|
|
45
|
-
// burst is aimed down the bore instead of half of it escaping backwards.
|
|
46
|
-
emitter: [SphereEmitter, { ...SPEC.emitter }],
|
|
47
|
-
// 2+3. THE BORE AND THE OUTPUT COUPLER — one directed Tunnel carrying the exit gate.
|
|
48
|
-
// mode 'directed': every wall hit re-emits forward within 180 degrees of the axis.
|
|
49
|
-
// exitRecenter / exitPhaseReset are the two coupler flags, both live in the Inspector.
|
|
50
|
-
point_exit_tunnel: [Tunnel, { ...TUNNEL }],
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export default function buildExperiment(exp) {
|
|
54
|
-
exp.createCanvas(W, H, SphereEmitter.worldCfg(parts.emitter[1])); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
55
|
-
exp.declare(parts);
|
|
56
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SCENE — 1A · Mirror (3D)
|
|
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 (`export const parts` + `exp.declare`, or `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 cl1a-mirror-3d).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { SphereEmitter, Wall } from '@descix/frqtl-web/parts';
|
|
14
|
-
|
|
15
|
-
// Collision Lab 1A · 3D — a collimated LASER (particle-walk pins PURE +x in 2D AND
|
|
16
|
-
// 3D) into a 3D mirror SLAB (depth → z-slab containing z=0), rendered as a box;
|
|
17
|
-
// orbit to view. Same LASER tuning as the 2D 1A: single positive
|
|
18
|
-
// charge, wavelength 1, wavelengthScale 1 (fundamentalWaveLength 4), tiny burst.
|
|
19
|
-
// FACE-ON default: orientation 0 presents the full
|
|
20
|
-
// drawn face to the beam → retro-reflection (beam straight back, recolored) — the
|
|
21
|
-
// one mirror visual that reads correctly from EVERY camera azimuth. (A tilted
|
|
22
|
-
// default's L-shaped 2th exit is correct optics but its mirror-flipped projection
|
|
23
|
-
// reads as pass-through from presets that view the xy-plane from behind — measured:
|
|
24
|
-
// zero leaves ever behind the face; the read was projection handedness, not physics.)
|
|
25
|
-
// Rotate the orientation slider to steer: 45 -> straight up, 30 -> R(120), etc.
|
|
26
|
-
const W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
27
|
-
const cy = Math.round(H / 2);
|
|
28
|
-
|
|
29
|
-
export const parts = {
|
|
30
|
-
source: [SphereEmitter, {
|
|
31
|
-
preset: 'particle-walk', is2D: false, center: [Math.round(W * 0.15), cy],
|
|
32
|
-
chargeMode: 'positive', fundamentalDimension: 2, wavelength: 1,
|
|
33
|
-
fundamentalWaveLength: 4, burstSize: 2, emitEveryTicks: 1
|
|
34
|
-
}],
|
|
35
|
-
mirror: [Wall, {
|
|
36
|
-
rect: [Math.round(W * 0.6), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
|
|
37
|
-
z: -80, depth: 160,
|
|
38
|
-
collisionBehavior: 'bounce', bounceDirection: 'reflect', orientationDeg: 0,
|
|
39
|
-
color: [255, 120, 0], hooks: ['recolor']
|
|
40
|
-
}],
|
|
41
|
-
// SCENE Z-COLLIMATOR (without it the z lets things pass through instead of
|
|
42
|
-
// reflecting): the beam's z-tail DIFFUSES over time (measured:
|
|
43
|
-
// |z| at the wall trending 68 of the 80 rim within seconds), and any quantum
|
|
44
|
-
// beyond the plate's z-band would sail BESIDE the plate — reading as pass-
|
|
45
|
-
// through from rooftop. Two INVISIBLE full-canvas destroy boundaries flush with
|
|
46
|
-
// the plate's z-band clamp the scene to the mirror's face: the universe z-edge
|
|
47
|
-
// moved inward (the sanctioned experiment-boundary sink, same as 2D strays
|
|
48
|
-
// dying at the canvas edge) — so the mirror provably faces EVERY alive quantum
|
|
49
|
-
// and nothing can ever appear past the plate. 40px deep: un-jumpable (max z
|
|
50
|
-
// step is ±2/tick).
|
|
51
|
-
zClipFar: [Wall, { rect: [0, 0, W, H], z: 80, depth: 40,
|
|
52
|
-
collisionBehavior: 'destroy', visible: false }],
|
|
53
|
-
zClipNear: [Wall, { rect: [0, 0, W, H], z: -120, depth: 40,
|
|
54
|
-
collisionBehavior: 'destroy', visible: false }],
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export default function buildExperiment(exp) {
|
|
58
|
-
exp.createScene(W, H, 600, SphereEmitter.worldCfg(parts.source[1])); // THE WORLD: a 3D scene + universe, 600x400x600 — first, then parts
|
|
59
|
-
exp.declare(parts);
|
|
60
|
-
}
|