@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +87 -0
- package/LICENSE.md +664 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2920 -0
- package/dist/frqtl/objects/ObjectFactory.js +296 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +710 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +351 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
- package/dist/frqtl/objects/v1-replacements.js +29 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +34 -0
- package/dist/frqtl/parts/CollisionTypes.js +30 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
- package/dist/frqtl/parts/LargeObject.js +405 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +717 -0
- package/dist/frqtl/parts/OvenBox.js +611 -0
- package/dist/frqtl/parts/PointSource.js +762 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +211 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +690 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +325 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +671 -0
- package/dist/shim/notebook/knob.js +150 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +507 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +213 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +804 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +63 -4
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file CircuitGate — density-activated gate for circuit experiments.
|
|
5
|
+
*
|
|
6
|
+
* A gate body is a {@link LargeObject} rectangle that sits across a wire
|
|
7
|
+
* channel. When closed it has collision type `WALL` and blocks quanta; when
|
|
8
|
+
* open it has collision type `NONE` and quanta pass through freely. Gate
|
|
9
|
+
* state is decided by sampling particle density in one or more control regions
|
|
10
|
+
* via `universe.queryRect()` and applying the gate's Boolean function to the
|
|
11
|
+
* resulting HIGH/LOW signals.
|
|
12
|
+
*
|
|
13
|
+
* Supported gate types: `AND`, `OR`, `NOT`, `XOR`, `BUFFER`.
|
|
14
|
+
*
|
|
15
|
+
* ## What you can drive
|
|
16
|
+
*
|
|
17
|
+
* | Parameter | Default | Observable effect |
|
|
18
|
+
* |-----------|---------|-------------------|
|
|
19
|
+
* | `thresholdHigh` | `2` | Average quanta per tick in a control region required to call that input HIGH. Raising it demands a denser particle stream before the gate responds. |
|
|
20
|
+
* | `thresholdLow` | `1` | Average below which a HIGH input reverts to LOW (hysteresis). Must be < `thresholdHigh`; gap determines stability of the open state under noise. |
|
|
21
|
+
* | `windowTicks` | `20` | Sliding-window length in ticks for the density average. Longer windows produce stable gates that are slower to react to input changes; shorter windows react quickly but can chatter. |
|
|
22
|
+
* | `gateType` | — | Determines which Boolean function is applied to the control-region states. `XOR` is currently the only type with XOR-specific hysteresis; all others use symmetric thresholding. |
|
|
23
|
+
*
|
|
24
|
+
* **NOT gate:** starts open by default and creates an internal `QuantumEmitter`
|
|
25
|
+
* (`notEmitter`) that fires while the gate is open. `notEmissionRate` controls
|
|
26
|
+
* its burst size (default 2).
|
|
27
|
+
*
|
|
28
|
+
* Engine-agnostic: works with both the JS reference engine and the Rust/WASM engine.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { LargeObject } from '../../parts/LargeObject.js';
|
|
32
|
+
import { QuantumEmitter } from './QuantumEmitter.js';
|
|
33
|
+
import { CollisionTypes } from '../../parts/CollisionTypes.js';
|
|
34
|
+
import { registerKind } from '../kinds.js';
|
|
35
|
+
|
|
36
|
+
var rectToArray = LargeObject.rectToArray;
|
|
37
|
+
|
|
38
|
+
// ── CircuitGate ─────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Density-activated gate for circuit experiments.
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} universe - Engine universe instance (JS or WASM).
|
|
44
|
+
* @param {string} gateType - Gate logic: `'AND'`, `'OR'`, `'NOT'`, `'XOR'`, or `'BUFFER'`.
|
|
45
|
+
* @param {number} x - Gate body center X (pixels).
|
|
46
|
+
* @param {number} y - Gate body center Y (pixels).
|
|
47
|
+
* @param {number} signalWidth - Height (horizontal gate) or width (vertical gate) of the
|
|
48
|
+
* wire channel the gate body spans.
|
|
49
|
+
* @param {number} gateLength - Length of the gate body along the flow axis.
|
|
50
|
+
* @param {boolean} horizontal - `true` for left-to-right flow (most common); `false`
|
|
51
|
+
* for top-to-bottom flow.
|
|
52
|
+
* @param {Object} [options]
|
|
53
|
+
* @param {number} [options.thresholdHigh=2] - Density average (quanta/tick over
|
|
54
|
+
* `windowTicks`) required to declare a control input HIGH. Raising this requires a
|
|
55
|
+
* denser particle stream; see the "What you can drive" block in the file JSDoc.
|
|
56
|
+
* @param {number} [options.thresholdLow=1] - Density average below which a HIGH control
|
|
57
|
+
* input reverts to LOW. Must be < `thresholdHigh`.
|
|
58
|
+
* @param {number} [options.windowTicks=20] - Sliding-window length in ticks for density
|
|
59
|
+
* averaging. Longer windows are more stable but slower to track input changes.
|
|
60
|
+
* @param {number} [options.fundamentalDim=0] - Currently unused (reserved for multi-
|
|
61
|
+
* dimension addressing). Has no observable effect.
|
|
62
|
+
* @param {number} [options.notEmissionRate=2] - Burst size of the internal emitter
|
|
63
|
+
* created for NOT gates. Unused for all other gate types.
|
|
64
|
+
*/
|
|
65
|
+
function CircuitGate(universe, gateType, x, y, signalWidth, gateLength, horizontal, options) {
|
|
66
|
+
signalWidth = (signalWidth !== undefined) ? signalWidth : 6;
|
|
67
|
+
gateLength = (gateLength !== undefined) ? gateLength : 4;
|
|
68
|
+
horizontal = (horizontal !== undefined) ? horizontal : true;
|
|
69
|
+
options = options || {};
|
|
70
|
+
|
|
71
|
+
this.universe = universe;
|
|
72
|
+
this._schemaKind = 'CircuitGate';
|
|
73
|
+
this.gateType = gateType.toUpperCase();
|
|
74
|
+
this.x = x;
|
|
75
|
+
this.y = y;
|
|
76
|
+
this.signalWidth = signalWidth;
|
|
77
|
+
this.gateLength = gateLength;
|
|
78
|
+
this.horizontal = horizontal;
|
|
79
|
+
|
|
80
|
+
this.thresholdHigh = options.thresholdHigh || 2;
|
|
81
|
+
this.thresholdLow = options.thresholdLow || 1;
|
|
82
|
+
this.windowTicks = options.windowTicks || 20;
|
|
83
|
+
this.fundamentalDim = options.fundamentalDim || 0;
|
|
84
|
+
this.notEmissionRate = options.notEmissionRate || 2;
|
|
85
|
+
|
|
86
|
+
this.controlRegions = [];
|
|
87
|
+
this.controlCounts = [];
|
|
88
|
+
this._controlStates = [];
|
|
89
|
+
this.isOpen = (this.gateType === 'NOT');
|
|
90
|
+
|
|
91
|
+
// Create gate body LargeObject
|
|
92
|
+
var rect;
|
|
93
|
+
if (horizontal) {
|
|
94
|
+
rect = [x - gateLength / 2, y - signalWidth / 2, gateLength, signalWidth];
|
|
95
|
+
} else {
|
|
96
|
+
rect = [x - signalWidth / 2, y - gateLength / 2, signalWidth, gateLength];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// G2: Always compile the WALL program into the gate body (actionCompiler compiles
|
|
100
|
+
// WALL eject ops from collision_type=WALL). The gate starts as a wall (WALL program
|
|
101
|
+
// compiled + active). NOT gate then immediately setActive(false) to start open.
|
|
102
|
+
// Toggle uses setActive() — no runtime collision_type change needed.
|
|
103
|
+
var reposition = horizontal ? 'left' : 'top';
|
|
104
|
+
|
|
105
|
+
this.gateBody = new LargeObject(
|
|
106
|
+
universe, rect, CollisionTypes.WALL, [], 'gate_' + this.gateType, reposition
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// NOT gate starts open — disable the LO (pass-through).
|
|
110
|
+
if (this.gateType === 'NOT') {
|
|
111
|
+
this.gateBody.setActive(false);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.color = (this.gateType === 'NOT') ? [50, 200, 50] : [200, 200, 50];
|
|
115
|
+
|
|
116
|
+
// NOT gate creates an internal emitter
|
|
117
|
+
this.notEmitter = null;
|
|
118
|
+
if (this.gateType === 'NOT') {
|
|
119
|
+
this._createNotEmitter(options);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// THE ONE UP-LINK: the gate composed its body LO and (NOT) its emitter. Zero behavior change.
|
|
123
|
+
this.gateBody._owner = this;
|
|
124
|
+
if (this.notEmitter !== null) { this.notEmitter._owner = this; }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create the NOT gate's internal emitter that fires when the gate is open.
|
|
129
|
+
* @private
|
|
130
|
+
*/
|
|
131
|
+
/**
|
|
132
|
+
* Full teardown of what this gate composed: the NOT emitter (splices its host tick-fn) and
|
|
133
|
+
* the gate body LO. Idempotent (each member guards itself).
|
|
134
|
+
*/
|
|
135
|
+
CircuitGate.prototype.dispose = function () {
|
|
136
|
+
if (this.notEmitter) { this.notEmitter.dispose(); }
|
|
137
|
+
this.gateBody.dispose();
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
CircuitGate.prototype._createNotEmitter = function (options) {
|
|
141
|
+
var ts = 4;
|
|
142
|
+
var emitX, emitY;
|
|
143
|
+
if (this.horizontal) {
|
|
144
|
+
emitX = this.x + this.gateLength / 2 + ts;
|
|
145
|
+
emitY = this.y;
|
|
146
|
+
} else {
|
|
147
|
+
emitX = this.x;
|
|
148
|
+
emitY = this.y + this.gateLength / 2 + ts;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.notEmitter = new QuantumEmitter(
|
|
152
|
+
this.universe,
|
|
153
|
+
[[emitX - ts / 2, emitY - ts / 2, ts, ts]],
|
|
154
|
+
0, 0, // vx, vy
|
|
155
|
+
this.notEmissionRate, // burst_size
|
|
156
|
+
null, // charge (random)
|
|
157
|
+
this.isOpen, // auto_fire (NOT gate defaults open)
|
|
158
|
+
true, // random_direction
|
|
159
|
+
Infinity, // quantum_limit
|
|
160
|
+
0 // delay
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Add a control input region.
|
|
166
|
+
* @param {Array|Object} rect - [left, top, w, h] or {left,top,w,h}
|
|
167
|
+
*/
|
|
168
|
+
CircuitGate.prototype.addControlRegion = function (rect) {
|
|
169
|
+
var r = rectToArray(rect);
|
|
170
|
+
this.controlRegions.push(r);
|
|
171
|
+
this.controlCounts.push([]);
|
|
172
|
+
this._controlStates.push(false);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Evaluate gate state using density queries. Must be called once per tick.
|
|
177
|
+
*/
|
|
178
|
+
CircuitGate.prototype.evaluate = function () {
|
|
179
|
+
var i, r, count, avg;
|
|
180
|
+
|
|
181
|
+
// Update control states from density queries
|
|
182
|
+
for (i = 0; i < this.controlRegions.length; i++) {
|
|
183
|
+
r = this.controlRegions[i];
|
|
184
|
+
count = this.universe.queryRect(r[0], r[1], r[2], r[3]);
|
|
185
|
+
|
|
186
|
+
this.controlCounts[i].push(count);
|
|
187
|
+
if (this.controlCounts[i].length > this.windowTicks) {
|
|
188
|
+
this.controlCounts[i].shift();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var sum = 0;
|
|
192
|
+
for (var j = 0; j < this.controlCounts[i].length; j++) {
|
|
193
|
+
sum += this.controlCounts[i][j];
|
|
194
|
+
}
|
|
195
|
+
avg = sum / this.controlCounts[i].length;
|
|
196
|
+
|
|
197
|
+
if (this._controlStates[i] && avg < this.thresholdLow) {
|
|
198
|
+
this._controlStates[i] = false;
|
|
199
|
+
} else if (!this._controlStates[i] && avg >= this.thresholdHigh) {
|
|
200
|
+
this._controlStates[i] = true;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Evaluate gate logic
|
|
205
|
+
var shouldOpen;
|
|
206
|
+
var states = this._controlStates;
|
|
207
|
+
|
|
208
|
+
switch (this.gateType) {
|
|
209
|
+
case 'NOT':
|
|
210
|
+
shouldOpen = states.length > 0 ? !states[0] : true;
|
|
211
|
+
break;
|
|
212
|
+
case 'AND':
|
|
213
|
+
shouldOpen = states.length >= 2 ? (states[0] && states[1]) : false;
|
|
214
|
+
break;
|
|
215
|
+
case 'OR':
|
|
216
|
+
shouldOpen = states.length >= 2 ? (states[0] || states[1]) : false;
|
|
217
|
+
break;
|
|
218
|
+
case 'XOR':
|
|
219
|
+
shouldOpen = states.length >= 2 ? (states[0] !== states[1]) : false;
|
|
220
|
+
break;
|
|
221
|
+
case 'BUFFER':
|
|
222
|
+
shouldOpen = states.length > 0 ? states[0] : false;
|
|
223
|
+
break;
|
|
224
|
+
default:
|
|
225
|
+
shouldOpen = false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Toggle gate body active state if logic state changed.
|
|
229
|
+
// G2: gate uses setActive() instead of setCollisionType() — the WALL eject
|
|
230
|
+
// program is compiled at init time; active=false makes the gate passthrough,
|
|
231
|
+
// active=true re-enables the WALL eject.
|
|
232
|
+
if (shouldOpen !== this.isOpen) {
|
|
233
|
+
this.isOpen = shouldOpen;
|
|
234
|
+
if (this.isOpen) {
|
|
235
|
+
this.gateBody.setActive(false);
|
|
236
|
+
this.color = [50, 200, 50];
|
|
237
|
+
} else {
|
|
238
|
+
this.gateBody.setActive(true);
|
|
239
|
+
this.color = [200, 50, 50];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Toggle NOT emitter
|
|
244
|
+
if (this.notEmitter !== null) {
|
|
245
|
+
this.notEmitter.setActive(this.isOpen);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// ── The kind (objects/kinds.js): descriptor, construction defaults, builder ──────────
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* CircuitGate — Density-activated logic gate for circuit experiments.
|
|
253
|
+
* Constructor: CircuitGate(universe, gateType, x, y, signalWidth, gateLength,
|
|
254
|
+
* horizontal, options)
|
|
255
|
+
* options: { thresholdHigh, thresholdLow, windowTicks, fundamentalDim, notEmissionRate }
|
|
256
|
+
* Group wrapper: the inner gateBody LargeObject (and optional notEmitter for NOT gates)
|
|
257
|
+
* are stamped with the ONE up-link `_owner` = <this>.
|
|
258
|
+
*/
|
|
259
|
+
var CircuitGateDescriptor = {
|
|
260
|
+
description: 'A density-activated logic gate for quantum circuit experiments. ' +
|
|
261
|
+
'Evaluates gate logic (AND, OR, NOT, XOR, BUFFER) by querying particle density ' +
|
|
262
|
+
'in control regions, then toggles the gate body\'s collision type to open/close. ' +
|
|
263
|
+
'Demonstrates how classical Boolean logic emerges from particle density thresholds. ' +
|
|
264
|
+
'NOT gates include an internal emitter that fires when no input signal is present.',
|
|
265
|
+
fields: [
|
|
266
|
+
{
|
|
267
|
+
name: 'gateType',
|
|
268
|
+
type: 'enum',
|
|
269
|
+
constraint: { values: { AND: 'AND', OR: 'OR', NOT: 'NOT', XOR: 'XOR', BUFFER: 'BUFFER' } },
|
|
270
|
+
engineVisible: false,
|
|
271
|
+
description: 'Gate logic type. AND/OR/XOR use two control regions; NOT/BUFFER use one. ' +
|
|
272
|
+
'Gate body is WALL (closed/blocking) when logic output is false, NONE (open) when true.'
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'x',
|
|
276
|
+
type: 'number',
|
|
277
|
+
constraint: { integer: true },
|
|
278
|
+
engineVisible: true,
|
|
279
|
+
description: 'Gate body center X (physics pixels).'
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'y',
|
|
283
|
+
type: 'number',
|
|
284
|
+
constraint: { integer: true },
|
|
285
|
+
engineVisible: true,
|
|
286
|
+
description: 'Gate body center Y (physics pixels).'
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'signalWidth',
|
|
290
|
+
type: 'number',
|
|
291
|
+
constraint: { min: 1, integer: true },
|
|
292
|
+
engineVisible: true,
|
|
293
|
+
description: 'Height/width of the signal channel through the gate (physics pixels, default 6).'
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'gateLength',
|
|
297
|
+
type: 'number',
|
|
298
|
+
constraint: { min: 1, integer: true },
|
|
299
|
+
engineVisible: true,
|
|
300
|
+
description: 'Length of the gate body along the flow direction (physics pixels, default 4).'
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'horizontal',
|
|
304
|
+
type: 'boolean',
|
|
305
|
+
engineVisible: true,
|
|
306
|
+
description: 'Flow direction: true = particles flow horizontally (left→right); false = vertically (top→bottom).'
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: 'thresholdHigh',
|
|
310
|
+
type: 'number',
|
|
311
|
+
constraint: { min: 0, integer: false },
|
|
312
|
+
engineVisible: false,
|
|
313
|
+
description: 'Average density (over windowTicks) above which a control region is considered HIGH (logic 1). Default 2.'
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: 'thresholdLow',
|
|
317
|
+
type: 'number',
|
|
318
|
+
constraint: { min: 0, integer: false },
|
|
319
|
+
engineVisible: false,
|
|
320
|
+
description: 'Average density below which a control region returns to LOW (logic 0). Default 1. ' +
|
|
321
|
+
'Hysteresis band = [thresholdLow, thresholdHigh] prevents rapid toggling.'
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: 'windowTicks',
|
|
325
|
+
type: 'number',
|
|
326
|
+
constraint: { min: 1, integer: true },
|
|
327
|
+
engineVisible: false,
|
|
328
|
+
description: 'Sliding window size (ticks) for density averaging (default 20). ' +
|
|
329
|
+
'Larger values smooth burst fluctuations; smaller values make the gate more responsive.'
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: 'notEmissionRate',
|
|
333
|
+
type: 'number',
|
|
334
|
+
constraint: { min: 1, integer: true },
|
|
335
|
+
engineVisible: false,
|
|
336
|
+
description: 'Burst size of the NOT gate\'s internal emitter (particles/burst, default 2). Only applies to gateType NOT.'
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
registerKind(CircuitGate, {
|
|
342
|
+
from: import.meta.url,
|
|
343
|
+
descriptor: CircuitGateDescriptor,
|
|
344
|
+
defaults: {
|
|
345
|
+
gateType: 'AND',
|
|
346
|
+
x: 100, y: 100,
|
|
347
|
+
signalWidth: 6,
|
|
348
|
+
gateLength: 4,
|
|
349
|
+
horizontal: true,
|
|
350
|
+
thresholdHigh: 2,
|
|
351
|
+
thresholdLow: 1,
|
|
352
|
+
windowTicks: 20,
|
|
353
|
+
notEmissionRate: 2
|
|
354
|
+
},
|
|
355
|
+
build: function (props, ctx, Class) {
|
|
356
|
+
var CGateCls = Class; // the class that registered (a composed fork's own class)
|
|
357
|
+
var cg = new CGateCls(
|
|
358
|
+
ctx.runner.universe,
|
|
359
|
+
props.gateType || 'AND',
|
|
360
|
+
props.x != null ? props.x : Math.round(ctx.x),
|
|
361
|
+
props.y != null ? props.y : Math.round(ctx.y),
|
|
362
|
+
props.signalWidth || 6,
|
|
363
|
+
props.gateLength || 4,
|
|
364
|
+
props.horizontal != null ? props.horizontal : true,
|
|
365
|
+
{
|
|
366
|
+
thresholdHigh: props.thresholdHigh != null ? props.thresholdHigh : 2,
|
|
367
|
+
thresholdLow: props.thresholdLow != null ? props.thresholdLow : 1,
|
|
368
|
+
windowTicks: props.windowTicks != null ? props.windowTicks : 20,
|
|
369
|
+
notEmissionRate: props.notEmissionRate != null ? props.notEmissionRate : 2
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
cg._schemaKind = Class.name;
|
|
373
|
+
return cg;
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
export { CircuitGate };
|
|
380
|
+
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file CircuitProbe — non-intrusive density probe for circuit experiments.
|
|
5
|
+
*
|
|
6
|
+
* Calls `universe.queryRect()` each tick to count quanta in a rectangular
|
|
7
|
+
* region without absorbing them. Maintains a sliding-window history
|
|
8
|
+
* (`windowSize` = 10 ticks) for `getFlowRate()` — the same metric the
|
|
9
|
+
* experiment page compares against `PROBE_THRESHOLD` (default 20) to decide
|
|
10
|
+
* HIGH vs LOW output.
|
|
11
|
+
*
|
|
12
|
+
* ## What you can drive
|
|
13
|
+
*
|
|
14
|
+
* | Parameter | Default | Observable effect |
|
|
15
|
+
* |-----------|---------|-------------------|
|
|
16
|
+
* | `size` | (caller-set) | Width and height of the probe rectangle in pixels. A larger probe region samples more wire cross-section; too large and it overlaps neighbouring channels. |
|
|
17
|
+
* | `windowSize` | 10 | (set on the instance after construction) Sliding-window length. Larger window smooths transient noise; smaller window responds faster to input changes. |
|
|
18
|
+
*
|
|
19
|
+
* A `CircuitProbe` registers a visual-only `LargeObject` marker (collision type
|
|
20
|
+
* `NONE`) so it appears as a distinct region in the renderer layers panel.
|
|
21
|
+
*
|
|
22
|
+
* Engine-agnostic: works with both the JS reference engine and the Rust/WASM engine.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { LargeObject } from '../../parts/LargeObject.js';
|
|
26
|
+
import { registerKind } from '../kinds.js';
|
|
27
|
+
|
|
28
|
+
// ── Imports ─────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
// ── CircuitProbe ────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Non-intrusive density probe for circuit experiments.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} universe - Engine universe instance (JS or WASM).
|
|
36
|
+
* @param {number} x - Probe center X (pixels).
|
|
37
|
+
* @param {number} y - Probe center Y (pixels).
|
|
38
|
+
* @param {number} size - Probe region width and height (pixels). Should
|
|
39
|
+
* match or be slightly smaller than the wire channel it monitors.
|
|
40
|
+
* @param {string} [name='probe'] - Probe identifier; used for the visual
|
|
41
|
+
* marker label and in `getCircuitState()` result keys.
|
|
42
|
+
*/
|
|
43
|
+
function CircuitProbe(universe, x, y, size, name) {
|
|
44
|
+
name = name || 'probe';
|
|
45
|
+
|
|
46
|
+
this.universe = universe;
|
|
47
|
+
this._schemaKind = 'CircuitProbe';
|
|
48
|
+
this.x = x;
|
|
49
|
+
this.y = y;
|
|
50
|
+
this.size = size;
|
|
51
|
+
this.name = name;
|
|
52
|
+
this.color = [0, 255, 0];
|
|
53
|
+
this.history = [];
|
|
54
|
+
this.windowSize = 10;
|
|
55
|
+
|
|
56
|
+
this.rect = {
|
|
57
|
+
left: x - size / 2,
|
|
58
|
+
top: y - size / 2,
|
|
59
|
+
w: size,
|
|
60
|
+
h: size
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Register a visual-only LargeObject for rendering (no collision)
|
|
64
|
+
this.marker = new LargeObject(
|
|
65
|
+
universe,
|
|
66
|
+
[this.rect.left, this.rect.top, this.rect.w, this.rect.h],
|
|
67
|
+
'NONE', // no collision
|
|
68
|
+
[], // no actions
|
|
69
|
+
'probe_' + name,
|
|
70
|
+
'closest'
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
this.marker._owner = this; // THE ONE UP-LINK: the probe composed its marker LO
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Full teardown of what this probe composed: its marker LO leaves the world. Idempotent.
|
|
78
|
+
*/
|
|
79
|
+
CircuitProbe.prototype.dispose = function () {
|
|
80
|
+
this.marker.dispose();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Query particle density in the probe region.
|
|
85
|
+
* @returns {number} Particle count
|
|
86
|
+
*/
|
|
87
|
+
CircuitProbe.prototype.queryDensity = function () {
|
|
88
|
+
var count = this.universe.queryRect(
|
|
89
|
+
this.rect.left, this.rect.top, this.rect.w, this.rect.h
|
|
90
|
+
);
|
|
91
|
+
this.history.push(count);
|
|
92
|
+
if (this.history.length > this.windowSize) {
|
|
93
|
+
this.history.shift();
|
|
94
|
+
}
|
|
95
|
+
return count;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the full density history.
|
|
100
|
+
* @returns {Array<number>}
|
|
101
|
+
*/
|
|
102
|
+
CircuitProbe.prototype.getDensityHistory = function () {
|
|
103
|
+
return this.history;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the average flow rate over the sliding window.
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
110
|
+
CircuitProbe.prototype.getFlowRate = function () {
|
|
111
|
+
if (this.history.length === 0) return 0;
|
|
112
|
+
var sum = 0;
|
|
113
|
+
for (var i = 0; i < this.history.length; i++) {
|
|
114
|
+
sum += this.history[i];
|
|
115
|
+
}
|
|
116
|
+
return sum / this.history.length;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get the most recent count.
|
|
121
|
+
* @returns {number}
|
|
122
|
+
*/
|
|
123
|
+
CircuitProbe.prototype.getLastCount = function () {
|
|
124
|
+
return this.history.length > 0 ? this.history[this.history.length - 1] : 0;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Returns true if average flow is at or above threshold.
|
|
129
|
+
* @param {number} threshold
|
|
130
|
+
* @returns {boolean}
|
|
131
|
+
*/
|
|
132
|
+
CircuitProbe.prototype.isHigh = function (threshold) {
|
|
133
|
+
threshold = (threshold !== undefined) ? threshold : 1;
|
|
134
|
+
return this.getFlowRate() >= threshold;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// ── The kind (objects/kinds.js): descriptor, construction defaults, builder ──────────
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* CircuitProbe — Non-intrusive density probe for circuit experiments.
|
|
141
|
+
* Constructor: CircuitProbe(universe, x, y, size, name)
|
|
142
|
+
* Group wrapper: the inner marker LargeObject is stamped with the ONE up-link `_owner` = <this>.
|
|
143
|
+
*/
|
|
144
|
+
var CircuitProbeDescriptor = {
|
|
145
|
+
description: 'A non-intrusive density probe for quantum circuit experiments. ' +
|
|
146
|
+
'Uses universe.queryRect() to count particles in a rectangular region each tick ' +
|
|
147
|
+
'without absorbing or deflecting them. Maintains a sliding window history for ' +
|
|
148
|
+
'flow rate analysis. Renders a visual marker rectangle (NONE collision type).',
|
|
149
|
+
fields: [
|
|
150
|
+
{
|
|
151
|
+
name: 'x',
|
|
152
|
+
type: 'number',
|
|
153
|
+
constraint: { integer: true },
|
|
154
|
+
engineVisible: true,
|
|
155
|
+
description: 'Probe center X (physics pixels).'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'y',
|
|
159
|
+
type: 'number',
|
|
160
|
+
constraint: { integer: true },
|
|
161
|
+
engineVisible: true,
|
|
162
|
+
description: 'Probe center Y (physics pixels).'
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'size',
|
|
166
|
+
type: 'number',
|
|
167
|
+
constraint: { min: 1, integer: true },
|
|
168
|
+
engineVisible: true,
|
|
169
|
+
unit: 'px',
|
|
170
|
+
description: 'Width and height of the probe region (physics pixels). Square region centered at (x, y).'
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'name',
|
|
174
|
+
type: 'string',
|
|
175
|
+
engineVisible: false,
|
|
176
|
+
description: 'Probe identifier string (display/debug only).'
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'windowSize',
|
|
180
|
+
type: 'number',
|
|
181
|
+
constraint: { min: 1, integer: true },
|
|
182
|
+
engineVisible: false,
|
|
183
|
+
description: 'Sliding window size (ticks) for flow rate averaging (default 10). getFlowRate() returns the average over this window.'
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
registerKind(CircuitProbe, {
|
|
189
|
+
from: import.meta.url,
|
|
190
|
+
descriptor: CircuitProbeDescriptor,
|
|
191
|
+
defaults: {
|
|
192
|
+
x: 100, y: 100,
|
|
193
|
+
size: 10,
|
|
194
|
+
name: 'probe',
|
|
195
|
+
windowSize: 10
|
|
196
|
+
},
|
|
197
|
+
build: function (props, ctx, Class) {
|
|
198
|
+
var CProbeCls = Class; // the class that registered (a composed fork's own class)
|
|
199
|
+
var cp = new CProbeCls(
|
|
200
|
+
ctx.runner.universe,
|
|
201
|
+
props.x != null ? props.x : Math.round(ctx.x),
|
|
202
|
+
props.y != null ? props.y : Math.round(ctx.y),
|
|
203
|
+
props.size || 10,
|
|
204
|
+
props.name || 'probe'
|
|
205
|
+
);
|
|
206
|
+
cp._schemaKind = Class.name;
|
|
207
|
+
return cp;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ── Export ───────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
export { CircuitProbe };
|
|
214
|
+
|