@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,33 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* parts/index.js — THE parts barrel (@descix/frqtl-web/parts): one export per file in this
|
|
5
|
+
* directory, named by the file. The directory IS the roster — a part joins by being a file here,
|
|
6
|
+
* and the barrel lists exactly the directory (lib/frqtl/parts/test/barrel-equals-directory.test.mjs).
|
|
7
|
+
*
|
|
8
|
+
* Importing it registers every published kind in this realm (each part module registers its own
|
|
9
|
+
* kind with objects/kinds.js when it evaluates).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { CameraControls } from './CameraControls.js';
|
|
13
|
+
export { CameraPath } from './CameraPath.js';
|
|
14
|
+
export { CollisionActions } from './CollisionActions.js';
|
|
15
|
+
export { CollisionTypes } from './CollisionTypes.js';
|
|
16
|
+
export { DetectorWall } from './DetectorWall.js';
|
|
17
|
+
export { FrqtlControlOverlay } from './FrqtlControlOverlay.js';
|
|
18
|
+
export { FrqtlControlsPanel } from './FrqtlControlsPanel.js';
|
|
19
|
+
export { FrqtlVision } from './FrqtlVision.js';
|
|
20
|
+
export { GraphingDetectorWall } from './GraphingDetectorWall.js';
|
|
21
|
+
export { LargeObject } from './LargeObject.js';
|
|
22
|
+
export { LightColor } from './LightColor.js';
|
|
23
|
+
export { LiveChart } from './LiveChart.js';
|
|
24
|
+
export { LogicGate } from './LogicGate.js';
|
|
25
|
+
export { OvenBox } from './OvenBox.js';
|
|
26
|
+
export { PointSource } from './PointSource.js';
|
|
27
|
+
export { Rectangle } from './Rectangle.js';
|
|
28
|
+
export { SlitLightSource } from './SlitLightSource.js';
|
|
29
|
+
export { SlitScreen } from './SlitScreen.js';
|
|
30
|
+
export { SphereEmitter } from './SphereEmitter.js';
|
|
31
|
+
export { Trace } from './Trace.js';
|
|
32
|
+
export { Tunnel } from './Tunnel.js';
|
|
33
|
+
export { Wall } from './Wall.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — 1B · Absorbing Wall (3D)
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung cl1b-absorbing-wall-3d).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
14
|
+
|
|
15
|
+
export default function buildExperiment(exp) {
|
|
16
|
+
// Collision Lab 1B · 3D — a volumetric (spherical) source into a 3D ABSORB wall
|
|
17
|
+
// slab (z-slab centered on 0 so the 3D spray collides); orbit the camera to view.
|
|
18
|
+
var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
19
|
+
var cy = Math.round(H / 2);
|
|
20
|
+
var SRC = {
|
|
21
|
+
preset: 'spherical', is2D: false, center: [Math.round(W * 0.15), cy],
|
|
22
|
+
chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
|
|
23
|
+
burstSize: 24, emitEveryTicks: 1
|
|
24
|
+
};
|
|
25
|
+
exp.createScene(W, H, 600, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 3D scene + universe, 600x400x600 — first, then parts
|
|
26
|
+
exp.addObject('SphereEmitter', SRC);
|
|
27
|
+
exp.addObject('Wall', {
|
|
28
|
+
rect: [Math.round(W * 0.6), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
|
|
29
|
+
z: -80, depth: 160,
|
|
30
|
+
collisionBehavior: 'absorb', capacity: 64,
|
|
31
|
+
name: 'absorber', color: [150, 90, 210]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — 1B · Absorbing Wall
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung cl1b-absorbing-wall).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
14
|
+
|
|
15
|
+
export default function buildExperiment(exp) {
|
|
16
|
+
// Collision Lab 1B (Absorbing Wall) — ABSORB v2 (Track A): the wall LO is backed by
|
|
17
|
+
// a REAL leaf frame; a host tick-fn tallies the killed masses into it + radiates.
|
|
18
|
+
var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
19
|
+
var cy = Math.round(H / 2);
|
|
20
|
+
var SRC = {
|
|
21
|
+
preset: 'spherical', is2D: true, center: [Math.round(W * 0.15), cy],
|
|
22
|
+
chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
|
|
23
|
+
burstSize: 24, emitEveryTicks: 1
|
|
24
|
+
};
|
|
25
|
+
exp.createCanvas(W, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
26
|
+
exp.addObject('SphereEmitter', SRC);
|
|
27
|
+
exp.addObject('Wall', {
|
|
28
|
+
rect: [Math.round(W * 0.6), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
|
|
29
|
+
collisionBehavior: 'absorb', capacity: 64,
|
|
30
|
+
name: 'absorber', color: [150, 90, 210]
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — Atomic model
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one `export default function buildExperiment(exp)` module. The
|
|
7
|
+
* world and the three-level frame hierarchy are built here from the engine's own primitives (leaf
|
|
8
|
+
* frames — `engine().Frame`, read when the scene builds — and parent frames from a rect). Registered by
|
|
9
|
+
* lib/shim/registry/walkthrough.js (rung atomic-model).
|
|
10
|
+
*
|
|
11
|
+
* Physics (zero new — bench H1, A1, A2, M1, M4): 48 one-pixel quarks fill an 8x8 proton frame; the
|
|
12
|
+
* proton and one electron sit in a 64x64 atom frame. There is no force equation. Each parent's
|
|
13
|
+
* compress() pulls its children one pixel toward its centre each tick (A1: a stationary parent
|
|
14
|
+
* CONFINES a child), a parent's motion is carried by its children (A2), a parent's mass is its
|
|
15
|
+
* child count (H1), and its wavelength is capacity/mass (M4) — 64/48 → the proton's oscillation.
|
|
16
|
+
* The atom is stationary: the lab frame. `bonding` (noEscape) traps each quark in its parent's
|
|
17
|
+
* live rect, so the nucleus holds together: binding emerges from compress, not from a law.
|
|
18
|
+
*
|
|
19
|
+
* Order (it seeds the run): world → the three layers → quarks (grid-filled, registered) → proton
|
|
20
|
+
* from its rect → electron (registered) → atom from its rect → electron into the atom.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { Rectangle } from '@descix/frqtl-web/parts';
|
|
24
|
+
import * as OrbitBag from '@descix/frqtl-web/compat/renderer/orbit-bag.js';
|
|
25
|
+
import { engine } from '@descix/frqtl-web/compat/engine.js';
|
|
26
|
+
|
|
27
|
+
export default function buildExperiment(exp) {
|
|
28
|
+
var W = 600, H = 600; // THE WORLD this scene states (its co-init call below)
|
|
29
|
+
|
|
30
|
+
var QUARK_LAYER = 0; // 1x1 leaves
|
|
31
|
+
var SUBATOMIC_LAYER = 3; // the proton: 8x8, capacity 64
|
|
32
|
+
var ATOM_LAYER = 6; // the atom: 64x64, capacity 4096
|
|
33
|
+
var QUARKS_PER_PROTON = 48; // the proton's mass at t=0 → wavelength 64/48
|
|
34
|
+
|
|
35
|
+
// ── WORLD — a 2D canvas + universe: quark layer fundamental, λ multiplier 1, quarks confined ──
|
|
36
|
+
exp.createCanvas(W, H, { dimension: QUARK_LAYER, wavelengthScale: 1, bonding: true });
|
|
37
|
+
var u = exp.universe;
|
|
38
|
+
|
|
39
|
+
var quarkDim = u.addDimension(QUARK_LAYER);
|
|
40
|
+
var subatomicDim = u.addDimension(SUBATOMIC_LAYER);
|
|
41
|
+
var atomDim = u.addDimension(ATOM_LAYER);
|
|
42
|
+
var subatomicW = subatomicDim.mb_w, atomW = atomDim.mb_w;
|
|
43
|
+
|
|
44
|
+
var Frame = engine().Frame, Charge = engine().Charge; // the engine's own frame primitive + charge ABI
|
|
45
|
+
var cx = Math.round(W / 2), cy = Math.round(H / 2);
|
|
46
|
+
var atomRect = new Rectangle(cx, cy, atomW, atomW, null, true);
|
|
47
|
+
var protonRect = new Rectangle(cx, cy, subatomicW, subatomicW, null, true);
|
|
48
|
+
var rect = exp.runner.canvas_rect;
|
|
49
|
+
|
|
50
|
+
// ── QUARKS — fill the proton rect column by column ─────────────────────
|
|
51
|
+
var quarkW = Math.max(1, Math.pow(2, QUARK_LAYER));
|
|
52
|
+
var quarks = [];
|
|
53
|
+
for (var x = protonRect.left; x < protonRect.left + protonRect.w && quarks.length < QUARKS_PER_PROTON; x += quarkW) {
|
|
54
|
+
for (var y = protonRect.top; y < protonRect.top + protonRect.h && quarks.length < QUARKS_PER_PROTON; y += quarkW) {
|
|
55
|
+
var q = Frame.makeLeafFrame(x, y, QUARK_LAYER, 1, 0, 0, false, Charge.POSITIVE, rect);
|
|
56
|
+
q.name = 'quark_' + quarks.length;
|
|
57
|
+
quarks.push(q);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (var i = 0; i < quarks.length; i++) { quarkDim.addFrame(quarks[i]); }
|
|
61
|
+
|
|
62
|
+
// ── PROTON — the parent frame of the quarks ────────────────────────────
|
|
63
|
+
var proton = subatomicDim.createFrameFromRect(protonRect, quarks, 0, 0);
|
|
64
|
+
proton.name = 'proton';
|
|
65
|
+
|
|
66
|
+
// ── ELECTRON — one negative leaf, upper-right of the atom ──────────────
|
|
67
|
+
var electron = Frame.makeLeafFrame(atomRect.left + Math.round(atomW * 0.75), atomRect.top + Math.round(atomW * 0.25),
|
|
68
|
+
QUARK_LAYER, 1, 0, 0, false, Charge.NEGATIVE, rect);
|
|
69
|
+
electron.name = 'electron';
|
|
70
|
+
quarkDim.addFrame(electron);
|
|
71
|
+
|
|
72
|
+
// ── ATOM — stationary parent of the proton; the electron joins directly ─
|
|
73
|
+
var atom = atomDim.createFrameFromRect(atomRect, [proton], 0, 0, true, true);
|
|
74
|
+
atom.name = 'atom';
|
|
75
|
+
atom.insert(electron);
|
|
76
|
+
|
|
77
|
+
u.leafQuantaWidth = quarkW;
|
|
78
|
+
u.brownianMotion = true;
|
|
79
|
+
|
|
80
|
+
// ── VIEW — frame the atom (render only: the 2D camera's `distance` is the visible physics-px
|
|
81
|
+
// height). The atom is 64 px in the world; at 1.5x its width it fills two thirds of the view,
|
|
82
|
+
// so the proton, its quarks and the electron are individually legible. ─────────────────────
|
|
83
|
+
var cam = exp.runner.cameraControl;
|
|
84
|
+
var flat = !OrbitBag.hasAngularPose(cam);
|
|
85
|
+
if (cam && typeof cam.setOrbit === 'function' && flat) {
|
|
86
|
+
cam.setOrbit({ target: { x: cx, y: cy }, distance: Math.round(atomW * 1.5) });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — Big Bang
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world and the
|
|
7
|
+
* point source are built here; the sugar (PointSource — deprecated V1 sugar, byte-identical), the
|
|
8
|
+
* renderer and the engine are imports. Registered by lib/shim/registry/walkthrough.js (rung big-bang).
|
|
9
|
+
*
|
|
10
|
+
* Physics (zero new — bench M1, H1, H2a-c): one radial burst at the canvas centre — 72 rays (5°) at
|
|
11
|
+
* radii 32 → 2 — of leaves with NO initial momentum (emergentPhysics). The world generates parent
|
|
12
|
+
* frames top-down (by how many frames an area can parent, brownian on), so a uniform field clumps into
|
|
13
|
+
* filaments and voids with no gravity law and no cosmological constant. The source shuts off after
|
|
14
|
+
* 20 s of ticks (1200).
|
|
15
|
+
*
|
|
16
|
+
* Order (it seeds the run): world (with its parent-generation interval) → source → the shut-off tick-fn.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { PointSource } from '@descix/frqtl-web/parts';
|
|
20
|
+
|
|
21
|
+
export default function buildExperiment(exp) {
|
|
22
|
+
var W = 600, H = 600; // THE WORLD this scene states (its co-init call below)
|
|
23
|
+
|
|
24
|
+
var SOURCE_DIAMETER = 64; // emission radii 32 → 2 (step 2)
|
|
25
|
+
var ANGLE_STEP = 5; // degrees between rays → 72 rays
|
|
26
|
+
var EMIT_TICKS = 20 * 60; // the source fires for 1200 ticks
|
|
27
|
+
|
|
28
|
+
// ── WORLD — a 2D canvas + universe: fundamental layer 0, λ multiplier 1, parents top-down ──
|
|
29
|
+
// iframeIntervalSeconds 1/60: parents are generated EVERY tick (the engine freezes the interval at
|
|
30
|
+
// init, so it rides the world cfg — at the default 1 s, Phase B ran every 60 ticks).
|
|
31
|
+
exp.createCanvas(W, H, { dimension: 0, wavelengthScale: 1, frames: true, iframeIntervalSeconds: 1 / 60 });
|
|
32
|
+
var u = exp.universe;
|
|
33
|
+
|
|
34
|
+
// ── THE SOURCE — at the centre, no initial momentum, random charge ──────
|
|
35
|
+
var source = new PointSource(u, Math.floor(W / 2), Math.floor(H / 2), SOURCE_DIAMETER,
|
|
36
|
+
0, null, true, Infinity, 0, ANGLE_STEP,
|
|
37
|
+
{ emergentPhysics: true, minRadius: 2 });
|
|
38
|
+
|
|
39
|
+
// ── SHUT-OFF — the bang is one burst era, then the field evolves on its own ──
|
|
40
|
+
var t = 0;
|
|
41
|
+
exp.addTickFunction(function () {
|
|
42
|
+
t++;
|
|
43
|
+
if (t >= EMIT_TICKS) { source.setActive(false); }
|
|
44
|
+
return [];
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — Blackbody
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world, the
|
|
7
|
+
* oven, its thermostat and the radiation sink are all constructed here; the sugar objects
|
|
8
|
+
* (OvenBox, LargeObject), the renderer and the engine are imports. Registered by
|
|
9
|
+
* lib/shim/registry/walkthrough.js (rung blackbody).
|
|
10
|
+
*
|
|
11
|
+
* Physics (zero new — composition of goldened behaviours): a cavity of absorbing walls holds a
|
|
12
|
+
* population of leaves; the OvenBox thermostat keeps the cavity's quanta near the TEMPERATURE
|
|
13
|
+
* target by emitting, and colliding leaves merge into parent frames (bench H2a–c,
|
|
14
|
+
* collide → merge → promote; `withInterQuantumCollisions`). A parent's fullness IS its frequency
|
|
15
|
+
* (wavelength = capacity/mass, bench M4), so what leaks out of the slit in the right wall is a
|
|
16
|
+
* spectrum of frame sizes — the emergent Planck-like curve — with no energy quanta assumed.
|
|
17
|
+
* Everything that escapes is absorbed by the sink at the right edge (the experiment boundary).
|
|
18
|
+
*
|
|
19
|
+
* Order matters (it seeds the run): the world, the brownian/collision flags, the quantum
|
|
20
|
+
* layer, the thermostat SLOT (reserved before the oven so the oven's own tick-fns run after it),
|
|
21
|
+
* the oven, the thermostat, the sink. Same calls, same order, same seed ⇒ the same RNG stream.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { CollisionActions, CollisionTypes, FrqtlControlOverlay, FrqtlControlsPanel, Rectangle } from '@descix/frqtl-web/parts';
|
|
25
|
+
import * as ControlRegistry from '@descix/frqtl-web/shim/controls/registry.js';
|
|
26
|
+
|
|
27
|
+
export default function buildExperiment(exp) {
|
|
28
|
+
var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
29
|
+
|
|
30
|
+
var TEMPERATURE = 1500; // target quanta inside the cavity (the knob below)
|
|
31
|
+
var QUANTUM_DIM = 3; // the layer leaves promote into: an 8 px box edge, 64 cells of capacity in 2D
|
|
32
|
+
var BOX_FRACTION = 1 / 3; // the cavity's outer box, as a fraction of the canvas
|
|
33
|
+
var WALL = 10; // wall thickness (px)
|
|
34
|
+
var SLIT_HEIGHT = 60; // the radiation slit in the right wall (px)
|
|
35
|
+
var SINK_DEPTH = 30; // the absorbing strip at the right canvas edge (px)
|
|
36
|
+
|
|
37
|
+
// ── WORLD — a 2D canvas + universe, fundamental layer 0 (1x1 leaves), λ multiplier 1 ──
|
|
38
|
+
exp.createCanvas(W, H, { dimension: 0, wavelengthScale: 1 });
|
|
39
|
+
var u = exp.universe;
|
|
40
|
+
u.brownianMotion = true;
|
|
41
|
+
u.withInterQuantumCollisions = true;
|
|
42
|
+
u.addDimension(QUANTUM_DIM);
|
|
43
|
+
|
|
44
|
+
// ── THE OVEN — the cavity, centred on the canvas ────────────────────────
|
|
45
|
+
var outer = new Rectangle(Math.round(W / 2), Math.round(H / 2),
|
|
46
|
+
Math.round(W * BOX_FRACTION), Math.round(H * BOX_FRACTION), null, true);
|
|
47
|
+
// The thermostat runs FIRST each tick: reserve its slot before the oven registers its own.
|
|
48
|
+
var thermostatSlot = u.experimentTickFunctions.length;
|
|
49
|
+
u.experimentTickFunctions.push(null);
|
|
50
|
+
var oven = exp.addObject('OvenBox', {
|
|
51
|
+
name: 'oven',
|
|
52
|
+
left: outer.left + WALL, top: outer.top + WALL,
|
|
53
|
+
width: outer.w - 2 * WALL, height: outer.h - 2 * WALL,
|
|
54
|
+
wallThickness: WALL, temperature: TEMPERATURE,
|
|
55
|
+
slitHeight: SLIT_HEIGHT, // the slit is in the RIGHT wall (OvenBox's own slit side)
|
|
56
|
+
color: [120, 120, 120]
|
|
57
|
+
});
|
|
58
|
+
u.experimentTickFunctions[thermostatSlot] = oven.getTemperatureTickFunction();
|
|
59
|
+
|
|
60
|
+
// ── THE SINK — the experiment boundary on the slit side ─────────────────
|
|
61
|
+
exp.addObject('LargeObject', {
|
|
62
|
+
name: 'radiation_sink',
|
|
63
|
+
rect: { left: W - SINK_DEPTH, top: 0, w: SINK_DEPTH, h: H },
|
|
64
|
+
collision_type: CollisionTypes.NONE, actions: [CollisionActions.ABSORB],
|
|
65
|
+
reposition: 'closest', color: [0, 0, 0]
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ── THE KNOB — temperature, live (the oven's own thermostat target) ─────
|
|
69
|
+
var registry = ControlRegistry.makeControlRegistry();
|
|
70
|
+
registry.registerControls('blackbody', [
|
|
71
|
+
{ kind: 'scalar', id: 'temperature', label: 'temperature (quanta)', min: 50, max: 8000, step: 50,
|
|
72
|
+
value: TEMPERATURE, fmt: function (v) { return String(v); },
|
|
73
|
+
onChange: function (v) { oven.setTemperature(v); },
|
|
74
|
+
place: { x: 0.02, y: 0.03 } }
|
|
75
|
+
]);
|
|
76
|
+
// Chrome (a page only): expose the knobs to a host frame, and mount the overlay through the
|
|
77
|
+
// runner's ONE door (runner.controlHost) — before a canvas is mounted (a notebook pane) there is
|
|
78
|
+
// none, so the knobs stay in the exposed registry only.
|
|
79
|
+
FrqtlControlsPanel.exposeRegistry(registry, { runner: exp.runner });
|
|
80
|
+
if (exp.runner.controlHost) { FrqtlControlOverlay.mount(exp.runner, { registry: registry, name: 'blackbody' }); }
|
|
81
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — Circuit SAT (a half-adder)
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world, the two
|
|
7
|
+
* logic gates, the four rail traces with their input emitters, the two grounded readouts, the live
|
|
8
|
+
* input knobs and the framing are all here; the sugar (LogicGate, Trace, SphereEmitter), the
|
|
9
|
+
* renderer and the engine are imports. Registered by lib/shim/registry/walkthrough.js (rung
|
|
10
|
+
* circuit-sat).
|
|
11
|
+
*
|
|
12
|
+
* Physics (zero new — bench LO1/LO6/LO7 + the Trace wiring): input A and input B are laser
|
|
13
|
+
* emitters that drive quanta down rail traces into an XOR gate (SUM) and an AND gate (CARRY). A
|
|
14
|
+
* gate's valve opens only when its two input ammeters read the logic it embodies, so the circuit
|
|
15
|
+
* SETTLES onto the half-adder's answer: SUM = A xor B, CARRY = A and B, read as the current on each
|
|
16
|
+
* grounded readout. No search algorithm runs.
|
|
17
|
+
*
|
|
18
|
+
* Order (it seeds the run): world → SUM gate → CARRY gate → per lane plan row,
|
|
19
|
+
* the rail trace then its emitter (switched to its input) → the two readouts.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { FrqtlControlOverlay, FrqtlControlsPanel, LogicGate, SphereEmitter, Trace } from '@descix/frqtl-web/parts';
|
|
23
|
+
import * as ControlRegistry from '@descix/frqtl-web/shim/controls/registry.js';
|
|
24
|
+
|
|
25
|
+
export default function buildExperiment(exp) {
|
|
26
|
+
var W = 760, H = 520; // THE WORLD this scene states (its co-init call below)
|
|
27
|
+
var LEFT = 60, TOP = 15; // the circuit's top-left
|
|
28
|
+
var RAIL = 6; // rail voltage: quanta per tick per input emitter
|
|
29
|
+
var LEAD = 24, READOUT = 24; // input-rail and readout trace lengths
|
|
30
|
+
var INSET = 4; // each input emitter sits this far inside its rail's bore mouth
|
|
31
|
+
var STATE = { a: true, b: false }; // input A HIGH at start: SUM reads 1, CARRY reads 0
|
|
32
|
+
|
|
33
|
+
// The input emitter: a narrow 2D laser on the fundamental layer, random ± charge.
|
|
34
|
+
function emitterBag(center) {
|
|
35
|
+
var bag = { preset: 'laser', is2D: true, R: 24, orientationDeg: 0, emissionBoxSide: 0,
|
|
36
|
+
fundamentalDimension: 0, burstSize: RAIL, emitEveryTicks: 1, chargeMode: 'random' };
|
|
37
|
+
if (center) { bag.center = center; }
|
|
38
|
+
return bag;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── WORLD — a 2D canvas + universe; the emitter's own world (its worldCfg) ──
|
|
42
|
+
exp.createCanvas(W, H, SphereEmitter.worldCfg(emitterBag(null)));
|
|
43
|
+
var u = exp.universe;
|
|
44
|
+
|
|
45
|
+
var MIN = LogicGate.MINIMAL; // bore 3 / wall 3 / length 20
|
|
46
|
+
var cell = LogicGate.cellSize(MIN.bore, MIN.wallThickness, MIN.length);
|
|
47
|
+
var gateOpts = { bore: MIN.bore, wallThickness: MIN.wallThickness, length: MIN.length, supplyVoltage: RAIL };
|
|
48
|
+
|
|
49
|
+
// ── THE GATES — SUM (XOR) above CARRY (AND) ─────────────────────────────
|
|
50
|
+
var sum = exp.addObject('LogicGate', Object.assign({ logic: 'XOR', left: LEFT, top: TOP, name: 'SUM', color: [200, 170, 70] }, gateOpts));
|
|
51
|
+
var carry = exp.addObject('LogicGate', Object.assign({ logic: 'AND', left: LEFT, top: TOP + cell.h, name: 'CARRY', color: [170, 200, 90] }, gateOpts));
|
|
52
|
+
var gates = { SUM: sum, CARRY: carry };
|
|
53
|
+
|
|
54
|
+
// ── THE INPUT RAILS — the lane plan: B and A are SWAPPED on CARRY (AND commutes; the swap buys
|
|
55
|
+
// a lane pitch of separation between the two inputs) ──────────────────────────────────────
|
|
56
|
+
var LANES = [
|
|
57
|
+
{ input: 'A', gate: 'SUM', port: 'inPortA' }, { input: 'B', gate: 'SUM', port: 'inPortB' },
|
|
58
|
+
{ input: 'B', gate: 'CARRY', port: 'inPortA' }, { input: 'A', gate: 'CARRY', port: 'inPortB' }
|
|
59
|
+
];
|
|
60
|
+
var rails = { A: [], B: [] }, parts = [];
|
|
61
|
+
LANES.forEach(function (row, i) {
|
|
62
|
+
var port = gates[row.gate][row.port], y = port[1];
|
|
63
|
+
var t = new Trace(u, { path: [[port[0] - LEAD, y], [port[0], y]], bore: MIN.bore,
|
|
64
|
+
wallThickness: MIN.wallThickness, name: 'rail' + row.input + i, color: [90, 120, 200] });
|
|
65
|
+
var em = exp.addObject('SphereEmitter', Object.assign(emitterBag([t.inPort[0] + INSET, y]),
|
|
66
|
+
{ name: 'input' + row.input + i })); // parked inside the bore mouth
|
|
67
|
+
em.setActive(row.input === 'A' ? STATE.a : STATE.b);
|
|
68
|
+
rails[row.input].push(em);
|
|
69
|
+
parts.push(t);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ── THE READOUTS — grounded traces off each gate's outlet: their current IS the answer ──
|
|
73
|
+
function readout(gate, name) {
|
|
74
|
+
return new Trace(u, { path: [[gate.outPort[0], gate.outPort[1]], [gate.outPort[0] + READOUT, gate.outPort[1]]],
|
|
75
|
+
bore: MIN.bore, wallThickness: MIN.wallThickness, ground: true, name: name, color: [60, 160, 220] });
|
|
76
|
+
}
|
|
77
|
+
var readouts = { SUM: readout(sum, 'SUM_out'), CARRY: readout(carry, 'CARRY_out') };
|
|
78
|
+
parts.push(readouts.SUM, readouts.CARRY);
|
|
79
|
+
parts.forEach(function (p) { p.attachToRunner(exp.runner); }); // every trace reads the tick stream (a declared gate subscribed at addObject)
|
|
80
|
+
|
|
81
|
+
function setInput(which, on) {
|
|
82
|
+
STATE[which] = !!on;
|
|
83
|
+
rails[which === 'a' ? 'A' : 'B'].forEach(function (em) { em.setActive(STATE[which]); });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── THE CIRCUIT, PUBLISHED — the parts and the geometry a harness reads (the half-adder
|
|
87
|
+
// acceptance rig): each gate's own getState(), each readout's calibrated getDelivered(), the
|
|
88
|
+
// input switch, and the layout values its settle budget is computed from. ───────────────
|
|
89
|
+
exp.circuit = {
|
|
90
|
+
gates: gates, readouts: readouts,
|
|
91
|
+
rails: rails, lanes: LANES, setInput: setInput,
|
|
92
|
+
lead: LEAD, readout: READOUT, inset: INSET
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// ── THE VIEW — frame the 72x54 px circuit (render only: the 2D camera's distance IS the visible
|
|
96
|
+
// physics-px height) ─────────────────────────────────────────────────────────────────────
|
|
97
|
+
var fp = [LEFT - LEAD, TOP, LEAD + cell.w + READOUT + 4, cell.h * 2];
|
|
98
|
+
var cam = exp.runner.cameraControl;
|
|
99
|
+
if (cam && typeof cam.setOrbit === 'function') {
|
|
100
|
+
cam.setOrbit({ target: { x: fp[0] + fp[2] / 2, y: fp[1] + fp[3] / 2 },
|
|
101
|
+
distance: Math.max(fp[3] * 1.35, fp[2] * 1.35 * H / W) });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── THE KNOBS (on the canvas): the two inputs and the rail ──────────────
|
|
105
|
+
var registry = ControlRegistry.makeControlRegistry();
|
|
106
|
+
registry.registerControls('circuit-sat', [
|
|
107
|
+
{ kind: 'scalar', id: 'inputA', label: 'input A', min: 0, max: 1, step: 1, value: STATE.a ? 1 : 0, widget: 'toggle',
|
|
108
|
+
fmt: function (v) { return v ? '1 (HIGH)' : '0 (LOW)'; }, onChange: function (v) { setInput('a', v); },
|
|
109
|
+
place: { x: 0.02, y: 0.80 } },
|
|
110
|
+
{ kind: 'scalar', id: 'inputB', label: 'input B', min: 0, max: 1, step: 1, value: STATE.b ? 1 : 0, widget: 'toggle',
|
|
111
|
+
fmt: function (v) { return v ? '1 (HIGH)' : '0 (LOW)'; }, onChange: function (v) { setInput('b', v); },
|
|
112
|
+
place: { x: 0.02, y: 0.88 } },
|
|
113
|
+
{ kind: 'scalar', id: 'rail', label: 'rail (burst/tick)', min: 1, max: 16, step: 1, value: RAIL,
|
|
114
|
+
fmt: function (v) { return String(v) + ' q/tick'; },
|
|
115
|
+
onChange: function (v) {
|
|
116
|
+
RAIL = Math.max(0, Math.round(v));
|
|
117
|
+
sum.setSupplyVoltage(RAIL); carry.setSupplyVoltage(RAIL);
|
|
118
|
+
rails.A.concat(rails.B).forEach(function (em) { em.setBurstSize(RAIL); });
|
|
119
|
+
},
|
|
120
|
+
place: { x: 0.30, y: 0.88 } }
|
|
121
|
+
]);
|
|
122
|
+
// Chrome (a page only): expose the knobs to a host frame, and mount the overlay through the
|
|
123
|
+
// runner's ONE door (runner.controlHost) — before a canvas is mounted (a notebook pane) there is
|
|
124
|
+
// none, so the knobs stay in the exposed registry only.
|
|
125
|
+
FrqtlControlsPanel.exposeRegistry(registry, { runner: exp.runner });
|
|
126
|
+
if (exp.runner.controlHost) { FrqtlControlOverlay.mount(exp.runner, { registry: registry, name: 'circuit-sat' }); }
|
|
127
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — 3D Collisions Lab
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The 3D world, the
|
|
7
|
+
* seven head-on lanes of equal-mass voxel pairs, their live knobs and the look are all here; the
|
|
8
|
+
* lane density table (`collisionMatrixLanes`), the renderer and the engine are imports.
|
|
9
|
+
* Registered by lib/shim/registry/walkthrough.js (rung collision-matrix-3d).
|
|
10
|
+
*
|
|
11
|
+
* Physics (zero new — bench C2, C3, C5, SU1): each lane fires two equal bodies at each other at
|
|
12
|
+
* speed_prob 1 along x; a lane's density (mass / capacity) sets how hard the bodies are — rigid
|
|
13
|
+
* bodies bounce cleanly, soft ones press into each other before parting. The lanes sit on a 3x3
|
|
14
|
+
* y/z grid, so the same contact happens at seven depths: collision is identical on any axis
|
|
15
|
+
* (2D ≡ 3D). The same engine that makes light makes mechanics — no second physics.
|
|
16
|
+
*
|
|
17
|
+
* Order (it seeds the run): world → collisions on → the voxel layer →
|
|
18
|
+
* per lane, body A then body B (engine-built, capacity read back once) → seat every body.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { CameraControls, FrqtlControlOverlay, FrqtlControlsPanel } from '@descix/frqtl-web/parts';
|
|
22
|
+
import * as ControlRegistry from '@descix/frqtl-web/shim/controls/registry.js';
|
|
23
|
+
import { collisionMatrixLanes } from '@descix/frqtl-web/objects/setup/_collisionMatrixLanes.js';
|
|
24
|
+
|
|
25
|
+
export default function buildExperiment(exp) {
|
|
26
|
+
var W = 700, H = 700, D = 700; // THE WORLD this scene states (its co-init call below)
|
|
27
|
+
var LEVEL = 6; // the voxel layer: 64 px bodies, capacity 4096
|
|
28
|
+
|
|
29
|
+
// ── WORLD — a 3D scene + universe W×H×D, fundamental layer 0, λ multiplier 1 ──
|
|
30
|
+
exp.createScene(W, H, D, { dimension: 0, wavelengthScale: 1 });
|
|
31
|
+
var u = exp.universe;
|
|
32
|
+
u.withInterQuantumCollisions = true;
|
|
33
|
+
u.addDimension(LEVEL);
|
|
34
|
+
exp.runner.setRateLimits({ tpsLimit: 240 }); // pacing only: wall-clock, never the ticks
|
|
35
|
+
|
|
36
|
+
var lanesTable = collisionMatrixLanes; // the ONE lane density table (imported)
|
|
37
|
+
var voxel = Math.pow(2, LEVEL), half = voxel / 2;
|
|
38
|
+
var midX = Math.floor(W * 0.5);
|
|
39
|
+
var ax0 = midX - Math.floor(1.5 * voxel); // body A, left of centre
|
|
40
|
+
var bx0 = midX + Math.floor(1.5 * voxel); // body B, right of centre
|
|
41
|
+
var N = lanesTable.LANE_DENSITIES.length; // 7 lanes
|
|
42
|
+
var spacing = Math.round(H / 4);
|
|
43
|
+
var laneYs = [], laneZs = [];
|
|
44
|
+
for (var k = 0; k < N; k++) {
|
|
45
|
+
laneYs.push(Math.round(H / 2 + (Math.floor(k / 3) - 1) * spacing)); // rows 0,0,0,1,1,1,2
|
|
46
|
+
laneZs.push(((k % 3) - 1) * spacing); // cols 0,1,2,…
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The live knobs: an offset (the bodies miss by that much) and an angle (vy against vx).
|
|
50
|
+
var offsetPx = 0, angleVy = 0;
|
|
51
|
+
function seat(qid, cx, cy, cz, dirX, mass) {
|
|
52
|
+
u.setFrameProperty(qid, 'x', cx);
|
|
53
|
+
u.setFrameProperty(qid, 'y', cy + dirX * offsetPx);
|
|
54
|
+
u.setFrameProperty(qid, 'z', cz);
|
|
55
|
+
u.setFrameProperty(qid, 'vx', dirX);
|
|
56
|
+
u.setFrameProperty(qid, 'vy', -dirX * angleVy);
|
|
57
|
+
u.setFrameProperty(qid, 'vz', 0);
|
|
58
|
+
u.setFrameProperty(qid, 'mass', mass);
|
|
59
|
+
u.setFrameProperty(qid, 'speed_prob', 1.0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── THE BODIES — A then B per lane; the capacity is read back from the engine, never assumed ──
|
|
63
|
+
var built = [], capacity = null;
|
|
64
|
+
for (var i = 0; i < N; i++) {
|
|
65
|
+
var a = u.makeLeafFrame(ax0 - half, laneYs[i] - half, LEVEL, null, 1, 0, true, 0, null);
|
|
66
|
+
u.injectFrame(a);
|
|
67
|
+
var aQid = u.getFrameQuantumId(a);
|
|
68
|
+
var b = u.makeLeafFrame(bx0 - half, laneYs[i] - half, LEVEL, null, -1, 0, true, 0, null);
|
|
69
|
+
u.injectFrame(b);
|
|
70
|
+
var bQid = u.getFrameQuantumId(b);
|
|
71
|
+
if (capacity === null) { capacity = u.getFrameProperty(aQid, 'mass'); }
|
|
72
|
+
built.push({ aQid: aQid, bQid: bQid });
|
|
73
|
+
}
|
|
74
|
+
var masses = lanesTable.laneMassesForCapacity(capacity);
|
|
75
|
+
var lanes = built.map(function (q, i) {
|
|
76
|
+
return { aQid: q.aQid, bQid: q.bQid, mass: masses[i], laneY: laneYs[i], laneZ: laneZs[i] };
|
|
77
|
+
});
|
|
78
|
+
function reseatAll() {
|
|
79
|
+
lanes.forEach(function (l) {
|
|
80
|
+
seat(l.aQid, ax0, l.laneY, l.laneZ, 1, l.mass);
|
|
81
|
+
seat(l.bQid, bx0, l.laneY, l.laneZ, -1, l.mass);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
reseatAll();
|
|
85
|
+
|
|
86
|
+
// ── THE LOOK — lit spheres, coloured by fullness (sparse red → dense violet) ──
|
|
87
|
+
exp.render({ shape: 'sphere', colorScheme: 'roygbiv' });
|
|
88
|
+
|
|
89
|
+
// ── THE KNOBS (on the canvas) + MOVE/LOOK pads ──
|
|
90
|
+
var registry = ControlRegistry.makeControlRegistry();
|
|
91
|
+
registry.registerControls('collision-matrix-3d', [
|
|
92
|
+
{ kind: 'scalar', id: 'offset', label: 'offset', min: 0, max: 48, step: 4, value: 0,
|
|
93
|
+
fmt: function (v) { return String(v); }, onChange: function (v) { offsetPx = v; reseatAll(); },
|
|
94
|
+
place: { x: 0.02, y: 0.03 } },
|
|
95
|
+
{ kind: 'scalar', id: 'angle', label: 'angle', min: 0, max: 1, step: 0.1, value: 0,
|
|
96
|
+
fmt: function (v) { return v.toFixed(1); }, onChange: function (v) { angleVy = v; reseatAll(); },
|
|
97
|
+
place: { x: 0.02, y: 0.12 } }
|
|
98
|
+
]);
|
|
99
|
+
// Chrome (a page only): expose the knobs to a host frame, and mount the overlay through the
|
|
100
|
+
// runner's ONE door (runner.controlHost) — before a canvas is mounted (a notebook pane) there is
|
|
101
|
+
// none, so the knobs stay in the exposed registry only.
|
|
102
|
+
FrqtlControlsPanel.exposeRegistry(registry, { runner: exp.runner });
|
|
103
|
+
if (exp.runner.controlHost) { FrqtlControlOverlay.mount(exp.runner, { registry: registry, name: 'collision-matrix-3d' }); }
|
|
104
|
+
if (typeof document !== 'undefined') {
|
|
105
|
+
if (exp.runner.controlHost) { CameraControls.attach(exp.runner, exp.runner.controlHost); }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — 1C · Counting Wall (3D)
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung cl1c-counting-wall-3d).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
14
|
+
|
|
15
|
+
export default function buildExperiment(exp) {
|
|
16
|
+
// Collision Lab 1C · 3D — a volumetric source into a 3D Detector Screen slab
|
|
17
|
+
// (destroy+count) + a LIVE in-scene chart, textured in three; orbit to view.
|
|
18
|
+
var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
19
|
+
var cy = Math.round(H / 2);
|
|
20
|
+
var SRC = {
|
|
21
|
+
preset: 'spherical', is2D: false, center: [Math.round(W * 0.15), cy],
|
|
22
|
+
chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
|
|
23
|
+
burstSize: 24, emitEveryTicks: 1
|
|
24
|
+
};
|
|
25
|
+
exp.createScene(W, H, 600, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 3D scene + universe, 600x400x600 — first, then parts
|
|
26
|
+
exp.addObject('SphereEmitter', SRC);
|
|
27
|
+
var screen = exp.addObject('Wall', {
|
|
28
|
+
rect: [Math.round(W * 0.6), 0, 20, H], z: -80, depth: 160,
|
|
29
|
+
preset: 'detector-screen', name: 'screen', color: [120, 200, 120]
|
|
30
|
+
});
|
|
31
|
+
exp.addObject('LiveChart', {
|
|
32
|
+
source: screen,
|
|
33
|
+
rect: [Math.round(W * 0.66), Math.round(H * 0.08), Math.round(W * 0.28), Math.round(H * 0.5)],
|
|
34
|
+
series: 'bar', label: 'arrivals by y', refreshMs: 100, color: [24, 32, 46]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SCENE — 1C · Counting Wall
|
|
5
|
+
*
|
|
6
|
+
* A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
|
|
7
|
+
* (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
|
|
8
|
+
* the parts (`exp.addObject`) and the render intent are all here; the sugar
|
|
9
|
+
* objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
|
|
10
|
+
* (rung cl1c-counting-wall).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { SphereEmitter } from '@descix/frqtl-web/parts';
|
|
14
|
+
|
|
15
|
+
export default function buildExperiment(exp) {
|
|
16
|
+
// Collision Lab 1C (Counting Wall) — the Detector Screen preset (destroy + count),
|
|
17
|
+
// with a LIVE in-scene LiveChart of the running y-histogram placed beside it.
|
|
18
|
+
var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
|
|
19
|
+
var cy = Math.round(H / 2);
|
|
20
|
+
var SRC = {
|
|
21
|
+
preset: 'spherical', is2D: true, center: [Math.round(W * 0.15), cy],
|
|
22
|
+
chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
|
|
23
|
+
burstSize: 24, emitEveryTicks: 1
|
|
24
|
+
};
|
|
25
|
+
exp.createCanvas(W, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
|
|
26
|
+
exp.addObject('SphereEmitter', SRC);
|
|
27
|
+
var screen = exp.addObject('Wall', {
|
|
28
|
+
rect: [Math.round(W * 0.6), 0, 20, H],
|
|
29
|
+
preset: 'detector-screen', name: 'screen', color: [120, 200, 120]
|
|
30
|
+
});
|
|
31
|
+
// The interactive counting demo: a live chart of the detector's histogram,
|
|
32
|
+
// placed to the RIGHT of the screen (past where the quanta are absorbed).
|
|
33
|
+
exp.addObject('LiveChart', {
|
|
34
|
+
source: screen,
|
|
35
|
+
rect: [Math.round(W * 0.66), Math.round(H * 0.08), Math.round(W * 0.28), Math.round(H * 0.5)],
|
|
36
|
+
series: 'bar', label: 'arrivals by y', refreshMs: 100, color: [24, 32, 46]
|
|
37
|
+
});
|
|
38
|
+
}
|