@descix/frqtl-node 0.6.0-p0.1 → 0.6.0-p0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -1
- package/LICENSE.md +664 -0
- package/README.md +1 -1
- package/dist/dist-manifest.json +125 -125
- package/dist/fat/pkg-node/egpt_wasm.js +7 -7
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +1 -1
- package/dist/frqtl/compat/providers/wasm-engine.js +29 -31
- package/dist/frqtl/compat/providers/wasm-load-browser.js +2 -2
- package/dist/frqtl/compat/renderer/RenderFamily.js +1 -2
- package/dist/frqtl/compat/renderer/RenderMesh.js +2 -3
- package/dist/frqtl/compat/renderer/RenderShape.js +1 -1
- package/dist/frqtl/compat/renderer/captureStill-common.js +1 -2
- package/dist/frqtl/compat/renderer/display-slot.js +1 -1
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +5 -5
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +1 -1
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +4 -4
- package/dist/frqtl/compat/renderer/providers/three-provider.js +23 -27
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +2 -2
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +1 -1
- package/dist/frqtl/compat/vendor.js +2 -2
- package/dist/frqtl/objects/FrqtlApp.js +22 -24
- package/dist/frqtl/objects/ObjectFactory.js +1 -2
- package/dist/frqtl/objects/constants/WallBehaviors.js +5 -5
- package/dist/frqtl/objects/geometry/SlabFace.js +6 -6
- package/dist/frqtl/objects/instruments/capture-host.js +2 -2
- package/dist/frqtl/objects/instruments/capture.js +4 -5
- package/dist/frqtl/objects/instruments/hosted-eyes.js +3 -4
- package/dist/frqtl/objects/objects/DirectedTunnel.js +10 -11
- package/dist/frqtl/objects/objects/LightSource.js +22 -25
- package/dist/frqtl/objects/objects/QuantumEmitter.js +14 -14
- package/dist/frqtl/objects/objects/Tube.js +14 -14
- package/dist/frqtl/objects/objects/absorbRadiation.js +12 -13
- package/dist/frqtl/objects/objects/actionCompiler.js +52 -52
- package/dist/frqtl/objects/objects/controlAction.js +2 -2
- package/dist/frqtl/objects/objects/controlBind.js +2 -2
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +3 -3
- package/dist/frqtl/objects/objects/emitterProgram.js +9 -9
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +3 -3
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +9 -9
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +2 -2
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +2 -2
- package/dist/frqtl/objects/objects/makeControlsForObject.js +25 -29
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +2 -2
- package/dist/frqtl/objects/objects/slitLinerScatter.js +8 -10
- package/dist/frqtl/objects/objects/sustainedSource.js +15 -16
- package/dist/frqtl/objects/objects/tagger.js +2 -2
- package/dist/frqtl/objects/schema/engine-scopes.js +11 -12
- package/dist/frqtl/objects/schema/index.js +6 -6
- package/dist/frqtl/objects/schema/scopes.js +10 -10
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +1 -1
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +1 -1
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +3 -3
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +7 -8
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +20 -21
- package/dist/frqtl/objects/setup/setupFxSmoke.js +1 -1
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +23 -26
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +39 -40
- package/dist/frqtl/objects/setup/setupTunnelLab.js +17 -19
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +1 -1
- package/dist/frqtl/objects/setup/setupWaveInterference.js +4 -5
- package/dist/frqtl/objects/v1-replacements.js +0 -1
- package/dist/frqtl/parts/CameraControls.js +5 -5
- package/dist/frqtl/parts/CameraPath.js +1 -1
- package/dist/frqtl/parts/CollisionActions.js +2 -3
- package/dist/frqtl/parts/CollisionTypes.js +1 -2
- package/dist/frqtl/parts/DetectorWall.js +10 -10
- package/dist/frqtl/parts/FrqtlControlsPanel.js +11 -11
- package/dist/frqtl/parts/FrqtlVision.js +1 -1
- package/dist/frqtl/parts/GraphingDetectorWall.js +3 -4
- package/dist/frqtl/parts/LargeObject.js +7 -8
- package/dist/frqtl/parts/LightColor.js +2 -2
- package/dist/frqtl/parts/LiveChart.js +1 -1
- package/dist/frqtl/parts/LogicGate.js +4 -7
- package/dist/frqtl/parts/OvenBox.js +1 -2
- package/dist/frqtl/parts/PointSource.js +10 -11
- package/dist/frqtl/parts/SlitLightSource.js +6 -6
- package/dist/frqtl/parts/SlitScreen.js +14 -14
- package/dist/frqtl/parts/SphereEmitter.js +24 -24
- package/dist/frqtl/parts/Tunnel.js +17 -17
- package/dist/frqtl/parts/Wall.js +17 -17
- package/dist/frqtl/scenes/dse-one-emitter.js +1 -1
- package/dist/frqtl/scenes/dse-one-slit.js +2 -2
- package/dist/frqtl/scenes/dse-photoelectric.js +3 -3
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +5 -6
- package/dist/frqtl/scenes/dse-two-emitters.js +1 -1
- package/dist/frqtl/scenes/dse-two-slits.js +2 -2
- package/dist/frqtl/scenes/laser-buildup.js +1 -1
- package/dist/frqtl/scenes/mirror-3d.js +4 -4
- package/dist/frqtl/scenes/mirror-route.js +9 -9
- package/dist/frqtl/scenes/mirror.js +1 -1
- package/dist/frqtl/scenes/self-interference.js +1 -1
- package/dist/frqtl/scenes/smoking-gun.js +1 -1
- package/dist/frqtl/scenes/sun.js +2 -2
- package/dist/shim/bundle/frqnb.js +3 -3
- package/dist/shim/bundle/index.js +1 -1
- package/dist/shim/bundle/manifest.js +2 -2
- package/dist/shim/bundle/zip.js +1 -1
- package/dist/shim/controls/index.js +14 -14
- package/dist/shim/controls/registry.js +3 -3
- package/dist/shim/engine/artifact-shell.js +1 -2
- package/dist/shim/engine/router.js +15 -17
- package/dist/shim/expr/ast-schema.js +5 -5
- package/dist/shim/field-descriptor/index.js +7 -8
- package/dist/shim/frame-data/index.js +3 -3
- package/dist/shim/index.js +4 -4
- package/dist/shim/notebook/canvas-guard.js +1 -1
- package/dist/shim/notebook/catalog.js +2 -2
- package/dist/shim/notebook/format.js +26 -26
- package/dist/shim/notebook/kernel.js +3 -4
- package/dist/shim/notebook/knob.js +0 -1
- package/dist/shim/notebook/module-resolver.js +6 -6
- package/dist/shim/notebook/produced-bindings.js +1 -1
- package/dist/shim/notebook/setup-builder.js +9 -12
- package/dist/shim/notebook/vendor-importmap.js +1 -1
- package/dist/shim/notebook/view-builder.js +1 -1
- package/dist/shim/notebook/view-ctx.js +1 -1
- package/dist/shim/notebook/warmup.js +2 -2
- package/dist/shim/page-params/index.js +8 -9
- package/dist/shim/registry/copy-gate.js +1 -1
- package/dist/shim/registry/exhibits.js +4 -4
- package/dist/shim/registry/experiments.js +3 -3
- package/dist/shim/registry/walkthrough.js +21 -24
- package/dist/shim/render/render-stages.js +2 -2
- package/dist/shim/render/sun-look.js +2 -2
- package/dist/shim/rpc/index.js +2 -2
- package/dist/shim/runtime/index.js +12 -12
- package/dist/shim/storage/modules.js +8 -8
- package/llms.txt +2 -2
- package/package.json +4 -3
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* controlAction.js — OBJECTS-SIDE execution for an `action` control descriptor
|
|
5
|
-
* (presentation pass Item 2,
|
|
5
|
+
* (presentation pass Item 2, E-P2a/E-P2b). The sibling of
|
|
6
6
|
* `controlBind.js`, same home, same FAIL-LOUD style.
|
|
7
7
|
*
|
|
8
8
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
9
9
|
*
|
|
10
10
|
* `resolveAction(action, ctx)` → `{ run() }`.
|
|
11
11
|
*
|
|
12
|
-
* Arrow
|
|
12
|
+
* Arrow rule (mirrors the ratified bind split EXACTLY): `lib/shim/controls`
|
|
13
13
|
* owns the NAME VOCABULARY (`ControlsSchema.ACTIONS` — the data shape);
|
|
14
14
|
* EXECUTION — mapping a name to a live `FrqtlApp` method and calling it
|
|
15
15
|
* — is OBJECTS-SIDE and lives here, beside the panel/overlay/sync sugar that
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* controlBind.js — OBJECTS-SIDE execution for a scalar control descriptor's
|
|
5
|
-
* `bind: {object, prop}` (
|
|
5
|
+
* `bind: {object, prop}` (gym-controls-as-sugar, FULL CONVERGENCE build,
|
|
6
6
|
* Stage i item 2).
|
|
7
7
|
*
|
|
8
8
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
9
9
|
*
|
|
10
10
|
* `resolveBind(bind, ctx, channel)` → `{ set(value), get() }`.
|
|
11
11
|
*
|
|
12
|
-
* Arrow
|
|
12
|
+
* Arrow rule (§5 converged): `lib/shim/controls` owns the `bind` DATA SHAPE
|
|
13
13
|
* only (`{object, prop}`, both non-empty strings — `ControlsSchema.BIND_KEYS`).
|
|
14
14
|
* EXECUTION — resolving `bind.object` to a live handle, dispatching by
|
|
15
15
|
* `channel`/object-kind, and routing through the real property accessors — is
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* This is a HOST TICK-FN COMPONENT in the SAME sense as `emitterProgram.js` — an
|
|
19
19
|
* engine-agnostic function composed entirely from generic primitives
|
|
20
20
|
* (`universe.random()`, `universe.setFrameProperty()`), NOT a native op-program (the
|
|
21
|
-
* op-vocabulary's `FN_NAMES` has no `cos`/`sin` —
|
|
21
|
+
* op-vocabulary's `FN_NAMES` has no `cos`/`sin` — an established FN_NAMES gap,
|
|
22
22
|
* report §2(b)). UNLIKE `emitterProgram.js` (which registers itself onto
|
|
23
23
|
* `universe.experimentTickFunctions` and runs during Phase A, BEFORE that tick's own
|
|
24
24
|
* collision/collect), this component must run AFTER `doTick()` returns — it needs
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* `[angleRad-spreadRad, angleRad+spreadRad]`. `0` (default) = fully directed,
|
|
45
45
|
* zero spread (every redirected frame gets the EXACT same angle) — byte-identical
|
|
46
46
|
* opt-out for a pure-directed beam.
|
|
47
|
-
* @param {number} [spec.polarSpreadRad=0] - 3D EXTENSION (
|
|
47
|
+
* @param {number} [spec.polarSpreadRad=0] - 3D EXTENSION (§4
|
|
48
48
|
* option (c) — "extend the redirect to draw a direction on the unit SPHERE,
|
|
49
49
|
* azimuth + polar"): an independent isotropic-spread draw for the POLAR angle
|
|
50
50
|
* (elevation out of the XY plane), `phi = (random()*2-1)*polarSpreadRad`. `0`
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
var speed = (fixedSpeed !== null)
|
|
93
93
|
? fixedSpeed
|
|
94
94
|
: (Math.sqrt(fd.vx * fd.vx + fd.vy * fd.vy) || 1);
|
|
95
|
-
// 3D EXTENSION
|
|
95
|
+
// 3D EXTENSION: phi (polar, elevation out of the XY
|
|
96
96
|
// plane) draws ONLY when polarSpreadRad is non-zero — otherwise
|
|
97
97
|
// phi stays exactly 0, making this byte-identical to the pre-3D
|
|
98
98
|
// 2-axis formula (cos(0)=1 -> vx=cos(theta)*speed,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `universe.random()`, `universe.makeLeafFrame(...)`, and `universe.injectFrame(...)`. Both engines
|
|
9
9
|
* (WASM canonical + the JS reference) expose those primitives identically, so this ONE function is
|
|
10
10
|
* the single source of emit behavior — replacing the former dual per-engine `registerEmitter`
|
|
11
|
-
* implementations (compat/providers +
|
|
11
|
+
* implementations (compat/providers + the JS reference engine).
|
|
12
12
|
*
|
|
13
13
|
* DRAW-ORDER CONTRACT (golden-locked on WASM — do not reorder):
|
|
14
14
|
* per emitted leaf: random()→x, random()→y, [random()→angle if randomDirection],
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* makeLeafFrame draws 0 there (charge is passed in) — engines may diverge
|
|
18
18
|
* (WASM is canonical); only the WASM goldens are asserted.
|
|
19
19
|
*
|
|
20
|
-
* OPT-IN 3D z-SPREAD
|
|
20
|
+
* OPT-IN 3D z-SPREAD: `em.zMin`/`em.zDepth` default to `null` — when
|
|
21
21
|
* both are absent, ZERO additional draws execute and this tick-fn is byte-identical to the
|
|
22
22
|
* contract above (every existing 2D caller — QuantumEmitter, PointSource — is unaffected).
|
|
23
23
|
* When a caller opts in (`em.zMin` a number), ONE additional `random()` draw executes AFTER
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* (a THIRD setFrameProperty call, never inserted into the locked x/y/[angle]/[charge] sequence).
|
|
27
27
|
* `makeLeafFrame` has no z argument — no engine change, this composes existing primitives.
|
|
28
28
|
*
|
|
29
|
-
* OPT-IN 3D ISOTROPIC SCATTER
|
|
29
|
+
* OPT-IN 3D ISOTROPIC SCATTER: `em.zIsotropy`
|
|
30
30
|
* (default falsy) — when BOTH `em.randomDirection` AND `em.zIsotropy` are true, the
|
|
31
31
|
* random-direction draw becomes a full unit-SPHERE direction (azimuth + polar angle,
|
|
32
32
|
* TWO draws replacing the 2D branch's ONE) instead of a 2D-plane circle, and the drawn
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* directly for active / velocity / burst-size / source-rect changes — there is no provider
|
|
40
40
|
* setEmitter* ABI. The tick-fn closes over emState so those mutations take effect live.
|
|
41
41
|
*
|
|
42
|
-
* DSE LAB (
|
|
42
|
+
* DSE LAB (systematic controls): `em.everyNTicks` (opt-in,
|
|
43
43
|
* default null/undefined = fire every active tick, byte-identical to every existing
|
|
44
44
|
* caller) throttles firing to once every N ticks — a LIVE, per-tick-read cadence knob
|
|
45
45
|
* (mutate `em.everyNTicks` directly on the live emState any time; no re-init). This is
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
|
|
74
74
|
var vx = em.vx;
|
|
75
75
|
var vy = em.vy;
|
|
76
|
-
var vzOut = null; //
|
|
76
|
+
var vzOut = null; // set only by the zIsotropy branch below
|
|
77
77
|
if (em.randomDirection) {
|
|
78
78
|
if (em.zIsotropy) {
|
|
79
|
-
//
|
|
80
|
-
// profile
|
|
79
|
+
// Velocity z-symmetry consistent with its
|
|
80
|
+
// profile: a real-depth-armed ISOTROPIC point source scatters
|
|
81
81
|
// on the unit SPHERE (azimuth + polar), not just the XY plane —
|
|
82
82
|
// mirrors directedRedirectProgram.js's own polarSpreadRad>0
|
|
83
83
|
// sphere draw. OPT-IN (em.zIsotropy defaults false) — zero extra
|
|
@@ -136,8 +136,8 @@
|
|
|
136
136
|
|
|
137
137
|
// OPT-IN 3D z-SPREAD (see module docstring). em.zMin is null for every
|
|
138
138
|
// existing 2D caller — this branch is dead code (0 extra draws) unless a
|
|
139
|
-
// caller explicitly opts in (PointSource `opts.zMin`/`opts.zDepth
|
|
140
|
-
//
|
|
139
|
+
// caller explicitly opts in (PointSource `opts.zMin`/`opts.zDepth`).
|
|
140
|
+
// Shares the SAME qid lookup with the zIsotropy vz write
|
|
141
141
|
// below (never a second getFrameQuantumId call for the same handle).
|
|
142
142
|
var zMinArmed = (em.zMin !== null && em.zMin !== undefined);
|
|
143
143
|
if (zMinArmed || vzOut !== null) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* frqtl-control-slider.js — the SINGLE scalar-control widget
|
|
5
|
-
* (
|
|
5
|
+
* (gym-controls-as-sugar, Layer B).
|
|
6
6
|
*
|
|
7
7
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
8
8
|
*
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
* value on drag; its (possibly-Promised) return refreshes the displayed
|
|
75
75
|
* value/text.
|
|
76
76
|
* @returns {{dispose: () => void, setValue: (value:number, valueText?:string) => void}}
|
|
77
|
-
* `setValue` (
|
|
77
|
+
* `setValue` (gym-controls-as-sugar FULL CONVERGENCE build, Stage ii item 5
|
|
78
78
|
* — the `ControlSync` reconciliation target) is DISPLAY-ONLY: it writes the
|
|
79
79
|
* slider's `.value` DOM property and the value label directly and NEVER
|
|
80
80
|
* fires `onInput`/`onChange` (setting an `<input>`'s `.value` property does
|
|
@@ -103,7 +103,7 @@ function mount(hostEl, opts) {
|
|
|
103
103
|
// execute — this file deliberately has zero dependency on registries/
|
|
104
104
|
// controlBind.js (Layer B: "the widget knows nothing about transports").
|
|
105
105
|
// Dragging it would otherwise silently do nothing (the exact class of
|
|
106
|
-
// bug
|
|
106
|
+
// bug the gym-controls-as-sugar FULL CONVERGENCE build fixed in the panel).
|
|
107
107
|
// Route a bind-only descriptor through frqtl-controls-panel.js instead
|
|
108
108
|
// (mode:'in-app'/'iframe', which DOES resolve bind execution), or mount
|
|
109
109
|
// this widget via the opts.initial/onInput decoupled path with onInput
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* frqtl-control-sync.js — TRIGGERED-PULL reconciliation across N widgets
|
|
5
|
-
* driving the SAME declared-once control (
|
|
5
|
+
* driving the SAME declared-once control (gym-controls-as-sugar FULL
|
|
6
6
|
* CONVERGENCE build, Stage ii item 4).
|
|
7
7
|
*
|
|
8
8
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
* `makeControlSync(opts) → { register(id, widget)->unregisterFn, pull()->Promise,
|
|
11
11
|
* notify(trigger), dispose() }`.
|
|
12
12
|
*
|
|
13
|
-
* The North Star (§5
|
|
13
|
+
* The North Star (§5 design constraint) is STUDIO live drive-and-placement
|
|
14
14
|
* — multiple users/widgets "batting a sun around" the SAME control. A single
|
|
15
15
|
* widget mutating a value is NOT reconciliation; N widgets (a page slider, a
|
|
16
16
|
* panel row, a standalone widget, a REMOTE panel over an iframe) must all
|
|
17
17
|
* REFLECT the SAME canonical state after ANY of them writes. `ControlSync`
|
|
18
18
|
* is the PULL side of that: it periodically (per its configured `trigger`)
|
|
19
19
|
* re-reads every registered control's CURRENT value (via the existing
|
|
20
|
-
* `listControls` verb —
|
|
20
|
+
* `listControls` verb — the RPC, reused unmodified: `frqtl-controls-panel.js`'s
|
|
21
21
|
* `listControls` now refreshes bind-based descriptors from LIVE state before
|
|
22
22
|
* serializing, see that file's own docstring) and calls `widget.setValue(value,
|
|
23
23
|
* valueText)` on every OTHER registered widget for that control id — a
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
*
|
|
49
49
|
* A configured `trigger:'tick'`/`'pause'` that never receives its matching
|
|
50
50
|
* `notify()` call within `opts.devWarnMs` (default 3000ms) logs ONE
|
|
51
|
-
* `console.warn` (
|
|
51
|
+
* `console.warn` (DEV-WARN — the page forgot its one-line wiring;
|
|
52
52
|
* this is a development aid, never a thrown error — a sync with a forgotten
|
|
53
53
|
* trigger is inert, not broken).
|
|
54
54
|
*
|
|
@@ -60,15 +60,15 @@
|
|
|
60
60
|
* responder already share (DRY: a THIRD consumer of one implementation, not
|
|
61
61
|
* a re-derived listing).
|
|
62
62
|
*
|
|
63
|
-
* `opts.handle` (remote, over an iframe/postMessage —
|
|
64
|
-
* RPC
|
|
63
|
+
* `opts.handle` (remote, over an iframe/postMessage — the EXISTING
|
|
64
|
+
* RPC): pulls via `handle.call('listControls', [name])` — the identical RPC
|
|
65
65
|
* primitive `frqtl-controls-panel.js`'s `mount({mode:'iframe'})` already
|
|
66
66
|
* builds via `lib/shim/rpc`. `ControlSync` does not care which — both
|
|
67
67
|
* resolve to a `Promise<Array<{id,label,min,max,step,value,valueText}>>`.
|
|
68
68
|
*
|
|
69
69
|
* Exactly one of `opts.registry` / `opts.handle` is required.
|
|
70
70
|
*
|
|
71
|
-
* ──
|
|
71
|
+
* ── Performance constraints (build requirements, not suggestions) ────────────
|
|
72
72
|
*
|
|
73
73
|
* - rAF-coalesced: `notify()` schedules AT MOST one pending pull per
|
|
74
74
|
* animation frame, however many times it is called within that frame.
|
|
@@ -203,7 +203,7 @@ function makeControlSync(opts) {
|
|
|
203
203
|
var arr = widgetsById[d.id];
|
|
204
204
|
if (!arr || !arr.length) { return; } // no widget registered for this id — nothing to reconcile
|
|
205
205
|
// Skip-if-unchanged: ONE comparison per control id, shared across
|
|
206
|
-
// every widget registered under it (
|
|
206
|
+
// every widget registered under it (performance constraint).
|
|
207
207
|
if (lastValueById[d.id] === d.value) { return; }
|
|
208
208
|
lastValueById[d.id] = d.value;
|
|
209
209
|
arr.forEach(function (w) { w.setValue(d.value, d.valueText); });
|
|
@@ -211,7 +211,7 @@ function makeControlSync(opts) {
|
|
|
211
211
|
});
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
// ── DEV-WARN
|
|
214
|
+
// ── DEV-WARN: a configured tick/pause trigger that
|
|
215
215
|
// never receives its matching notify() within the grace window means the
|
|
216
216
|
// page forgot its one-line wiring. Cleared the FIRST time notify() fires
|
|
217
217
|
// for the configured trigger (even a single call proves it's wired).
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* `slider` hands straight to `frqtl-control-slider.js` — the ONE slider-rendering
|
|
26
26
|
* implementation, unchanged. This table adds only the widgets that did not exist:
|
|
27
27
|
* toggle/checkbox (a scalar 0/1 rendered as a switch — NOT a new kind, per the
|
|
28
|
-
* schema's
|
|
28
|
+
* schema's rule), text (the string kind), and button (the action kind).
|
|
29
29
|
* `frqtl-controls-panel.js`'s renderRows consults THIS table instead of hardcoding
|
|
30
30
|
* the slider, so a second resolution authority never comes into existence.
|
|
31
31
|
*
|
|
@@ -97,7 +97,7 @@ function _settle(promiseLike, onValue, onError) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
/** A checkbox over a SCALAR 0/1 (the schema's "a toggle IS a scalar"
|
|
100
|
+
/** A checkbox over a SCALAR 0/1 (the schema's "a toggle IS a scalar" rule). */
|
|
101
101
|
function _mountToggle(hostEl, initial, onInput, styleAsSwitch) {
|
|
102
102
|
var row = _row();
|
|
103
103
|
var box = document.createElement('input');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* frqtl-control-write-queue.js — the WRITE-side symmetric primitive to
|
|
5
|
-
* `frqtl-control-sync.js`'s PULL-side coalescing (
|
|
5
|
+
* `frqtl-control-sync.js`'s PULL-side coalescing (Studio Controls
|
|
6
6
|
* Realignment S4, design §2.3.4 — "add the symmetric primitive: an
|
|
7
7
|
* rAF-coalesced, last-write-wins-per-frame-per-control-id write queue").
|
|
8
8
|
*
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* from a per-page opt-in" reasoning as those four siblings (see the
|
|
40
40
|
* manifest's own comment block for the S3 precedent).
|
|
41
41
|
*
|
|
42
|
-
* ── FAIL-LOUD condition (
|
|
42
|
+
* ── FAIL-LOUD condition (non-negotiable) ────────────────────────────────────
|
|
43
43
|
*
|
|
44
44
|
* A throwing accessor write mid-drag MUST PROPAGATE — never be silently
|
|
45
45
|
* swallowed under last-write-wins coalescing. `flush()`/`flushSync()` below
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* makeControlsForObject.js — the schema→controls FACTORY (
|
|
5
|
-
* Realignment
|
|
4
|
+
* makeControlsForObject.js — the schema→controls FACTORY (Studio Controls
|
|
5
|
+
* Realignment S2, "the factory spine").
|
|
6
6
|
*
|
|
7
7
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
8
8
|
*
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* sibling) — this factory NEVER synthesizes an `onChange` closure (see the
|
|
18
18
|
* "no-onChange-synthesis" scope note below; it is a hard constraint, not an
|
|
19
19
|
* oversight, and it is WHY several field types below are DISPLAY-ONLY rather
|
|
20
|
-
* than live controls
|
|
20
|
+
* than live controls).
|
|
21
21
|
*
|
|
22
22
|
* @param {*} wrapper — the live object to introspect:
|
|
23
23
|
* - LargeObject/QuantumEmitter/PointSource/DetectorWall/SlitScreen/Box/OvenBox/
|
|
@@ -88,8 +88,7 @@
|
|
|
88
88
|
* dependency. DEPENDENCY INJECTION (accepting `normalizeControlDescriptor` via
|
|
89
89
|
* `ctx`/`opts` instead of importing it) was the escape hatch IF `verify-arrows`
|
|
90
90
|
* rejected the import — it did not, so this file imports directly, same as its
|
|
91
|
-
* schema/index.js precedent.
|
|
92
|
-
* build report.)
|
|
91
|
+
* schema/index.js precedent.
|
|
93
92
|
*
|
|
94
93
|
* ── Layer 2: physics behavior ────────────────────────────────────────────────
|
|
95
94
|
*
|
|
@@ -100,13 +99,13 @@
|
|
|
100
99
|
* factory-generated control produces the IDENTICAL effect as hand-writing the
|
|
101
100
|
* same `{kind, id, bind}` literal.
|
|
102
101
|
*
|
|
103
|
-
* ── Scope decisions
|
|
102
|
+
* ── Scope decisions made explicit here (read before extending) ──────────────
|
|
104
103
|
*
|
|
105
104
|
* 1. NO onChange synthesis (dispatch constraint) — every live control's write
|
|
106
105
|
* path is `bind:{object, prop}` ONLY. This is SAFE for every numeric field
|
|
107
106
|
* (JSON/WASM round-trips a JS number losslessly either way) and safe for
|
|
108
107
|
* every STRING-valued enum/flags field (see #2) — but it is UNSAFE for two
|
|
109
|
-
* field shapes, which are therefore DISPLAY-ONLY
|
|
108
|
+
* field shapes, which are therefore DISPLAY-ONLY, not scalar/
|
|
110
109
|
* enum live controls (documented per-branch below, not silently degraded):
|
|
111
110
|
* (a) a `boolean`-typed field with `engineVisible !== false` — its value
|
|
112
111
|
* reaches the engine ABI via `setFrameProperty`'s
|
|
@@ -131,7 +130,7 @@
|
|
|
131
130
|
* back through a bare bind would write `'NONE'` into a field the
|
|
132
131
|
* engine expects as the NUMBER `0`. Needs either an onChange
|
|
133
132
|
* translator (forbidden here) or a `resolveBind`-side value-mapping
|
|
134
|
-
* extension —
|
|
133
|
+
* extension — a design decision, not a factory guess.
|
|
135
134
|
* Both are named, tested exceptions — NOT a silent drop. See
|
|
136
135
|
* `_booleanHandler`/`_enumHandler`/`_flagsHandler`/`_writableHandler` below.
|
|
137
136
|
* 2. `rect-array` fields (`QuantumEmitter.source_rects`) become N DISPLAY
|
|
@@ -139,8 +138,7 @@
|
|
|
139
138
|
* contract (`lib/shim/controls` `BIND_KEYS = ['object','prop']`) has no
|
|
140
139
|
* array-index addressing; a correct per-entry live write needs either
|
|
141
140
|
* `bind.index` (a shim-contract extension) or an onChange closure
|
|
142
|
-
* (forbidden). Flagged per-entry in the `description` field
|
|
143
|
-
* Bite S2 build report for S3/S4/architect follow-up.
|
|
141
|
+
* (forbidden). Flagged per-entry in the `description` field.
|
|
144
142
|
* 3. `QuantumEmitter`/`PointSource`'s singular `rect` field has NO live getter
|
|
145
143
|
* on the wrapper today (a PRE-EXISTING gap — `AuthorView.jsx`'s current
|
|
146
144
|
* Inspector reads `obj['rect']` too and gets the identical `undefined`).
|
|
@@ -159,10 +157,9 @@
|
|
|
159
157
|
* DO get live `enum`/`flags` controls here (their values are identity-mapped
|
|
160
158
|
* strings, safe per #1's rule) — but per design §2.3.5, a live edit only
|
|
161
159
|
* mutates the RENDER-layer JS wrapper mirror; it does NOT recompile/repush
|
|
162
|
-
* the running op-program. That gap is
|
|
160
|
+
* the running op-program. That gap is the `recompile` write class,
|
|
163
161
|
* explicitly deferred to S4 — this factory does not attempt it.
|
|
164
|
-
* 6. **Bind-executable-kind gate (S3,
|
|
165
|
-
* s2_factory.md` watch-out #1 — MANDATORY, closes an S2-documented KNOWN
|
|
162
|
+
* 6. **Bind-executable-kind gate (S3 — MANDATORY, closes an S2-documented KNOWN
|
|
166
163
|
* GAP).** `controlBind.js`'s `resolveBind` only EXECUTES a bind for a
|
|
167
164
|
* registry-resolved kind in `{Frame, LargeObject, backdrop}`, plus the
|
|
168
165
|
* `bind.object === 'universe'` sentinel (this factory's `kind === 'Universe'`)
|
|
@@ -193,11 +190,11 @@ import { ObjectSchema as _ObjectSchemaNode } from '../schema/index.js';
|
|
|
193
190
|
import * as _ControlsSchemaNode from '../../../shim/controls/index.js';
|
|
194
191
|
|
|
195
192
|
|
|
196
|
-
// ── Nullable-override sentinel (W0-D
|
|
193
|
+
// ── Nullable-override sentinel (W0-D) ───────────────────────────────────────────
|
|
197
194
|
// A LIVE per-leaf render-intent OVERRIDE field (SphereEmitter.renderShape,
|
|
198
195
|
// .renderColor) defaults to NULL — "no key, byte-identical, scheme-driven default"
|
|
199
196
|
// (the SU1 guarantee: a non-null default would stamp every leaf and move a golden).
|
|
200
|
-
// Its control must still be EDITABLE (the
|
|
197
|
+
// Its control must still be EDITABLE (the "renderShape/Color is not changeable
|
|
201
198
|
// in the Inspector" defect), so a null-valued live override renders a control seeded
|
|
202
199
|
// with this reserved sentinel meaning "unset / scheme default". The consuming
|
|
203
200
|
// Inspector maps the sentinel BACK to null on write (AuthorView commitField/
|
|
@@ -256,7 +253,7 @@ function _resolveBounds(field, value) {
|
|
|
256
253
|
}
|
|
257
254
|
|
|
258
255
|
function _label(field) {
|
|
259
|
-
// An optional display `label`
|
|
256
|
+
// An optional display `label` overrides the field NAME for the
|
|
260
257
|
// control's shown label WITHOUT renaming the schema field (the codegen/capture
|
|
261
258
|
// key stays `field.name`). e.g. leafDimension → "Emit Dimension", the global
|
|
262
259
|
// shape capability → "Default Shape". Absent → the name, as before (byte-identical).
|
|
@@ -287,7 +284,7 @@ var FIELD_VALUE_OVERRIDES = {
|
|
|
287
284
|
return [w.x - d / 2, w.y - d / 2, d, d];
|
|
288
285
|
}
|
|
289
286
|
},
|
|
290
|
-
// SphereEmitter
|
|
287
|
+
// SphereEmitter: `rect` is the WRITE-ONLY reposition adapter — its
|
|
291
288
|
// direct getter is undefined (so captureProps skips it; `center` is the captured
|
|
292
289
|
// position authority). Seed the Inspector's live rect vector from `center` so the
|
|
293
290
|
// field still shows/edits a position (edit → updateObjectProperty → setCenter).
|
|
@@ -395,7 +392,7 @@ function _enumHandler(field, env) {
|
|
|
395
392
|
var keys = Object.keys(valuesObj);
|
|
396
393
|
var value = _readValue(env, field.name);
|
|
397
394
|
if (typeof value !== 'string' || keys.indexOf(value) === -1) {
|
|
398
|
-
// W0-D
|
|
395
|
+
// W0-D: a LIVE enum whose value is null/unset — the
|
|
399
396
|
// SphereEmitter.renderShape override, kept null for byte-identity (SU1) — still
|
|
400
397
|
// renders an EDITABLE control, seeded with the '(default)' sentinel option
|
|
401
398
|
// representing "unset / scheme default". Picking a real key writes the string
|
|
@@ -447,7 +444,7 @@ function _flagsHandler(field, env) {
|
|
|
447
444
|
// override is genuinely UNSET (scheme-driven fullness→ROYGBIV) until the user picks
|
|
448
445
|
// a color, at which point setRenderColor stamps the chosen [r,g,b]. (A true
|
|
449
446
|
// "return to unset" gesture needs a companion clear affordance — out of the minimal
|
|
450
|
-
// scope
|
|
447
|
+
// scope.)
|
|
451
448
|
var NULL_RGB_DEFAULT_SWATCH = [255, 255, 255];
|
|
452
449
|
|
|
453
450
|
/**
|
|
@@ -470,7 +467,7 @@ function _rgbHandler(field, env) {
|
|
|
470
467
|
var value = _readValue(env, field.name);
|
|
471
468
|
var isValid = _isValidForField(field, value, env.kind);
|
|
472
469
|
if (!isValid) {
|
|
473
|
-
// W0-D2
|
|
470
|
+
// W0-D2: a LIVE rgb override whose value is null/unset — the
|
|
474
471
|
// SphereEmitter.renderColor override, kept null for byte-identity (SU1) — still
|
|
475
472
|
// renders an EDITABLE color control seeded with the neutral default swatch, so
|
|
476
473
|
// the field is not a dead "–" in the Inspector. Picking a color writes [r,g,b]
|
|
@@ -646,13 +643,12 @@ function _demoteToDisplay(field, env, result) {
|
|
|
646
643
|
' (DISPLAY ONLY — kind "' + env.kind + '" is not in controlBind.js\'s bind-execution ' +
|
|
647
644
|
'allowlist {Frame, LargeObject, backdrop, Universe}; resolveBind FAILS LOUD for any ' +
|
|
648
645
|
'other kind rather than silently no-op through an accessor path that would not reach ' +
|
|
649
|
-
'this object\'s real state.
|
|
650
|
-
'.claude/agent-memory/frqtl-architect/ruling_studio_controls_s2_factory.md watch-out #1.)'
|
|
646
|
+
'this object\'s real state.)'
|
|
651
647
|
}]
|
|
652
648
|
};
|
|
653
649
|
}
|
|
654
650
|
|
|
655
|
-
// ── Conditional visibility (Collision LEXICON v2,
|
|
651
|
+
// ── Conditional visibility (Collision LEXICON v2, §1b) ──────────────────────────
|
|
656
652
|
//
|
|
657
653
|
// ONE declarative `visibleWhen` predicate + ONE evaluator — the general substrate for a
|
|
658
654
|
// field that shows only when a SIBLING field holds a value (bounceDirection only for a
|
|
@@ -743,7 +739,7 @@ function makeControlsForObject(wrapper, ctx, opts) {
|
|
|
743
739
|
'explicit TYPE_DISPATCH entry in makeControlsForObject.js (even a documented display/skip). ' +
|
|
744
740
|
'(FAIL-LOUD — never a silent drop.)');
|
|
745
741
|
}
|
|
746
|
-
// ── Explicit per-field LIVE attribute
|
|
742
|
+
// ── Explicit per-field LIVE attribute ────────────────────────────────
|
|
747
743
|
// `live:false` → a read-only DISPLAY row regardless of type (boot-level /
|
|
748
744
|
// reload-to-change / derived fields — SphereEmitter's is2D, fundamentalDimension,
|
|
749
745
|
// emissionBox, …). captureProps STILL captures the value; the Inspector just
|
|
@@ -766,7 +762,7 @@ function makeControlsForObject(wrapper, ctx, opts) {
|
|
|
766
762
|
if (result.kind === 'control' && field.live !== true && BIND_EXECUTABLE_KINDS.indexOf(kind) === -1) {
|
|
767
763
|
result = _demoteToDisplay(field, env, result);
|
|
768
764
|
}
|
|
769
|
-
// Pass `visibleWhen` THROUGH onto the emitted entry (
|
|
765
|
+
// Pass `visibleWhen` THROUGH onto the emitted entry (§1b) — a live,
|
|
770
766
|
// sibling-dependent RENDER concern the Inspector evaluates at render time; NOT a
|
|
771
767
|
// generation-time skip (unlike internal:true). Attached AFTER normalization so the
|
|
772
768
|
// control descriptor's own contract is unchanged (normalize never sees it).
|
|
@@ -787,7 +783,7 @@ function makeControlsForObject(wrapper, ctx, opts) {
|
|
|
787
783
|
|
|
788
784
|
var MakeControlsForObject = {
|
|
789
785
|
makeControlsForObject: makeControlsForObject,
|
|
790
|
-
// The ONE conditional-visibility evaluator (Collision LEXICON v2,
|
|
786
|
+
// The ONE conditional-visibility evaluator (Collision LEXICON v2, §1b) —
|
|
791
787
|
// the AuthorView Inspector filter calls THIS beside its existing internal-field drop,
|
|
792
788
|
// never a hand-rolled per-field branch.
|
|
793
789
|
evalVisibleWhen: evalVisibleWhen,
|
|
@@ -803,9 +799,9 @@ var MakeControlsForObject = {
|
|
|
803
799
|
// SAME gate list this factory enforces, never a hand-copied duplicate —
|
|
804
800
|
// see scope note #6 / BIND_EXECUTABLE_KINDS above.
|
|
805
801
|
BIND_EXECUTABLE_KINDS: BIND_EXECUTABLE_KINDS,
|
|
806
|
-
// ── Reuse seam for makeControlsForRenderSurface.js (
|
|
807
|
-
// Realignment §7 r2
|
|
808
|
-
// TYPE_DISPATCH — the big DRY win
|
|
802
|
+
// ── Reuse seam for makeControlsForRenderSurface.js (Studio Controls ───────
|
|
803
|
+
// Realignment §7 r2: REUSES makeControlsForObject's
|
|
804
|
+
// TYPE_DISPATCH — the big DRY win, NOT a third hand-parallel conversion).
|
|
809
805
|
// Exposes ONLY the per-field-type handlers a renderCapabilities() descriptor
|
|
810
806
|
// uses (`number`, `enum`, `boolean`, `string`) — deliberately NOT the full
|
|
811
807
|
// TYPE_DISPATCH map: the rest bake in Frame/Universe-specific assumptions (the
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* makeControlsForRenderSurface.js — the RENDER-side factory twin of
|
|
5
|
-
* `makeControlsForObject.js` (
|
|
5
|
+
* `makeControlsForObject.js` (Studio Controls Realignment S5a).
|
|
6
6
|
*
|
|
7
7
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
8
8
|
*
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Each renderCapabilities() entry is a `lib/shim/field-descriptor`-shaped
|
|
21
21
|
* field descriptor (`{name, type, constraint, unit, default, description}` —
|
|
22
|
-
* §7 r2
|
|
22
|
+
* §7 r2 C1's shared vocabulary): `type:'number'` → a `scalar`
|
|
23
23
|
* control; `type:'enum'` → an `enum` control; a render-only `type:'boolean'`
|
|
24
24
|
* → a 0/1 `scalar`; `type:'string'` (an OPEN-ID asset id) → no control. The
|
|
25
25
|
* conversions REUSE `makeControlsForObject.js`'s OWN handlers (exposed as
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* slitLinerScatter.js — the ONE authority for the double-slit edge-diffraction
|
|
5
5
|
* liner's collision op-program (behavior × resetLocalTime, ORTHOGONAL).
|
|
6
6
|
*
|
|
7
|
-
* DSE LAB (
|
|
8
|
-
* ORTHOGONAL controls
|
|
7
|
+
* DSE LAB (systematic controls): behavior × reset are
|
|
8
|
+
* ORTHOGONAL controls — this file composes the velocity-tail (`behavior`) and the
|
|
9
9
|
* `local_time=0` reset independently, so every (behavior, resetLocalTime) combo is
|
|
10
10
|
* reachable, INCLUDING combos that had no name before (`bounce`+reset,
|
|
11
11
|
* `ejectY`+reset, `ejectYReflect`+reset). The pre-existing compound `slitScatter`
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* ## Ground-truth (pkg-node instrumentation, EXPERIMENT LOOP delivery report)
|
|
22
22
|
* The liner's compiled `RANDOM_XY` op DOES fire; its OWN writes are `vx=random()`
|
|
23
23
|
* (ALWAYS non-negative) and `vy=random()*2-1` (already symmetric) — the sign bias
|
|
24
|
-
* the
|
|
24
|
+
* the original hypothesis named (vy) was actually in `vx`, which the engine's
|
|
25
25
|
* 2D diagonal wave-kick (`frame.rs` `axy` branch, `dy_perp=(vx>0?+1:-1)*is_heads`)
|
|
26
26
|
* keys its transverse-Y sign off; `is_heads` is coherently biased by the SAME
|
|
27
|
-
* `local_time=0` phase-lock mechanism (`compute_spin`)
|
|
27
|
+
* `local_time=0` phase-lock mechanism (`compute_spin`) found driving
|
|
28
28
|
* the separately-escalated 3D z-asymmetry. `local_time=0` is deliberate — it is
|
|
29
29
|
* what CREATES the y-interference fringes for the un-amended collision — hence
|
|
30
30
|
* this file's variants for visual/statistical A/B comparison, not a "fix."
|
|
@@ -121,10 +121,8 @@ import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
|
121
121
|
* The `ejectY` NAME is historical (the DSE liner is a horizontal strip that always ejects
|
|
122
122
|
* on Y); a vertical corridor (`Tunnel` axis 'y') passes 'left'/'right' through the SAME
|
|
123
123
|
* builder. Documenting the true domain, not widening it — no code change.
|
|
124
|
-
* @param {boolean} [hasRealDepth=false] -
|
|
125
|
-
*
|
|
126
|
-
* axes is missing... immediately obvious that we are not using the same
|
|
127
|
-
* objects with the same contracts"): when true, `randomXY`/`symmetric`
|
|
124
|
+
* @param {boolean} [hasRealDepth=false] - (a real-depth tunnel scatters on
|
|
125
|
+
* all three axes, through the same objects and contracts): when true, `randomXY`/`symmetric`
|
|
128
126
|
* (the two SCATTER behaviors — a wall-normal-agnostic velocity redraw, not
|
|
129
127
|
* a physical y-wall reflection) ALSO append `set frame.vz = random()*2-1`
|
|
130
128
|
* — the SAME `randomSigned()` term already used for `vy` — so a scatter
|
|
@@ -133,7 +131,7 @@ import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
|
133
131
|
* (Y-specific by design, the DSE liner's own cavity-eject convention) are
|
|
134
132
|
* UNCHANGED regardless of this flag — reflecting vz off a Y-normal wall
|
|
135
133
|
* would be physically wrong (this is a TOP/BOTTOM face; NEAR/FAR is
|
|
136
|
-
* handled correctly already by native BOUNCE
|
|
134
|
+
* handled correctly already by native BOUNCE).
|
|
137
135
|
* Default `false` — byte-identical for every existing caller (`SlitScreen.js`'s
|
|
138
136
|
* own 2D liner construction, which never passes this).
|
|
139
137
|
* @returns {Array<Object>} the FULL composed ops array (eject-prefix + tail +
|
|
@@ -206,7 +204,7 @@ import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
|
206
204
|
* exclusive with opts.behavior/opts.resetLocalTime).
|
|
207
205
|
* @param {string} [opts.behavior] - explicit composable behavior (LIVE controls).
|
|
208
206
|
* @param {boolean} [opts.resetLocalTime] - explicit composable reset (LIVE controls).
|
|
209
|
-
* @param {boolean} [opts.hasRealDepth=false] -
|
|
207
|
+
* @param {boolean} [opts.hasRealDepth=false] - see
|
|
210
208
|
* `buildLinerProgram`'s own param doc. Default false — byte-identical for
|
|
211
209
|
* every existing caller.
|
|
212
210
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* sustainedSource — the ONE hand-placed-quantum source surface (
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* sustainedSource — the ONE hand-placed-quantum source surface (Tunnel Lab and
|
|
5
|
+
* LightSource Lab share ONE source surface and differ only in declared
|
|
6
|
+
* composition). REPLACES two independently
|
|
7
7
|
* hand-rolled injector tick-fns (`setupTunnelLab.js`'s and
|
|
8
8
|
* `setupLightSourceLab.js`'s own, admitted by the latter's own comment to
|
|
9
9
|
* "mirror setupTunnelLab.js byte-for-byte") with ONE authority that both labs,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* discipline (root CLAUDE.md "Engineering Culture"): the owner (this file)
|
|
17
17
|
* exports the field list + defaults; every consumer calls `normalizeSourceSpec`
|
|
18
18
|
* to get a FULL bag (never hand-re-enumerates a subset), so a field omission is
|
|
19
|
-
* structurally impossible to silently diverge on (the exact bug this
|
|
19
|
+
* structurally impossible to silently diverge on (the exact bug this
|
|
20
20
|
* fixes: one injector stamped `wavelength`, the other didn't, and nothing
|
|
21
21
|
* forced them to agree). `sustained-source.test.mjs` is driven off this exact
|
|
22
22
|
* exported list — adding a field here without a matching test entry is a CI
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
* injectors independently derived), draws a velocity per `spec.velocityProfile`,
|
|
30
30
|
* and calls `makeLeafFrame`/`injectFrame`/`setFrameProperty` — the SAME
|
|
31
31
|
* generic primitives every hand-placed-quantum experiment already uses (G-EMIT-2,
|
|
32
|
-
*
|
|
32
|
+
* the engine boundary model). Used for BOTH a one-shot burst (LightSource's
|
|
33
33
|
* own construction-time placement) and a recurring burst (`buildSustainedSource`'s
|
|
34
34
|
* tick-fn) — "the one injector then stamps quantum properties ... in exactly ONE
|
|
35
|
-
* place" (the
|
|
35
|
+
* place" (the worked-example blueprint, Section 4 item 1).
|
|
36
36
|
*
|
|
37
37
|
* `buildSustainedSource(universe, innerRect, capNegX, hasRealDepth, specPartial)`
|
|
38
38
|
* builds+registers a recurring Phase-A host tick-fn (mirrors `emitterProgram.js`'s
|
|
@@ -54,8 +54,7 @@
|
|
|
54
54
|
* Invariant #6 `mass = max(1, capacity/wavelength)`). `null` (default) =
|
|
55
55
|
* engine default (`mass = capacity`, i.e. fullness=1, top of the ROYGBIV
|
|
56
56
|
* ramp). THIS is the field whose divergence (one injector set it, the other
|
|
57
|
-
* didn't) produced
|
|
58
|
-
* `docs/physics/COLOR_RENDER_LEARNINGS.md` and the ws-dse-3d investigation.
|
|
57
|
+
* didn't) produced "different particle colors".
|
|
59
58
|
* @property {number|null} charge - Per-frame charge override (0=Positive,
|
|
60
59
|
* 1=Negative). `null` (default) = random draw per quantum.
|
|
61
60
|
* @property {'beam'|'spray'} velocityProfile - `'beam'` (default) = collimated
|
|
@@ -63,7 +62,7 @@
|
|
|
63
62
|
* `vy=vz=0` always, a deliberately directed/phase-locked stream). `'spray'` =
|
|
64
63
|
* a biased random fan (`spraySpec`) — `setupTunnelLab.js`'s own original
|
|
65
64
|
* uniformization-demo shape. Named, selectable, NEVER divergent hand-code
|
|
66
|
-
* (
|
|
65
|
+
* (Section 3 finding E).
|
|
67
66
|
* @property {number} speed - `'beam'` profile only: speed magnitude.
|
|
68
67
|
* @property {number} orientationRad - `'beam'` profile only: direction,
|
|
69
68
|
* radians (`0` = +X).
|
|
@@ -79,9 +78,9 @@
|
|
|
79
78
|
* @property {boolean} zIsotropy - When real depth is armed AND
|
|
80
79
|
* `velocityProfile==='spray'`: draw `vz = (random()*2-1)*spraySpec.vyRange`
|
|
81
80
|
* (the SAME half-range as the vy spread) — the 3D-SYMMETRIC EMISSION fix
|
|
82
|
-
* (
|
|
81
|
+
* (§4 option (a): vz is drawn symmetric to the vy spread).
|
|
83
82
|
* `'beam'` profile is UNAFFECTED regardless of this flag (`vy=vz=0` stays —
|
|
84
|
-
* collimation is deliberate;
|
|
83
|
+
* collimation is deliberate; §4 option (b): the beam's isotropic
|
|
85
84
|
* TRANSVERSE WAVE already does the 3D work, measured y≈z). Default `true`.
|
|
86
85
|
* @property {number|null} level - `makeLeafFrame`'s `level` override (rarely
|
|
87
86
|
* used; `LightSource.js`'s own `options.particle.level`). `null` (default).
|
|
@@ -203,14 +202,14 @@
|
|
|
203
202
|
var s = spec.spraySpec;
|
|
204
203
|
vx = s.vxMin + universe.random() * s.vxRange;
|
|
205
204
|
vy = s.vyMin + universe.random() * s.vyRange;
|
|
206
|
-
// 3D-SYMMETRIC EMISSION (
|
|
205
|
+
// 3D-SYMMETRIC EMISSION (§4 option (a)): draw
|
|
207
206
|
// vz symmetric to the vy spread, SAME half-range, only when real
|
|
208
207
|
// depth is armed AND the caller opted into zIsotropy (default
|
|
209
208
|
// true) — makes the injected fan 3D-isotropic instead of pinning
|
|
210
209
|
// z-momentum at zero forever (the measured z/y=0.18-0.23 flatness
|
|
211
|
-
// root cause —
|
|
210
|
+
// root cause — Section 2e, Section C).
|
|
212
211
|
vz = (hasRealDepth && spec.zIsotropy) ? (universe.random() * 2 - 1) * s.vyRange : 0;
|
|
213
|
-
} else { // 'beam' — collimated; vy=vz=0 stays (deliberate,
|
|
212
|
+
} else { // 'beam' — collimated; vy=vz=0 stays (deliberate, §4 option (b))
|
|
214
213
|
vx = Math.cos(spec.orientationRad) * spec.speed;
|
|
215
214
|
vy = Math.sin(spec.orientationRad) * spec.speed;
|
|
216
215
|
vz = 0;
|
|
@@ -283,8 +282,8 @@
|
|
|
283
282
|
* capacityAtDimension(dim) — the ONE authority for "capacity of a leaf at
|
|
284
283
|
* fundamental dimension `dim`" (Engine Invariant #6: `width=2^dim,
|
|
285
284
|
* capacity=width²`). Both tunnel-lab and lightsource-lab derive their
|
|
286
|
-
* `MAX_USEFUL_WAVELENGTH` slider ceiling from this (
|
|
287
|
-
* fix
|
|
285
|
+
* `MAX_USEFUL_WAVELENGTH` slider ceiling from this (the ceiling
|
|
286
|
+
* fix — same MAX_USEFUL_WAVELENGTH clamp authority) —
|
|
288
287
|
* never a hand-typed "16" in either factory.
|
|
289
288
|
* @param {number} dim
|
|
290
289
|
* @returns {number}
|