@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,471 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tube — Engine-agnostic 3D containing tube (Dim-scaled walls; capped 0, 1, or 2 ends).
|
|
5
|
+
*
|
|
6
|
+
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
7
|
+
*
|
|
8
|
+
* A tube and a wire are the same object at different FRQTL Dims: the SAME
|
|
9
|
+
* primitive is a wire, a barrel, a car
|
|
10
|
+
* tunnel, or a wormhole — only the parameters and end-capping differ. Tube builds
|
|
11
|
+
* a rectangular Y/Z perimeter (top/bottom + near/far walls) running the length of
|
|
12
|
+
* its X (long) axis, with each X-end OPTIONALLY capped (a barrel = one capped end,
|
|
13
|
+
* one open; a wire/tunnel = both open; a sealed chamber = both capped).
|
|
14
|
+
*
|
|
15
|
+
* FRQTL-MAINT (2026-07-01, #61) — ALL SIX FACES, ONE PATH: every wall (TOP, BOTTOM,
|
|
16
|
+
* LEFT/RIGHT end-caps, and — when `zContain` — NEAR/FAR) is now a standard per-face
|
|
17
|
+
* `LargeObject` built with an explicit 6-element rect `[left, top, z, w, h, d]` (the
|
|
18
|
+
* landed `rect_to_z_capable_rectangle` wire contract, `egpt_wasm/src/lib.rs`, commit
|
|
19
|
+
* `1c349ab`) sized to the tube's TRUE z cross-section. `LargeObject`'s own constructor
|
|
20
|
+
* auto-compiles `collisionType`+`reposition` into an op-program for every face
|
|
21
|
+
* uniformly (`actionCompiler.compileActions`) — there is no manual op-program
|
|
22
|
+
* construction anywhere in this file. This RETIRES two prior workarounds (both
|
|
23
|
+
* deleted, not kept alongside — see the "RETIRED" comment blocks in
|
|
24
|
+
* `actionCompiler.js`): the literal-bounds `buildZContainmentOps` combined-program
|
|
25
|
+
* (one fake full-footprint LO faking a NEAR/FAR test) and the manual x/y-only
|
|
26
|
+
* `buildXYIntersectsTrigger` the thin end-caps needed (their z-slab used to be
|
|
27
|
+
* pinned to their own — deliberately thin — x-width). Both were needed only because
|
|
28
|
+
* every LO's z-slab used to be hardcoded to `[0, own-x-width]`; now that the wire
|
|
29
|
+
* contract carries real z/d, `self.near`/`self.far` (read by the `'near'`/`'far'`
|
|
30
|
+
* cases added to `actionCompiler.js`'s `buildEjectOps`/`buildVelocityFlipOp`) are
|
|
31
|
+
* meaningful for the first time, so NEAR/FAR (and the end-caps) work exactly like
|
|
32
|
+
* TOP/BOTTOM/LEFT/RIGHT always did.
|
|
33
|
+
*
|
|
34
|
+
* @param {Object} universe - The engine universe instance (JS or WASM), already `init()`'d.
|
|
35
|
+
* @param {Object} options - Configuration.
|
|
36
|
+
* @param {number} options.left - Interior min-X corner (pixels) — the tube's X origin
|
|
37
|
+
* (breech end when used as a barrel).
|
|
38
|
+
* @param {number} options.top - Interior min-Y corner (pixels).
|
|
39
|
+
* @param {number} [options.z=0] - Interior min-Z corner (pixels). Default 0 — the SAME
|
|
40
|
+
* non-negative corner convention as `left`/`top` (an arbitrary reference origin; any
|
|
41
|
+
* value — including negative — is valid now that z/d ride an explicit wire field
|
|
42
|
+
* instead of a hardcoded 0.0).
|
|
43
|
+
* @param {number} options.interiorWidth - Interior X extent (pixels) — the tube's
|
|
44
|
+
* LENGTH along its long axis (breech to muzzle for a barrel).
|
|
45
|
+
* @param {number} options.interiorHeight - Interior Y extent (pixels) — the bore's
|
|
46
|
+
* vertical cross-section.
|
|
47
|
+
* @param {number} options.interiorDepth - Interior Z extent (pixels) — the bore's
|
|
48
|
+
* lateral cross-section.
|
|
49
|
+
* @param {number} [options.wallThickness=10] - Exterior wall thickness (pixels). Every
|
|
50
|
+
* face extends outward from the interior by this amount on its own thin axis, AND
|
|
51
|
+
* into its neighboring faces' corners on the other two axes (matching the
|
|
52
|
+
* Box.js/OvenBox.js X/Y convention, now applied uniformly to Z too) so the six
|
|
53
|
+
* faces close every edge of the box with no corner gap.
|
|
54
|
+
* @param {boolean} [options.capNegX=false] - Build a wall capping the −X end (the
|
|
55
|
+
* "breech" when Tube is used as a barrel). Default false = open (a wire/tunnel).
|
|
56
|
+
* @param {boolean} [options.capPosX=false] - Build a wall capping the +X end (the
|
|
57
|
+
* "muzzle" when Tube is used as a barrel). Default false = open. **A barrel is
|
|
58
|
+
* `capNegX:true, capPosX:false`** — the muzzle is a bare ABSENCE of a wall, not a
|
|
59
|
+
* sensor (Q-L1-2).
|
|
60
|
+
* @param {string} [options.collisionType='BOUNCE'] - CollisionTypes value applied to
|
|
61
|
+
* every built face (a `CollisionTypes` constant) — TOP/BOTTOM/end-caps/NEAR/FAR alike.
|
|
62
|
+
* @param {Array<string>} [options.actions=[]] - Additional CollisionActions applied to
|
|
63
|
+
* every wall (stacks with collisionType; passed through to LargeObject).
|
|
64
|
+
* @param {Array|null} [options.observedDimensions=null] - Dimension layers the tube's
|
|
65
|
+
* walls observe. null = all dimensions (passed through to every LargeObject —
|
|
66
|
+
* Q4 "passes through the LO's params").
|
|
67
|
+
* @param {Array<number>} [options.color=[90,90,96]] - Wall RGB tint.
|
|
68
|
+
* @param {boolean} [options.zContain=true] - Build the NEAR/FAR z-perimeter walls.
|
|
69
|
+
* Set false to leave the z-axis open (e.g. a flat 2D-in-3D scene) — TOP/BOTTOM/
|
|
70
|
+
* end-caps then fall back to the legacy 4-element rect (z=0, depth=own-x-width;
|
|
71
|
+
* unaffected by this option, byte-identical to a z-open scene).
|
|
72
|
+
* @param {number} [options.wallOpacity=0.3] - Render opacity 0 (fully transparent) to
|
|
73
|
+
* 1 (fully opaque), applied to every wall (STANDARDIZED sugar-level knob, CEO
|
|
74
|
+
* 2026-07-02 — so contained quanta stay visible from outside views). Reuses the
|
|
75
|
+
* existing LO `alpha` schema field (0-255; see the Render shape block below) —
|
|
76
|
+
* NOT a new render hint. The white edge outline stays crisp at any opacity.
|
|
77
|
+
* @param {boolean} [options.collectCollided=false] - DIRECTED TUNNEL BUILD (2026-07-13):
|
|
78
|
+
* passed through as `LargeObject`'s own 10th
|
|
79
|
+
* ctor arg (`collectCollided`) for EVERY wall built by this Tube. When true, the
|
|
80
|
+
* engine accumulates each wall's collided frames into `tickData.objects[handle]
|
|
81
|
+
* .frames` every tick (zero physics change — a collection buffer, not a behavior).
|
|
82
|
+
* Default false — byte-identical for every existing Tube caller, none of which
|
|
83
|
+
* read per-wall collided
|
|
84
|
+
* frames. Needed by `DirectedTunnel`'s host-trig redirect (report §2 path (c)):
|
|
85
|
+
* the host tick-loop reads `tickData.objects[topWallHandle|bottomWallHandle]
|
|
86
|
+
* .frames` between ticks and writes back `vx`/`vy` via `setFrameProperty` — this
|
|
87
|
+
* flag is what makes those frames appear in the marshalled tick output at all.
|
|
88
|
+
* @param {Object|null} [options.emitter=null] - DIRECTED TUNNEL BUILD (2026-07-13):
|
|
89
|
+
* promotes the hand-rolled breech-emitter
|
|
90
|
+
* pattern into Tube's own constructor as an opt-in, auto-wired
|
|
91
|
+
* `QuantumEmitter` positioned just inside the `capNegX` breech wall, centred on the
|
|
92
|
+
* bore's Y (and, when `zContain`, Z) centreline. Requires `options.capNegX:true`
|
|
93
|
+
* (throws otherwise — FAIL-LOUD, not a silent no-op: an emitter with no breech to
|
|
94
|
+
* emit from is a caller error). Exposed as `this.emitter`. Fields (all optional,
|
|
95
|
+
* matching `QuantumEmitter`'s own ctor defaults exactly where omitted):
|
|
96
|
+
* - `vx`,`vy` (default: `speed`,`0` — a directed +X beam into the bore, mirroring
|
|
97
|
+
* a propellant emitter; `speed` below).
|
|
98
|
+
* - `speed` (default 1) - convenience: when `vx`/`vy` are BOTH omitted, the
|
|
99
|
+
* emitter fires at `(speed, 0)` (straight down the bore's local +X).
|
|
100
|
+
* - `burstSize` (default 1), `charge` (default null = random, back-compat
|
|
101
|
+
* 0/1/'POSITIVE'/'NEGATIVE' — matches `QuantumEmitter`'s own vocabulary),
|
|
102
|
+
* `randomDirection` (default false - a DIRECTED beam;
|
|
103
|
+
* true = an isotropic point-source INSIDE the bore, for a diffuse fill),
|
|
104
|
+
* `quantumLimit` (default Infinity), `delay` (default 0), `wrap` (default false).
|
|
105
|
+
* - `wavelength` (default null = engine default mass) - ws-dse-3d AC1: per-
|
|
106
|
+
* emitted-particle wavelength override (`QuantumEmitter`'s own new `opts.
|
|
107
|
+
* wavelength`, Engine Invariant #6). LIVE via `this.emitter.setWavelength()`.
|
|
108
|
+
* When `zContain` is armed, this Tube ALSO auto-centres the emitter's z
|
|
109
|
+
* (`opts.zMin`) on the bore's z-centreline and, when `randomDirection` is
|
|
110
|
+
* also true, makes the random-direction draw 3D-isotropic (`opts.
|
|
111
|
+
* zIsotropy`) — a DIRECTED emitter (`randomDirection:false`, the default)
|
|
112
|
+
* stays `vz=0` deliberately (collimation unaffected).
|
|
113
|
+
* - `sourceHeight` (default: half the bore height, the propellant-aperture
|
|
114
|
+
* convention) - the emission aperture's Y-extent, centred
|
|
115
|
+
* on the bore.
|
|
116
|
+
* Default `null` - byte-identical for every existing Tube caller (none pass
|
|
117
|
+
* `options.emitter`).
|
|
118
|
+
* @param {boolean} [options.sinkOpenEnds=false] - Build an ABSORB sink LO just beyond
|
|
119
|
+
* EACH open (uncapped) X-end — the reusable home for "the muzzle absorber-sink"
|
|
120
|
+
* (CEO 2026-07-02, L2.2): any open-ended contained-flow scene (a barrel firing gas
|
|
121
|
+
* out the muzzle, a wire/tunnel with through-traffic on both ends) wants a bounded
|
|
122
|
+
* population, not unbounded growth as frames exit and are never removed. Named
|
|
123
|
+
* generically (not `muzzleSink`) because Tube itself is generic (wire ≡ barrel ≡
|
|
124
|
+
* tunnel ≡ wormhole) — a barrel (`capNegX:true, capPosX:false`) gets exactly one
|
|
125
|
+
* sink, at the open +X (muzzle) end; a wire (both ends open) gets two. Reuses the
|
|
126
|
+
* EXISTING KILL/ABSORB precedent (`DetectorWall.js`: `CollisionTypes.NONE` +
|
|
127
|
+
* `[COUNT, ABSORB]` actions, compiled by `actionCompiler.js`'s standard path to
|
|
128
|
+
* `set frame.is_alive = 0` — NOT `mass=0`, per the theorist's L1 note) — no new
|
|
129
|
+
* op-program, no engine change. Each sink is invisible (`visible:false`) by
|
|
130
|
+
* default (a floating absorber box outside the bore reads as a rendering
|
|
131
|
+
* artifact, not a real object) but IS included in `this.largeObjects` (so the
|
|
132
|
+
* Layers panel / IDE tooling can still find and toggle it) and separately on
|
|
133
|
+
* `this.sinks` for direct introspection (e.g. reading each sink's `.count`).
|
|
134
|
+
*
|
|
135
|
+
|
|
136
|
+
* @property {Array<LargeObject>} walls - ALL built walls (TOP, BOTTOM, LEFT/RIGHT for
|
|
137
|
+
* any capped end, NEAR/FAR when `zContain`) — the caller's visible/render list.
|
|
138
|
+
* @property {Array<LargeObject>} sinks - The built open-end ABSORB sinks (when
|
|
139
|
+
* `sinkOpenEnds`), one per uncapped X-end. Empty array when `sinkOpenEnds` is false.
|
|
140
|
+
* @property {Object} innerRect - `{left, top, z, w, h, d}` — the interior box, for
|
|
141
|
+
* callers that need to place contents (a bullet, an emitter, preplaced quanta)
|
|
142
|
+
* inside the tube.
|
|
143
|
+
*
|
|
144
|
+
* Custom tick functions STACK on the tube's own (Q4): Tube registers no tick
|
|
145
|
+
* functions of its own — it only registers LargeObjects at construction time — so a
|
|
146
|
+
* caller's `universe.experimentTickFunctions.push(...)` composes freely alongside it.
|
|
147
|
+
*
|
|
148
|
+
* ── Layer 2: physics behavior ────────────────────────────────────────────────
|
|
149
|
+
*
|
|
150
|
+
* Tube adds ZERO new physics (canonical-bench SU1: sugar only WIRES the engine). Every
|
|
151
|
+
* face — X/Y perimeter AND (when `zContain`) the Z perimeter — is the existing
|
|
152
|
+
* OvenBox/Box eject+bounce op-program pattern (proven bit-identical to native
|
|
153
|
+
* WALL/BOUNCE, EP21-EP28) — `LargeObject`'s own constructor compiles
|
|
154
|
+
* `collisionType`+`reposition` to an op-program automatically for every face, including
|
|
155
|
+
* NEAR/FAR (see `lib/frqtl/objects/objects/actionCompiler.js` `compileActions` +
|
|
156
|
+
* its `'near'`/`'far'` reposition cases).
|
|
157
|
+
*
|
|
158
|
+
* OBSERVED (this workstream, pkg-node WASM, seed 42): a Dim-6 quantum fired at each of
|
|
159
|
+
* the tube's 6 possible faces (top/bottom/near/far/breech/muzzle, `capNegX:true
|
|
160
|
+
* capPosX:false`) bounces and stays contained on the 5 closed faces over 600+ ticks; a
|
|
161
|
+
* quantum fired at the open muzzle exits without any wall interaction — re-verified
|
|
162
|
+
* end-to-end against the landed z/depth wire contract (empirical re-proof, not assumed;
|
|
163
|
+
* see the delivery report for the exact run).
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
import { LargeObject } from '../../parts/LargeObject.js';
|
|
167
|
+
import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
168
|
+
import { CollisionActions } from '../../parts/CollisionActions.js';
|
|
169
|
+
import { QuantumEmitter } from './QuantumEmitter.js';
|
|
170
|
+
|
|
171
|
+
// ── Imports ─────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
// ── Tube ────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @param {Object} universe - Already-init'd universe (JS or WASM).
|
|
177
|
+
* @param {Object} options - See module-level JSDoc above.
|
|
178
|
+
*/
|
|
179
|
+
function Tube(universe, options) {
|
|
180
|
+
options = options || {};
|
|
181
|
+
|
|
182
|
+
this.universe = universe;
|
|
183
|
+
this._schemaKind = 'Tube';
|
|
184
|
+
|
|
185
|
+
var interiorWidth = options.interiorWidth;
|
|
186
|
+
var interiorHeight = options.interiorHeight;
|
|
187
|
+
var interiorDepth = options.interiorDepth;
|
|
188
|
+
if (!(interiorWidth > 0) || !(interiorHeight > 0) || !(interiorDepth > 0)) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
'[Tube] interiorWidth/interiorHeight/interiorDepth must all be positive numbers.'
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
var wt = options.wallThickness !== undefined ? options.wallThickness : 10;
|
|
195
|
+
|
|
196
|
+
var innerLeft = options.left;
|
|
197
|
+
var innerTop = options.top;
|
|
198
|
+
// z defaults to 0 — the SAME non-negative corner convention as left/top (an
|
|
199
|
+
// arbitrary reference origin; FRQTL-MAINT 2026-07-01: any value is now valid,
|
|
200
|
+
// since z/d ride the explicit 6-element wire field rather than a hardcoded 0.0 —
|
|
201
|
+
// see the module docstring).
|
|
202
|
+
var innerZ = options.z !== undefined ? options.z : 0;
|
|
203
|
+
if (innerLeft === undefined || innerTop === undefined) {
|
|
204
|
+
throw new Error('[Tube] options.left and options.top are required.');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
var capNegX = !!options.capNegX;
|
|
208
|
+
var capPosX = !!options.capPosX;
|
|
209
|
+
|
|
210
|
+
var ct = options.collisionType || CollisionTypes.BOUNCE;
|
|
211
|
+
var actions = options.actions || [];
|
|
212
|
+
var color = options.color || [90, 90, 96];
|
|
213
|
+
var observedDimensions = options.observedDimensions !== undefined ? options.observedDimensions : null;
|
|
214
|
+
var zContain = options.zContain !== undefined ? !!options.zContain : true;
|
|
215
|
+
// wallOpacity: the STANDARDIZED render knob (CEO 2026-07-02) — see the Render
|
|
216
|
+
// shape block below for the mechanism (reuses the existing LO `alpha` schema
|
|
217
|
+
// field, lib/frqtl/objects/schema/index.js:166 — NOT a new hint).
|
|
218
|
+
var wallOpacity = options.wallOpacity !== undefined ? options.wallOpacity : 0.3;
|
|
219
|
+
// sinkOpenEnds: the muzzle-absorber-sink home (CEO 2026-07-02, L2.2) — see the
|
|
220
|
+
// module docstring's param note. Default false — byte-identical to every
|
|
221
|
+
// existing Tube consumer that doesn't
|
|
222
|
+
// opt in.
|
|
223
|
+
var sinkOpenEnds = options.sinkOpenEnds !== undefined ? !!options.sinkOpenEnds : false;
|
|
224
|
+
// collectCollided: DIRECTED TUNNEL BUILD — see module docstring param note.
|
|
225
|
+
// Default false — byte-identical for every existing caller.
|
|
226
|
+
var collectCollided = options.collectCollided !== undefined ? !!options.collectCollided : false;
|
|
227
|
+
|
|
228
|
+
this.innerRect = {
|
|
229
|
+
left: innerLeft, top: innerTop, z: innerZ,
|
|
230
|
+
w: interiorWidth, h: interiorHeight, d: interiorDepth,
|
|
231
|
+
};
|
|
232
|
+
this.wallThickness = wt;
|
|
233
|
+
|
|
234
|
+
// ── ALL SIX FACES, ONE PATH (FRQTL-MAINT 2026-07-01, #61) ────────────────
|
|
235
|
+
// Every wall gets an explicit 6-element rect [left, top, z, w, h, d] (when
|
|
236
|
+
// zContain) sized to the tube's TRUE z cross-section — z spans
|
|
237
|
+
// [innerZ - wt, innerZ + interiorDepth + wt] (the bore plus a wt margin at each
|
|
238
|
+
// end, so all six faces close every corner, the same way the X-cap extension
|
|
239
|
+
// (extLeft/extRight) already closed the TOP/BOTTOM corners against the caps).
|
|
240
|
+
// LargeObject's own constructor auto-compiles collisionType+reposition into an
|
|
241
|
+
// op-program (actionCompiler.compileActions) — no manual program-building here,
|
|
242
|
+
// for ANY face.
|
|
243
|
+
var extLeft = capNegX ? wt : 0;
|
|
244
|
+
var extRight = capPosX ? wt : 0;
|
|
245
|
+
var zLo = innerZ - wt; // this wall's own near (−z) corner
|
|
246
|
+
var zD = interiorDepth + 2 * wt; // z span: the bore + a wt margin each end
|
|
247
|
+
|
|
248
|
+
// rect(left, top, w, h) → 6-element [left,top,z,w,h,d] when zContain (real,
|
|
249
|
+
// always-correct z-slab for this face); else the legacy 4-element form
|
|
250
|
+
// (back-compat: z=0/d=w, unaffected — matches a z-open scene exactly as before).
|
|
251
|
+
function faceRect(left, top, w, h) {
|
|
252
|
+
return zContain ? [left, top, zLo, w, h, zD] : [left, top, w, h];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
this.walls = [];
|
|
256
|
+
|
|
257
|
+
// TOP wall (Y-min side) — pushes intersecting frames DOWN (+y) into the interior.
|
|
258
|
+
this.walls.push(new LargeObject(
|
|
259
|
+
universe,
|
|
260
|
+
faceRect(innerLeft - extLeft, innerTop - wt, interiorWidth + extLeft + extRight, wt),
|
|
261
|
+
ct, actions, 'TOP', 'bottom', observedDimensions, 1, color, collectCollided
|
|
262
|
+
));
|
|
263
|
+
|
|
264
|
+
// BOTTOM wall (Y-max side) — pushes intersecting frames UP (-y) into the interior.
|
|
265
|
+
this.walls.push(new LargeObject(
|
|
266
|
+
universe,
|
|
267
|
+
faceRect(innerLeft - extLeft, innerTop + interiorHeight, interiorWidth + extLeft + extRight, wt),
|
|
268
|
+
ct, actions, 'BOTTOM', 'top', observedDimensions, 1, color, collectCollided
|
|
269
|
+
));
|
|
270
|
+
|
|
271
|
+
// −X cap (breech, when requested) — pushes intersecting frames RIGHT (+x) inward.
|
|
272
|
+
// FRQTL-MAINT (2026-07-01): now a STANDARD per-face LO (was a manual
|
|
273
|
+
// collision_type:NONE + x/y-only-trigger program — see the RETIRED note in
|
|
274
|
+
// actionCompiler.js) — the explicit z/d above gives this thin-in-x wall a
|
|
275
|
+
// correct z-slab, so the native 3D Fn('intersects') trigger (LargeObject's
|
|
276
|
+
// normal auto-compile) now correctly catches a z-drifted frame too.
|
|
277
|
+
if (capNegX) {
|
|
278
|
+
this.walls.push(new LargeObject(
|
|
279
|
+
universe,
|
|
280
|
+
faceRect(innerLeft - wt, innerTop - wt, wt, interiorHeight + 2 * wt),
|
|
281
|
+
ct, actions, 'LEFT', 'right', observedDimensions, 1, color, collectCollided
|
|
282
|
+
));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// +X cap (muzzle, when requested) — pushes intersecting frames LEFT (-x) inward.
|
|
286
|
+
// OMITTED by default (capPosX:false) — the open muzzle is a bare absence of a
|
|
287
|
+
// wall (Q-L1-2), not a sensor. Same standard construction as −X.
|
|
288
|
+
if (capPosX) {
|
|
289
|
+
this.walls.push(new LargeObject(
|
|
290
|
+
universe,
|
|
291
|
+
faceRect(innerLeft + interiorWidth, innerTop - wt, wt, interiorHeight + 2 * wt),
|
|
292
|
+
ct, actions, 'RIGHT', 'left', observedDimensions, 1, color, collectCollided
|
|
293
|
+
));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ── NEAR/FAR (Z faces) — FRQTL-MAINT (2026-07-01, #61): REAL per-face walls,
|
|
297
|
+
// unblocked by the landed z/depth wire contract. Previously structurally
|
|
298
|
+
// impossible (every LO's z-slab was pinned to the origin) and worked around with
|
|
299
|
+
// a manual literal-bounds combined program on one full-footprint LO
|
|
300
|
+
// (buildZContainmentOps, now RETIRED — see actionCompiler.js). NEAR sits at the
|
|
301
|
+
// bore's −z face (thin in z, full X/Y span, extended by wt into the X-cap and
|
|
302
|
+
// Y-perimeter corners exactly like TOP/BOTTOM/LEFT/RIGHT extend into EACH OTHER's
|
|
303
|
+
// corners); FAR mirrors it at the +z face. Both use the SAME standard
|
|
304
|
+
// auto-compile path (collisionType+reposition) as every other face — the
|
|
305
|
+
// 'near'/'far' reposition cases added to actionCompiler.js's buildEjectOps/
|
|
306
|
+
// buildVelocityFlipOp make self.near/self.far (now real, non-degenerate values)
|
|
307
|
+
// drive the eject/flip formulas exactly like self.left/self.right do for
|
|
308
|
+
// LEFT/RIGHT.
|
|
309
|
+
if (zContain) {
|
|
310
|
+
this.walls.push(new LargeObject(
|
|
311
|
+
universe,
|
|
312
|
+
[innerLeft - extLeft, innerTop - wt, zLo,
|
|
313
|
+
interiorWidth + extLeft + extRight, interiorHeight + 2 * wt, wt],
|
|
314
|
+
ct, actions, 'NEAR', 'far', observedDimensions, 1, color, collectCollided
|
|
315
|
+
));
|
|
316
|
+
this.walls.push(new LargeObject(
|
|
317
|
+
universe,
|
|
318
|
+
[innerLeft - extLeft, innerTop - wt, innerZ + interiorDepth,
|
|
319
|
+
interiorWidth + extLeft + extRight, interiorHeight + 2 * wt, wt],
|
|
320
|
+
ct, actions, 'FAR', 'near', observedDimensions, 1, color, collectCollided
|
|
321
|
+
));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Open-end ABSORB sinks (CEO 2026-07-02, L2.2 — the muzzle-absorber-sink) ──
|
|
325
|
+
// ONE sink per uncapped X-end (never on a capped end — a closed face already
|
|
326
|
+
// contains everything; a sink there would be redundant and would fight the
|
|
327
|
+
// wall's own bounce). REUSES the existing KILL/ABSORB precedent verbatim
|
|
328
|
+
// (DetectorWall.js: CollisionTypes.NONE + [COUNT, ABSORB] actions, compiled by
|
|
329
|
+
// actionCompiler.js's standard compileActions path to `set frame.is_alive = 0`
|
|
330
|
+
// — NOT mass=0, per the theorist's L1 note) — zero new op-program, zero engine
|
|
331
|
+
// change. Positioned a small margin OUTSIDE the bore's open face (never
|
|
332
|
+
// overlapping the interior, so normal bore physics near the opening is
|
|
333
|
+
// unaffected) with the SAME z-slab (`faceRect`) every other face uses, so it
|
|
334
|
+
// catches an exiting frame regardless of its Y/Z position — mirrors the NEAR/
|
|
335
|
+
// FAR walls' "full-cross-section, thin-on-its-own-axis" shape.
|
|
336
|
+
this.sinks = [];
|
|
337
|
+
if (sinkOpenEnds) {
|
|
338
|
+
var sinkDepth = wt; // reuse wallThickness as the sink's own thin-axis extent
|
|
339
|
+
var sinkMargin = 2; // small gap so the sink never touches/overlaps the bore
|
|
340
|
+
|
|
341
|
+
if (!capNegX) {
|
|
342
|
+
// Sink just before the open −X (breech) end.
|
|
343
|
+
this.sinks.push(new LargeObject(
|
|
344
|
+
universe,
|
|
345
|
+
faceRect(innerLeft - sinkMargin - sinkDepth, innerTop - wt, sinkDepth, interiorHeight + 2 * wt),
|
|
346
|
+
CollisionTypes.NONE, [CollisionActions.COUNT, CollisionActions.ABSORB],
|
|
347
|
+
'NEG_X_SINK', 'closest', observedDimensions, 1, color
|
|
348
|
+
));
|
|
349
|
+
}
|
|
350
|
+
if (!capPosX) {
|
|
351
|
+
// Sink just beyond the open +X (muzzle) end.
|
|
352
|
+
this.sinks.push(new LargeObject(
|
|
353
|
+
universe,
|
|
354
|
+
faceRect(innerLeft + interiorWidth + sinkMargin, innerTop - wt, sinkDepth, interiorHeight + 2 * wt),
|
|
355
|
+
CollisionTypes.NONE, [CollisionActions.COUNT, CollisionActions.ABSORB],
|
|
356
|
+
'POS_X_SINK', 'closest', observedDimensions, 1, color
|
|
357
|
+
));
|
|
358
|
+
}
|
|
359
|
+
// Invisible by default — a floating absorber box outside the bore reads as
|
|
360
|
+
// a render artifact, not a real object. Physics (registerLargeObject) is
|
|
361
|
+
// unaffected by visibility (a renderer-only property) — the sink still
|
|
362
|
+
// absorbs whether or not it is drawn.
|
|
363
|
+
for (var _si = 0; _si < this.sinks.length; _si++) {
|
|
364
|
+
this.sinks[_si].visible = false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ── Breech emitter (DIRECTED TUNNEL BUILD, 2026-07-13, opt-in) ────────────
|
|
369
|
+
// Promotes the hand-rolled breech-emitter pattern into Tube's own
|
|
370
|
+
// constructor — see the module docstring's
|
|
371
|
+
// `options.emitter` param note for the full field list. FAIL-LOUD (not a
|
|
372
|
+
// silent no-op): an emitter needs a closed breech to emit FROM, so
|
|
373
|
+
// `options.emitter` without `capNegX:true` throws rather than building a
|
|
374
|
+
// directed emitter that immediately fires out an open end.
|
|
375
|
+
this.emitter = null;
|
|
376
|
+
if (options.emitter) {
|
|
377
|
+
if (!capNegX) {
|
|
378
|
+
throw new Error(
|
|
379
|
+
'[Tube] options.emitter requires options.capNegX:true (the emitter is ' +
|
|
380
|
+
'positioned just inside the breech wall — there is no breech to emit from otherwise).'
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
var espec = options.emitter;
|
|
384
|
+
var espeed = espec.speed !== undefined ? espec.speed : 1;
|
|
385
|
+
var evx = espec.vx !== undefined ? espec.vx : espeed;
|
|
386
|
+
var evy = espec.vy !== undefined ? espec.vy : 0;
|
|
387
|
+
var eburst = espec.burstSize !== undefined ? espec.burstSize : 1;
|
|
388
|
+
var echarge = espec.charge !== undefined ? espec.charge : null;
|
|
389
|
+
var erandomDir = espec.randomDirection !== undefined ? !!espec.randomDirection : false;
|
|
390
|
+
var equantumLimit = espec.quantumLimit !== undefined ? espec.quantumLimit : Infinity;
|
|
391
|
+
var edelay = espec.delay !== undefined ? espec.delay : 0;
|
|
392
|
+
var ewrap = espec.wrap !== undefined ? !!espec.wrap : false;
|
|
393
|
+
// Aperture: centred on the bore's Y centreline, positioned just inside the
|
|
394
|
+
// breech wall (`breechEmitX = breechX + 2`). Height default is the
|
|
395
|
+
// `propellantSourceH` convention (half the bore height).
|
|
396
|
+
var esourceH = espec.sourceHeight !== undefined ? espec.sourceHeight : Math.max(1, Math.floor(interiorHeight / 2));
|
|
397
|
+
var eEmitX = innerLeft + 2;
|
|
398
|
+
var eCenterY = innerTop + interiorHeight / 2;
|
|
399
|
+
// ws-dse-3d AC1 (CEO drive #2 round-2 ruling, "the emitter source mode
|
|
400
|
+
// consumes the SAME normalized SOURCE_SPEC bag"): wavelength/z-centring/
|
|
401
|
+
// z-isotropy — derived from THIS Tube's own `zContain`/`innerZ`/
|
|
402
|
+
// `interiorDepth` (already in scope), never a caller burden. zContain
|
|
403
|
+
// false (every pre-existing Tube.options.emitter caller) -> eZMin stays
|
|
404
|
+
// null, eZIsotropy stays false -> byte-identical (QuantumEmitter's own
|
|
405
|
+
// opts defaults).
|
|
406
|
+
var ewavelength = espec.wavelength !== undefined ? espec.wavelength : null;
|
|
407
|
+
var eZMin = zContain ? (innerZ + interiorDepth / 2) : null; // bore's z-centreline
|
|
408
|
+
var eZDepth = zContain ? 0 : null; // pinned (single value), not a spread
|
|
409
|
+
var eZIsotropy = zContain; // only consumed when erandomDir is also true
|
|
410
|
+
this.emitter = new QuantumEmitter(
|
|
411
|
+
universe,
|
|
412
|
+
[[eEmitX, eCenterY - Math.floor(esourceH / 2), 1, esourceH]],
|
|
413
|
+
evx, evy, eburst, echarge,
|
|
414
|
+
true, // auto_fire
|
|
415
|
+
erandomDir,
|
|
416
|
+
equantumLimit,
|
|
417
|
+
edelay,
|
|
418
|
+
ewrap,
|
|
419
|
+
{ wavelength: ewavelength, zMin: eZMin, zDepth: eZDepth, zIsotropy: eZIsotropy }
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ── Render shape (renderer-only hints; physics is unaffected) ────────────
|
|
424
|
+
// FRQTL-MAINT (2026-07-01, CEO-flagged): shape:'box' + depth wraps each wall LO
|
|
425
|
+
// with a real 3D BoxGeometry (Bullet.js:112-116 precedent) instead of the
|
|
426
|
+
// flat-plane default (_buildLoMesh, three-provider.js) — a zero-thickness plane
|
|
427
|
+
// reads as a thin 2D outline at oblique orbit angles, which is exactly the CEO's
|
|
428
|
+
// "walls draw as thin 2D outlines" finding. depth is read straight off the
|
|
429
|
+
// wall's OWN rect.d (set above by the 6-element rect) so the box's z-extent
|
|
430
|
+
// matches its true physical z-slab exactly; when zContain is false (legacy
|
|
431
|
+
// 4-element rect, no rect.d), depth stays undefined and _buildLoMesh's existing
|
|
432
|
+
// `lo.depth || lo.rect.w` fallback applies unchanged.
|
|
433
|
+
//
|
|
434
|
+
// wallOpacity (CEO 2026-07-02, translucent walls so contained quanta stay
|
|
435
|
+
// visible from outside): REUSES the existing `alpha` LO field — already a
|
|
436
|
+
// first-class, schema-declared render-only property (`lib/frqtl/objects/schema/
|
|
437
|
+
// index.js` name:'alpha', 0-255, widgetHint:'slider') that `_buildLoMesh`'s
|
|
438
|
+
// box-shape branch already reads for its material's `transparent`/`opacity`
|
|
439
|
+
// (`three-provider.js` ~line 520/557) — NOT a new hint, NOT a hardcoded material.
|
|
440
|
+
// `alpha` is 0-255 (matches `color`'s convention); `wallOpacity` is the
|
|
441
|
+
// standardized 0-1 sugar-level knob every caller passes, converted here once.
|
|
442
|
+
// The box mesh's white edge outline (`EdgesGeometry`+`LineBasicMaterial`,
|
|
443
|
+
// three-provider.js) is unconditional at its own fixed opacity regardless of the
|
|
444
|
+
// fill's alpha, so the duct silhouette stays crisp at any wallOpacity.
|
|
445
|
+
var wallAlpha = Math.round(255 * Math.max(0, Math.min(1, wallOpacity)));
|
|
446
|
+
for (var _wi = 0; _wi < this.walls.length; _wi++) {
|
|
447
|
+
this.walls[_wi].shape = 'box';
|
|
448
|
+
if (this.walls[_wi].rect.d !== undefined) {
|
|
449
|
+
this.walls[_wi].depth = this.walls[_wi].rect.d;
|
|
450
|
+
}
|
|
451
|
+
this.walls[_wi].alpha = wallAlpha;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ── THE ONE UP-LINK on every wall and sink this Tube composed ─────────────
|
|
455
|
+
for (var _gi = 0; _gi < this.walls.length; _gi++) {
|
|
456
|
+
this.walls[_gi]._owner = this; // THE ONE UP-LINK
|
|
457
|
+
}
|
|
458
|
+
for (var _si2 = 0; _si2 < this.sinks.length; _si2++) {
|
|
459
|
+
this.sinks[_si2]._owner = this; // THE ONE UP-LINK
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ── Return-shape convenience ─────────────────────────────────────────────
|
|
463
|
+
// Sinks ARE included (so Layers-panel/IDE tooling can find + toggle them) —
|
|
464
|
+
// each is already visible:false, so the renderer draws nothing for it.
|
|
465
|
+
this.largeObjects = this.walls.concat(this.sinks);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
469
|
+
|
|
470
|
+
export { Tube };
|
|
471
|
+
|