@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.
Files changed (218) hide show
  1. package/CLAUDE.md +87 -0
  2. package/LICENSE.md +664 -0
  3. package/README.md +84 -2
  4. package/dist/dist-manifest.json +218 -0
  5. package/dist/eyes/index.js +109 -0
  6. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  7. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  8. package/dist/fat/pkg-node/package.json +12 -0
  9. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  10. package/dist/frqtl/compat/boot-node.js +34 -0
  11. package/dist/frqtl/compat/engine.js +127 -0
  12. package/dist/frqtl/compat/package-root.js +24 -0
  13. package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
  14. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  15. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  16. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  17. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  18. package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
  19. package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
  20. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  21. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  22. package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
  23. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  24. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  25. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  26. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  27. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  28. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  30. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  31. package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
  32. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  33. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  34. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  35. package/dist/frqtl/compat/vendor.js +196 -0
  36. package/dist/frqtl/node-entry.js +17 -0
  37. package/dist/frqtl/objects/FrqtlApp.js +2920 -0
  38. package/dist/frqtl/objects/ObjectFactory.js +296 -0
  39. package/dist/frqtl/objects/RecipeContext.js +322 -0
  40. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  41. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  42. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  43. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  44. package/dist/frqtl/objects/instruments/capture.js +710 -0
  45. package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
  46. package/dist/frqtl/objects/kinds-entry.js +14 -0
  47. package/dist/frqtl/objects/kinds.js +190 -0
  48. package/dist/frqtl/objects/objects/Box.js +273 -0
  49. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  50. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  51. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  52. package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
  53. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  54. package/dist/frqtl/objects/objects/LightSource.js +351 -0
  55. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  56. package/dist/frqtl/objects/objects/Tube.js +471 -0
  57. package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
  58. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  59. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  60. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  61. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  62. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  66. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  67. package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
  68. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  69. package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
  70. package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
  71. package/dist/frqtl/objects/objects/tagger.js +228 -0
  72. package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
  73. package/dist/frqtl/objects/schema/index.js +256 -0
  74. package/dist/frqtl/objects/schema/scopes.js +457 -0
  75. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  76. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  77. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  78. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  79. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  80. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  81. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  82. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  83. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
  85. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  86. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  87. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  88. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  89. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  90. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  91. package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
  92. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  94. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  95. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
  96. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  97. package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
  98. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  99. package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
  100. package/dist/frqtl/objects/v1-replacements.js +29 -0
  101. package/dist/frqtl/package-core.js +23 -0
  102. package/dist/frqtl/parts/CameraControls.js +643 -0
  103. package/dist/frqtl/parts/CameraPath.js +575 -0
  104. package/dist/frqtl/parts/CollisionActions.js +34 -0
  105. package/dist/frqtl/parts/CollisionTypes.js +30 -0
  106. package/dist/frqtl/parts/DetectorWall.js +496 -0
  107. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  108. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  109. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  110. package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
  111. package/dist/frqtl/parts/LargeObject.js +405 -0
  112. package/dist/frqtl/parts/LightColor.js +255 -0
  113. package/dist/frqtl/parts/LiveChart.js +285 -0
  114. package/dist/frqtl/parts/LogicGate.js +717 -0
  115. package/dist/frqtl/parts/OvenBox.js +611 -0
  116. package/dist/frqtl/parts/PointSource.js +762 -0
  117. package/dist/frqtl/parts/Rectangle.js +200 -0
  118. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  119. package/dist/frqtl/parts/SlitScreen.js +628 -0
  120. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  121. package/dist/frqtl/parts/Trace.js +508 -0
  122. package/dist/frqtl/parts/Tunnel.js +1057 -0
  123. package/dist/frqtl/parts/Wall.js +1211 -0
  124. package/dist/frqtl/parts/index.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  126. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  127. package/dist/frqtl/scenes/atomic-model.js +88 -0
  128. package/dist/frqtl/scenes/big-bang.js +46 -0
  129. package/dist/frqtl/scenes/blackbody.js +81 -0
  130. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  131. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  132. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  133. package/dist/frqtl/scenes/counting-wall.js +38 -0
  134. package/dist/frqtl/scenes/double-slit.js +31 -0
  135. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  136. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  137. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  138. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
  139. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  140. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  141. package/dist/frqtl/scenes/empty.js +14 -0
  142. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  143. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  144. package/dist/frqtl/scenes/mirror-route.js +140 -0
  145. package/dist/frqtl/scenes/mirror.js +38 -0
  146. package/dist/frqtl/scenes/omni-3d.js +27 -0
  147. package/dist/frqtl/scenes/omni-source.js +22 -0
  148. package/dist/frqtl/scenes/self-interference.js +39 -0
  149. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  150. package/dist/frqtl/scenes/straight-walk.js +22 -0
  151. package/dist/frqtl/scenes/sun.js +39 -0
  152. package/dist/frqtl/scenes/two-source.js +37 -0
  153. package/dist/frqtl/scenes/wave.js +26 -0
  154. package/dist/shim/bundle/frqnb.js +112 -0
  155. package/dist/shim/bundle/index.js +121 -0
  156. package/dist/shim/bundle/indexHtml.js +107 -0
  157. package/dist/shim/bundle/manifest.js +95 -0
  158. package/dist/shim/bundle/zip.js +243 -0
  159. package/dist/shim/bus.js +67 -0
  160. package/dist/shim/controls/index.js +627 -0
  161. package/dist/shim/controls/registry.js +274 -0
  162. package/dist/shim/engine/artifact-shell.js +211 -0
  163. package/dist/shim/engine/boot-phases.js +44 -0
  164. package/dist/shim/engine/engine-stack-boot.js +59 -0
  165. package/dist/shim/engine/router.js +690 -0
  166. package/dist/shim/experiment-frame/index.js +296 -0
  167. package/dist/shim/expr/ast-schema.js +369 -0
  168. package/dist/shim/field-descriptor/index.js +325 -0
  169. package/dist/shim/frame-data/index.js +224 -0
  170. package/dist/shim/index.js +67 -0
  171. package/dist/shim/notebook/canvas-guard.js +82 -0
  172. package/dist/shim/notebook/catalog.js +35 -0
  173. package/dist/shim/notebook/format.js +736 -0
  174. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  175. package/dist/shim/notebook/kernel.js +671 -0
  176. package/dist/shim/notebook/knob.js +150 -0
  177. package/dist/shim/notebook/module-resolver.js +260 -0
  178. package/dist/shim/notebook/notebook-knobs.js +65 -0
  179. package/dist/shim/notebook/produced-bindings.js +67 -0
  180. package/dist/shim/notebook/realm-registry.js +121 -0
  181. package/dist/shim/notebook/realm-transport.js +73 -0
  182. package/dist/shim/notebook/setup-builder.js +507 -0
  183. package/dist/shim/notebook/store.js +159 -0
  184. package/dist/shim/notebook/vendor-importmap.js +134 -0
  185. package/dist/shim/notebook/view-builder.js +189 -0
  186. package/dist/shim/notebook/view-ctx.js +100 -0
  187. package/dist/shim/notebook/view-handle.js +202 -0
  188. package/dist/shim/notebook/warmup.js +102 -0
  189. package/dist/shim/package.json +4 -0
  190. package/dist/shim/page-params/index.js +213 -0
  191. package/dist/shim/project/identity.js +112 -0
  192. package/dist/shim/project/index.js +19 -0
  193. package/dist/shim/project/link.js +402 -0
  194. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  195. package/dist/shim/project/register-node.mjs +17 -0
  196. package/dist/shim/project/specifiers.js +106 -0
  197. package/dist/shim/registry/copy-gate.js +110 -0
  198. package/dist/shim/registry/exhibits.js +455 -0
  199. package/dist/shim/registry/experiments.js +215 -0
  200. package/dist/shim/registry/index.js +19 -0
  201. package/dist/shim/registry/scene-source.js +157 -0
  202. package/dist/shim/registry/walkthrough.js +804 -0
  203. package/dist/shim/render/capture-envelope.js +279 -0
  204. package/dist/shim/render/heat-colormap.js +92 -0
  205. package/dist/shim/render/render-stages.js +855 -0
  206. package/dist/shim/render/sun-look.js +33 -0
  207. package/dist/shim/render-state/apply.js +262 -0
  208. package/dist/shim/render-state/index.js +74 -0
  209. package/dist/shim/rpc/index.js +207 -0
  210. package/dist/shim/runtime/index.js +1023 -0
  211. package/dist/shim/storage/boot.js +69 -0
  212. package/dist/shim/storage/index.js +169 -0
  213. package/dist/shim/storage/local-storage-provider.js +230 -0
  214. package/dist/shim/storage/make-namespace-store.js +132 -0
  215. package/dist/shim/storage/modules.js +249 -0
  216. package/dist/shim/storage/projects.js +150 -0
  217. package/llms.txt +12 -0
  218. 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
+