@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
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
* Emits the CANONICAL spec: charge in raw form (0/1/'POSITIVE'/'NEGATIVE'/null).
|
|
14
14
|
* All charge translation (numeric↔string) lives in the shim providers
|
|
15
15
|
* (compat/providers/js-engine.js and compat/providers/wasm-engine.js).
|
|
16
|
-
* No engine-sniffing above the shim (
|
|
16
|
+
* No engine-sniffing above the shim (§2 invariant).
|
|
17
17
|
*
|
|
18
|
-
* chargeMode / random_direction (sugar-layer modes,
|
|
18
|
+
* chargeMode / random_direction (sugar-layer modes, emitter-modes-sugar):
|
|
19
19
|
* chargeMode ∈ 'random' | 'positive' | 'negative' | 'both'
|
|
20
20
|
* random_direction ∈ boolean
|
|
21
21
|
*
|
|
@@ -100,8 +100,8 @@ import { declaredCount } from '../ObjectFactory.js';
|
|
|
100
100
|
* @param {number} quantum_limit - Max quanta to emit (Infinity = unlimited)
|
|
101
101
|
* @param {number} delay - Ticks between bursts
|
|
102
102
|
* @param {boolean} wrap - Wrap emitted particles at canvas boundaries.
|
|
103
|
-
* @param {Object} [opts] -
|
|
104
|
-
* emitter source mode consumes the SAME normalized SOURCE_SPEC bag
|
|
103
|
+
* @param {Object} [opts] - the
|
|
104
|
+
* emitter source mode consumes the SAME normalized SOURCE_SPEC bag:
|
|
105
105
|
* `{ wavelength, zMin, zDepth, zIsotropy }` — the SAME trailing-`opts`
|
|
106
106
|
* idiom + the SAME field names/semantics `PointSource`'s own constructor
|
|
107
107
|
* already established for this EXACT emState contract (`emitterProgram.js`
|
|
@@ -154,13 +154,13 @@ import { declaredCount } from '../ObjectFactory.js';
|
|
|
154
154
|
// colorOverride: render-only attribute for emitted particle color.
|
|
155
155
|
// Set via updateObjectProperty(lo, 'colorOverride', [r,g,b]) from the IDE.
|
|
156
156
|
this.colorOverride = null;
|
|
157
|
-
// renderShape / renderOutline (
|
|
157
|
+
// renderShape / renderOutline (R3-1): render-only per-emitted-frame overrides,
|
|
158
158
|
// peers of colorOverride — the same per-frame reach SphereEmitter3D has. null default →
|
|
159
159
|
// no key stamped (byte-identical). Threaded through the em spec → emitterProgram → the
|
|
160
160
|
// makeLeafFrame WASM string_map merge stamp (render.shape / render.outline).
|
|
161
161
|
this.renderShape = null;
|
|
162
162
|
this.renderOutline = null;
|
|
163
|
-
//
|
|
163
|
+
// Exposed for reads (round-trip save/restore, inspector) —
|
|
164
164
|
// mutate via setWavelength(), never a bare assignment (mirrors vx/vy's
|
|
165
165
|
// own setVelocity()-only-mutation discipline).
|
|
166
166
|
this.wavelength = wavelength;
|
|
@@ -240,16 +240,16 @@ import { declaredCount } from '../ObjectFactory.js';
|
|
|
240
240
|
wrap: !!tmpl.wrap,
|
|
241
241
|
wavelength: tmpl.wavelength || null,
|
|
242
242
|
fundamentalDim: null, // null → engine uses the universe's fundamental-dim as the leaf level
|
|
243
|
-
//
|
|
243
|
+
// The SAME em.zMin/em.zDepth fields PointSource already
|
|
244
244
|
// wires (emitterProgram.js's existing opt-in z-spread mechanism) +
|
|
245
|
-
// em.zIsotropy (
|
|
245
|
+
// em.zIsotropy (gates the 3D unit-sphere
|
|
246
246
|
// random-direction draw). null/false (default) — byte-identical.
|
|
247
247
|
zMin: (tmpl.zMin !== undefined && tmpl.zMin !== null) ? tmpl.zMin : null,
|
|
248
248
|
zDepth: (tmpl.zDepth !== undefined && tmpl.zDepth !== null) ? tmpl.zDepth : null,
|
|
249
249
|
zIsotropy: !!tmpl.zIsotropy,
|
|
250
250
|
};
|
|
251
251
|
var tickFn = buildEmitterTickFn(this.universe, em);
|
|
252
|
-
// Back-reference so dispose() (
|
|
252
|
+
// Back-reference so dispose() (Studio Controls Realignment S4) can find
|
|
253
253
|
// and splice OUT exactly the tick-fn(s) belonging to this emitter, without
|
|
254
254
|
// needing a separate id→fn registry. Harmless extra property on a function.
|
|
255
255
|
tickFn._emState = em;
|
|
@@ -469,8 +469,8 @@ import { declaredCount } from '../ObjectFactory.js';
|
|
|
469
469
|
};
|
|
470
470
|
|
|
471
471
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
472
|
+
* The emitter source mode consumes the SAME normalized SOURCE_SPEC bag:
|
|
473
|
+
* change per-emitted-particle
|
|
474
474
|
* wavelength (Engine Invariant #6 `mass=max(1,capacity/wavelength)`) LIVE —
|
|
475
475
|
* mirrors `setVelocity`/`setBurstSize`'s own "write every handle" discipline
|
|
476
476
|
* exactly (real state lives in `emState`, mutated ONLY by a dedicated setter
|
|
@@ -490,7 +490,7 @@ import { declaredCount } from '../ObjectFactory.js';
|
|
|
490
490
|
};
|
|
491
491
|
|
|
492
492
|
/**
|
|
493
|
-
* Reposition the emitter's single canonical source rect (
|
|
493
|
+
* Reposition the emitter's single canonical source rect (Studio Controls
|
|
494
494
|
* Realignment S4, design §2.2/§2.5.5 — the "composite emitter move"
|
|
495
495
|
* accessor). One path: mutates `source_rects` on the wrapper (inspector/
|
|
496
496
|
* serializeDesign source of truth) AND every underlying emState's
|
|
@@ -519,8 +519,8 @@ import { declaredCount } from '../ObjectFactory.js';
|
|
|
519
519
|
};
|
|
520
520
|
|
|
521
521
|
/**
|
|
522
|
-
* Full teardown (
|
|
523
|
-
*
|
|
522
|
+
* Full teardown (Studio Controls Realignment S4, design item 1 — the
|
|
523
|
+
* "delete leaves the emitter tick-fn running" defect). Stops this
|
|
524
524
|
* emitter for good:
|
|
525
525
|
* 1. Tombstones every underlying emState — `emitterProgram.js`'s
|
|
526
526
|
* `buildEmitterTickFn` checks `em.tombstoned` FIRST and returns `[]`
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
* end-caps then fall back to the legacy 4-element rect (z=0, depth=own-x-width;
|
|
71
71
|
* unaffected by this option, byte-identical to a z-open scene).
|
|
72
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,
|
|
74
|
-
*
|
|
73
|
+
* 1 (fully opaque), applied to every wall (STANDARDIZED sugar-level knob,
|
|
74
|
+
* so contained quanta stay visible from outside views). Reuses the
|
|
75
75
|
* existing LO `alpha` schema field (0-255; see the Render shape block below) —
|
|
76
76
|
* NOT a new render hint. The white edge outline stays crisp at any opacity.
|
|
77
77
|
* @param {boolean} [options.collectCollided=false] - DIRECTED TUNNEL BUILD (2026-07-13):
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
* `randomDirection` (default false - a DIRECTED beam;
|
|
103
103
|
* true = an isotropic point-source INSIDE the bore, for a diffuse fill),
|
|
104
104
|
* `quantumLimit` (default Infinity), `delay` (default 0), `wrap` (default false).
|
|
105
|
-
* - `wavelength` (default null = engine default mass) -
|
|
105
|
+
* - `wavelength` (default null = engine default mass) - per-
|
|
106
106
|
* emitted-particle wavelength override (`QuantumEmitter`'s own new `opts.
|
|
107
107
|
* wavelength`, Engine Invariant #6). LIVE via `this.emitter.setWavelength()`.
|
|
108
108
|
* When `zContain` is armed, this Tube ALSO auto-centres the emitter's z
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
* `options.emitter`).
|
|
118
118
|
* @param {boolean} [options.sinkOpenEnds=false] - Build an ABSORB sink LO just beyond
|
|
119
119
|
* EACH open (uncapped) X-end — the reusable home for "the muzzle absorber-sink"
|
|
120
|
-
* (
|
|
120
|
+
* (L2.2): any open-ended contained-flow scene (a barrel firing gas
|
|
121
121
|
* out the muzzle, a wire/tunnel with through-traffic on both ends) wants a bounded
|
|
122
122
|
* population, not unbounded growth as frames exit and are never removed. Named
|
|
123
123
|
* generically (not `muzzleSink`) because Tube itself is generic (wire ≡ barrel ≡
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
* sink, at the open +X (muzzle) end; a wire (both ends open) gets two. Reuses the
|
|
126
126
|
* EXISTING KILL/ABSORB precedent (`DetectorWall.js`: `CollisionTypes.NONE` +
|
|
127
127
|
* `[COUNT, ABSORB]` actions, compiled by `actionCompiler.js`'s standard path to
|
|
128
|
-
* `set frame.is_alive = 0` — NOT `mass=0`, per the
|
|
128
|
+
* `set frame.is_alive = 0` — NOT `mass=0`, per the L1 note) — no new
|
|
129
129
|
* op-program, no engine change. Each sink is invisible (`visible:false`) by
|
|
130
130
|
* default (a floating absorber box outside the bore reads as a rendering
|
|
131
131
|
* artifact, not a real object) but IS included in `this.largeObjects` (so the
|
|
@@ -212,11 +212,11 @@ import { QuantumEmitter } from './QuantumEmitter.js';
|
|
|
212
212
|
var color = options.color || [90, 90, 96];
|
|
213
213
|
var observedDimensions = options.observedDimensions !== undefined ? options.observedDimensions : null;
|
|
214
214
|
var zContain = options.zContain !== undefined ? !!options.zContain : true;
|
|
215
|
-
// wallOpacity: the STANDARDIZED render knob
|
|
215
|
+
// wallOpacity: the STANDARDIZED render knob — see the Render
|
|
216
216
|
// shape block below for the mechanism (reuses the existing LO `alpha` schema
|
|
217
217
|
// field, lib/frqtl/objects/schema/index.js:166 — NOT a new hint).
|
|
218
218
|
var wallOpacity = options.wallOpacity !== undefined ? options.wallOpacity : 0.3;
|
|
219
|
-
// sinkOpenEnds: the muzzle-absorber-sink home (
|
|
219
|
+
// sinkOpenEnds: the muzzle-absorber-sink home (L2.2) — see the
|
|
220
220
|
// module docstring's param note. Default false — byte-identical to every
|
|
221
221
|
// existing Tube consumer that doesn't
|
|
222
222
|
// opt in.
|
|
@@ -321,13 +321,13 @@ import { QuantumEmitter } from './QuantumEmitter.js';
|
|
|
321
321
|
));
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
-
// ── Open-end ABSORB sinks (
|
|
324
|
+
// ── Open-end ABSORB sinks (L2.2 — the muzzle-absorber-sink) ──────────────────
|
|
325
325
|
// ONE sink per uncapped X-end (never on a capped end — a closed face already
|
|
326
326
|
// contains everything; a sink there would be redundant and would fight the
|
|
327
327
|
// wall's own bounce). REUSES the existing KILL/ABSORB precedent verbatim
|
|
328
328
|
// (DetectorWall.js: CollisionTypes.NONE + [COUNT, ABSORB] actions, compiled by
|
|
329
329
|
// actionCompiler.js's standard compileActions path to `set frame.is_alive = 0`
|
|
330
|
-
// — NOT mass=0, per the
|
|
330
|
+
// — NOT mass=0, per the L1 note) — zero new op-program, zero engine
|
|
331
331
|
// change. Positioned a small margin OUTSIDE the bore's open face (never
|
|
332
332
|
// overlapping the interior, so normal bore physics near the opening is
|
|
333
333
|
// unaffected) with the SAME z-slab (`faceRect`) every other face uses, so it
|
|
@@ -396,8 +396,8 @@ import { QuantumEmitter } from './QuantumEmitter.js';
|
|
|
396
396
|
var esourceH = espec.sourceHeight !== undefined ? espec.sourceHeight : Math.max(1, Math.floor(interiorHeight / 2));
|
|
397
397
|
var eEmitX = innerLeft + 2;
|
|
398
398
|
var eCenterY = innerTop + interiorHeight / 2;
|
|
399
|
-
//
|
|
400
|
-
//
|
|
399
|
+
// The emitter source mode consumes the SAME normalized SOURCE_SPEC bag:
|
|
400
|
+
// wavelength/z-centring/
|
|
401
401
|
// z-isotropy — derived from THIS Tube's own `zContain`/`innerZ`/
|
|
402
402
|
// `interiorDepth` (already in scope), never a caller burden. zContain
|
|
403
403
|
// false (every pre-existing Tube.options.emitter caller) -> eZMin stays
|
|
@@ -421,17 +421,17 @@ import { QuantumEmitter } from './QuantumEmitter.js';
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
// ── Render shape (renderer-only hints; physics is unaffected) ────────────
|
|
424
|
-
// FRQTL-MAINT
|
|
424
|
+
// FRQTL-MAINT: shape:'box' + depth wraps each wall LO
|
|
425
425
|
// with a real 3D BoxGeometry (Bullet.js:112-116 precedent) instead of the
|
|
426
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
|
|
427
|
+
// reads as a thin 2D outline at oblique orbit angles, which is exactly the
|
|
428
428
|
// "walls draw as thin 2D outlines" finding. depth is read straight off the
|
|
429
429
|
// wall's OWN rect.d (set above by the 6-element rect) so the box's z-extent
|
|
430
430
|
// matches its true physical z-slab exactly; when zContain is false (legacy
|
|
431
431
|
// 4-element rect, no rect.d), depth stays undefined and _buildLoMesh's existing
|
|
432
432
|
// `lo.depth || lo.rect.w` fallback applies unchanged.
|
|
433
433
|
//
|
|
434
|
-
// wallOpacity (
|
|
434
|
+
// wallOpacity (translucent walls so contained quanta stay
|
|
435
435
|
// visible from outside): REUSES the existing `alpha` LO field — already a
|
|
436
436
|
// first-class, schema-declared render-only property (`lib/frqtl/objects/schema/
|
|
437
437
|
// index.js` name:'alpha', 0-255, widgetHint:'slider') that `_buildLoMesh`'s
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* absorbRadiation.js — ABSORB v2 on the LO's REAL backing-frame mass (Track A).
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* The mass-surface rule: use the OBJECT approach, not a
|
|
7
7
|
* duplicative op-prop mass surface. A LargeObject is backed by a REAL leaf Frame, so the
|
|
8
8
|
* absorbed mass lands in a real frame's `mass` reachable through the SAME frame accessors
|
|
9
9
|
* everything else uses.
|
|
10
10
|
*
|
|
11
|
-
* MANDATORY BACKING
|
|
11
|
+
* MANDATORY BACKING: the backing frame is now created by the engine
|
|
12
12
|
* at `register_large_object` (every LO is frame-backed — no unbacked path). This sugar READS
|
|
13
13
|
* that ledger via `getLargeObjectFrameId` instead of creating + attaching one.
|
|
14
14
|
*
|
|
15
15
|
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
16
16
|
* `attachAbsorberBackingFrame(universe, loHandle, rect, capacity)` → READS the LO's
|
|
17
17
|
* registration-created backing leaf (store-resident mass ledger — uninjected, zero
|
|
18
|
-
* single-occupancy / quadtree interaction,
|
|
18
|
+
* single-occupancy / quadtree interaction, §2a), primes it with the mass-1 empty
|
|
19
19
|
* baseline, and returns `{ backingFrameHandle, backingQid, level, capacity }`. `capacity` is
|
|
20
20
|
* the absorb CLOCK — the fill ceiling and the `1/capacity` emission probability — snapped to
|
|
21
21
|
* the nearest LEVEL the engine can actually size (`level` is that quantization's record).
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* The default radiating efficiency for an absorber that never sets one
|
|
45
|
-
*
|
|
44
|
+
* The default radiating efficiency for an absorber that never sets one. A half-full
|
|
45
|
+
* leaf: it steps half the ticks and carries twice the wavelength
|
|
46
46
|
* of a full one — a visibly "warm" re-emitter rather than a light-speed one.
|
|
47
47
|
*/
|
|
48
48
|
var DEFAULT_EMISSIVITY = 0.5;
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
* gate above 1, which cannot mean anything. FAIL LOUD rather than clamp: a silently
|
|
53
53
|
* clamped 2.0 would look like it worked and quietly emit light-speed leaves.
|
|
54
54
|
*
|
|
55
|
-
* NOTE (open, see the datasheet): 0 is REFUSED, not read as "never re-emit". The
|
|
55
|
+
* NOTE (open, see the datasheet): 0 is REFUSED, not read as "never re-emit". The mass
|
|
56
56
|
* law floors the emitted mass at 1, so a 0 could not actually suppress emission — accepting
|
|
57
|
-
* it would be a silent lie. A true perfect-absorber knob is a separate,
|
|
57
|
+
* it would be a silent lie. A true perfect-absorber knob is a separate, open question.
|
|
58
58
|
*/
|
|
59
59
|
function _validEmissivity(v, whence) {
|
|
60
60
|
var n = Number(v);
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
* the engine (`universe.levelCapacity`) rather than inverting `4^L` — which is the A={x,y}
|
|
72
72
|
* answer only. Returns the smallest level whose real capacity covers the request, so the
|
|
73
73
|
* clock never promises a ceiling the frame has no cells for. Minimum level 1 (an LO is a
|
|
74
|
-
* leaf frame at Dim>0
|
|
74
|
+
* leaf frame at Dim>0).
|
|
75
75
|
*/
|
|
76
76
|
function deriveBackingLevel(universe, capacity) {
|
|
77
77
|
var want = Math.max(1, Number(capacity) || 1);
|
|
@@ -174,11 +174,10 @@
|
|
|
174
174
|
* at EACH layer in proportion to the mass it absorbed from that layer's collisions, so
|
|
175
175
|
* conservation holds per layer and not merely in total.
|
|
176
176
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* stops being a candidate instead of borrowing another layer's mass.
|
|
177
|
+
* The proportion is drawn against each layer's UNRADIATED stored mass, so each layer's
|
|
178
|
+
* radiated total never exceeds what it absorbed (stored + radiated == absorbed per layer,
|
|
179
|
+
* in the ledger, not only in expectation) — a layer that has already given back
|
|
180
|
+
* everything it soaked stops being a candidate instead of borrowing another layer's mass.
|
|
182
181
|
*
|
|
183
182
|
* The draw is SKIPPED when only one layer is in play, so a single-layer world consumes
|
|
184
183
|
* exactly the RNG stream it did before this rule existed.
|
|
@@ -331,13 +331,13 @@ function _buildEjectOps(reposition, HANDLE_FRAME, HANDLE_SELF, Lit, Ref, Bin, Cm
|
|
|
331
331
|
// collision trigger (intersects) is now 3D — only frames inside the LO's z-slab fire,
|
|
332
332
|
// so a z-eject can never teleport a far-z frame.
|
|
333
333
|
//
|
|
334
|
-
// WASM-ONLY PATH
|
|
335
|
-
// in the op scope bag; the FROZEN JS reference (
|
|
334
|
+
// WASM-ONLY PATH: the canonical Rust/WASM engine exposes z/near/far
|
|
335
|
+
// in the op scope bag; the FROZEN JS reference (its evalExpr) is 2D-only —
|
|
336
336
|
// z/near/far read 0 there — so closest3d is exercised against WASM only. Conformance
|
|
337
337
|
// C19 (lib/frqtl/conformance/collide_scenarios.cjs — a WASM-only oracle) covers it; do NOT
|
|
338
338
|
// add closest3d to the JS-cross-checked suites (action_compiler_scenarios) without first
|
|
339
339
|
// mirroring z/near/far into the JS-ref scope bag (which the frozen-fixture rule forbids).
|
|
340
|
-
// NIT-2 (
|
|
340
|
+
// NIT-2 (non-blocking, next substantive edit): factor the four x/y eject-position
|
|
341
341
|
// expressions (ejectLeft/Right/Top/Bottom) shared with the 'closest' branch above into one
|
|
342
342
|
// helper; closest3d then adds only near/far + the 3-axis priority. Deferred to avoid
|
|
343
343
|
// touching the green 'closest' path at closure.
|
|
@@ -447,12 +447,12 @@ function _buildVelocityFlipOp(loName, HANDLE_FRAME, Lit, Ref, Bin) {
|
|
|
447
447
|
// added to `_buildEjectOps`/`_buildVelocityFlipOp` above. Tube.js builds these two extra
|
|
448
448
|
// faces directly; the manual workaround is deleted, not kept alongside (DRY).
|
|
449
449
|
|
|
450
|
-
// ── Collision LEXICON v2 builders (Collision Lab,
|
|
450
|
+
// ── Collision LEXICON v2 builders (Collision Lab, lexicon v2) ────────────────────
|
|
451
451
|
//
|
|
452
452
|
// These are ADDITIVE, pure op-program builders for the v2 wall vocabulary
|
|
453
453
|
// (behavior nothing|block|bounce|absorb|destroy; bounceDirection reflect|random;
|
|
454
454
|
// hooks mark|count). They compose the SAME closed NODE_KINDS the rest of this
|
|
455
|
-
// file uses — ZERO engine change (
|
|
455
|
+
// file uses — ZERO engine change (v2 advisory, all RUN-verified pkg-node).
|
|
456
456
|
// The existing compileActions() path is UNTOUCHED (byte-identity preserved); v2
|
|
457
457
|
// callers (Wall.js via resolveWallSpec) go through compileWallV2() below.
|
|
458
458
|
//
|
|
@@ -465,7 +465,7 @@ function _buildVelocityFlipOp(loName, HANDLE_FRAME, Lit, Ref, Bin) {
|
|
|
465
465
|
// NOT(x) = Cond(x, 0, 1)
|
|
466
466
|
|
|
467
467
|
/**
|
|
468
|
-
* B2 — incidence-aware single-LO Reflect
|
|
468
|
+
* B2 — incidence-aware single-LO Reflect. Replaces the W1
|
|
469
469
|
* name-prefix flip (`_buildVelocityFlipOp` keyed on the LO NAME) with a Cond tree
|
|
470
470
|
* over the frame's PREVIOUS position: the axis whose move carried the frame INTO
|
|
471
471
|
* the wall this tick is the one whose velocity is inverted — the physical
|
|
@@ -523,7 +523,7 @@ function _buildIncidenceReflectOps(HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn) {
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
/**
|
|
526
|
-
* Z1 — 3D incidence-aware Reflect (
|
|
526
|
+
* Z1 — 3D incidence-aware Reflect (Q2 PROVEN as the
|
|
527
527
|
* op-data generalization on top of the landed prev_z scope-bag key — C26). The exact
|
|
528
528
|
* 3-axis lift of `_buildIncidenceReflectOps`: adds the z-axis (near/far faces), flipping
|
|
529
529
|
* whichever of the SIX faces' axis carried the frame INTO the wall's z-slab this tick.
|
|
@@ -591,7 +591,7 @@ function _buildIncidenceReflectOps3D(HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn) {
|
|
|
591
591
|
}
|
|
592
592
|
|
|
593
593
|
/**
|
|
594
|
-
* R1 — ARBITRARY-ANGLE mirror reflection (
|
|
594
|
+
* R1 — ARBITRARY-ANGLE mirror reflection (P3b PROVEN
|
|
595
595
|
* EXACT). A mirror is a REFLECTION of the velocity about a mirror LINE at angle α
|
|
596
596
|
* (the mirror SURFACE angle from horizontal, = the wall's `orientationDeg`):
|
|
597
597
|
*
|
|
@@ -603,7 +603,7 @@ function _buildIncidenceReflectOps3D(HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn) {
|
|
|
603
603
|
* genuine tilted mirror plane, so NO Cond-tree on the velocity part). Trig is legal
|
|
604
604
|
* HOST-side at compile time (the SAME pattern the constrained-cone endpoints already
|
|
605
605
|
* bake). Two-op sequential form (op2 reads the NEW vx written by op1, exploiting the
|
|
606
|
-
* write-propagation into the scope bag —
|
|
606
|
+
* write-propagation into the scope bag — v2 P3):
|
|
607
607
|
* op1: vx' = c2·vx + s2·vy (reads both OLD values)
|
|
608
608
|
* op2: vy' = (s2/c2)·vx' − (1/c2)·vy_old ≡ s2·vx_old − c2·vy_old (exact)
|
|
609
609
|
* Requires c2 = cos2α ≠ 0 (the α ≡ 45° mod 90° singularity routes to the exact swap
|
|
@@ -621,10 +621,10 @@ function _buildAngledReflectOps(HF, alphaDeg, Lit, Ref, Bin) {
|
|
|
621
621
|
}
|
|
622
622
|
|
|
623
623
|
/**
|
|
624
|
-
* R1 — the α ≡ 45° mod 90° diagonal mirror, EXACT (
|
|
624
|
+
* R1 — the α ≡ 45° mod 90° diagonal mirror, EXACT (P1/P2 PROVEN). At these
|
|
625
625
|
* orientations cos2α = 0, so R(α) = [0, s2; s2, 0] with s2 = ±1 — the 2-op form above
|
|
626
626
|
* divides by zero. The exact realization is the 3-op add/subtract SWAP (a naive
|
|
627
|
-
* `vx=vy; vy=vx` is WRONG — the second op reads the already-overwritten vx;
|
|
627
|
+
* `vx=vy; vy=vx` is WRONG — the second op reads the already-overwritten vx;
|
|
628
628
|
* v2 P3), then a negate for the s2 = −1 branch:
|
|
629
629
|
* α ≡ 45° mod 180° (s2=+1): pure swap vx' = vy, vy' = vx (+x → +y)
|
|
630
630
|
* α ≡ 135° mod 180° (s2=−1): swap + negate vx' = −vy, vy' = −vx (+x → −y)
|
|
@@ -656,7 +656,7 @@ function _buildSwapReflectOps(HF, alphaDeg, Lit, Ref, Bin) {
|
|
|
656
656
|
* • orientation ≡ 45 mod 90 (45/135/225/315) → the exact swap / swap+negate ops.
|
|
657
657
|
* • any other α → the 2-op host-baked-trig fixed-plane reflection.
|
|
658
658
|
*
|
|
659
|
-
* 3D ORIENTATION SCOPE (
|
|
659
|
+
* 3D ORIENTATION SCOPE (kept to what the advisory PROVED): a non-cardinal
|
|
660
660
|
* `orientationDeg` is orientation about the Z-AXIS (rotation in the xy-plane) — the 2D
|
|
661
661
|
* swap/angled law LIFTED to a slab, with vz passing straight through (the beam's z-component
|
|
662
662
|
* is untouched). This is the proven, exact case. FULL 3D orientation (a SECOND tilt angle —
|
|
@@ -685,7 +685,7 @@ function _buildReflectOps(orientationDeg, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn,
|
|
|
685
685
|
? _buildIncidenceReflectOps3D(HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn)
|
|
686
686
|
: _buildIncidenceReflectOps(HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn);
|
|
687
687
|
}
|
|
688
|
-
// DRAWN-FACE REFLECTION (
|
|
688
|
+
// DRAWN-FACE REFLECTION (supersedes the FIX-3
|
|
689
689
|
// screenα=−orientationDeg guess). The mirror's reflecting surface is the LONG face of the
|
|
690
690
|
// slab the renderer actually draws. The Wall mirror is a TALL slab (h > w): its long face
|
|
691
691
|
// is VERTICAL at orientation 0, so a +x beam bounces straight back (−x) — EXACTLY what the
|
|
@@ -699,13 +699,13 @@ function _buildReflectOps(orientationDeg, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn,
|
|
|
699
699
|
// −o are congruent mod 180 ONLY at o ≡ 45 (mod 90), so R(−o) coincided with the drawn face
|
|
700
700
|
// at 45/135/225/315 (and, via the separate incidence tree, at cardinals) but reflected a +x
|
|
701
701
|
// beam to the OPPOSITE horizontal side at every OTHER angle (30/37/60/120/150…). That off-
|
|
702
|
-
// face deflection at non-45 tilt is the "reflection isn't correct all the time"
|
|
702
|
+
// face deflection at non-45 tilt is the "reflection isn't correct all the time" defect seen
|
|
703
703
|
// when ROTATING the mirror. This change is BYTE-IDENTICAL at cardinals (untouched incidence
|
|
704
704
|
// tree) and at 45/135/225/315 (90+o ≡ −o mod 180 ⇒ same swap ops), and corrects every
|
|
705
705
|
// arbitrary angle for the tall mirror. NOTE (flagged, not fixed here): a WIDE mirror slab
|
|
706
706
|
// (w > h, e.g. 1D mirrorB) has a HORIZONTAL long face (base 0, not 90); an aspect-blind law
|
|
707
707
|
// cannot honor both bases — a wide mirror at a non-45 angle needs the aspect passed in, or
|
|
708
|
-
// the experiment retuned to a tall slab.
|
|
708
|
+
// the experiment retuned to a tall slab.
|
|
709
709
|
var alphaScreen = slab.reflectBaseDeg; // (90 + onorm) % 360 — from the ONE SlabFace authority.
|
|
710
710
|
if (Math.abs(mod90 - 45) < EPS) {
|
|
711
711
|
// 45/135/225/315 → exact swap (cos2α = 0 singularity of the 2-op form). For a 3D
|
|
@@ -718,8 +718,8 @@ function _buildReflectOps(orientationDeg, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn,
|
|
|
718
718
|
}
|
|
719
719
|
|
|
720
720
|
/**
|
|
721
|
-
* R1-GUARD — the ONE-SIDED FACE-CROSSING GUARD for a TILTED reflect mirror
|
|
722
|
-
* red-team §2, MEASURED single-reflection optics;
|
|
721
|
+
* R1-GUARD — the ONE-SIDED FACE-CROSSING GUARD for a TILTED reflect mirror
|
|
722
|
+
* (red-team §2, MEASURED single-reflection optics; the drawn-face rule).
|
|
723
723
|
*
|
|
724
724
|
* ## Why this exists (the tilted-reflect LEAK-THROUGH the AABB trigger caused)
|
|
725
725
|
*
|
|
@@ -749,10 +749,10 @@ function _buildReflectOps(orientationDeg, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn,
|
|
|
749
749
|
* Firing at the SURFACE (not the mid-line) means a beam reflects the moment it touches the
|
|
750
750
|
* face the user SEES — and, fused with the same-tick face-normal exit eject below, a
|
|
751
751
|
* reflected leaf is placed back OUTSIDE the slab in the SAME tick, so no post-tick snapshot
|
|
752
|
-
* ever renders a reflected particle INSIDE the slab (
|
|
752
|
+
* ever renders a reflected particle INSIDE the slab (reflect
|
|
753
753
|
* must reposition outside the collided edge in the same tick — particles seen rendering
|
|
754
754
|
* inside the LO then walking through were the bug). The approach term (v·n sign) is what
|
|
755
|
-
* kills the rattle
|
|
755
|
+
* kills the rattle MEASURED for a naive two-sided crossing test: after the
|
|
756
756
|
* reflection v·n flips and the eject holds the leaf outside its incident surface, so neither
|
|
757
757
|
* jitter re-crossings nor the reflected step satisfy the guard again. Handling BOTH surfaces
|
|
758
758
|
* makes the wall a scene-agnostic two-faced mirror (a generic Wall never knows the beam's
|
|
@@ -844,7 +844,7 @@ function _buildTiltedReflectGuardTrigger(n, hw, HF, HS, Lit, Ref, Bin, Cmp, Cond
|
|
|
844
844
|
|
|
845
845
|
/**
|
|
846
846
|
* R1-GUARD eject — the PREV-ANCHORED FACE-NORMAL EXIT (the ONE eject story for a tilted
|
|
847
|
-
* reflect;
|
|
847
|
+
* reflect; the same-tick-exteriority rule). Places the leaf's WHOLE BODY just
|
|
848
848
|
* outside the slab on its INCIDENT side, in the SAME tick as the velocity flip:
|
|
849
849
|
*
|
|
850
850
|
* P' = P_prev + (T − s(P_prev)) · n, T = −sign(v·n) · (hw + w/2 + 1)
|
|
@@ -853,9 +853,9 @@ function _buildTiltedReflectGuardTrigger(n, hw, HF, HS, Lit, Ref, Bin, Cmp, Cond
|
|
|
853
853
|
* guard requires it) and push along the face normal to clearance |s| = hw + w/2 + 1: half
|
|
854
854
|
* slab thickness + half leaf width + 1px. The incident side is −sign(v·n) because the eject
|
|
855
855
|
* ops run BEFORE the reflect ops, while v is still the INCOMING velocity. This supersedes
|
|
856
|
-
* BOTH earlier stories: the plain prev-restore (
|
|
856
|
+
* BOTH earlier stories: the plain prev-restore (§2) only guaranteed the leaf was on
|
|
857
857
|
* the incident side of the MID-LINE — a restored leaf could still sit up to hw INSIDE the
|
|
858
|
-
* slab body and render there while walking out (the
|
|
858
|
+
* slab body and render there while walking out (exactly the defect seen); and the AABB
|
|
859
859
|
* 'closest' eject scattered or far-face-ejected (the original leak). Exit clearance holds for
|
|
860
860
|
* the swap (45/135) AND arbitrary-angle paths identically — the ops are angle-generic data.
|
|
861
861
|
*
|
|
@@ -864,7 +864,7 @@ function _buildTiltedReflectGuardTrigger(n, hw, HF, HS, Lit, Ref, Bin, Cmp, Cond
|
|
|
864
864
|
* Both ops read ONLY `prev_x`/`prev_y`/`vx`/`vy` — refs that NEITHER op writes (and the
|
|
865
865
|
* reflect ops, which run after, touch only vx/vy). So op1 (set x) and op2 (set y) evaluate
|
|
866
866
|
* the IDENTICAL displacement Δ = T − s(P_prev) and land exactly at P_prev + Δ·n; s(P') = T
|
|
867
|
-
* by construction. Compare the withdrawn OBB-normal eject
|
|
867
|
+
* by construction. Compare the withdrawn OBB-normal eject, which computed s from
|
|
868
868
|
* the CURRENT x/y and needed a vz-scratch to survive its own eviction of x — a trick that
|
|
869
869
|
* breaks on a 3D wall where vz is real momentum. prev-anchoring dissolves the problem.
|
|
870
870
|
* WASM-only (prev_* scope family), like the cardinal incidence tree. z passes straight
|
|
@@ -957,8 +957,8 @@ function _tilted3DGates(n, hw, HF, HS, Lit, Ref, Bin, Cmp, Cond) {
|
|
|
957
957
|
|
|
958
958
|
/**
|
|
959
959
|
* R1-3D — the z-face EXIT eject for a 3D tilted slab: place the leaf's whole body just
|
|
960
|
-
* outside the z-face it entered through, in the SAME tick as the vz flip (the
|
|
961
|
-
* same-tick-exteriority
|
|
960
|
+
* outside the z-face it entered through, in the SAME tick as the vz flip (the
|
|
961
|
+
* same-tick-exteriority rule, z-lifted):
|
|
962
962
|
* z' = cz + sign(prev_z − cz) · (hz + w/2 + 1)
|
|
963
963
|
* with cz/hz derived LIVE from `self.near`/`self.far` (the engine-recentered z-band —
|
|
964
964
|
* the broad-phase superset rect widens only x/y, so near/far are the TRUE z-faces) and
|
|
@@ -988,7 +988,7 @@ function _gateOps(ops, gateFactory, HF, Ref, Cond) {
|
|
|
988
988
|
});
|
|
989
989
|
}
|
|
990
990
|
|
|
991
|
-
// ── PITCHED (two-axis) mirror builders
|
|
991
|
+
// ── PITCHED (two-axis) mirror builders ───────────────────────────────────────────
|
|
992
992
|
//
|
|
993
993
|
// A PITCHED plate (pitchDeg ≠ 0 about its own long axis) reflects by the TRUE 3-space law
|
|
994
994
|
// R = I − 2nnᵀ about the plate normal n (SlabFace plate3.n — the ONE authority). This
|
|
@@ -1135,7 +1135,7 @@ function _buildPitchedExitEjectOps(plate, HF, HS, Lit, Ref, Bin, Cmp, Cond) {
|
|
|
1135
1135
|
}
|
|
1136
1136
|
|
|
1137
1137
|
/**
|
|
1138
|
-
* B3 — omni direction-only Random
|
|
1138
|
+
* B3 — omni direction-only Random: redraw the exit direction
|
|
1139
1139
|
* uniformly over the square, DIRECTION ONLY, NO decoherence. This is also the
|
|
1140
1140
|
* round-1 half-plane fix (the legacy RANDOM_XY drew vx ∈ [0,1) = forward hemisphere).
|
|
1141
1141
|
* Uniform-over-square (not angle-uniform — ops cannot loop ⇒ no in-tick rejection);
|
|
@@ -1150,10 +1150,10 @@ function _buildOmniRandomOps(HF, Lit, Bin, Fn) {
|
|
|
1150
1150
|
}
|
|
1151
1151
|
|
|
1152
1152
|
/**
|
|
1153
|
-
* B3 — angle-CONSTRAINED Random
|
|
1153
|
+
* B3 — angle-CONSTRAINED Random: redraw the exit direction over a
|
|
1154
1154
|
* cone bounded by two endpoint direction vectors A=(ax,ay), B=(bx,by), computed
|
|
1155
1155
|
* HOST-SIDE from the wall's INITIAL orientation + [minDeg,maxDeg] and BAKED as Lits
|
|
1156
|
-
* (immune to later spin by construction — a re-aim must recompile). Uses the
|
|
1156
|
+
* (immune to later spin by construction — a re-aim must recompile). Uses the
|
|
1157
1157
|
* single-draw scratch-prop trick (proven P3, 60/60 exact on the consistency law):
|
|
1158
1158
|
* every Fn('random') is a fresh draw, so store ONE t in a scratch prop (frame.vx),
|
|
1159
1159
|
* read it in BOTH lerps. op2 (vy) reads t BEFORE op3 (vx) overwrites it. 1 RNG draw.
|
|
@@ -1174,10 +1174,10 @@ function _buildConstrainedRandomOps(HF, ax, ay, bx, by, Lit, Ref, Bin, Fn) {
|
|
|
1174
1174
|
}
|
|
1175
1175
|
|
|
1176
1176
|
/**
|
|
1177
|
-
* B3 — decoherence as an INDEPENDENT toggle
|
|
1177
|
+
* B3 — decoherence as an INDEPENDENT toggle: the phase reset
|
|
1178
1178
|
* (`local_time=0`, offset-decoherence) + charge redraw (sign-decoherence, bench Q1)
|
|
1179
1179
|
* that the legacy RANDOM_XY bundled in — here SEPARABLE, stackable onto any behavior.
|
|
1180
|
-
* Proven separable (
|
|
1180
|
+
* Proven separable (P1/P2). 1 RNG draw (charge). @returns {Array<object>}
|
|
1181
1181
|
*/
|
|
1182
1182
|
function _buildDecoherenceOps(HF, Lit, Cmp, Cond, Fn) {
|
|
1183
1183
|
return [
|
|
@@ -1186,9 +1186,9 @@ function _buildDecoherenceOps(HF, Lit, Cmp, Cond, Fn) {
|
|
|
1186
1186
|
];
|
|
1187
1187
|
}
|
|
1188
1188
|
|
|
1189
|
-
// ── ABSORB v2 tally — WITHDRAWN (
|
|
1190
|
-
// The v1 `count`-as-virtual-mass tally op was
|
|
1191
|
-
// the
|
|
1189
|
+
// ── ABSORB v2 tally — WITHDRAWN (round-3) ────────────────────────────────────────
|
|
1190
|
+
// The v1 `count`-as-virtual-mass tally op was a duplicative parallel LO-prop surface,
|
|
1191
|
+
// which the mass-surface rule rejects. ABSORB v2 now accumulates into the LO's REAL
|
|
1192
1192
|
// backing-frame mass via the existing frame accessors (the "LO extends Frame" composition;
|
|
1193
1193
|
// under MANDATORY BACKING the LO's backing frame is created at registration and READ via
|
|
1194
1194
|
// getLargeObjectFrameId) — see `absorbRadiation.js`. The compileWallV2 absorb branch
|
|
@@ -1209,7 +1209,7 @@ function _buildDecoherenceOps(HF, Lit, Cmp, Cond, Fn) {
|
|
|
1209
1209
|
* native dispatch_collision_type draw order: velocity first, then reset() charge)
|
|
1210
1210
|
* - MOMENTUM_BASED and WALL draw 0 RNG values
|
|
1211
1211
|
*
|
|
1212
|
-
* ## RNG discipline (from
|
|
1212
|
+
* ## RNG discipline (from the JS reference's native dispatch, MUST MATCH):
|
|
1213
1213
|
*
|
|
1214
1214
|
* RANDOM_X: 1 draw — vx = _rng()*2-1, vy = 1 (no charge draw)
|
|
1215
1215
|
* RANDOM_Y: 2 draws — vy = _rng()*2-1, then reset() draws _rng() for charge
|
|
@@ -1220,7 +1220,7 @@ function _buildDecoherenceOps(HF, Lit, Cmp, Cond, Fn) {
|
|
|
1220
1220
|
* is a no-op here (action already compiled)
|
|
1221
1221
|
* NONE: 0 draws — nothing emitted
|
|
1222
1222
|
*
|
|
1223
|
-
* ## charge draw (reset()
|
|
1223
|
+
* ## charge draw (reset())
|
|
1224
1224
|
* `_rng() < 0.5 ? Charge.POSITIVE : Charge.NEGATIVE`
|
|
1225
1225
|
* AST: Cond(Cmp('<', Fn('random',[]), Lit(0.5)), Lit(0), Lit(1))
|
|
1226
1226
|
* (0 = POSITIVE, 1 = NEGATIVE in both JS and Rust)
|
|
@@ -1364,7 +1364,7 @@ function compileActions(actions, loName, reposition, collisionType) {
|
|
|
1364
1364
|
|
|
1365
1365
|
if (ct === 'RANDOM_X') {
|
|
1366
1366
|
// RANDOM_X: 1 RNG draw — vx = _rng()*2-1, vy = 1 (no charge reset).
|
|
1367
|
-
// Native
|
|
1367
|
+
// Native: vx = _rng()*2-1; vy = 1;
|
|
1368
1368
|
ops.push({ set: HANDLE_FRAME + '.vx',
|
|
1369
1369
|
to: Bin('-', Bin('*', Fn('random', []), Lit(2)), Lit(1)) });
|
|
1370
1370
|
ops.push({ set: HANDLE_FRAME + '.vy',
|
|
@@ -1372,7 +1372,7 @@ function compileActions(actions, loName, reposition, collisionType) {
|
|
|
1372
1372
|
|
|
1373
1373
|
} else if (ct === 'RANDOM_Y') {
|
|
1374
1374
|
// RANDOM_Y: 2 RNG draws — vy = _rng()*2-1, then reset() draws charge.
|
|
1375
|
-
// Native
|
|
1375
|
+
// Native: vy = _rng()*2-1; vx = 1; reset() → charge draw.
|
|
1376
1376
|
// reset() also clears local_time=0 (wave-phase reset — G2 mirrors this with a 0-draw op).
|
|
1377
1377
|
ops.push({ set: HANDLE_FRAME + '.vy',
|
|
1378
1378
|
to: Bin('-', Bin('*', Fn('random', []), Lit(2)), Lit(1)) });
|
|
@@ -1386,7 +1386,7 @@ function compileActions(actions, loName, reposition, collisionType) {
|
|
|
1386
1386
|
|
|
1387
1387
|
} else if (ct === 'RANDOM_XY') {
|
|
1388
1388
|
// RANDOM_XY: 3 RNG draws — vx = _rng(), vy = _rng()*2-1, then reset() draws charge.
|
|
1389
|
-
// Native
|
|
1389
|
+
// Native: vx = _rng(); vy = _rng()*2-1; reset() → charge.
|
|
1390
1390
|
// reset() also clears local_time=0 (wave-phase reset — G2 mirrors this with a 0-draw op).
|
|
1391
1391
|
ops.push({ set: HANDLE_FRAME + '.vx',
|
|
1392
1392
|
to: Fn('random', []) });
|
|
@@ -1475,7 +1475,7 @@ function compileActions(actions, loName, reposition, collisionType) {
|
|
|
1475
1475
|
* @param {Array<number>} [spec.angleConstraint] - [minDeg,maxDeg] offsets around the
|
|
1476
1476
|
* wall's INITIAL orientation (random only). Omitted/full ⇒ omni.
|
|
1477
1477
|
* @param {number} [spec.orientationDeg=0]- the wall's INITIAL orientation (baked; a
|
|
1478
|
-
* later spin must recompile
|
|
1478
|
+
* later spin must recompile — the cone endpoints are compile-time Lits).
|
|
1479
1479
|
* @param {boolean}[spec.decoherence=false]- append the phase/charge reset (random only).
|
|
1480
1480
|
* @param {Array<string>}[spec.hooks] - ['mark','count'] (composable with any behavior).
|
|
1481
1481
|
* @param {string} [spec.markTag] - the user string MARK stamps (default '').
|
|
@@ -1523,11 +1523,11 @@ function compileWallV2(spec) {
|
|
|
1523
1523
|
var ops = [];
|
|
1524
1524
|
|
|
1525
1525
|
// ── Motion / life response (the ONE behavior axis) ───────────────────────
|
|
1526
|
-
// TILTED REFLECT GUARD (
|
|
1526
|
+
// TILTED REFLECT GUARD (§2 + the same-tick-exteriority rule): a
|
|
1527
1527
|
// NON-cardinal REFLECT mirror is fired ONCE per genuine drawn-SURFACE crossing (the guard
|
|
1528
1528
|
// trigger, applied below) and ejects by the PREV-ANCHORED FACE-NORMAL EXIT — the ONE
|
|
1529
|
-
// coherent eject story that replaces BOTH the interim OBB-normal eject
|
|
1530
|
-
//
|
|
1529
|
+
// coherent eject story that replaces BOTH the interim OBB-normal eject and the
|
|
1530
|
+
// plain prev-restore. Cardinal / random-bounce / non-reflect walls keep the
|
|
1531
1531
|
// AABB 'closest' eject + plain-intersects trigger — BYTE-IDENTICAL (SU1). The guard normal
|
|
1532
1532
|
// is CONSUMED from the ONE SlabFace authority (obb.normal = the −1-eigenvector the reflect
|
|
1533
1533
|
// law flips, engine y-down frame) — never a second inline sin/cos convention. Applies to
|
|
@@ -1539,7 +1539,7 @@ function compileWallV2(spec) {
|
|
|
1539
1539
|
? SlabFace.resolveSlabFace({ orientationDeg: spec.orientationDeg,
|
|
1540
1540
|
w: spec.slabW, h: spec.slabH,
|
|
1541
1541
|
pitchDeg: spec.pitchDeg, d: spec.slabD }) : null;
|
|
1542
|
-
// R2AX — a PITCHED plate (two-axis mirror
|
|
1542
|
+
// R2AX — a PITCHED plate (two-axis mirror) takes the true
|
|
1543
1543
|
// 3-space reflection path (R = I − 2nnᵀ about SlabFace plate3.n + the n-dot guard +
|
|
1544
1544
|
// the prev-anchored 3-space exit). pitch 0/absent never sets `pitched` — every
|
|
1545
1545
|
// existing program stays byte-identical.
|
|
@@ -1619,7 +1619,7 @@ function compileWallV2(spec) {
|
|
|
1619
1619
|
// legacy `CollisionActions.BLOCK` compiled — so porting a legacy `block` caller
|
|
1620
1620
|
// moves ZERO physics.
|
|
1621
1621
|
//
|
|
1622
|
-
// WHY "closest outside" and NOT "revert the last move" (
|
|
1622
|
+
// WHY "closest outside" and NOT "revert the last move" (both were options;
|
|
1623
1623
|
// measured head-to-head on the canonical WASM engine, wall x∈[200,220]):
|
|
1624
1624
|
// - approaching leaf (x=150, vx=+1): the two are equivalent in outcome — the leaf
|
|
1625
1625
|
// stops dead at the face and stays (eject parks at x=198.5, revert at x=199) for
|
|
@@ -1641,11 +1641,11 @@ function compileWallV2(spec) {
|
|
|
1641
1641
|
var blockEjectOps = _buildEjectOps(reposition, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn) || [];
|
|
1642
1642
|
for (var bq = 0; bq < blockEjectOps.length; bq++) { ops.push(blockEjectOps[bq]); }
|
|
1643
1643
|
} else if (behavior === 'absorb') {
|
|
1644
|
-
// Track A (
|
|
1644
|
+
// Track A (the mass-surface rule): the collision op just KILLS; the host
|
|
1645
1645
|
// radiation tick-fn (absorbRadiation.js) tallies the collected killed masses into the
|
|
1646
1646
|
// LO's REAL BACKING-FRAME mass (via the existing frame accessors) and radiates per the
|
|
1647
|
-
// conservation contract. The v1 count-as-virtual-mass tally op is WITHDRAWN (
|
|
1648
|
-
//
|
|
1647
|
+
// conservation contract. The v1 count-as-virtual-mass tally op is WITHDRAWN (round-3
|
|
1648
|
+
// — it was exactly the duplicative parallel surface the rule rejects).
|
|
1649
1649
|
var cap = (typeof spec.capacity === 'number' && spec.capacity > 0) ? spec.capacity : 64;
|
|
1650
1650
|
ops.push({ set: HF + '.is_alive', to: Lit(0) });
|
|
1651
1651
|
collectCollided = true; // buffer killed frames' masses for the host tally
|
|
@@ -1700,8 +1700,8 @@ function compileWallV2(spec) {
|
|
|
1700
1700
|
_reflectSlab.plate3, HF, HS, Lit, Ref, Bin, Cmp, Cond, Fn));
|
|
1701
1701
|
} else if (tiltedReflect) {
|
|
1702
1702
|
// GUARDED tilted reflect: fire the reflection ONCE per drawn-SURFACE crossing
|
|
1703
|
-
// (surface-crossing + approach guard —
|
|
1704
|
-
// per the
|
|
1703
|
+
// (surface-crossing + approach guard — §2 upgraded to the ±hw surfaces
|
|
1704
|
+
// per the same-tick-exteriority rule). The crossing NORMAL is `obb.normal`
|
|
1705
1705
|
// and the half-thickness `obb.hw`, BOTH from the ONE SlabFace authority: the
|
|
1706
1706
|
// normal is the axis R(reflectBaseDeg) actually FLIPS (its −1-eigenvector,
|
|
1707
1707
|
// engine y-down frame) — the SAME derivation the reflect law, the renderer's
|
|
@@ -1725,8 +1725,8 @@ function compileWallV2(spec) {
|
|
|
1725
1725
|
|
|
1726
1726
|
// ── Export ─────────────────────────────────────────────────────────────────────
|
|
1727
1727
|
|
|
1728
|
-
// Collision LEXICON v2 builders (
|
|
1729
|
-
// LO
|
|
1728
|
+
// Collision LEXICON v2 builders (proven; consumed by compileWallV2 and driven RAW by the bench
|
|
1729
|
+
// LO behaviors — the SINGLE authority for these shapes) and the shared eject/flip builders (OvenBox, Tube).
|
|
1730
1730
|
export {
|
|
1731
1731
|
compileActions,
|
|
1732
1732
|
compileWallV2,
|