@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +87 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2501 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +85 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +100 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +259 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2474 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2922 -0
- package/dist/frqtl/objects/ObjectFactory.js +297 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +711 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +985 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +412 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +354 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +310 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +824 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +263 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +299 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +469 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +251 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +327 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +280 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1259 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +242 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +178 -0
- package/dist/frqtl/objects/v1-replacements.js +30 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +35 -0
- package/dist/frqtl/parts/CollisionTypes.js +31 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +490 -0
- package/dist/frqtl/parts/LargeObject.js +406 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +720 -0
- package/dist/frqtl/parts/OvenBox.js +612 -0
- package/dist/frqtl/parts/PointSource.js +763 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +244 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +212 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +692 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +326 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +672 -0
- package/dist/shim/notebook/knob.js +151 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +510 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +214 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +807 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +62 -4
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* setupCircuitBasic — Engine-agnostic basic circuit experiment factory.
|
|
5
|
+
* Battery -> wire -> probe A -> resistor -> probe B -> sink.
|
|
6
|
+
*
|
|
7
|
+
* Engine-agnostic: works with both the JS reference engine and the Rust/WASM engine.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { assertSetupOptions as _assertSetupOptions, requireWorldRect as _requireWorldRect } from './_setupOptions.js';
|
|
11
|
+
import { LargeObject } from '../../parts/LargeObject.js';
|
|
12
|
+
import { QuantumEmitter } from '../objects/QuantumEmitter.js';
|
|
13
|
+
import { CircuitProbe } from '../objects/CircuitProbe.js';
|
|
14
|
+
import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
15
|
+
import { CollisionActions } from '../../parts/CollisionActions.js';
|
|
16
|
+
|
|
17
|
+
// The ONE unknown-option-key validator (objects/setup/_setupOptions.js). Resolved
|
|
18
|
+
// LAZILY in the browser: the game layer defines window.assertSetupOptions before any
|
|
19
|
+
// page script runs, but reading it at eval time would bake in a load-order dependency.
|
|
20
|
+
|
|
21
|
+
// ── Imports ─────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a horizontal or vertical wire (two parallel walls).
|
|
27
|
+
* Returns array of LargeObject instances.
|
|
28
|
+
*/
|
|
29
|
+
function createWire(universe, x1, y1, x2, y2, width, wallThickness, color) {
|
|
30
|
+
color = color || [80, 80, 80];
|
|
31
|
+
var walls = [];
|
|
32
|
+
var dx = x2 - x1;
|
|
33
|
+
var dy = y2 - y1;
|
|
34
|
+
var isHorizontal = Math.abs(dx) >= Math.abs(dy);
|
|
35
|
+
|
|
36
|
+
if (isHorizontal) {
|
|
37
|
+
var left = Math.min(x1, x2);
|
|
38
|
+
var right = Math.max(x1, x2);
|
|
39
|
+
var centerY = (y1 + y2) / 2;
|
|
40
|
+
var segLen = right - left;
|
|
41
|
+
|
|
42
|
+
// Top wall
|
|
43
|
+
walls.push(new LargeObject(
|
|
44
|
+
universe,
|
|
45
|
+
[left, centerY - width / 2 - wallThickness, segLen, wallThickness],
|
|
46
|
+
CollisionTypes.WALL, [], 'TOP', 'bottom', null, 1, color
|
|
47
|
+
));
|
|
48
|
+
// Bottom wall
|
|
49
|
+
walls.push(new LargeObject(
|
|
50
|
+
universe,
|
|
51
|
+
[left, centerY + width / 2, segLen, wallThickness],
|
|
52
|
+
CollisionTypes.WALL, [], 'BOTTOM', 'top', null, 1, color
|
|
53
|
+
));
|
|
54
|
+
} else {
|
|
55
|
+
var top = Math.min(y1, y2);
|
|
56
|
+
var bottom = Math.max(y1, y2);
|
|
57
|
+
var centerX = (x1 + x2) / 2;
|
|
58
|
+
var segLen2 = bottom - top;
|
|
59
|
+
|
|
60
|
+
// Left wall
|
|
61
|
+
walls.push(new LargeObject(
|
|
62
|
+
universe,
|
|
63
|
+
[centerX - width / 2 - wallThickness, top, wallThickness, segLen2],
|
|
64
|
+
CollisionTypes.WALL, [], 'LEFT', 'right', null, 1, color
|
|
65
|
+
));
|
|
66
|
+
// Right wall
|
|
67
|
+
walls.push(new LargeObject(
|
|
68
|
+
universe,
|
|
69
|
+
[centerX + width / 2, top, wallThickness, segLen2],
|
|
70
|
+
CollisionTypes.WALL, [], 'RIGHT', 'left', null, 1, color
|
|
71
|
+
));
|
|
72
|
+
}
|
|
73
|
+
return walls;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create a battery (source emitter + sink absorber + seal walls).
|
|
78
|
+
* Returns { emitter, sink, sourceMarker, sealWalls, setAmperage, setVoltage, setEnabled, isEnabled }.
|
|
79
|
+
*/
|
|
80
|
+
function createBattery(universe, sourceX, sourceY, sinkX, sinkY, amperage, terminalSize, fundamentalDim, voltage, channelWidth, wallThickness) {
|
|
81
|
+
var sourceColor = [255, 60, 60];
|
|
82
|
+
var sinkColor = [60, 60, 255];
|
|
83
|
+
var wallColor = [80, 80, 80];
|
|
84
|
+
|
|
85
|
+
// Compute velocity direction
|
|
86
|
+
var dx = sinkX - sourceX;
|
|
87
|
+
var dy = sinkY - sourceY;
|
|
88
|
+
var dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
89
|
+
var vx = (dx / dist) * voltage;
|
|
90
|
+
var vy = (dy / dist) * voltage;
|
|
91
|
+
|
|
92
|
+
// Source emitter
|
|
93
|
+
var sourceRect = [
|
|
94
|
+
sourceX - terminalSize / 2, sourceY - terminalSize / 2,
|
|
95
|
+
terminalSize, terminalSize
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
var emitter = new QuantumEmitter(
|
|
99
|
+
universe,
|
|
100
|
+
[sourceRect],
|
|
101
|
+
vx, vy,
|
|
102
|
+
amperage,
|
|
103
|
+
null, // random charge
|
|
104
|
+
true, // auto_fire
|
|
105
|
+
false, // directional, not random
|
|
106
|
+
Infinity,
|
|
107
|
+
0
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Sink (absorber) — must span the FULL sealed channel cross-section.
|
|
111
|
+
// A terminal-sized (8px) absorber inside a 10px channel let edge quanta
|
|
112
|
+
// slip past it into the end-cap wall behind it and bounce back, filling
|
|
113
|
+
// the right of the wire and building back-pressure across the resistor.
|
|
114
|
+
// Sizing the absorber across the whole channel (oriented perpendicular to
|
|
115
|
+
// flow) guarantees every quantum that reaches the terminal is removed.
|
|
116
|
+
var sinkIsHorizontal = Math.abs(dx) >= Math.abs(dy);
|
|
117
|
+
var sinkCross = channelWidth > 0 ? channelWidth + 2 * wallThickness : terminalSize;
|
|
118
|
+
var sinkRect = sinkIsHorizontal
|
|
119
|
+
? [sinkX - terminalSize / 2, sinkY - sinkCross / 2, terminalSize, sinkCross]
|
|
120
|
+
: [sinkX - sinkCross / 2, sinkY - terminalSize / 2, sinkCross, terminalSize];
|
|
121
|
+
var sink = new LargeObject(
|
|
122
|
+
universe,
|
|
123
|
+
sinkRect,
|
|
124
|
+
CollisionTypes.NONE, [CollisionActions.ABSORB], 'battery_sink', 'closest', null, 1, sinkColor
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Source visual marker
|
|
128
|
+
var sourceMarker = new LargeObject(
|
|
129
|
+
universe,
|
|
130
|
+
[sourceX - terminalSize / 2, sourceY - terminalSize / 2, terminalSize, terminalSize],
|
|
131
|
+
CollisionTypes.NONE, [], 'battery_source_marker', 'closest', null, 1, sourceColor
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Seal walls
|
|
135
|
+
var sealWalls = [];
|
|
136
|
+
if (channelWidth > 0) {
|
|
137
|
+
var isHorizontal = Math.abs(dx) >= Math.abs(dy);
|
|
138
|
+
var cw = channelWidth;
|
|
139
|
+
var wt = wallThickness;
|
|
140
|
+
var ts = terminalSize;
|
|
141
|
+
|
|
142
|
+
if (isHorizontal) {
|
|
143
|
+
var flowRight = dx > 0;
|
|
144
|
+
|
|
145
|
+
// Source end-cap
|
|
146
|
+
var capX = flowRight ? sourceX - ts / 2 - wt : sourceX + ts / 2;
|
|
147
|
+
sealWalls.push(new LargeObject(
|
|
148
|
+
universe,
|
|
149
|
+
[capX, sourceY - cw / 2 - wt, wt, cw + 2 * wt],
|
|
150
|
+
CollisionTypes.WALL, [], flowRight ? 'LEFT' : 'RIGHT', flowRight ? 'right' : 'left', null, 1, wallColor
|
|
151
|
+
));
|
|
152
|
+
|
|
153
|
+
// Source top/bottom walls
|
|
154
|
+
var zoneLeft = sourceX - ts / 2;
|
|
155
|
+
sealWalls.push(new LargeObject(
|
|
156
|
+
universe, [zoneLeft, sourceY - cw / 2 - wt, ts, wt],
|
|
157
|
+
CollisionTypes.WALL, [], 'TOP', 'bottom', null, 1, wallColor
|
|
158
|
+
));
|
|
159
|
+
sealWalls.push(new LargeObject(
|
|
160
|
+
universe, [zoneLeft, sourceY + cw / 2, ts, wt],
|
|
161
|
+
CollisionTypes.WALL, [], 'BOTTOM', 'top', null, 1, wallColor
|
|
162
|
+
));
|
|
163
|
+
|
|
164
|
+
// Sink end-cap
|
|
165
|
+
var sinkCapX = flowRight ? sinkX + ts / 2 : sinkX - ts / 2 - wt;
|
|
166
|
+
sealWalls.push(new LargeObject(
|
|
167
|
+
universe,
|
|
168
|
+
[sinkCapX, sinkY - cw / 2 - wt, wt, cw + 2 * wt],
|
|
169
|
+
CollisionTypes.WALL, [], flowRight ? 'RIGHT' : 'LEFT', flowRight ? 'left' : 'right', null, 1, wallColor
|
|
170
|
+
));
|
|
171
|
+
|
|
172
|
+
// Sink top/bottom walls
|
|
173
|
+
var sinkZoneLeft = sinkX - ts / 2;
|
|
174
|
+
sealWalls.push(new LargeObject(
|
|
175
|
+
universe, [sinkZoneLeft, sinkY - cw / 2 - wt, ts, wt],
|
|
176
|
+
CollisionTypes.WALL, [], 'TOP', 'bottom', null, 1, wallColor
|
|
177
|
+
));
|
|
178
|
+
sealWalls.push(new LargeObject(
|
|
179
|
+
universe, [sinkZoneLeft, sinkY + cw / 2, ts, wt],
|
|
180
|
+
CollisionTypes.WALL, [], 'BOTTOM', 'top', null, 1, wallColor
|
|
181
|
+
));
|
|
182
|
+
} else {
|
|
183
|
+
var flowDown = dy > 0;
|
|
184
|
+
|
|
185
|
+
// Source end-cap
|
|
186
|
+
var capY = flowDown ? sourceY - ts / 2 - wt : sourceY + ts / 2;
|
|
187
|
+
sealWalls.push(new LargeObject(
|
|
188
|
+
universe,
|
|
189
|
+
[sourceX - cw / 2 - wt, capY, cw + 2 * wt, wt],
|
|
190
|
+
CollisionTypes.WALL, [], flowDown ? 'TOP' : 'BOTTOM', flowDown ? 'bottom' : 'top', null, 1, wallColor
|
|
191
|
+
));
|
|
192
|
+
|
|
193
|
+
// Source left/right walls
|
|
194
|
+
var zoneTop = sourceY - ts / 2;
|
|
195
|
+
sealWalls.push(new LargeObject(
|
|
196
|
+
universe, [sourceX - cw / 2 - wt, zoneTop, wt, ts],
|
|
197
|
+
CollisionTypes.WALL, [], 'LEFT', 'right', null, 1, wallColor
|
|
198
|
+
));
|
|
199
|
+
sealWalls.push(new LargeObject(
|
|
200
|
+
universe, [sourceX + cw / 2, zoneTop, wt, ts],
|
|
201
|
+
CollisionTypes.WALL, [], 'RIGHT', 'left', null, 1, wallColor
|
|
202
|
+
));
|
|
203
|
+
|
|
204
|
+
// Sink end-cap
|
|
205
|
+
var sinkCapY = flowDown ? sinkY + ts / 2 : sinkY - ts / 2 - wt;
|
|
206
|
+
sealWalls.push(new LargeObject(
|
|
207
|
+
universe,
|
|
208
|
+
[sourceX - cw / 2 - wt, sinkCapY, cw + 2 * wt, wt],
|
|
209
|
+
CollisionTypes.WALL, [], flowDown ? 'BOTTOM' : 'TOP', flowDown ? 'top' : 'bottom', null, 1, wallColor
|
|
210
|
+
));
|
|
211
|
+
|
|
212
|
+
// Sink left/right walls
|
|
213
|
+
var sinkZoneTop = sinkY - ts / 2;
|
|
214
|
+
sealWalls.push(new LargeObject(
|
|
215
|
+
universe, [sinkX - cw / 2 - wt, sinkZoneTop, wt, ts],
|
|
216
|
+
CollisionTypes.WALL, [], 'LEFT', 'right', null, 1, wallColor
|
|
217
|
+
));
|
|
218
|
+
sealWalls.push(new LargeObject(
|
|
219
|
+
universe, [sinkX + cw / 2, sinkZoneTop, wt, ts],
|
|
220
|
+
CollisionTypes.WALL, [], 'RIGHT', 'left', null, 1, wallColor
|
|
221
|
+
));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
var currentAmperage = amperage;
|
|
226
|
+
var currentVoltage = voltage;
|
|
227
|
+
var enabled = true;
|
|
228
|
+
|
|
229
|
+
var batteryObj = {
|
|
230
|
+
emitter: emitter,
|
|
231
|
+
sink: sink,
|
|
232
|
+
sourceMarker: sourceMarker,
|
|
233
|
+
sealWalls: sealWalls,
|
|
234
|
+
enabled: enabled,
|
|
235
|
+
setAmperage: function (amp) {
|
|
236
|
+
currentAmperage = amp;
|
|
237
|
+
emitter.setBurstSize(amp);
|
|
238
|
+
},
|
|
239
|
+
setVoltage: function (v) {
|
|
240
|
+
currentVoltage = v;
|
|
241
|
+
var ndx = sinkX - sourceX;
|
|
242
|
+
var ndy = sinkY - sourceY;
|
|
243
|
+
var ndist = Math.sqrt(ndx * ndx + ndy * ndy) || 1;
|
|
244
|
+
emitter.setVelocity((ndx / ndist) * v, (ndy / ndist) * v);
|
|
245
|
+
},
|
|
246
|
+
setEnabled: function (en) {
|
|
247
|
+
enabled = en;
|
|
248
|
+
batteryObj.enabled = en;
|
|
249
|
+
emitter.setActive(en);
|
|
250
|
+
},
|
|
251
|
+
isEnabled: function () { return enabled; }
|
|
252
|
+
};
|
|
253
|
+
return batteryObj;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Create a resistor (constriction walls).
|
|
258
|
+
*/
|
|
259
|
+
function createResistor(universe, x, y, length, channelWidth, outerWidth, horizontal, wallThickness, color) {
|
|
260
|
+
color = color || [140, 100, 50];
|
|
261
|
+
var walls = [];
|
|
262
|
+
if (horizontal) {
|
|
263
|
+
var left = x - length / 2;
|
|
264
|
+
var topWallH = (outerWidth - channelWidth) / 2;
|
|
265
|
+
walls.push(new LargeObject(
|
|
266
|
+
universe,
|
|
267
|
+
[left, y - outerWidth / 2, length, topWallH],
|
|
268
|
+
CollisionTypes.WALL, [], 'TOP', 'bottom', null, 1, color
|
|
269
|
+
));
|
|
270
|
+
walls.push(new LargeObject(
|
|
271
|
+
universe,
|
|
272
|
+
[left, y + channelWidth / 2, length, topWallH],
|
|
273
|
+
CollisionTypes.WALL, [], 'BOTTOM', 'top', null, 1, color
|
|
274
|
+
));
|
|
275
|
+
} else {
|
|
276
|
+
var top2 = y - length / 2;
|
|
277
|
+
var leftWallW = (outerWidth - channelWidth) / 2;
|
|
278
|
+
walls.push(new LargeObject(
|
|
279
|
+
universe,
|
|
280
|
+
[x - outerWidth / 2, top2, leftWallW, length],
|
|
281
|
+
CollisionTypes.WALL, [], 'LEFT', 'right', null, 1, color
|
|
282
|
+
));
|
|
283
|
+
walls.push(new LargeObject(
|
|
284
|
+
universe,
|
|
285
|
+
[x + channelWidth / 2, top2, leftWallW, length],
|
|
286
|
+
CollisionTypes.WALL, [], 'RIGHT', 'left', null, 1, color
|
|
287
|
+
));
|
|
288
|
+
}
|
|
289
|
+
return walls;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ── Setup Factory ───────────────────────────────────────────────────
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Create a basic circuit experiment: battery -> wire -> probeA -> resistor -> probeB -> sink.
|
|
296
|
+
*
|
|
297
|
+
* @param {Object} universe - The engine universe instance (JS or WASM), not yet init'd
|
|
298
|
+
* @param {Object} [options] - Configuration overrides
|
|
299
|
+
* @param {number} [options.wireWidth=10] - Wire channel width
|
|
300
|
+
* @param {number} [options.amperage=1] - Emission rate (particles per tick)
|
|
301
|
+
* @param {number} [options.voltage=10] - Emission velocity
|
|
302
|
+
* @param {number} [options.resistorWidth=4] - Resistor channel width
|
|
303
|
+
* @returns {Object} { battery, probes, largeObjects }
|
|
304
|
+
*/
|
|
305
|
+
/**
|
|
306
|
+
* OPTIONS — this factory's own option vocabulary: EXACTLY the keys it reads.
|
|
307
|
+
* Exported as `setupCircuitBasic.OPTIONS` so a composing caller consumes it instead of
|
|
308
|
+
* retyping it. `setup-factory-option-vocabulary.test.mjs` re-derives the
|
|
309
|
+
* read-set by static trace and fails if this list drifts from the code.
|
|
310
|
+
*/
|
|
311
|
+
var OPTIONS = [
|
|
312
|
+
'amperage', 'emissionRate', 'resistorWidth', 'voltage',
|
|
313
|
+
'wireWidth',
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
function setupCircuitBasic(universe, options) {
|
|
317
|
+
options = _assertSetupOptions('setupCircuitBasic', options, OPTIONS);
|
|
318
|
+
|
|
319
|
+
// The world rect the co-init call wrote (EGPTUniverse.create({rect})) — the ONE size.
|
|
320
|
+
var canvas_rect = universe.universe_rect;
|
|
321
|
+
_requireWorldRect('setupCircuitBasic universe.universe_rect', canvas_rect);
|
|
322
|
+
|
|
323
|
+
var wireWidth = options.wireWidth || 10;
|
|
324
|
+
// 1 amp now means 10 quanta/tick (was 1). More emitted current → real
|
|
325
|
+
// crowding/back-pressure at the resistor gap instead of a thin trickle.
|
|
326
|
+
var QUANTA_PER_AMP = 10;
|
|
327
|
+
var amperage = (options.amperage || options.emissionRate || 1) * QUANTA_PER_AMP;
|
|
328
|
+
var voltage = options.voltage || 10;
|
|
329
|
+
var resistorWidth = options.resistorWidth || 4;
|
|
330
|
+
var fundamentalDim = 0;
|
|
331
|
+
var wallThickness = 3;
|
|
332
|
+
|
|
333
|
+
// Init universe: ground-up mode with brownian motion
|
|
334
|
+
universe.init(fundamentalDim, { wavelengthScale: 1 });
|
|
335
|
+
universe.brownianMotion = true;
|
|
336
|
+
universe.withInterQuantumCollisions = true;
|
|
337
|
+
|
|
338
|
+
// Layout constants (matching WASM adapter exactly)
|
|
339
|
+
var margin = 40;
|
|
340
|
+
var centerY = Math.round(canvas_rect.h / 2);
|
|
341
|
+
var wireLeft = margin;
|
|
342
|
+
var wireRight = canvas_rect.w - margin;
|
|
343
|
+
var wireLen = wireRight - wireLeft;
|
|
344
|
+
|
|
345
|
+
var batSourceX = wireLeft + 10;
|
|
346
|
+
var probeAX = wireLeft + Math.round(wireLen * 0.3);
|
|
347
|
+
var resistorX = wireLeft + Math.round(wireLen * 0.5);
|
|
348
|
+
var probeBX = wireLeft + Math.round(wireLen * 0.7);
|
|
349
|
+
var batSinkX = wireRight - 10;
|
|
350
|
+
|
|
351
|
+
// Main wire tube
|
|
352
|
+
var wireWalls = createWire(universe, wireLeft, centerY, wireRight, centerY, wireWidth, wallThickness);
|
|
353
|
+
|
|
354
|
+
// Battery
|
|
355
|
+
var battery = createBattery(
|
|
356
|
+
universe,
|
|
357
|
+
batSourceX, centerY,
|
|
358
|
+
batSinkX, centerY,
|
|
359
|
+
amperage,
|
|
360
|
+
wireWidth - 2,
|
|
361
|
+
fundamentalDim,
|
|
362
|
+
voltage,
|
|
363
|
+
wireWidth,
|
|
364
|
+
wallThickness
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
// Probes
|
|
368
|
+
var probeA = new CircuitProbe(universe, probeAX, centerY, wireWidth, 'before_resistor');
|
|
369
|
+
var probeB = new CircuitProbe(universe, probeBX, centerY, wireWidth, 'after_resistor');
|
|
370
|
+
|
|
371
|
+
// Resistor
|
|
372
|
+
var resistorWalls = createResistor(
|
|
373
|
+
universe, resistorX, centerY, 20, resistorWidth, wireWidth, true, wallThickness
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// Right-end drain (absorber). Quanta that have crossed the resistor are
|
|
377
|
+
// removed at the RIGHT END of the wire (full channel cross-section,
|
|
378
|
+
// extending to the true wire end past the terminal) rather than bouncing
|
|
379
|
+
// off the terminator and back-filling the wire. Registered for collisions
|
|
380
|
+
// but intentionally NOT rendered (kept invisible).
|
|
381
|
+
var drainLeft = wireLeft + Math.round(wireLen * 0.88);
|
|
382
|
+
var drainRight = wireRight;
|
|
383
|
+
var drainW = Math.max(1, drainRight - drainLeft);
|
|
384
|
+
var drain = new LargeObject(
|
|
385
|
+
universe,
|
|
386
|
+
[drainLeft, centerY - (wireWidth / 2 + wallThickness), drainW, wireWidth + 2 * wallThickness],
|
|
387
|
+
CollisionTypes.NONE, [CollisionActions.ABSORB], 'RIGHT_drain', 'closest', null, 1, [40, 40, 70]
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
// Evaluation tick function: sample probes each tick
|
|
391
|
+
universe.experimentTickFunctions.push(function () {
|
|
392
|
+
probeA.queryDensity();
|
|
393
|
+
probeB.queryDensity();
|
|
394
|
+
return [];
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
var gateThreshold = 3;
|
|
398
|
+
|
|
399
|
+
// Collect all large objects for rendering
|
|
400
|
+
var largeObjects = [];
|
|
401
|
+
largeObjects.push.apply(largeObjects, wireWalls);
|
|
402
|
+
largeObjects.push(battery.sink);
|
|
403
|
+
largeObjects.push(battery.sourceMarker);
|
|
404
|
+
largeObjects.push.apply(largeObjects, battery.sealWalls);
|
|
405
|
+
largeObjects.push.apply(largeObjects, resistorWalls);
|
|
406
|
+
largeObjects.push(probeA.marker);
|
|
407
|
+
largeObjects.push(probeB.marker);
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
battery: battery,
|
|
411
|
+
drain: drain,
|
|
412
|
+
probes: { before_resistor: probeA, after_resistor: probeB },
|
|
413
|
+
wireWalls: wireWalls,
|
|
414
|
+
resistorWalls: resistorWalls,
|
|
415
|
+
setBatteryAmperage: function (amp) { battery.setAmperage(amp); },
|
|
416
|
+
setBatteryVoltage: function (v) { battery.setVoltage(v); },
|
|
417
|
+
getCircuitState: function () {
|
|
418
|
+
var state = {};
|
|
419
|
+
var probeMap = { before_resistor: probeA, after_resistor: probeB };
|
|
420
|
+
for (var name in probeMap) {
|
|
421
|
+
if (probeMap.hasOwnProperty(name)) {
|
|
422
|
+
var probe = probeMap[name];
|
|
423
|
+
state[name] = {
|
|
424
|
+
flowRate: probe.getFlowRate(),
|
|
425
|
+
lastCount: probe.getLastCount(),
|
|
426
|
+
isHigh: probe.isHigh(gateThreshold)
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return state;
|
|
431
|
+
},
|
|
432
|
+
largeObjects: largeObjects
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ── Statics (reusable helpers) ──────────────────────────────────────
|
|
437
|
+
|
|
438
|
+
setupCircuitBasic.createWire = createWire;
|
|
439
|
+
setupCircuitBasic.createBattery = createBattery;
|
|
440
|
+
setupCircuitBasic.createResistor = createResistor;
|
|
441
|
+
|
|
442
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
443
|
+
|
|
444
|
+
setupCircuitBasic.OPTIONS = OPTIONS;
|
|
445
|
+
|
|
446
|
+
export { setupCircuitBasic, createWire, createBattery, createResistor };
|
|
447
|
+
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* setupCollisionMatrix — density-sweep collision experiment.
|
|
5
|
+
*
|
|
6
|
+
* ── Layer 1: API contract ────────────────────────────────────────────────────
|
|
7
|
+
* @param {Object} universe - WASM universe (wasmOnly experiment); init()'d here.
|
|
8
|
+
* @param {Object} [opts]
|
|
9
|
+
* @param {number} [opts.level=6] - Dimension of every body (2^level px voxel; 6 = 64px).
|
|
10
|
+
* @param {number} [opts.offset=0] - Transverse launch offset (px): A below / B above the lane.
|
|
11
|
+
* @param {number} [opts.angle=0] - Converging vy magnitude (the approach angle).
|
|
12
|
+
*
|
|
13
|
+
* @returns {{
|
|
14
|
+
* level: number, voxel: number, capacity: number, laneYs: number[], ax0: number, bx0: number,
|
|
15
|
+
* lanes: Array<{ name:string, label:string, aQid:number, bQid:number,
|
|
16
|
+
* mass:number, ax0:number, bx0:number, laneY:number }>,
|
|
17
|
+
* matrixControl: { getOffset, setOffset, getAngle, setAngle, reset }
|
|
18
|
+
* }}
|
|
19
|
+
*
|
|
20
|
+
* ── Layer 2: physics behavior (what this PROVES) ─────────────────────────────
|
|
21
|
+
* SEVEN horizontal lanes, each a SYMMETRIC pair of Dim-6 boxes (equal mass) launched head-on
|
|
22
|
+
* — A from the left (+x), B from the right (−x) — at speed_prob=1. Mass is swept on a LOG
|
|
23
|
+
* (powers-of-2) scale: 1, 8, 64, 512, 1024, 2048, 4096(=capacity), so density ρ = mass/capacity
|
|
24
|
+
* runs ≈0.0002 … 1.0 DOWN the lanes: SPARSE at the TOP, FULL at the BOTTOM. Bodies are
|
|
25
|
+
* COLORLESS; the ROYGBIV render scheme colours each by its fullness.
|
|
26
|
+
*
|
|
27
|
+
* withInterQuantumCollisions=true runs voxel_collision_pass — the TWO-BUCKET momentum exchange
|
|
28
|
+
* (docs/internal/physics-lab/sam_notes/On_Collisions.md §Two-Bucket): colliding boxes EXCHANGE momentum and
|
|
29
|
+
* CONSERVE it; the per-tick exchange ceiling carries occupancy (ρ_A·ρ_B), so the bounce TIMING
|
|
30
|
+
* emerges from density — rigid (full) lanes bounce fast and clean, soft (sparse) lanes bounce
|
|
31
|
+
* slowly. The sweep reads this spectrum at a glance. Cross-lane coupling is impossible: lanes are
|
|
32
|
+
* y-separated ≫ the voxel, so no two lanes share a collision-proximity bucket.
|
|
33
|
+
*
|
|
34
|
+
* activeAxes {x,y}: x carries the head-on approach; the live OFFSET/ANGLE controls (no reload) add
|
|
35
|
+
* a transverse launch offset + a converging vy so collisions go OFF-AXIS — exercising the engine's
|
|
36
|
+
* SEPARATE per-axis momentum buckets (inc_x vs inc_y). density-sweep × offset/angle = the "offset
|
|
37
|
+
* angle combinations". At offset=0, angle=0 it is clean head-on.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { assertSetupOptions as _assertSetupOptions, requireWorldRect as _requireWorldRect } from './_setupOptions.js';
|
|
41
|
+
import { collisionMatrixLanes as _lanes } from './_collisionMatrixLanes.js';
|
|
42
|
+
|
|
43
|
+
// The ONE unknown-option-key validator (objects/setup/_setupOptions.js). Resolved
|
|
44
|
+
// LAZILY in the browser: the game layer defines window.assertSetupOptions before any
|
|
45
|
+
// page script runs, but reading it at eval time would bake in a load-order dependency.
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Lane descriptor: each lane is a SYMMETRIC pair of bodies (both the same mass) at a fixed y.
|
|
49
|
+
* DENSITY is swept on a strict LOGARITHMIC (powers-of-2) scale from ρ≈0 (one Dim-0 child →
|
|
50
|
+
* near-total pass-through) to ρ=1 (full box → hard abut) — non-linear probabilistic
|
|
51
|
+
* effects (collision prob ~ density² with density=mass/capacity) read cleanly on a log scale
|
|
52
|
+
* (CEO 2026-06-29). speed_prob=1 on every body so ALL lanes approach at the same 1px/tick,
|
|
53
|
+
* isolating density→collision from the fullness-gate movement-speed effect (that is G1's job).
|
|
54
|
+
* For a 2D Dim-6 box capacity = 64×64 = 4096 = 2^12, so the ladder is 2^0…2^12. The table is
|
|
55
|
+
* `_collisionMatrixLanes.js` — shared with the 3D matrix, whose capacity is 64³ and whose
|
|
56
|
+
* masses therefore differ while the DENSITIES are identical.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* OPTIONS — this factory's own option vocabulary: EXACTLY the keys it reads.
|
|
60
|
+
* Exported as `setupCollisionMatrix.OPTIONS` so a composing caller consumes it instead of
|
|
61
|
+
* retyping it. `setup-factory-option-vocabulary.test.mjs` re-derives the
|
|
62
|
+
* read-set by static trace and fails if this list drifts from the code.
|
|
63
|
+
*/
|
|
64
|
+
var OPTIONS = [
|
|
65
|
+
'angle', 'level',
|
|
66
|
+
'offset',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
function setupCollisionMatrix(universe, opts) {
|
|
70
|
+
opts = _assertSetupOptions('setupCollisionMatrix', opts, OPTIONS);
|
|
71
|
+
var level = (opts.level !== undefined) ? opts.level : 6;
|
|
72
|
+
|
|
73
|
+
// The world rect the co-init call wrote (EGPTUniverse.create({rect})) — the ONE size.
|
|
74
|
+
var rect = universe.universe_rect;
|
|
75
|
+
var world = _requireWorldRect('setupCollisionMatrix universe.universe_rect', rect);
|
|
76
|
+
var cw = world.w, ch = world.h;
|
|
77
|
+
|
|
78
|
+
// activeAxes: BOTH x and y. x carries the head-on approach; y carries the OFFSET/ANGLE
|
|
79
|
+
// switch (transverse offset + converging vy) that exercises the engine's SEPARATE per-axis
|
|
80
|
+
// momentum buckets (inc_x vs inc_y) and the per-axis contact-cross-section ceiling. At
|
|
81
|
+
// offset=0, angle=0 the motion is pure-x head-on (vy=0); the transverse yo-yo adds only a
|
|
82
|
+
// small ±1px wiggle that does not cross the ~100px lane spacing over a collision.
|
|
83
|
+
universe.init(0, {
|
|
84
|
+
wavelengthScale: 1,
|
|
85
|
+
brownianMotion: false,
|
|
86
|
+
activeAxes: { x: true, y: true, z: false }
|
|
87
|
+
});
|
|
88
|
+
// Enable the voxel_collision_pass so same-dimension boxes exchange momentum.
|
|
89
|
+
universe.withInterQuantumCollisions = true;
|
|
90
|
+
|
|
91
|
+
universe.addDimension(level);
|
|
92
|
+
var voxel = Math.pow(2, level); // 64 for level=6
|
|
93
|
+
var half = voxel / 2; // makeLeafFrame takes TOP-LEFT; we want CENTRES.
|
|
94
|
+
|
|
95
|
+
// Canonical launch CENTRES (same for every lane). A left of centre, B right, 3 voxels apart.
|
|
96
|
+
var midX = Math.floor(cw * 0.5);
|
|
97
|
+
var ax0 = midX - Math.floor(1.5 * voxel); // ~254 centre for 700-wide canvas
|
|
98
|
+
var bx0 = midX + Math.floor(1.5 * voxel); // ~446 centre
|
|
99
|
+
|
|
100
|
+
// Lane y-CENTRES: N lanes evenly distributed (centres at ch*(2i+1)/(2N)). These are CENTRES
|
|
101
|
+
// — the boxes are placed top-left = centre − half, so a 64px box centred at the bottom lane
|
|
102
|
+
// (y≈650) spans 618..682 and stays IN BOUNDS (H=700). Passing the centre AS the top-left was
|
|
103
|
+
// the boundary-kill bug: the bottom lane ran off the edge and the boundary check zeroed it.
|
|
104
|
+
var N = _lanes.LANE_DENSITIES.length;
|
|
105
|
+
var laneYs = _lanes.LANE_DENSITIES.map(function (_, i) { return Math.round(ch * (2 * i + 1) / (2 * N)); });
|
|
106
|
+
|
|
107
|
+
// OFFSET / ANGLE state (the switch). offsetPx = transverse separation of the pair at launch
|
|
108
|
+
// (A below centre, B above → glancing); angleVy = converging y-velocity (A up, B down →
|
|
109
|
+
// both x AND y momentum at impact). Both default 0 = clean head-on.
|
|
110
|
+
var offsetPx = (opts.offset !== undefined) ? opts.offset : 0;
|
|
111
|
+
var angleVy = (opts.angle !== undefined) ? opts.angle : 0;
|
|
112
|
+
|
|
113
|
+
var capacity = null;
|
|
114
|
+
|
|
115
|
+
// Seat one body's position + velocity for the current offset/angle (CENTRE coords).
|
|
116
|
+
function seat(qid, cx, cy, dirX, mass) {
|
|
117
|
+
universe.setFrameProperty(qid, 'x', cx);
|
|
118
|
+
universe.setFrameProperty(qid, 'y', cy + dirX * offsetPx); // A(dirX=+1) below, B above
|
|
119
|
+
universe.setFrameProperty(qid, 'vx', dirX);
|
|
120
|
+
universe.setFrameProperty(qid, 'vy', -dirX * angleVy); // converge toward the lane
|
|
121
|
+
universe.setFrameProperty(qid, 'mass', mass);
|
|
122
|
+
universe.setFrameProperty(qid, 'speed_prob', 1.0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Build every body FIRST — the capacity that sets the lane ladder is whatever the
|
|
126
|
+
// ENGINE reports for the body it actually built, so the masses cannot be known
|
|
127
|
+
// until a body exists.
|
|
128
|
+
var built = [];
|
|
129
|
+
for (var i = 0; i < N; i++) {
|
|
130
|
+
var laneY = laneYs[i];
|
|
131
|
+
// Place COLORLESS boxes (ROYGBIV scheme drives colour from fullness) at top-left = centre−half.
|
|
132
|
+
var aHandle = universe.makeLeafFrame(ax0 - half, laneY - half, level, null, 1, 0, true, 0, null);
|
|
133
|
+
universe.injectFrame(aHandle);
|
|
134
|
+
var aQid = universe.getFrameQuantumId(aHandle);
|
|
135
|
+
var bHandle = universe.makeLeafFrame(bx0 - half, laneY - half, level, null, -1, 0, true, 0, null);
|
|
136
|
+
universe.injectFrame(bHandle);
|
|
137
|
+
var bQid = universe.getFrameQuantumId(bHandle);
|
|
138
|
+
|
|
139
|
+
if (capacity === null) { capacity = universe.getFrameProperty(aQid, 'mass'); }
|
|
140
|
+
built.push({ aQid: aQid, bQid: bQid });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
var laneMasses = _lanes.laneMassesForCapacity(capacity);
|
|
144
|
+
|
|
145
|
+
var lanes = built.map(function (b, i) {
|
|
146
|
+
var laneY = laneYs[i];
|
|
147
|
+
var m = laneMasses[i];
|
|
148
|
+
// SYMMETRIC lane; seat applies mass + the current offset/angle.
|
|
149
|
+
seat(b.aQid, ax0, laneY, 1, m);
|
|
150
|
+
seat(b.bQid, bx0, laneY, -1, m);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
name: 'm=' + m,
|
|
154
|
+
label: 'mass ' + m + ' / ' + capacity + ' (density ' + (m / capacity).toPrecision(2) + ')',
|
|
155
|
+
aQid: b.aQid, bQid: b.bQid, mass: m, ax0: ax0, bx0: bx0, laneY: laneY
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
function reseatAll() {
|
|
160
|
+
lanes.forEach(function (lane) {
|
|
161
|
+
seat(lane.aQid, lane.ax0, lane.laneY, 1, lane.mass);
|
|
162
|
+
seat(lane.bQid, lane.bx0, lane.laneY, -1, lane.mass);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Live control handle (Blackbody pattern — no reload) ──────────────────
|
|
167
|
+
// setOffset/setAngle change the transverse offset and converging angle and RE-SEAT every
|
|
168
|
+
// lane so the new geometry takes effect immediately; reset just re-seats at the current
|
|
169
|
+
// offset/angle. The density sweep (per lane) × the offset/angle slider = the "offset angle
|
|
170
|
+
// combinations" the matrix is meant to explore.
|
|
171
|
+
var matrixControl = {
|
|
172
|
+
getOffset: function () { return offsetPx; },
|
|
173
|
+
setOffset: function (v) { offsetPx = v; reseatAll(); },
|
|
174
|
+
getAngle: function () { return angleVy; },
|
|
175
|
+
setAngle: function (v) { angleVy = v; reseatAll(); },
|
|
176
|
+
reset: function () { reseatAll(); }
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
level: level, voxel: voxel, capacity: capacity, laneYs: laneYs,
|
|
181
|
+
ax0: ax0, bx0: bx0, lanes: lanes, matrixControl: matrixControl
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
setupCollisionMatrix.OPTIONS = OPTIONS;
|
|
186
|
+
|
|
187
|
+
export { setupCollisionMatrix };
|
|
188
|
+
|