@descix/frqtl-node 0.0.0-stage → 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 +87 -0
- package/LICENSE.md +664 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2920 -0
- package/dist/frqtl/objects/ObjectFactory.js +296 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +710 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +351 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
- package/dist/frqtl/objects/v1-replacements.js +29 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +34 -0
- package/dist/frqtl/parts/CollisionTypes.js +30 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
- package/dist/frqtl/parts/LargeObject.js +405 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +717 -0
- package/dist/frqtl/parts/OvenBox.js +611 -0
- package/dist/frqtl/parts/PointSource.js +762 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +211 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +690 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +325 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +671 -0
- package/dist/shim/notebook/knob.js +150 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +507 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +213 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +804 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +63 -4
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* absorbRadiation.js — ABSORB v2 on the LO's REAL backing-frame mass (Track A).
|
|
5
|
+
*
|
|
6
|
+
* The mass-surface rule: use the OBJECT approach, not a
|
|
7
|
+
* duplicative op-prop mass surface. A LargeObject is backed by a REAL leaf Frame, so the
|
|
8
|
+
* absorbed mass lands in a real frame's `mass` reachable through the SAME frame accessors
|
|
9
|
+
* everything else uses.
|
|
10
|
+
*
|
|
11
|
+
* MANDATORY BACKING: the backing frame is now created by the engine
|
|
12
|
+
* at `register_large_object` (every LO is frame-backed — no unbacked path). This sugar READS
|
|
13
|
+
* that ledger via `getLargeObjectFrameId` instead of creating + attaching one.
|
|
14
|
+
*
|
|
15
|
+
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
16
|
+
* `attachAbsorberBackingFrame(universe, loHandle, rect, capacity)` → READS the LO's
|
|
17
|
+
* registration-created backing leaf (store-resident mass ledger — uninjected, zero
|
|
18
|
+
* single-occupancy / quadtree interaction, §2a), primes it with the mass-1 empty
|
|
19
|
+
* baseline, and returns `{ backingFrameHandle, backingQid, level, capacity }`. `capacity` is
|
|
20
|
+
* the absorb CLOCK — the fill ceiling and the `1/capacity` emission probability — snapped to
|
|
21
|
+
* the nearest LEVEL the engine can actually size (`level` is that quantization's record).
|
|
22
|
+
* `buildAbsorbRadiationTickFn(cfg)` → a host tick-fn (the emitter/thermostat seam)
|
|
23
|
+
* that each tick: (1) tallies the collected KILLED colliders' masses into the
|
|
24
|
+
* backing frame's REAL mass, KEYED BY THE LAYER each collider came from; (2) at-cap gating —
|
|
25
|
+
* deactivates the LO's collision when full, reactivates when radiation drains it below cap;
|
|
26
|
+
* (3) radiates a leaf with probability 1/capacity, AT one of the layers it absorbed from and
|
|
27
|
+
* carrying `emissivity` of that layer's cell capacity, decrementing EXACTLY. Mass is
|
|
28
|
+
* conserved exactly, now PER LAYER as well as in total.
|
|
29
|
+
*
|
|
30
|
+
* ── Layer 2: physics behavior ────────────────────────────────────────────────
|
|
31
|
+
* A wall that fills up as it absorbs, stops soaking at capacity, and drips the stored mass
|
|
32
|
+
* back out as leaves OF THE SCALE IT SOAKED — with NO mass created or destroyed (Invariant
|
|
33
|
+
* 5/7). This is the blackbody absorb/re-emit paradigm as pure sugar.
|
|
34
|
+
*
|
|
35
|
+
* EMISSIVITY is the wall's radiating efficiency, expressed the only way this engine expresses
|
|
36
|
+
* "how much of a body is there": as a FULLNESS FRACTION of the emitted leaf. 1.0 emits a FULL
|
|
37
|
+
* leaf (fullness 1 — light speed, shortest wavelength); 0.5 (the default) emits a half-full
|
|
38
|
+
* one, which moves at half the gate probability and carries twice the wavelength. Emissivity
|
|
39
|
+
* is therefore a colour/temperature knob, not a bookkeeping constant: it sets what the
|
|
40
|
+
* re-emitted quantum IS, and speed and λ fall out of that together.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
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
|
+
* of a full one — a visibly "warm" re-emitter rather than a light-speed one.
|
|
47
|
+
*/
|
|
48
|
+
var DEFAULT_EMISSIVITY = 0.5;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Emissivity is a FULLNESS fraction, so it lives in (0, 1] — a fullness above 1 is a move
|
|
52
|
+
* gate above 1, which cannot mean anything. FAIL LOUD rather than clamp: a silently
|
|
53
|
+
* clamped 2.0 would look like it worked and quietly emit light-speed leaves.
|
|
54
|
+
*
|
|
55
|
+
* NOTE (open, see the datasheet): 0 is REFUSED, not read as "never re-emit". The mass
|
|
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, open question.
|
|
58
|
+
*/
|
|
59
|
+
function _validEmissivity(v, whence) {
|
|
60
|
+
var n = Number(v);
|
|
61
|
+
if (!isFinite(n) || n <= 0 || n > 1) {
|
|
62
|
+
throw new Error('[' + whence + '] emissivity must be a fullness fraction in (0, 1] — got ' +
|
|
63
|
+
JSON.stringify(v) + '. Above 1 is a move gate above 1; 0 is refused because the ' +
|
|
64
|
+
'emitted-mass law floors at 1 cell and could not honour it.');
|
|
65
|
+
}
|
|
66
|
+
return n;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Snap a requested absorb-clock capacity to a LEVEL the engine can actually size, by ASKING
|
|
71
|
+
* the engine (`universe.levelCapacity`) rather than inverting `4^L` — which is the A={x,y}
|
|
72
|
+
* answer only. Returns the smallest level whose real capacity covers the request, so the
|
|
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).
|
|
75
|
+
*/
|
|
76
|
+
function deriveBackingLevel(universe, capacity) {
|
|
77
|
+
var want = Math.max(1, Number(capacity) || 1);
|
|
78
|
+
for (var lvl = 1; lvl <= 64; lvl++) {
|
|
79
|
+
if (universe.levelCapacity(lvl) >= want) { return lvl; }
|
|
80
|
+
}
|
|
81
|
+
throw new Error('[deriveBackingLevel] no leaf level up to 64 reaches the requested absorb ' +
|
|
82
|
+
'capacity ' + want + ' on this universe\'s active axes.');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Read [cx, cy] centre from a rect ([l,t,w,h] / [l,t,z,w,h,d] / {left,top,w,h} / Rectangle).
|
|
86
|
+
function _rectCenter(rect) {
|
|
87
|
+
if (Array.isArray(rect)) {
|
|
88
|
+
if (rect.length === 6) { return [rect[0] + rect[3] / 2, rect[1] + rect[4] / 2]; }
|
|
89
|
+
return [rect[0] + rect[2] / 2, rect[1] + rect[3] / 2];
|
|
90
|
+
}
|
|
91
|
+
var l = (rect.left !== undefined) ? rect.left : rect._left;
|
|
92
|
+
var t = (rect.top !== undefined) ? rect.top : rect._top;
|
|
93
|
+
return [l + rect.w / 2, t + rect.h / 2];
|
|
94
|
+
}
|
|
95
|
+
// Right-face exit point (radiated leaves are placed OUTSIDE the wall so they fly
|
|
96
|
+
// away rather than immediately re-colliding with their own absorber).
|
|
97
|
+
function _rectRightExit(rect) {
|
|
98
|
+
var c = _rectCenter(rect);
|
|
99
|
+
var right;
|
|
100
|
+
if (Array.isArray(rect)) { right = (rect.length === 6) ? (rect[0] + rect[3]) : (rect[0] + rect[2]); }
|
|
101
|
+
else { var l = (rect.left !== undefined) ? rect.left : rect._left; right = l + rect.w; }
|
|
102
|
+
return [right, c[1]];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Per-layer ledger helpers (Map<layer:number, mass:number>).
|
|
106
|
+
function _bump(map, layer, delta) {
|
|
107
|
+
map.set(layer, (map.get(layer) || 0) + delta);
|
|
108
|
+
}
|
|
109
|
+
function _layersAscending(map) {
|
|
110
|
+
var out = [];
|
|
111
|
+
map.forEach(function (_v, k) { out.push(k); });
|
|
112
|
+
out.sort(function (a, b) { return a - b; });
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* READ the LO's mandatory backing leaf (created at registration) and prime it as the
|
|
118
|
+
* absorber's mass ledger. Returns the backing-frame descriptor.
|
|
119
|
+
*
|
|
120
|
+
* `level`/`capacity` are the ABSORB-CLOCK knobs (how fast the wall fills and how often it
|
|
121
|
+
* radiates), derived from the requested `capacity` through the engine's published capacity
|
|
122
|
+
* law — independent of the backing frame's own geometric level. `rect` is retained in the
|
|
123
|
+
* signature for call-site compatibility but is no longer used (the engine owns the center).
|
|
124
|
+
*/
|
|
125
|
+
function attachAbsorberBackingFrame(universe, loHandle, rect, capacity) { // eslint-disable-line no-unused-vars
|
|
126
|
+
var level = deriveBackingLevel(universe, capacity);
|
|
127
|
+
var realCap = universe.levelCapacity(level);
|
|
128
|
+
var fh = universe.getLargeObjectFrameId(loHandle); // the registration-created ledger
|
|
129
|
+
var backingQid = universe.getFrameQuantumId(fh);
|
|
130
|
+
// Start EMPTY (mass 1 = nearly-empty floor; mass 0 risks the cap/mass wavelength write).
|
|
131
|
+
// The +1 baseline is subtracted out of every conservation reading below.
|
|
132
|
+
universe.setFrameProperty(backingQid, 'mass', 1);
|
|
133
|
+
return { backingFrameHandle: fh, backingQid: backingQid, level: level, capacity: realCap };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build the ABSORB radiation host tick-fn. `cfg`:
|
|
138
|
+
* universe, loHandle, backingQid, capacity, rect, [emissivity], [loActiveSetter], [exit]
|
|
139
|
+
* Exposes:
|
|
140
|
+
* `.stats()` → { stored, absorbedTotal, radiatedTotal, byLayer[] } for tests/readouts
|
|
141
|
+
* `.emissivity()` / `.setEmissivity(v)` → the LIVE radiating-efficiency knob
|
|
142
|
+
* `.setRect(rect)` → the wall moved: the exit follows it (Wall.setRect calls this)
|
|
143
|
+
* `stored`/`absorbedTotal`/`radiatedTotal` all EXCLUDE the mass-1 backing baseline.
|
|
144
|
+
*/
|
|
145
|
+
function buildAbsorbRadiationTickFn(cfg) {
|
|
146
|
+
var universe = cfg.universe, loHandle = cfg.loHandle, backingQid = cfg.backingQid;
|
|
147
|
+
var capacity = cfg.capacity, rect = cfg.rect;
|
|
148
|
+
var exit = cfg.exit || _rectRightExit(rect);
|
|
149
|
+
// Emission happens AT a layer the wall absorbed from (per-layer ledgers below), and an
|
|
150
|
+
// absorbed frame lived in its own dimension — so every emission layer already exists.
|
|
151
|
+
// Nothing here can make routing allocate a layer, and so nothing here can re-base the
|
|
152
|
+
// world's floor (universe.rs::route_injected_frames).
|
|
153
|
+
var BASE = 1; // the mass-1 empty baseline (see attach)
|
|
154
|
+
var absorbedTotal = 0, radiatedTotal = 0, referenceMass = BASE;
|
|
155
|
+
var emissivity = (cfg.emissivity === undefined || cfg.emissivity === null)
|
|
156
|
+
? DEFAULT_EMISSIVITY
|
|
157
|
+
: _validEmissivity(cfg.emissivity, 'buildAbsorbRadiationTickFn');
|
|
158
|
+
|
|
159
|
+
// Per-layer provenance of the ONE scalar backing mass. `stored` is what is still held
|
|
160
|
+
// from each layer; `absorbed`/`radiated` are cumulative. The invariant this exists to
|
|
161
|
+
// make checkable: for EVERY layer, stored + radiated == absorbed, and the stored values
|
|
162
|
+
// sum to the backing frame's real mass.
|
|
163
|
+
var absorbedByLayer = new Map(), radiatedByLayer = new Map(), storedByLayer = new Map();
|
|
164
|
+
|
|
165
|
+
function setActive(active) {
|
|
166
|
+
if (typeof cfg.loActiveSetter === 'function') { cfg.loActiveSetter(active); }
|
|
167
|
+
else if (universe && typeof universe.setLargeObjectActive === 'function') {
|
|
168
|
+
universe.setLargeObjectActive(loHandle, active);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* MULTI-LAYER APPORTIONING — an LO that observes collisions at several layers radiates
|
|
174
|
+
* at EACH layer in proportion to the mass it absorbed from that layer's collisions, so
|
|
175
|
+
* conservation holds per layer and not merely in total.
|
|
176
|
+
*
|
|
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.
|
|
181
|
+
*
|
|
182
|
+
* The draw is SKIPPED when only one layer is in play, so a single-layer world consumes
|
|
183
|
+
* exactly the RNG stream it did before this rule existed.
|
|
184
|
+
*/
|
|
185
|
+
function pickEmissionLayer() {
|
|
186
|
+
var layers = _layersAscending(storedByLayer).filter(function (L) {
|
|
187
|
+
return (storedByLayer.get(L) || 0) > 0;
|
|
188
|
+
});
|
|
189
|
+
if (layers.length === 0) { return null; }
|
|
190
|
+
if (layers.length === 1) { return layers[0]; }
|
|
191
|
+
var total = 0, i;
|
|
192
|
+
for (i = 0; i < layers.length; i++) { total += storedByLayer.get(layers[i]); }
|
|
193
|
+
var draw = ((typeof universe.random === 'function') ? universe.random() : Math.random()) * total;
|
|
194
|
+
for (i = 0; i < layers.length; i++) {
|
|
195
|
+
draw -= storedByLayer.get(layers[i]);
|
|
196
|
+
if (draw < 0) { return layers[i]; }
|
|
197
|
+
}
|
|
198
|
+
return layers[layers.length - 1];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
var tick = function absorbRadiationTick(arg) {
|
|
202
|
+
// Resolve the tickData carrying THIS/last tick's collected KILLED frames.
|
|
203
|
+
//
|
|
204
|
+
// This host tick-fn rides `universe.experimentTickFunctions`, which the engine calls
|
|
205
|
+
// as `fn(universe)` BEFORE do_tick (the Phase-A emitter/thermostat seam). But a radiation
|
|
206
|
+
// consumer needs the POST-tick output (TickData.objects[handle].frames). So when called
|
|
207
|
+
// with the universe (no `.objects`), read the universe's cached LAST tickData — a
|
|
208
|
+
// one-tick lag; mass is still exactly conserved. The bench/tests call `fn(tickData)`
|
|
209
|
+
// directly (same-tick). The ORIGINAL K5-realm bug was reading `universe.objects`
|
|
210
|
+
// (undefined) here → `absorbedThisTick=0` every tick → silent zero, no radiation.
|
|
211
|
+
var tickData = (arg && arg.objects) ? arg
|
|
212
|
+
: (universe && universe._lastTickData) ? universe._lastTickData
|
|
213
|
+
: null;
|
|
214
|
+
|
|
215
|
+
// (1) tally the collected KILLED colliders into the backing frame's REAL mass,
|
|
216
|
+
// recording WHICH LAYER each one came from. The cap clamp is applied per
|
|
217
|
+
// collider (in collection order) rather than to the tick's sum: the landed
|
|
218
|
+
// total is identical, and every layer's share of it is then exact — no
|
|
219
|
+
// proportional fudge to apportion a clamped remainder with.
|
|
220
|
+
var o = tickData && tickData.objects && tickData.objects[loHandle];
|
|
221
|
+
var cur = universe.getFrameProperty(backingQid, 'mass');
|
|
222
|
+
var curBefore = cur;
|
|
223
|
+
if (o && Array.isArray(o.frames)) {
|
|
224
|
+
for (var i = 0; i < o.frames.length; i++) {
|
|
225
|
+
var f = o.frames[i];
|
|
226
|
+
if (!f || f.isAlive !== false || typeof f.mass !== 'number') { continue; }
|
|
227
|
+
var room = capacity - cur;
|
|
228
|
+
if (room <= 0) { break; } // full: the rest pile/pass (a sink)
|
|
229
|
+
var landed = Math.min(f.mass, room);
|
|
230
|
+
if (landed <= 0) { continue; }
|
|
231
|
+
cur += landed;
|
|
232
|
+
absorbedTotal += landed;
|
|
233
|
+
_bump(absorbedByLayer, f.layer, landed);
|
|
234
|
+
_bump(storedByLayer, f.layer, landed);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (cur !== curBefore) { universe.setFrameProperty(backingQid, 'mass', cur); }
|
|
238
|
+
if (cur > referenceMass) { referenceMass = cur; }
|
|
239
|
+
|
|
240
|
+
// (2) at-cap gating: stop absorbing when full; resume once radiation drains it.
|
|
241
|
+
setActive(cur < capacity);
|
|
242
|
+
|
|
243
|
+
// (3) radiate with p = 1/capacity, AT a layer this wall actually soaked from, with
|
|
244
|
+
// the emitted leaf's mass set by EMISSIVITY as a fraction of that layer's own
|
|
245
|
+
// cell capacity (read from the engine — never a hand-derived 4^level). Never
|
|
246
|
+
// more than that layer still holds, never below HALF the absorbed peak.
|
|
247
|
+
if ((cur - BASE) > 0) {
|
|
248
|
+
var coin = (typeof universe.random === 'function') ? universe.random() : Math.random();
|
|
249
|
+
if (coin < (1 / capacity)) {
|
|
250
|
+
var L = pickEmissionLayer();
|
|
251
|
+
if (L !== null) {
|
|
252
|
+
var heldAtL = storedByLayer.get(L);
|
|
253
|
+
var radMass = Math.min(
|
|
254
|
+
Math.max(1, Math.round(emissivity * universe.levelCapacity(L))),
|
|
255
|
+
heldAtL
|
|
256
|
+
);
|
|
257
|
+
var floor = BASE + (referenceMass - BASE) / 2; // never below half the peak
|
|
258
|
+
if ((cur - radMass) >= floor) {
|
|
259
|
+
var half = Math.pow(2, L) / 2;
|
|
260
|
+
var rh = universe.makeLeafFrame(exit[0] + half + 1, exit[1] - half, L, null, 1, 0, true, 0, null);
|
|
261
|
+
universe.injectFrame(rh);
|
|
262
|
+
var rq = universe.getFrameQuantumId(rh);
|
|
263
|
+
universe.setFrameProperty(rq, 'mass', radMass);
|
|
264
|
+
universe.setFrameProperty(backingQid, 'mass', cur - radMass);
|
|
265
|
+
radiatedTotal += radMass;
|
|
266
|
+
_bump(radiatedByLayer, L, radMass);
|
|
267
|
+
_bump(storedByLayer, L, -radMass);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
tick.stats = function () {
|
|
275
|
+
var stored = universe.getFrameProperty(backingQid, 'mass') - BASE;
|
|
276
|
+
var seen = new Map();
|
|
277
|
+
absorbedByLayer.forEach(function (_v, k) { seen.set(k, true); });
|
|
278
|
+
radiatedByLayer.forEach(function (_v, k) { seen.set(k, true); });
|
|
279
|
+
var byLayer = _layersAscending(seen).map(function (L) {
|
|
280
|
+
return {
|
|
281
|
+
layer: L,
|
|
282
|
+
absorbed: absorbedByLayer.get(L) || 0,
|
|
283
|
+
radiated: radiatedByLayer.get(L) || 0,
|
|
284
|
+
stored: storedByLayer.get(L) || 0
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
return { stored: stored, absorbedTotal: absorbedTotal, radiatedTotal: radiatedTotal, byLayer: byLayer };
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
/** The wall's LIVE radiating efficiency (fullness fraction of the emitted leaf). */
|
|
291
|
+
tick.emissivity = function () { return emissivity; };
|
|
292
|
+
/** Live-set the radiating efficiency; takes effect at the next emission (no recompile). */
|
|
293
|
+
tick.setEmissivity = function (v) {
|
|
294
|
+
emissivity = _validEmissivity(v, 'setEmissivity');
|
|
295
|
+
return emissivity;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/** The wall MOVED (Wall.setRect): re-derive the right-face exit from the new rect, so a
|
|
299
|
+
* radiated leaf leaves the wall where it now stands. A caller-stated `cfg.exit` is the
|
|
300
|
+
* caller's own placement and is kept. */
|
|
301
|
+
tick.setRect = function (r) {
|
|
302
|
+
if (!cfg.exit) { exit = _rectRightExit(r); }
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
return tick;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export { DEFAULT_EMISSIVITY, deriveBackingLevel, attachAbsorberBackingFrame, buildAbsorbRadiationTickFn };
|
|
309
|
+
|