@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.
Files changed (217) hide show
  1. package/CLAUDE.md +87 -0
  2. package/README.md +84 -2
  3. package/dist/dist-manifest.json +218 -0
  4. package/dist/eyes/index.js +109 -0
  5. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  6. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  7. package/dist/fat/pkg-node/package.json +12 -0
  8. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  9. package/dist/frqtl/compat/boot-node.js +34 -0
  10. package/dist/frqtl/compat/engine.js +127 -0
  11. package/dist/frqtl/compat/package-root.js +24 -0
  12. package/dist/frqtl/compat/providers/wasm-engine.js +2501 -0
  13. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  14. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  15. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  16. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  17. package/dist/frqtl/compat/renderer/RenderFamily.js +85 -0
  18. package/dist/frqtl/compat/renderer/RenderMesh.js +100 -0
  19. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  20. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  21. package/dist/frqtl/compat/renderer/captureStill-common.js +259 -0
  22. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  23. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  24. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  25. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  26. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  27. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  28. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  30. package/dist/frqtl/compat/renderer/providers/three-provider.js +2474 -0
  31. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  32. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  33. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  34. package/dist/frqtl/compat/vendor.js +196 -0
  35. package/dist/frqtl/node-entry.js +17 -0
  36. package/dist/frqtl/objects/FrqtlApp.js +2922 -0
  37. package/dist/frqtl/objects/ObjectFactory.js +297 -0
  38. package/dist/frqtl/objects/RecipeContext.js +322 -0
  39. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  40. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  41. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  42. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  43. package/dist/frqtl/objects/instruments/capture.js +711 -0
  44. package/dist/frqtl/objects/instruments/hosted-eyes.js +985 -0
  45. package/dist/frqtl/objects/kinds-entry.js +14 -0
  46. package/dist/frqtl/objects/kinds.js +190 -0
  47. package/dist/frqtl/objects/objects/Box.js +273 -0
  48. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  49. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  50. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  51. package/dist/frqtl/objects/objects/DirectedTunnel.js +412 -0
  52. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  53. package/dist/frqtl/objects/objects/LightSource.js +354 -0
  54. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  55. package/dist/frqtl/objects/objects/Tube.js +471 -0
  56. package/dist/frqtl/objects/objects/absorbRadiation.js +310 -0
  57. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  58. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  59. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  60. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  61. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  62. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  66. package/dist/frqtl/objects/objects/makeControlsForObject.js +824 -0
  67. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  68. package/dist/frqtl/objects/objects/slitLinerScatter.js +263 -0
  69. package/dist/frqtl/objects/objects/sustainedSource.js +299 -0
  70. package/dist/frqtl/objects/objects/tagger.js +228 -0
  71. package/dist/frqtl/objects/schema/engine-scopes.js +469 -0
  72. package/dist/frqtl/objects/schema/index.js +256 -0
  73. package/dist/frqtl/objects/schema/scopes.js +457 -0
  74. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  75. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  76. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  77. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  78. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  79. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  80. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  81. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  82. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +251 -0
  83. package/dist/frqtl/objects/setup/setupDoubleSlit.js +327 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  85. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  86. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  87. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  88. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  89. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  90. package/dist/frqtl/objects/setup/setupLightSourceLab.js +280 -0
  91. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  92. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  94. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1259 -0
  95. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  96. package/dist/frqtl/objects/setup/setupTunnelLab.js +242 -0
  97. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  98. package/dist/frqtl/objects/setup/setupWaveInterference.js +178 -0
  99. package/dist/frqtl/objects/v1-replacements.js +30 -0
  100. package/dist/frqtl/package-core.js +23 -0
  101. package/dist/frqtl/parts/CameraControls.js +643 -0
  102. package/dist/frqtl/parts/CameraPath.js +575 -0
  103. package/dist/frqtl/parts/CollisionActions.js +35 -0
  104. package/dist/frqtl/parts/CollisionTypes.js +31 -0
  105. package/dist/frqtl/parts/DetectorWall.js +496 -0
  106. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  107. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  108. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  109. package/dist/frqtl/parts/GraphingDetectorWall.js +490 -0
  110. package/dist/frqtl/parts/LargeObject.js +406 -0
  111. package/dist/frqtl/parts/LightColor.js +255 -0
  112. package/dist/frqtl/parts/LiveChart.js +285 -0
  113. package/dist/frqtl/parts/LogicGate.js +720 -0
  114. package/dist/frqtl/parts/OvenBox.js +612 -0
  115. package/dist/frqtl/parts/PointSource.js +763 -0
  116. package/dist/frqtl/parts/Rectangle.js +200 -0
  117. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  118. package/dist/frqtl/parts/SlitScreen.js +628 -0
  119. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  120. package/dist/frqtl/parts/Trace.js +508 -0
  121. package/dist/frqtl/parts/Tunnel.js +1057 -0
  122. package/dist/frqtl/parts/Wall.js +1211 -0
  123. package/dist/frqtl/parts/index.js +33 -0
  124. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  126. package/dist/frqtl/scenes/atomic-model.js +88 -0
  127. package/dist/frqtl/scenes/big-bang.js +46 -0
  128. package/dist/frqtl/scenes/blackbody.js +81 -0
  129. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  130. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  131. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  132. package/dist/frqtl/scenes/counting-wall.js +38 -0
  133. package/dist/frqtl/scenes/double-slit.js +31 -0
  134. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  135. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  136. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  137. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +244 -0
  138. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  139. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  140. package/dist/frqtl/scenes/empty.js +14 -0
  141. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  142. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  143. package/dist/frqtl/scenes/mirror-route.js +140 -0
  144. package/dist/frqtl/scenes/mirror.js +38 -0
  145. package/dist/frqtl/scenes/omni-3d.js +27 -0
  146. package/dist/frqtl/scenes/omni-source.js +22 -0
  147. package/dist/frqtl/scenes/self-interference.js +39 -0
  148. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  149. package/dist/frqtl/scenes/straight-walk.js +22 -0
  150. package/dist/frqtl/scenes/sun.js +39 -0
  151. package/dist/frqtl/scenes/two-source.js +37 -0
  152. package/dist/frqtl/scenes/wave.js +26 -0
  153. package/dist/shim/bundle/frqnb.js +112 -0
  154. package/dist/shim/bundle/index.js +121 -0
  155. package/dist/shim/bundle/indexHtml.js +107 -0
  156. package/dist/shim/bundle/manifest.js +95 -0
  157. package/dist/shim/bundle/zip.js +243 -0
  158. package/dist/shim/bus.js +67 -0
  159. package/dist/shim/controls/index.js +627 -0
  160. package/dist/shim/controls/registry.js +274 -0
  161. package/dist/shim/engine/artifact-shell.js +212 -0
  162. package/dist/shim/engine/boot-phases.js +44 -0
  163. package/dist/shim/engine/engine-stack-boot.js +59 -0
  164. package/dist/shim/engine/router.js +692 -0
  165. package/dist/shim/experiment-frame/index.js +296 -0
  166. package/dist/shim/expr/ast-schema.js +369 -0
  167. package/dist/shim/field-descriptor/index.js +326 -0
  168. package/dist/shim/frame-data/index.js +224 -0
  169. package/dist/shim/index.js +67 -0
  170. package/dist/shim/notebook/canvas-guard.js +82 -0
  171. package/dist/shim/notebook/catalog.js +35 -0
  172. package/dist/shim/notebook/format.js +736 -0
  173. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  174. package/dist/shim/notebook/kernel.js +672 -0
  175. package/dist/shim/notebook/knob.js +151 -0
  176. package/dist/shim/notebook/module-resolver.js +260 -0
  177. package/dist/shim/notebook/notebook-knobs.js +65 -0
  178. package/dist/shim/notebook/produced-bindings.js +67 -0
  179. package/dist/shim/notebook/realm-registry.js +121 -0
  180. package/dist/shim/notebook/realm-transport.js +73 -0
  181. package/dist/shim/notebook/setup-builder.js +510 -0
  182. package/dist/shim/notebook/store.js +159 -0
  183. package/dist/shim/notebook/vendor-importmap.js +134 -0
  184. package/dist/shim/notebook/view-builder.js +189 -0
  185. package/dist/shim/notebook/view-ctx.js +100 -0
  186. package/dist/shim/notebook/view-handle.js +202 -0
  187. package/dist/shim/notebook/warmup.js +102 -0
  188. package/dist/shim/package.json +4 -0
  189. package/dist/shim/page-params/index.js +214 -0
  190. package/dist/shim/project/identity.js +112 -0
  191. package/dist/shim/project/index.js +19 -0
  192. package/dist/shim/project/link.js +402 -0
  193. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  194. package/dist/shim/project/register-node.mjs +17 -0
  195. package/dist/shim/project/specifiers.js +106 -0
  196. package/dist/shim/registry/copy-gate.js +110 -0
  197. package/dist/shim/registry/exhibits.js +455 -0
  198. package/dist/shim/registry/experiments.js +215 -0
  199. package/dist/shim/registry/index.js +19 -0
  200. package/dist/shim/registry/scene-source.js +157 -0
  201. package/dist/shim/registry/walkthrough.js +807 -0
  202. package/dist/shim/render/capture-envelope.js +279 -0
  203. package/dist/shim/render/heat-colormap.js +92 -0
  204. package/dist/shim/render/render-stages.js +855 -0
  205. package/dist/shim/render/sun-look.js +33 -0
  206. package/dist/shim/render-state/apply.js +262 -0
  207. package/dist/shim/render-state/index.js +74 -0
  208. package/dist/shim/rpc/index.js +207 -0
  209. package/dist/shim/runtime/index.js +1023 -0
  210. package/dist/shim/storage/boot.js +69 -0
  211. package/dist/shim/storage/index.js +169 -0
  212. package/dist/shim/storage/local-storage-provider.js +230 -0
  213. package/dist/shim/storage/make-namespace-store.js +132 -0
  214. package/dist/shim/storage/modules.js +249 -0
  215. package/dist/shim/storage/projects.js +150 -0
  216. package/llms.txt +12 -0
  217. package/package.json +62 -4
@@ -0,0 +1,200 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * Rectangle — Spatial rectangle with auto-computed edges.
5
+ *
6
+ * Supports two construction modes:
7
+ * - Centered (isCentered=true): x,y is the CENTER; left/top/right/bottom auto-computed
8
+ * - Non-centered (isCentered=false): x,y is TOP-LEFT; center auto-computed
9
+ *
10
+ * All edge properties (left, top, right, bottom, x, y) are linked via
11
+ * getters/setters — changing any one updates the others consistently.
12
+ *
13
+ * The SOLE Rectangle authority: the WASM provider wrapper (compat/providers/wasm-engine.js) and
14
+ * every part import it.
15
+ */
16
+
17
+
18
+ function Rectangle(x, y, w, h, data, isCentered) {
19
+ if (isCentered === undefined) isCentered = true;
20
+ this.is_centered = isCentered;
21
+ this.data = data || null;
22
+ this.w = w;
23
+ this.h = h;
24
+
25
+ if (isCentered) {
26
+ // x,y is the center
27
+ this._x = x;
28
+ this._y = y;
29
+ this._left = x - w / 2;
30
+ this._right = x + w / 2;
31
+ this._top = y - h / 2;
32
+ this._bottom = y + h / 2;
33
+ } else {
34
+ // x,y is the top-left corner
35
+ this._x = x + w / 2;
36
+ this._y = y + h / 2;
37
+ this._left = x;
38
+ this._right = x + w;
39
+ this._top = y;
40
+ this._bottom = y + h;
41
+ }
42
+ this.r = w / 2;
43
+ }
44
+
45
+ // ── Linked property accessors ────────────────────────────────────────
46
+
47
+ Object.defineProperty(Rectangle.prototype, 'x', {
48
+ get: function () { return this._x; },
49
+ set: function (val) {
50
+ this._x = val;
51
+ this._left = val - this.w / 2;
52
+ this._right = val + this.w / 2;
53
+ }
54
+ });
55
+
56
+ Object.defineProperty(Rectangle.prototype, 'y', {
57
+ get: function () { return this._y; },
58
+ set: function (val) {
59
+ this._y = val;
60
+ this._top = val - this.h / 2;
61
+ this._bottom = val + this.h / 2;
62
+ }
63
+ });
64
+
65
+ Object.defineProperty(Rectangle.prototype, 'left', {
66
+ get: function () { return this._left; },
67
+ set: function (val) {
68
+ this._left = val;
69
+ this._x = val + this.w / 2;
70
+ this._right = val + this.w;
71
+ }
72
+ });
73
+
74
+ Object.defineProperty(Rectangle.prototype, 'right', {
75
+ get: function () { return this._right; },
76
+ set: function (val) {
77
+ this._right = val;
78
+ this._x = val - this.w / 2;
79
+ this._left = val - this.w;
80
+ }
81
+ });
82
+
83
+ Object.defineProperty(Rectangle.prototype, 'top', {
84
+ get: function () { return this._top; },
85
+ set: function (val) {
86
+ this._top = val;
87
+ this._y = val + this.h / 2;
88
+ this._bottom = val + this.h;
89
+ }
90
+ });
91
+
92
+ Object.defineProperty(Rectangle.prototype, 'bottom', {
93
+ get: function () { return this._bottom; },
94
+ set: function (val) {
95
+ this._bottom = val;
96
+ this._y = val - this.h / 2;
97
+ this._top = val - this.h;
98
+ }
99
+ });
100
+
101
+ Object.defineProperty(Rectangle.prototype, 'area', {
102
+ get: function () { return this.w * this.h; }
103
+ });
104
+
105
+ // ── Spatial queries ──────────────────────────────────────────────────
106
+
107
+ Rectangle.prototype.contains = function (other) {
108
+ return (this.left <= other.left && this.right >= other.right &&
109
+ this.top <= other.top && this.bottom >= other.bottom);
110
+ };
111
+
112
+ Rectangle.prototype.intersects = function (other) {
113
+ return (this.left <= other.right && this.right >= other.left &&
114
+ this.top <= other.bottom && this.bottom >= other.top);
115
+ };
116
+
117
+ Rectangle.prototype.overlaps = function (other) {
118
+ if (!this.intersects(other)) return null;
119
+ var oLeft = Math.max(this.left, other.left);
120
+ var oRight = Math.min(this.right, other.right);
121
+ var oTop = Math.max(this.top, other.top);
122
+ var oBottom = Math.min(this.bottom, other.bottom);
123
+ return new Rectangle(oLeft, oTop, oRight - oLeft, oBottom - oTop, null, false);
124
+ };
125
+
126
+ // ── Ejection (reposition other rect out of this rect) ────────────────
127
+
128
+ Rectangle.prototype.eject = function (other, toSide, contain) {
129
+ if (!this.overlaps(other)) return;
130
+
131
+ var newLeft = other.left;
132
+ var newTop = other.top;
133
+
134
+ switch (toSide) {
135
+ case 'top':
136
+ newTop = this.top - other.h - 1;
137
+ break;
138
+ case 'bottom':
139
+ newTop = this.bottom + 1;
140
+ break;
141
+ case 'left':
142
+ newLeft = this.left - other.w - 1;
143
+ break;
144
+ case 'right':
145
+ newLeft = this.right + 1;
146
+ break;
147
+ default:
148
+ // Closest side
149
+ var dLeft = Math.abs(this.left - other.left);
150
+ var dRight = Math.abs(this.right - other.left);
151
+ var dTop = Math.abs(this.top - other.top);
152
+ var dBottom = Math.abs(this.bottom - other.top);
153
+ var minD = Math.min(dLeft, dRight, dTop, dBottom);
154
+ if (minD === dLeft) {
155
+ newLeft = contain ? this.left + 1 : this.left - other.w - 1;
156
+ } else if (minD === dRight) {
157
+ newLeft = contain ? this.right - other.w - 1 : this.right + 1;
158
+ } else if (minD === dTop) {
159
+ newTop = contain ? this.top + 1 : this.top - other.h - 1;
160
+ } else {
161
+ newTop = contain ? this.bottom - other.h - 1 : this.bottom + 1;
162
+ }
163
+ }
164
+ other.top = newTop;
165
+ other.left = newLeft;
166
+ };
167
+
168
+ // ── Distance helpers ─────────────────────────────────────────────────
169
+
170
+ Rectangle.prototype.xDistanceFrom = function (point) {
171
+ return (this.left <= point.rect.x && point.rect.x <= this.right)
172
+ ? 0
173
+ : Math.min(Math.abs(point.rect.x - this.left), Math.abs(point.rect.x - this.right));
174
+ };
175
+
176
+ Rectangle.prototype.yDistanceFrom = function (point) {
177
+ return (this.top <= point.rect.y && point.rect.y <= this.bottom)
178
+ ? 0
179
+ : Math.min(Math.abs(point.rect.y - this.top), Math.abs(point.rect.y - this.bottom));
180
+ };
181
+
182
+ Rectangle.prototype.sqDistanceFrom = function (point) {
183
+ var dx = this.xDistanceFrom(point);
184
+ var dy = this.yDistanceFrom(point);
185
+ return dx * dx + dy * dy;
186
+ };
187
+
188
+ Rectangle.prototype.distanceFrom = function (point) {
189
+ return Math.sqrt(this.sqDistanceFrom(point));
190
+ };
191
+
192
+ Rectangle.prototype.toString = function () {
193
+ return 'Rectangle(x: ' + this._x + ', y: ' + this._y +
194
+ ', w: ' + this.w + ', h: ' + this.h +
195
+ ', left: ' + this._left + ', right: ' + this._right +
196
+ ', top: ' + this._top + ', bottom: ' + this._bottom +
197
+ ', is_centered: ' + this.is_centered + ')';
198
+ };
199
+
200
+ export { Rectangle };
@@ -0,0 +1,234 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SlitLightSource — Engine-agnostic light source for the double-slit experiment.
5
+ * Creates a PointSource centered between the two slits of a SlitScreen.
6
+ *
7
+ * Engine-agnostic: works with both the JS reference engine and the Rust/WASM engine.
8
+ */
9
+
10
+ import { PointSource } from './PointSource.js';
11
+ import { registerKind } from '../objects/kinds.js';
12
+ import { V1_REPLACEMENTS } from '../objects/v1-replacements.js';
13
+
14
+ // ── Imports (Node.js) or globals (browser) ──────────────────────────
15
+
16
+ // ── SlitLightSource ──────────────────────────────────────────────────
17
+
18
+ /**
19
+ * Radial point source positioned to illuminate both slits evenly.
20
+ *
21
+ * A thin wrapper around {@link PointSource} that positions the source
22
+ * automatically at the geometric center of the two slits: `x = 20`,
23
+ * `y = slit_wall_gap_top + slit_wall_gap_height / 2`. This equidistant
24
+ * placement ensures both slits receive identical particle flux, which is
25
+ * required for the interference fringes to be symmetric.
26
+ *
27
+ * ## Physics you can drive
28
+ *
29
+ * **`angle_increment`** (default 10 degrees) controls how many emission rays
30
+ * the source fires: `floor(360 / angle_increment)` rays. At 10° that is 36
31
+ * rays. The emission cone must reach both slits — the source is placed at
32
+ * x = 20 so rays at a wide range of angles hit the slit screen. Increasing
33
+ * `angle_increment` thins the ray count (coarser illumination); decreasing it
34
+ * increases ray density (smoother, more particle flux).
35
+ *
36
+ * **Emission radius** is hardcoded to `light_source_diameter / 2 = 8` px. Each
37
+ * ray's initial velocity is `vx = 8 * cos(θ)`, `vy = 8 * sin(θ)` (the
38
+ * `PointSource` emitter-registration pattern). The Universal Speed Limit caps
39
+ * actual movement to 1px/tick; excess becomes accumulated momentum.
40
+ *
41
+ * **Particle wavelength / mass:** the source passes `slitScreen.wavelength` to
42
+ * the inner `PointSource` as `opts.wavelength`, which sets each emitted
43
+ * particle's mass via `mass = capacity / wavelength`. This keeps particle mass
44
+ * consistent with the slit geometry scale.
45
+ *
46
+ * **`delay`** (default 1) sets ticks between bursts per angle. `delay = 0` fires
47
+ * every tick; `delay = 1` fires every other tick. Affects particle density but
48
+ * not fringe position.
49
+ *
50
+ * **`quantum_limit`** (default `Infinity`) caps total particles emitted per angle.
51
+ * Set a finite value to limit the total particle count (useful for single-shot
52
+ * or timed observations).
53
+ *
54
+ * @param {Object} universe - The engine universe instance (JS or WASM).
55
+ * @param {Object} slitScreen - A {@link SlitScreen} instance. Provides `slit_wall_gap_top`,
56
+ * `slit_wall_gap_height`, and `wavelength` for automatic positioning and mass.
57
+ * @param {number} [angle_increment=10] - Degrees between emission angles;
58
+ * `floor(360 / angle_increment)` total rays.
59
+ * @param {number} [quantum_limit=Infinity] - Max quanta emitted per angle before
60
+ * the emitter stops.
61
+ * @param {number} [delay=1] - Ticks between emission bursts per angle. 0 = every
62
+ * tick; 1 = every other tick.
63
+ * @param {Object} [opts] - WS-DSE-3D Bite B1.
64
+ * @param {number} [opts.depth] - When provided, threads `zMin=-depth/2,
65
+ * zDepth=depth` into the inner `PointSource`'s opts — the IDENTICAL shared
66
+ * z-spread mechanism WS-DSE-3D Bite B0 built for `setupWaveInterference`
67
+ * (`emitterProgram.js`'s opt-in `em.zMin`/`em.zDepth`). REUSE, not a fork —
68
+ * SlitLightSource is a thin PointSource wrapper (see module docstring), so
69
+ * this is the only line needed to make the DSE light source illuminate the
70
+ * full z-depth of the SLOT-shaped 3D slits (Bite B1's SlitScreen `depth`).
71
+ * OMITTED (default) → `zMin`/`zDepth` stay `undefined` on the PointSource
72
+ * opts object, which PointSource itself treats as `null` (byte-identical
73
+ * 2D behavior — zero extra `random()` draws, per `emitterProgram.js`).
74
+ */
75
+ function SlitLightSource(universe, slitScreen, angle_increment, quantum_limit, delay, opts) {
76
+ angle_increment = (angle_increment !== undefined) ? angle_increment : 10;
77
+ quantum_limit = (quantum_limit !== undefined) ? quantum_limit : Infinity;
78
+ delay = (delay !== undefined) ? delay : 1;
79
+ opts = opts || {};
80
+ var depth = (opts.depth !== undefined && opts.depth !== null) ? opts.depth : null;
81
+ var zMin = (depth !== null) ? -depth / 2 : null;
82
+ var zDepth = (depth !== null) ? depth : null;
83
+
84
+ this.universe = universe;
85
+ this._schemaKind = 'SlitLightSource';
86
+
87
+ // Light source diameter: hardcoded to 16 matching devsdk's pointSOURCESIZE.
88
+ // (wavelength * 0.25 * 2 = 32 would double the emission radius and velocity)
89
+ var light_source_diameter = 16;
90
+
91
+ // Position: x=20, y=midpoint of inter-slit gap (equidistant from both slit centers).
92
+ // Previously used slit_wall_gap_top (top edge of gap), which biased toward slit1.
93
+ // Correct position = gap_top + gap_height/2 = slit pair center = canvas center.
94
+ var light_source_x = 20;
95
+ var light_source_center_y = slitScreen.slit_wall_gap_top + slitScreen.slit_wall_gap_height / 2;
96
+
97
+ var charge = null; // random +/- per particle
98
+ var speed = 1;
99
+
100
+ // LAB DRIVE FIX BATCH item 1 (2026-07-13, CEO): color OFF by default —
101
+ // this used to hardcode `color: [0, 255, 0]` (devsdk parity green),
102
+ // which OVERRIDES the render layer's colorScheme (LightColor ramp;
103
+ // "an explicit render.color OVERRIDES colorScheme, so ROYGBIV never
104
+ // shows on colored bodies" — docs/physics/COLOR_RENDER_LEARNINGS.md).
105
+ // That override made every double-slit-family page render a fixed green
106
+ // regardless of physics, while setupWaveInterference's PointSources
107
+ // pass NO color at all (colorless -> colorScheme-driven, genuine
108
+ // fullness/frequency-driven presentation) — the exact asymmetry the
109
+ // CEO flagged (WI shows real physics color; the Lab showed a flat
110
+ // override). `opts.color` is now an EXPLICIT opt-in (default `null` —
111
+ // colorless, PointSource's own `opts.color || null` already handles
112
+ // this correctly) so any caller that WANTS the old green back can
113
+ // still request it; none currently do (grepped: setupDoubleSlit.js,
114
+ // slit-family-depth.test.mjs, schema.test.mjs — zero callers pass
115
+ // `opts.color` to SlitLightSource, so this is a pure default flip).
116
+ var lightSourceColor = (opts.color !== undefined) ? opts.color : null;
117
+
118
+ // Create a PointSource (already engine-agnostic)
119
+ this.lightSource = new PointSource(
120
+ universe,
121
+ light_source_x,
122
+ light_source_center_y,
123
+ light_source_diameter,
124
+ speed,
125
+ charge,
126
+ true, // auto_fire
127
+ quantum_limit,
128
+ delay,
129
+ angle_increment,
130
+ { color: lightSourceColor, wavelength: slitScreen.wavelength, zMin: zMin, zDepth: zDepth }
131
+ );
132
+
133
+ this.lightSource._owner = this; // THE ONE UP-LINK
134
+ }
135
+
136
+ /**
137
+ * Returns a tick function suitable for universe.experimentTickFunctions.
138
+ * Delegates to the PointSource's default tick function pattern:
139
+ * each emitter with auto_fire=true fires automatically via the engine.
140
+ * Returns a no-op since PointSource emitters auto-fire via the engine.
141
+ */
142
+ SlitLightSource.prototype.getTickFunction = function () {
143
+ // PointSource emitters are registered with auto_fire=true,
144
+ // so the engine fires them in Phase A automatically.
145
+ // Return a no-op tick function for compatibility.
146
+ return function () {};
147
+ };
148
+
149
+ /**
150
+ * Enable/disable the light source.
151
+ * @param {boolean} active
152
+ */
153
+ SlitLightSource.prototype.setActive = function (active) {
154
+ this.lightSource.setActive(active);
155
+ };
156
+
157
+ /**
158
+ * DSE LAB (2026-07-13): chargeMode passthrough — mirrors `setActive`'s delegation
159
+ * pattern. `PointSource.prototype.chargeMode`'s setter already covers
160
+ * `'random'|'positive'|'negative'|'both'` and is LIVE/read-per-emission with NO
161
+ * re-init (ground-truthed: it lazily registers whichever (charge,random_direction)
162
+ * handle groups the new mode needs, then flips `active` on the relevant groups —
163
+ * emitterProgram.js's tick-fn checks `em.active` fresh every tick, so the switch
164
+ * takes effect on the very next emission, not just future ones). The DSE Lab's
165
+ * `chargeMode` control (random|positive|negative) drives this directly —
166
+ * `slitLightSource.chargeMode = 'positive'` — reusing the IDENTICAL mechanism
167
+ * `setupWaveInterference`'s emitters would use, not a fork.
168
+ */
169
+ Object.defineProperty(SlitLightSource.prototype, 'chargeMode', {
170
+ get: function () { return this.lightSource.chargeMode; },
171
+ set: function (mode) { this.lightSource.chargeMode = mode; },
172
+ enumerable: true,
173
+ configurable: true
174
+ });
175
+
176
+ // ── The kind (objects/kinds.js): descriptor, construction defaults, builder ──────────
177
+
178
+ /**
179
+ * SlitLightSource — Radial emitter positioned between slit pairs (wraps PointSource).
180
+ * Constructor: SlitLightSource(universe, slitScreen, angle_increment,
181
+ * quantum_limit, delay)
182
+ * Position is derived from slitScreen geometry at construction time.
183
+ * Group wrapper: the inner PointSource's per-angle handles are stamped with the ONE up-link `_owner` = <this>.
184
+ */
185
+ var SlitLightSourceDescriptor = {
186
+ description: 'A radial light source for the double-slit experiment. ' +
187
+ 'Wraps a PointSource positioned equidistant between the two slits ' +
188
+ '(at the inter-slit gap center), so particles reach both openings with equal path length. ' +
189
+ 'Emits in all directions; the slit screen selects which particles pass through.',
190
+ fields: [
191
+ {
192
+ name: 'angle_increment',
193
+ type: 'number',
194
+ constraint: { min: 1, max: 360, integer: false },
195
+ engineVisible: true,
196
+ description: 'Degrees between emission angles (fewer degrees = more angles = denser wave front). 10° gives 36 directions, the double-slit experiment default.'
197
+ },
198
+ {
199
+ name: 'quantum_limit',
200
+ type: 'number',
201
+ constraint: { min: 1, integer: true },
202
+ engineVisible: true,
203
+ description: 'Max quanta per angle emitter (null/Infinity = unlimited). Limits total particle count for performance control.'
204
+ },
205
+ {
206
+ name: 'delay',
207
+ type: 'number',
208
+ constraint: { min: 0, integer: true },
209
+ engineVisible: true,
210
+ description: 'Ticks between bursts for each angle emitter. 1 = emit every other tick (default for the double-slit source).'
211
+ }
212
+ ]
213
+ };
214
+
215
+ // ── SlitLightSource — cannot be built standalone ─────────────────────────
216
+ registerKind(SlitLightSource, {
217
+ from: import.meta.url,
218
+ descriptor: SlitLightSourceDescriptor,
219
+ defaults: {
220
+ angle_increment: 10,
221
+ quantum_limit: null,
222
+ delay: 1
223
+ },
224
+ build: function () {
225
+ throw new Error('[ObjectFactory] create("SlitLightSource"): a SlitLightSource requires a paired ' +
226
+ 'SlitScreen and cannot be constructed independently — build it through its setup factory ' +
227
+ '(setupDoubleSlit), or compose ' + V1_REPLACEMENTS.SlitLightSource + '. (FAIL-LOUD.)');
228
+ }
229
+ });
230
+
231
+ // ── Export ───────────────────────────────────────────────────────────
232
+
233
+ export { SlitLightSource };
234
+