@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +87 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2501 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +85 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +100 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +259 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2474 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2922 -0
- package/dist/frqtl/objects/ObjectFactory.js +297 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +711 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +985 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +412 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +354 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +310 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +824 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +263 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +299 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +469 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +251 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +327 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +280 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1259 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +242 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +178 -0
- package/dist/frqtl/objects/v1-replacements.js +30 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +35 -0
- package/dist/frqtl/parts/CollisionTypes.js +31 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +490 -0
- package/dist/frqtl/parts/LargeObject.js +406 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +720 -0
- package/dist/frqtl/parts/OvenBox.js +612 -0
- package/dist/frqtl/parts/PointSource.js +763 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +244 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +212 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +692 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +326 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +672 -0
- package/dist/shim/notebook/knob.js +151 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +510 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +214 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +807 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +62 -4
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DetectorWall — the provenance-splitting detector screen.
|
|
5
|
+
*
|
|
6
|
+
* ── A DetectorWall IS … (the datasheet line) ────────────────────────────────
|
|
7
|
+
*
|
|
8
|
+
* A DetectorWall is a **`Wall{detect}` + a `Tagger` read**: the Wall supplies the
|
|
9
|
+
* compiled op-program (`[COUNT, ABSORB]` + `collect_collided:true`, canonical bench
|
|
10
|
+
* LO7 MARK+COUNT) that counts and removes each arriving frame and buffers a FrameData
|
|
11
|
+
* copy of it; the Tagger supplies the read of the provenance string an UPSTREAM tagger
|
|
12
|
+
* (SlitScreen's slit LOs, whose `CollisionActions.MARK` is itself a Tagger write) put
|
|
13
|
+
* on that frame. Everything this file adds on top is bookkeeping: partition the hits
|
|
14
|
+
* by that tag, and keep two accumulators (the Y-marginal and the Y×Z grid) whose
|
|
15
|
+
* grid-add / marginal-derive helpers are the Wall atom's own statics.
|
|
16
|
+
*
|
|
17
|
+
* It owns NO op-program, NO string_map path and NO grid algebra of its own. Composed
|
|
18
|
+
* from — never wrapping — `Wall` (`./Wall.js`) and `Tagger` (`../objects/objects/tagger.js`).
|
|
19
|
+
*
|
|
20
|
+
* ── Layer 1: API contract ───────────────────────────────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* - The inner Wall registers with `collect_collided:true` so the engine accumulates a
|
|
23
|
+
* point-in-time FrameData copy for every frame that collided this tick, AFTER the
|
|
24
|
+
* op-program (the upstream MARK wrote the slit name) and BEFORE Step-7 cleanup
|
|
25
|
+
* (ABSORB removes the frame).
|
|
26
|
+
* - `getCollisionHistogram(tickData)` reads this LO's collided frames from the
|
|
27
|
+
* marshalled tick output (`tickData.objects[handle].frames`) each tick and
|
|
28
|
+
* accumulates a running Map<y, count>, split by slit provenance read through
|
|
29
|
+
* `Tagger.readTag(fd)` (the canonical `detector_name` key — this file never names
|
|
30
|
+
* `data.string_map`).
|
|
31
|
+
*
|
|
32
|
+
* ── Layer 2: physics behavior ───────────────────────────────────────────────
|
|
33
|
+
*
|
|
34
|
+
* ZERO physics (SU1) beyond the Wall's own compiled program. Counting, tagging and
|
|
35
|
+
* reading move nothing: the canonical bench holds 28/28 across this composition.
|
|
36
|
+
*
|
|
37
|
+
* Engine-agnostic: the readout is identical on every engine (no capability gap).
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { LargeObject } from './LargeObject.js';
|
|
41
|
+
import { CollisionTypes } from './CollisionTypes.js';
|
|
42
|
+
import { CollisionActions } from './CollisionActions.js';
|
|
43
|
+
import { Wall } from './Wall.js';
|
|
44
|
+
import * as Tagger from '../objects/objects/tagger.js';
|
|
45
|
+
import { ObjectSchema } from '../objects/schema/index.js';
|
|
46
|
+
import { registerKind } from '../objects/kinds.js';
|
|
47
|
+
|
|
48
|
+
// ── Imports (Node.js) or globals (browser) ──────────────────────────
|
|
49
|
+
|
|
50
|
+
// ── DetectorWall ─────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Detector/absorber wall that counts arriving particles and removes them.
|
|
54
|
+
*
|
|
55
|
+
* Wraps a single {@link LargeObject} configured with `[COUNT, ABSORB]` actions
|
|
56
|
+
* and collision type `NONE`. The `ABSORB` action removes the particle so it
|
|
57
|
+
* does not pile up. The `COUNT` action increments the LO's internal counter.
|
|
58
|
+
*
|
|
59
|
+
* G1: the LO is registered with `collect_collided: true`. Each call to
|
|
60
|
+
* `getCollisionHistogram(tickData)` reads this LO's collided frames from the
|
|
61
|
+
* marshalled tick output (`tickData.objects[handle].frames`) and accumulates the
|
|
62
|
+
* running histogram (no per-LO collision query — the engine stays generic).
|
|
63
|
+
* Slit provenance is read through the `Tagger` part (`Tagger.readTag(fd)`) — the
|
|
64
|
+
* matching write is SlitScreen's slit LOs' MARK op-program (the same part), captured
|
|
65
|
+
* in the collided FrameData AFTER the op-program runs, before ABSORB kills the frame.
|
|
66
|
+
*
|
|
67
|
+
* ## Physics you can drive
|
|
68
|
+
*
|
|
69
|
+
* **`wallX`** — the x position of the detector. In `setupDoubleSlit` this is set
|
|
70
|
+
* to `slitScreen.left + slitScreen.width + 3 * wavelength`. Moving it closer to
|
|
71
|
+
* the slit screen (smaller `L`) compresses the fringe pattern vertically; moving
|
|
72
|
+
* it further (larger `L`) expands the fringe spacing. This is the `L` in the
|
|
73
|
+
* classical fringe-spacing formula `Δy = λL/d`.
|
|
74
|
+
*
|
|
75
|
+
* **Histogram readout** — call `detectorWall.getCollisionHistogram()` each tick
|
|
76
|
+
* to accumulate hits. Returns `{ combined: Map<y,count>, slit1: Map<y,count>,
|
|
77
|
+
* slit2: Map<y,count>, total: number }`. The accumulated `combined` map forms the
|
|
78
|
+
* detector interference pattern.
|
|
79
|
+
*
|
|
80
|
+
* **`wallWidth`** (default 20 px) — wider wall = more forgiving capture zone;
|
|
81
|
+
* narrower = sparser but more positionally precise hit data. Width does not affect
|
|
82
|
+
* fringe position, only total hit rate.
|
|
83
|
+
*
|
|
84
|
+
* @param {Object} universe - The engine universe instance (JS or WASM).
|
|
85
|
+
* @param {Object} canvasRect - The world rect `{left,top,w,h}` (or the rect tuple),
|
|
86
|
+
* read through `ObjectSchema.worldRect` — a missing or non-positive dimension THROWS.
|
|
87
|
+
* Used to derive default wall height and x position.
|
|
88
|
+
* @param {number} [wallX] - X position of the detector wall (physics px).
|
|
89
|
+
* Defaults to `canvasRect.w - 20` (near the right edge).
|
|
90
|
+
* @param {number} [wallY=0] - Y position (physics px).
|
|
91
|
+
* @param {string} [wallCollisionType] - RETIRED positional slot. The counting
|
|
92
|
+
* atom's collision mechanics are the compiled `destroy`+`count` op-program;
|
|
93
|
+
* there is no collision_type override. Passing anything other than `NONE`
|
|
94
|
+
* THROWS (FAIL-LOUD — never a silently ignored argument).
|
|
95
|
+
* @param {Array} [wallColor=[120,120,120]] - RGB color for rendering.
|
|
96
|
+
* @param {number} [wallHeight] - Wall height (physics px); defaults to
|
|
97
|
+
* `canvasRect.h`.
|
|
98
|
+
* @param {number} [wallWidth=20] - Wall thickness (physics px).
|
|
99
|
+
* @param {string} [wallName='detector_wall'] - Identifier used to key the
|
|
100
|
+
* collision histogram in the universe.
|
|
101
|
+
* @param {Object} [opts] - WS-DSE-3D Bite B1.
|
|
102
|
+
* @param {number} [opts.depth] - When provided, the detector LO is built as a
|
|
103
|
+
* 6-element `[left,top,z,w,h,d]` rect spanning the FULL z-extent
|
|
104
|
+
* `[-depth/2, +depth/2]` (Tube.js precedent) — without it, z-spread quanta
|
|
105
|
+
* (WS-DSE-3D Bite B0's emitter z-spread) fly past a paper-thin (z=0,d=w)
|
|
106
|
+
* detector. OMITTED (default) → legacy 4-element rect, byte-identical.
|
|
107
|
+
*/
|
|
108
|
+
|
|
109
|
+
function DetectorWall(universe, canvasRect, wallX, wallY, wallCollisionType, wallColor, wallHeight, wallWidth, wallName, opts) {
|
|
110
|
+
opts = opts || {};
|
|
111
|
+
var depth = (opts.depth !== undefined && opts.depth !== null) ? opts.depth : null;
|
|
112
|
+
|
|
113
|
+
this.universe = universe;
|
|
114
|
+
this._schemaKind = 'DetectorWall';
|
|
115
|
+
|
|
116
|
+
// The world rect, through the ONE normalizer (a zero-high detector counts nothing
|
|
117
|
+
// and would report it as physics — so a missing dimension throws, never zeroes).
|
|
118
|
+
var world = ObjectSchema.worldRect(canvasRect, 'DetectorWall canvasRect');
|
|
119
|
+
var cw = world.w, ch = world.h;
|
|
120
|
+
|
|
121
|
+
// Defaults
|
|
122
|
+
wallX = (wallX !== null && wallX !== undefined) ? wallX : (cw - 20);
|
|
123
|
+
wallY = (wallY !== null && wallY !== undefined) ? wallY : 0;
|
|
124
|
+
// The collision_type override died with the legacy wall lexicon: the counting
|
|
125
|
+
// atom's mechanics ARE its op-program. A caller still naming a non-NONE type
|
|
126
|
+
// is asking for something that no longer exists — say so.
|
|
127
|
+
if (wallCollisionType !== null && wallCollisionType !== undefined &&
|
|
128
|
+
wallCollisionType !== CollisionTypes.NONE) {
|
|
129
|
+
throw new Error('[DetectorWall] the wallCollisionType override is retired ("' +
|
|
130
|
+
wallCollisionType + '") — the counting atom is Wall{collisionBehavior:' +
|
|
131
|
+
'\'destroy\', hooks:[\'count\']} and its mechanics are the compiled ' +
|
|
132
|
+
'op-program. Pass NONE (or omit the argument).');
|
|
133
|
+
}
|
|
134
|
+
wallColor = wallColor || [120, 120, 120];
|
|
135
|
+
wallHeight = (wallHeight !== null && wallHeight !== undefined) ? wallHeight : ch;
|
|
136
|
+
wallWidth = (wallWidth !== null && wallWidth !== undefined) ? wallWidth : 20;
|
|
137
|
+
wallName = wallName || ('detector_wall' + Math.floor(Math.random() * 1000000));
|
|
138
|
+
|
|
139
|
+
this.wallX = wallX;
|
|
140
|
+
this._wallHeight = wallHeight;
|
|
141
|
+
this.depth = depth;
|
|
142
|
+
|
|
143
|
+
// G1: register with collect_collided:true so the engine populates the
|
|
144
|
+
// per-tick collided-frame buffer. The buffer is read by getCollisionHistogram()
|
|
145
|
+
// between ticks to accumulate the running histogram.
|
|
146
|
+
//
|
|
147
|
+
// NOTE: collect_collided is passed in the spec object that LargeObject forwards
|
|
148
|
+
// to universe.registerLargeObject(). It is a spec field (not a post-registration
|
|
149
|
+
// setter), so it is set atomically at registration time and never changes.
|
|
150
|
+
// WS-DSE-3D Bite B1: 6-element full-z-depth rect when opts.depth is set
|
|
151
|
+
// (Tube.js precedent); legacy 4-element rect otherwise (byte-identical).
|
|
152
|
+
var wallRect = (depth !== null)
|
|
153
|
+
? [wallX, wallY, -depth / 2, wallWidth, wallHeight, depth]
|
|
154
|
+
: [wallX, wallY, wallWidth, wallHeight];
|
|
155
|
+
// The detector's inner engine object IS the counting atom —
|
|
156
|
+
// `Wall{collisionBehavior:'destroy', hooks:['count']}` (= preset
|
|
157
|
+
// 'detector-screen'): kill the arrival, increment the wall's counter, buffer
|
|
158
|
+
// the collided FrameData. MEASURED equal to the legacy `detect` construction on
|
|
159
|
+
// the canonical WASM engine (same alive-trace tick-by-tick, same total); the two
|
|
160
|
+
// compiled programs carry the SAME two ops in the opposite order, and they
|
|
161
|
+
// commute (count reads only self.count, the kill writes only frame.is_alive).
|
|
162
|
+
// The rich histogram (grid + slit provenance + the #61 guard) stays OWNED here —
|
|
163
|
+
// this wrapper delegates only the plain-wall LO construction, not the counting.
|
|
164
|
+
this.name = wallName; // the declared name is the detector's own construction fact
|
|
165
|
+
this._innerWall = new Wall(universe, {
|
|
166
|
+
rect: wallRect,
|
|
167
|
+
collisionBehavior: 'destroy',
|
|
168
|
+
hooks: ['count'],
|
|
169
|
+
name: wallName,
|
|
170
|
+
color: wallColor
|
|
171
|
+
});
|
|
172
|
+
this.detector_wall = this._innerWall.lo;
|
|
173
|
+
// THE ONE UP-LINK: the detector composed this Wall (which stamps its own LO).
|
|
174
|
+
this._innerWall._owner = this;
|
|
175
|
+
// WS-DSE-3D fix-loop item 1 (render): the Tube.js precedent — a 6-element
|
|
176
|
+
// rect gives the ENGINE a real z-slab, but _buildLoMesh (three-provider.js)
|
|
177
|
+
// only builds a 3D BoxGeometry when the LO ALSO carries the render-only
|
|
178
|
+
// `shape='box'` + `depth` hint (see SlitScreen.js for the identical fix).
|
|
179
|
+
if (this.detector_wall.rect.d !== undefined) {
|
|
180
|
+
this.detector_wall.shape = 'box';
|
|
181
|
+
this.detector_wall.depth = this.detector_wall.rect.d;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Accumulated histogram state (G1) ─────────────────────────────
|
|
185
|
+
// Running Maps: y → hit count. Accumulated across ticks (double-slit pattern
|
|
186
|
+
// is cumulative — many ticks of hits build the interference fringes). This is
|
|
187
|
+
// the Y-MARGINAL — unchanged code path, byte-identical to pre-B1 (the CLI,
|
|
188
|
+
// charts, and the committed golden all read exactly these fields).
|
|
189
|
+
this._combined = new Map(); // all slits combined
|
|
190
|
+
this._slit1 = new Map(); // hits tagged 'slit1' by SlitScreen MARK op-program
|
|
191
|
+
this._slit2 = new Map(); // hits tagged 'slit2' by SlitScreen MARK op-program
|
|
192
|
+
this._total = 0;
|
|
193
|
+
this._slit1Total = 0;
|
|
194
|
+
this._slit2Total = 0;
|
|
195
|
+
|
|
196
|
+
// ── Y×Z grid (WS-DSE-3D Bite B1, ratified §4.2) ───────────────────
|
|
197
|
+
// Additive capture alongside the (untouched) Y-marginal above: Map<y, Map<z,count>>.
|
|
198
|
+
// In 2D (depth omitted), z is always 0 (fd.z defaults to 0 — see the loop below),
|
|
199
|
+
// so every grid entry has a single z=0 bucket and `DetectorWall.deriveMarginalY(grid)`
|
|
200
|
+
// reproduces `this._combined` exactly — the Y-marginal IS the derived marginal of
|
|
201
|
+
// this grid (verified by a unit test), even though the accumulator above is not
|
|
202
|
+
// rewritten to compute it lazily (kept untouched to protect the golden).
|
|
203
|
+
this._grid = new Map(); // all slits combined
|
|
204
|
+
this._gridSlit1 = new Map();
|
|
205
|
+
this._gridSlit2 = new Map();
|
|
206
|
+
|
|
207
|
+
// #61 throw-guard (fraqtl#61, architect-ruled shape: THROW, never silent dedup) —
|
|
208
|
+
// the last TickData object consumed by getCollisionHistogram(). Applies in 2D too.
|
|
209
|
+
this._lastTickData = undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Add one hit at (y,z) to a Y×Z grid (Map<y, Map<z,count>>). Delegates to the ONE
|
|
214
|
+
* grid-accumulation authority on the wall ATOM (`Wall.gridAdd`) — a DetectorWall
|
|
215
|
+
* is a Wall wrapper, so it consumes the atom's helper rather than keeping a
|
|
216
|
+
* second copy that could drift.
|
|
217
|
+
*/
|
|
218
|
+
function _gridAdd(grid, y, z) {
|
|
219
|
+
Wall.gridAdd(grid, y, z);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Derive the Y-marginal (Map<y,count>) from a Y×Z grid (Map<y, Map<z,count>>) —
|
|
224
|
+
* sums every z-bucket for each y. Pure/static; exists so a caller can PROVE the
|
|
225
|
+
* grid and the legacy marginal agree (ratified §4.2 "Y-marginal DERIVED").
|
|
226
|
+
* Thin re-export of the atom's `Wall.deriveMarginalY` (one authority; the
|
|
227
|
+
* DetectorWall name stays as the published API its callers already use).
|
|
228
|
+
* @param {Map<number, Map<number,number>>} grid
|
|
229
|
+
* @returns {Map<number,number>}
|
|
230
|
+
*/
|
|
231
|
+
DetectorWall.deriveMarginalY = function (grid) {
|
|
232
|
+
return Wall.deriveMarginalY(grid);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get the X position of the detector wall.
|
|
237
|
+
*/
|
|
238
|
+
Object.defineProperty(DetectorWall.prototype, 'x', {
|
|
239
|
+
get: function () { return this.wallX; },
|
|
240
|
+
set: function (val) { this.wallX = val; }
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Accumulate this tick's collided frames into the running histogram and return it.
|
|
245
|
+
*
|
|
246
|
+
* G1 implementation: reads this LO's collided frames from the marshalled tick output
|
|
247
|
+
* (`tickData.objects[handle].frames`) — the engine snapshots them AFTER each op-program
|
|
248
|
+
* pass and BEFORE Step-7 cleanup, then marshals them out in Phase D (no per-LO query).
|
|
249
|
+
* Because the engine copies FrameData after the MARK/DETECT op-program has run, the
|
|
250
|
+
* frame already carries the slit provenance tag (e.g. 'slit1' or 'slit2') written by
|
|
251
|
+
* SlitScreen's slit LOs — read back here with `Tagger.readTag(fd)`.
|
|
252
|
+
*
|
|
253
|
+
* Bins by the frame's Y position (floor of `rect.y` / `fd.y`), matching the
|
|
254
|
+
* physical pixel row at which the frame hit the detector.
|
|
255
|
+
*
|
|
256
|
+
* The returned object holds RUNNING totals across all calls — each call adds
|
|
257
|
+
* the current tick's hits to the accumulators.
|
|
258
|
+
*
|
|
259
|
+
* #61 throw-guard (fraqtl#61, architect-ruled): THROWS if called twice in a row
|
|
260
|
+
* with the SAME `tickData` object reference — each tick's data must be consumed
|
|
261
|
+
* exactly once, or collisions double-count silently. A correct caller (the CLI,
|
|
262
|
+
* FrqtlApp's onTick, the bench) calls this once per `universe.doTick()`
|
|
263
|
+
* result, so a fresh object reference every call means this never fires for them.
|
|
264
|
+
* Applies identically in 2D and 3D.
|
|
265
|
+
*
|
|
266
|
+
* @returns {{
|
|
267
|
+
* combined: Map<number,number>,
|
|
268
|
+
* slit1: Map<number,number>,
|
|
269
|
+
* slit2: Map<number,number>,
|
|
270
|
+
* total: number,
|
|
271
|
+
* slit1Total: number,
|
|
272
|
+
* slit2Total: number,
|
|
273
|
+
* grid: Map<number, Map<number,number>>,
|
|
274
|
+
* gridSlit1: Map<number, Map<number,number>>,
|
|
275
|
+
* gridSlit2: Map<number, Map<number,number>>
|
|
276
|
+
* }}
|
|
277
|
+
*/
|
|
278
|
+
DetectorWall.prototype.getCollisionHistogram = function (tickData) {
|
|
279
|
+
// #61 throw-guard — top of function, before any read (architect-ruled: THROW,
|
|
280
|
+
// never silent dedup). Reference equality only; two DIFFERENT tickData objects
|
|
281
|
+
// (even with identical content, e.g. the TICKS=0 `{objects:{}}` literal) never
|
|
282
|
+
// trip it — only the exact same object consumed twice in a row does.
|
|
283
|
+
if (tickData != null && tickData === this._lastTickData) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
'[DetectorWall] getCollisionHistogram() called twice with the SAME TickData ' +
|
|
286
|
+
'object — each tick must be consumed exactly once (fraqtl#61 double-count guard). ' +
|
|
287
|
+
'Call universe.doTick() again before re-consuming.'
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
this._lastTickData = tickData;
|
|
291
|
+
|
|
292
|
+
var handle = this.detector_wall.handle;
|
|
293
|
+
|
|
294
|
+
// Read THIS LO's collected frames from the MARSHALLED tick output — the engine emits them as
|
|
295
|
+
// TickData.objects[handle].frames (it answers no per-LO collision QUERY; the universe stays
|
|
296
|
+
// focused on generic physics). IDENTICAL on both engines — no capability gap, no FAIL-LOUD
|
|
297
|
+
// guard. Each frame carries its provenance TAG, written in-tick by the SlitScreen slit LOs'
|
|
298
|
+
// compiled MARK op-program and read below through the Tagger part. This is a pure reader.
|
|
299
|
+
var frames = (tickData && tickData.objects && tickData.objects[handle] &&
|
|
300
|
+
Array.isArray(tickData.objects[handle].frames))
|
|
301
|
+
? tickData.objects[handle].frames : [];
|
|
302
|
+
|
|
303
|
+
for (var i = 0; i < frames.length; i++) {
|
|
304
|
+
var fd = frames[i];
|
|
305
|
+
|
|
306
|
+
// Y bin: the frame's top-left Y. fd.rect.top (canonical wrap) or fd.y (flat snapshot).
|
|
307
|
+
var y;
|
|
308
|
+
if (fd.rect && fd.rect.top != null) {
|
|
309
|
+
y = Math.floor(fd.rect.top);
|
|
310
|
+
} else if (fd.y != null) {
|
|
311
|
+
y = Math.floor(fd.y);
|
|
312
|
+
} else {
|
|
313
|
+
y = 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Z bin (WS-DSE-3D Bite B1): stays 0 in every 2D run (fd.z defaults to 0 when
|
|
317
|
+
// the engine never wrote it), so the grid degenerates to one z-bucket per y.
|
|
318
|
+
var z = (fd.z != null) ? Math.floor(fd.z) : 0;
|
|
319
|
+
|
|
320
|
+
// Slit provenance — the TAGGER READ (the other half of the upstream slit LO's
|
|
321
|
+
// MARK write, which is the same part). This file names no bubble path and no
|
|
322
|
+
// key string: `Tagger.readTag(fd)` defaults to the canonical provenance key.
|
|
323
|
+
// Untagged frames read back as null (they go only into `combined`).
|
|
324
|
+
var slitName = Tagger.readTag(fd) || null;
|
|
325
|
+
|
|
326
|
+
// Y-marginal accumulation — UNTOUCHED from pre-B1 (protects the golden).
|
|
327
|
+
this._combined.set(y, (this._combined.get(y) || 0) + 1);
|
|
328
|
+
this._total++;
|
|
329
|
+
|
|
330
|
+
if (slitName === 'slit2') {
|
|
331
|
+
this._slit2.set(y, (this._slit2.get(y) || 0) + 1);
|
|
332
|
+
this._slit2Total++;
|
|
333
|
+
} else if (slitName === 'slit1') {
|
|
334
|
+
this._slit1.set(y, (this._slit1.get(y) || 0) + 1);
|
|
335
|
+
this._slit1Total++;
|
|
336
|
+
}
|
|
337
|
+
// Hits with no slit mark (slitName === null) go only into combined.
|
|
338
|
+
|
|
339
|
+
// Y×Z grid accumulation — additive, same frame data, same iteration.
|
|
340
|
+
_gridAdd(this._grid, y, z);
|
|
341
|
+
if (slitName === 'slit2') {
|
|
342
|
+
_gridAdd(this._gridSlit2, y, z);
|
|
343
|
+
} else if (slitName === 'slit1') {
|
|
344
|
+
_gridAdd(this._gridSlit1, y, z);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
combined: this._combined,
|
|
350
|
+
slit1: this._slit1,
|
|
351
|
+
slit2: this._slit2,
|
|
352
|
+
total: this._total,
|
|
353
|
+
slit1Total: this._slit1Total,
|
|
354
|
+
slit2Total: this._slit2Total,
|
|
355
|
+
grid: this._grid,
|
|
356
|
+
gridSlit1: this._gridSlit1,
|
|
357
|
+
gridSlit2: this._gridSlit2
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Full teardown of what this detector composed: its inner Wall (and so its LO). Idempotent.
|
|
363
|
+
*/
|
|
364
|
+
DetectorWall.prototype.dispose = function () {
|
|
365
|
+
this._innerWall.dispose();
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Reset the accumulated histogram (e.g. when experiment parameters change).
|
|
370
|
+
*/
|
|
371
|
+
DetectorWall.prototype.resetHistogram = function () {
|
|
372
|
+
this._combined = new Map();
|
|
373
|
+
this._slit1 = new Map();
|
|
374
|
+
this._slit2 = new Map();
|
|
375
|
+
this._total = 0;
|
|
376
|
+
this._slit1Total = 0;
|
|
377
|
+
this._slit2Total = 0;
|
|
378
|
+
// Y×Z grid (WS-DSE-3D Bite B1) — reset alongside the Y-marginal it derives from.
|
|
379
|
+
this._grid = new Map();
|
|
380
|
+
this._gridSlit1 = new Map();
|
|
381
|
+
this._gridSlit2 = new Map();
|
|
382
|
+
// NOTE: _lastTickData (the #61 throw-guard) is deliberately NOT reset here —
|
|
383
|
+
// resetting histogram DATA is orthogonal to the double-consume guard, which
|
|
384
|
+
// exists to catch a caller bug regardless of accumulator state.
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// ── The kind (objects/kinds.js): descriptor, construction defaults, builder ──────────
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* DetectorWall — Absorbing detector screen (wraps a single LargeObject).
|
|
391
|
+
* Constructor: DetectorWall(universe, canvasRect, wallX, wallY,
|
|
392
|
+
* wallCollisionType, wallColor, wallHeight,
|
|
393
|
+
* wallWidth, wallName)
|
|
394
|
+
* The inner LargeObject always has actions=[COUNT, ABSORB].
|
|
395
|
+
* Group wrapper: the inner LargeObject is stamped with the ONE up-link `_owner` = <this>.
|
|
396
|
+
*/
|
|
397
|
+
var DetectorWallDescriptor = {
|
|
398
|
+
description: 'A detector/absorber screen that counts and removes particles on contact. ' +
|
|
399
|
+
'Used as the observation screen in quantum experiments (e.g. double-slit). ' +
|
|
400
|
+
'Wraps a single LargeObject with COUNT+ABSORB actions. Accumulates a collision ' +
|
|
401
|
+
'histogram (y-position → count) for interference pattern analysis.',
|
|
402
|
+
fields: [
|
|
403
|
+
{
|
|
404
|
+
name: 'wallX',
|
|
405
|
+
type: 'number',
|
|
406
|
+
constraint: { integer: true },
|
|
407
|
+
engineVisible: true,
|
|
408
|
+
description: 'Left edge X position (physics pixels)'
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: 'wallY',
|
|
412
|
+
type: 'number',
|
|
413
|
+
constraint: { integer: true },
|
|
414
|
+
engineVisible: true,
|
|
415
|
+
description: 'Top edge Y position (physics pixels)'
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: 'wallCollisionType',
|
|
419
|
+
type: 'enum',
|
|
420
|
+
constraint: { values: CollisionTypes },
|
|
421
|
+
engineVisible: true,
|
|
422
|
+
description: 'Collision type for the detector wall. Default NONE — absorption is via the ' +
|
|
423
|
+
'ABSORB action in its actions list, not the collision type.'
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: 'wallColor',
|
|
427
|
+
type: 'rgb',
|
|
428
|
+
engineVisible: false,
|
|
429
|
+
description: 'Render color [r, g, b] of the detector wall'
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: 'wallHeight',
|
|
433
|
+
type: 'number',
|
|
434
|
+
constraint: { min: 1, integer: true },
|
|
435
|
+
engineVisible: true,
|
|
436
|
+
unit: 'px',
|
|
437
|
+
description: 'Height of the detector wall (physics pixels). Typically the full canvas height.'
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'wallWidth',
|
|
441
|
+
type: 'number',
|
|
442
|
+
constraint: { min: 1, integer: true },
|
|
443
|
+
engineVisible: true,
|
|
444
|
+
unit: 'px',
|
|
445
|
+
description: 'Width/thickness of the detector wall (physics pixels). Default 20.'
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: 'wallName',
|
|
449
|
+
type: 'string',
|
|
450
|
+
engineVisible: false,
|
|
451
|
+
description: 'Identifier string for this detector (display/debug only; not engine-visible after construction).'
|
|
452
|
+
}
|
|
453
|
+
]
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
registerKind(DetectorWall, {
|
|
457
|
+
from: import.meta.url,
|
|
458
|
+
descriptor: DetectorWallDescriptor,
|
|
459
|
+
defaults: {
|
|
460
|
+
wallX: 180,
|
|
461
|
+
wallY: 0,
|
|
462
|
+
wallCollisionType: 'NONE',
|
|
463
|
+
wallColor: [120, 120, 120],
|
|
464
|
+
wallHeight: 200,
|
|
465
|
+
wallWidth: 20
|
|
466
|
+
},
|
|
467
|
+
build: function (props, ctx, Class) {
|
|
468
|
+
var DW = Class; // the class that registered (a composed fork's own class)
|
|
469
|
+
var world = ObjectSchema.worldRect(ctx.runner.canvas_rect, 'ObjectFactory create(\"DetectorWall\") runner.canvas_rect');
|
|
470
|
+
var pw = world.w;
|
|
471
|
+
var ph = world.h;
|
|
472
|
+
var dw = new DW(
|
|
473
|
+
ctx.runner.universe,
|
|
474
|
+
{ w: pw, h: ph },
|
|
475
|
+
props.wallX != null ? props.wallX : Math.round(ctx.x),
|
|
476
|
+
props.wallY != null ? props.wallY : 0,
|
|
477
|
+
props.wallCollisionType || 'NONE',
|
|
478
|
+
props.wallColor || [120, 200, 120],
|
|
479
|
+
props.wallHeight || ph,
|
|
480
|
+
props.wallWidth || 20,
|
|
481
|
+
props.wallName || ('detector ' + Date.now())
|
|
482
|
+
);
|
|
483
|
+
if (!dw.detector_wall) {
|
|
484
|
+
throw new Error('[ObjectFactory] create("DetectorWall"): the DetectorWall was constructed but ' +
|
|
485
|
+
'exposes no `detector_wall` (the registered inner LargeObject this kind hands back) — ' +
|
|
486
|
+
'nothing placeable was built. (FAIL-LOUD.)');
|
|
487
|
+
}
|
|
488
|
+
dw._schemaKind = Class.name;
|
|
489
|
+
return dw;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
494
|
+
|
|
495
|
+
export { DetectorWall };
|
|
496
|
+
|