@guinetik/gcanvas 1.0.0

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 (349) hide show
  1. package/.github/workflows/release.yaml +70 -0
  2. package/.jshintrc +4 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CLAUDE.md +310 -0
  5. package/blackhole.jpg +0 -0
  6. package/demo.png +0 -0
  7. package/demos/CNAME +1 -0
  8. package/demos/animations.html +31 -0
  9. package/demos/basic.html +38 -0
  10. package/demos/baskara.html +31 -0
  11. package/demos/bezier.html +35 -0
  12. package/demos/beziersignature.html +29 -0
  13. package/demos/blackhole.html +28 -0
  14. package/demos/blob.html +35 -0
  15. package/demos/demos.css +289 -0
  16. package/demos/easing.html +28 -0
  17. package/demos/events.html +195 -0
  18. package/demos/fluent.html +647 -0
  19. package/demos/fractals.html +36 -0
  20. package/demos/genart.html +26 -0
  21. package/demos/gendream.html +26 -0
  22. package/demos/group.html +36 -0
  23. package/demos/home.html +587 -0
  24. package/demos/index.html +364 -0
  25. package/demos/isometric.html +34 -0
  26. package/demos/js/animations.js +452 -0
  27. package/demos/js/basic.js +204 -0
  28. package/demos/js/baskara.js +751 -0
  29. package/demos/js/bezier.js +692 -0
  30. package/demos/js/beziersignature.js +241 -0
  31. package/demos/js/blackhole/accretiondisk.obj.js +379 -0
  32. package/demos/js/blackhole/blackhole.obj.js +318 -0
  33. package/demos/js/blackhole/index.js +409 -0
  34. package/demos/js/blackhole/particle.js +56 -0
  35. package/demos/js/blackhole/starfield.obj.js +218 -0
  36. package/demos/js/blob.js +2263 -0
  37. package/demos/js/easing.js +477 -0
  38. package/demos/js/fluent.js +183 -0
  39. package/demos/js/fractals.js +931 -0
  40. package/demos/js/fractalworker.js +93 -0
  41. package/demos/js/genart.js +268 -0
  42. package/demos/js/gendream.js +209 -0
  43. package/demos/js/group.js +140 -0
  44. package/demos/js/info-toggle.js +25 -0
  45. package/demos/js/isometric.js +863 -0
  46. package/demos/js/kerr.js +1556 -0
  47. package/demos/js/lavalamp.js +590 -0
  48. package/demos/js/layout.js +354 -0
  49. package/demos/js/mondrian.js +285 -0
  50. package/demos/js/opacity.js +275 -0
  51. package/demos/js/painter.js +484 -0
  52. package/demos/js/particles-showcase.js +514 -0
  53. package/demos/js/particles.js +299 -0
  54. package/demos/js/patterns.js +397 -0
  55. package/demos/js/penrose/artifact.js +69 -0
  56. package/demos/js/penrose/blackhole.js +121 -0
  57. package/demos/js/penrose/constants.js +73 -0
  58. package/demos/js/penrose/game.js +943 -0
  59. package/demos/js/penrose/lore.js +278 -0
  60. package/demos/js/penrose/penrosescene.js +892 -0
  61. package/demos/js/penrose/ship.js +216 -0
  62. package/demos/js/penrose/sounds.js +211 -0
  63. package/demos/js/penrose/voidparticle.js +55 -0
  64. package/demos/js/penrose/voidscene.js +258 -0
  65. package/demos/js/penrose/voidship.js +144 -0
  66. package/demos/js/penrose/wormhole.js +46 -0
  67. package/demos/js/pipeline.js +555 -0
  68. package/demos/js/scene.js +304 -0
  69. package/demos/js/scenes.js +320 -0
  70. package/demos/js/schrodinger.js +410 -0
  71. package/demos/js/schwarzschild.js +1023 -0
  72. package/demos/js/shapes.js +628 -0
  73. package/demos/js/space/alien.js +171 -0
  74. package/demos/js/space/boom.js +98 -0
  75. package/demos/js/space/boss.js +353 -0
  76. package/demos/js/space/buff.js +73 -0
  77. package/demos/js/space/bullet.js +102 -0
  78. package/demos/js/space/constants.js +85 -0
  79. package/demos/js/space/game.js +1884 -0
  80. package/demos/js/space/hud.js +112 -0
  81. package/demos/js/space/laserbeam.js +179 -0
  82. package/demos/js/space/lightning.js +277 -0
  83. package/demos/js/space/minion.js +192 -0
  84. package/demos/js/space/missile.js +212 -0
  85. package/demos/js/space/player.js +430 -0
  86. package/demos/js/space/powerup.js +90 -0
  87. package/demos/js/space/starfield.js +58 -0
  88. package/demos/js/space/starpower.js +90 -0
  89. package/demos/js/spacetime.js +559 -0
  90. package/demos/js/svgtween.js +204 -0
  91. package/demos/js/tde/accretiondisk.js +418 -0
  92. package/demos/js/tde/blackhole.js +219 -0
  93. package/demos/js/tde/blackholescene.js +209 -0
  94. package/demos/js/tde/config.js +59 -0
  95. package/demos/js/tde/index.js +695 -0
  96. package/demos/js/tde/jets.js +290 -0
  97. package/demos/js/tde/lensedstarfield.js +147 -0
  98. package/demos/js/tde/tdestar.js +317 -0
  99. package/demos/js/tde/tidalstream.js +356 -0
  100. package/demos/js/tde_old/blackhole.obj.js +354 -0
  101. package/demos/js/tde_old/debris.obj.js +791 -0
  102. package/demos/js/tde_old/flare.obj.js +239 -0
  103. package/demos/js/tde_old/index.js +448 -0
  104. package/demos/js/tde_old/star.obj.js +812 -0
  105. package/demos/js/tiles.js +312 -0
  106. package/demos/js/tweendemo.js +79 -0
  107. package/demos/js/visibility.js +102 -0
  108. package/demos/kerr.html +28 -0
  109. package/demos/lavalamp.html +27 -0
  110. package/demos/layouts.html +37 -0
  111. package/demos/logo.svg +4 -0
  112. package/demos/loop.html +84 -0
  113. package/demos/mondrian.html +32 -0
  114. package/demos/og_image.png +0 -0
  115. package/demos/opacity.html +36 -0
  116. package/demos/painter.html +39 -0
  117. package/demos/particles-showcase.html +28 -0
  118. package/demos/particles.html +24 -0
  119. package/demos/patterns.html +33 -0
  120. package/demos/penrose-game.html +31 -0
  121. package/demos/pipeline.html +737 -0
  122. package/demos/scene.html +33 -0
  123. package/demos/scenes.html +96 -0
  124. package/demos/schrodinger.html +27 -0
  125. package/demos/schwarzschild.html +27 -0
  126. package/demos/shapes.html +16 -0
  127. package/demos/space.html +85 -0
  128. package/demos/spacetime.html +27 -0
  129. package/demos/svgtween.html +29 -0
  130. package/demos/tde.html +28 -0
  131. package/demos/tiles.html +28 -0
  132. package/demos/transforms.html +400 -0
  133. package/demos/tween.html +45 -0
  134. package/demos/visibility.html +33 -0
  135. package/disk_example.png +0 -0
  136. package/docs/README.md +222 -0
  137. package/docs/concepts/architecture-overview.md +204 -0
  138. package/docs/concepts/lifecycle.md +255 -0
  139. package/docs/concepts/rendering-pipeline.md +279 -0
  140. package/docs/concepts/tde-zorder.md +106 -0
  141. package/docs/concepts/two-layer-architecture.md +229 -0
  142. package/docs/getting-started/first-game.md +354 -0
  143. package/docs/getting-started/hello-world.md +269 -0
  144. package/docs/getting-started/installation.md +157 -0
  145. package/docs/modules/collision/README.md +453 -0
  146. package/docs/modules/fluent/README.md +1075 -0
  147. package/docs/modules/game/README.md +303 -0
  148. package/docs/modules/isometric-camera.md +210 -0
  149. package/docs/modules/isometric.md +275 -0
  150. package/docs/modules/painter/README.md +328 -0
  151. package/docs/modules/particle/README.md +559 -0
  152. package/docs/modules/shapes/README.md +221 -0
  153. package/docs/modules/shapes/base/euclidian.md +123 -0
  154. package/docs/modules/shapes/base/geometry2d.md +204 -0
  155. package/docs/modules/shapes/base/renderable.md +215 -0
  156. package/docs/modules/shapes/base/shape.md +262 -0
  157. package/docs/modules/shapes/base/transformable.md +243 -0
  158. package/docs/modules/shapes/hierarchy.md +218 -0
  159. package/docs/modules/state/README.md +577 -0
  160. package/docs/modules/util/README.md +99 -0
  161. package/docs/modules/util/camera3d.md +412 -0
  162. package/docs/modules/util/scene3d.md +395 -0
  163. package/index.html +17 -0
  164. package/jsdoc.json +50 -0
  165. package/package.json +55 -0
  166. package/readme.md +599 -0
  167. package/scripts/build-demo.js +69 -0
  168. package/scripts/bundle4llm.js +276 -0
  169. package/scripts/clearconsole.js +48 -0
  170. package/src/collision/collision-system.js +332 -0
  171. package/src/collision/collision.js +303 -0
  172. package/src/collision/index.js +10 -0
  173. package/src/fluent/fluent-game.js +430 -0
  174. package/src/fluent/fluent-go.js +1060 -0
  175. package/src/fluent/fluent-layer.js +152 -0
  176. package/src/fluent/fluent-scene.js +291 -0
  177. package/src/fluent/index.js +98 -0
  178. package/src/fluent/sketch.js +380 -0
  179. package/src/game/game.js +467 -0
  180. package/src/game/index.js +49 -0
  181. package/src/game/objects/go.js +220 -0
  182. package/src/game/objects/imagego.js +30 -0
  183. package/src/game/objects/index.js +54 -0
  184. package/src/game/objects/isometric-scene.js +260 -0
  185. package/src/game/objects/layoutscene.js +549 -0
  186. package/src/game/objects/scene.js +175 -0
  187. package/src/game/objects/scene3d.js +118 -0
  188. package/src/game/objects/text.js +221 -0
  189. package/src/game/objects/wrapper.js +232 -0
  190. package/src/game/pipeline.js +243 -0
  191. package/src/game/ui/button.js +396 -0
  192. package/src/game/ui/cursor.js +93 -0
  193. package/src/game/ui/fps.js +91 -0
  194. package/src/game/ui/index.js +5 -0
  195. package/src/game/ui/togglebutton.js +93 -0
  196. package/src/game/ui/tooltip.js +249 -0
  197. package/src/index.js +25 -0
  198. package/src/io/events.js +20 -0
  199. package/src/io/index.js +86 -0
  200. package/src/io/input.js +70 -0
  201. package/src/io/keys.js +152 -0
  202. package/src/io/mouse.js +61 -0
  203. package/src/io/touch.js +39 -0
  204. package/src/logger/debugtab.js +138 -0
  205. package/src/logger/index.js +3 -0
  206. package/src/logger/loggable.js +47 -0
  207. package/src/logger/logger.js +113 -0
  208. package/src/math/complex.js +37 -0
  209. package/src/math/constants.js +1 -0
  210. package/src/math/fractal.js +1271 -0
  211. package/src/math/gr.js +201 -0
  212. package/src/math/heat.js +202 -0
  213. package/src/math/index.js +12 -0
  214. package/src/math/noise.js +433 -0
  215. package/src/math/orbital.js +191 -0
  216. package/src/math/patterns.js +1339 -0
  217. package/src/math/penrose.js +259 -0
  218. package/src/math/quantum.js +115 -0
  219. package/src/math/random.js +195 -0
  220. package/src/math/tensor.js +1009 -0
  221. package/src/mixins/anchor.js +131 -0
  222. package/src/mixins/draggable.js +72 -0
  223. package/src/mixins/index.js +2 -0
  224. package/src/motion/bezier.js +132 -0
  225. package/src/motion/bounce.js +58 -0
  226. package/src/motion/easing.js +349 -0
  227. package/src/motion/float.js +130 -0
  228. package/src/motion/follow.js +125 -0
  229. package/src/motion/hop.js +52 -0
  230. package/src/motion/index.js +82 -0
  231. package/src/motion/motion.js +1124 -0
  232. package/src/motion/orbit.js +49 -0
  233. package/src/motion/oscillate.js +39 -0
  234. package/src/motion/parabolic.js +141 -0
  235. package/src/motion/patrol.js +147 -0
  236. package/src/motion/pendulum.js +48 -0
  237. package/src/motion/pulse.js +88 -0
  238. package/src/motion/shake.js +83 -0
  239. package/src/motion/spiral.js +144 -0
  240. package/src/motion/spring.js +150 -0
  241. package/src/motion/swing.js +47 -0
  242. package/src/motion/tween.js +92 -0
  243. package/src/motion/tweenetik.js +139 -0
  244. package/src/motion/waypoint.js +210 -0
  245. package/src/painter/index.js +8 -0
  246. package/src/painter/painter.colors.js +331 -0
  247. package/src/painter/painter.effects.js +230 -0
  248. package/src/painter/painter.img.js +229 -0
  249. package/src/painter/painter.js +295 -0
  250. package/src/painter/painter.lines.js +189 -0
  251. package/src/painter/painter.opacity.js +41 -0
  252. package/src/painter/painter.shapes.js +277 -0
  253. package/src/painter/painter.text.js +273 -0
  254. package/src/particle/emitter.js +124 -0
  255. package/src/particle/index.js +11 -0
  256. package/src/particle/particle-system.js +322 -0
  257. package/src/particle/particle.js +71 -0
  258. package/src/particle/updaters.js +170 -0
  259. package/src/shapes/arc.js +43 -0
  260. package/src/shapes/arrow.js +33 -0
  261. package/src/shapes/bezier.js +42 -0
  262. package/src/shapes/circle.js +62 -0
  263. package/src/shapes/clouds.js +56 -0
  264. package/src/shapes/cone.js +219 -0
  265. package/src/shapes/cross.js +70 -0
  266. package/src/shapes/cube.js +244 -0
  267. package/src/shapes/cylinder.js +254 -0
  268. package/src/shapes/diamond.js +48 -0
  269. package/src/shapes/euclidian.js +111 -0
  270. package/src/shapes/figure.js +115 -0
  271. package/src/shapes/geometry.js +220 -0
  272. package/src/shapes/group.js +375 -0
  273. package/src/shapes/heart.js +42 -0
  274. package/src/shapes/hexagon.js +26 -0
  275. package/src/shapes/image.js +192 -0
  276. package/src/shapes/index.js +111 -0
  277. package/src/shapes/line.js +29 -0
  278. package/src/shapes/pattern.js +90 -0
  279. package/src/shapes/pin.js +44 -0
  280. package/src/shapes/poly.js +31 -0
  281. package/src/shapes/prism.js +226 -0
  282. package/src/shapes/rect.js +35 -0
  283. package/src/shapes/renderable.js +333 -0
  284. package/src/shapes/ring.js +26 -0
  285. package/src/shapes/roundrect.js +95 -0
  286. package/src/shapes/shape.js +117 -0
  287. package/src/shapes/slice.js +26 -0
  288. package/src/shapes/sphere.js +314 -0
  289. package/src/shapes/sphere3d.js +537 -0
  290. package/src/shapes/square.js +15 -0
  291. package/src/shapes/star.js +99 -0
  292. package/src/shapes/svg.js +408 -0
  293. package/src/shapes/text.js +553 -0
  294. package/src/shapes/traceable.js +83 -0
  295. package/src/shapes/transform.js +357 -0
  296. package/src/shapes/transformable.js +172 -0
  297. package/src/shapes/triangle.js +26 -0
  298. package/src/sound/index.js +17 -0
  299. package/src/sound/sound.js +473 -0
  300. package/src/sound/synth.analyzer.js +149 -0
  301. package/src/sound/synth.effects.js +207 -0
  302. package/src/sound/synth.envelope.js +59 -0
  303. package/src/sound/synth.js +229 -0
  304. package/src/sound/synth.musical.js +160 -0
  305. package/src/sound/synth.noise.js +85 -0
  306. package/src/sound/synth.oscillators.js +293 -0
  307. package/src/state/index.js +10 -0
  308. package/src/state/state-machine.js +371 -0
  309. package/src/util/camera3d.js +438 -0
  310. package/src/util/index.js +6 -0
  311. package/src/util/isometric-camera.js +235 -0
  312. package/src/util/layout.js +317 -0
  313. package/src/util/position.js +147 -0
  314. package/src/util/tasks.js +47 -0
  315. package/src/util/zindex.js +287 -0
  316. package/src/webgl/index.js +9 -0
  317. package/src/webgl/shaders/sphere-shaders.js +994 -0
  318. package/src/webgl/webgl-renderer.js +388 -0
  319. package/tde.png +0 -0
  320. package/test/math/orbital.test.js +61 -0
  321. package/test/math/tensor.test.js +114 -0
  322. package/test/particle/emitter.test.js +204 -0
  323. package/test/particle/particle-system.test.js +310 -0
  324. package/test/particle/particle.test.js +116 -0
  325. package/test/particle/updaters.test.js +386 -0
  326. package/test/setup.js +120 -0
  327. package/test/shapes/euclidian.test.js +44 -0
  328. package/test/shapes/geometry.test.js +86 -0
  329. package/test/shapes/group.test.js +86 -0
  330. package/test/shapes/rectangle.test.js +64 -0
  331. package/test/shapes/transform.test.js +379 -0
  332. package/test/util/camera3d.test.js +428 -0
  333. package/test/util/scene3d.test.js +352 -0
  334. package/types/collision.d.ts +249 -0
  335. package/types/common.d.ts +155 -0
  336. package/types/game.d.ts +497 -0
  337. package/types/index.d.ts +309 -0
  338. package/types/io.d.ts +188 -0
  339. package/types/logger.d.ts +127 -0
  340. package/types/math.d.ts +268 -0
  341. package/types/mixins.d.ts +92 -0
  342. package/types/motion.d.ts +678 -0
  343. package/types/painter.d.ts +378 -0
  344. package/types/shapes.d.ts +864 -0
  345. package/types/sound.d.ts +672 -0
  346. package/types/state.d.ts +251 -0
  347. package/types/util.d.ts +253 -0
  348. package/vite.config.js +50 -0
  349. package/vitest.config.js +13 -0
@@ -0,0 +1,259 @@
1
+ import { Complex } from "./complex";
2
+
3
+ // Helper function to convert hex color to RGB array
4
+ function hexToRgb(hex) {
5
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
6
+ return result
7
+ ? [
8
+ parseInt(result[1], 16) / 255,
9
+ parseInt(result[2], 16) / 255,
10
+ parseInt(result[3], 16) / 255,
11
+ 1, // Add alpha
12
+ ]
13
+ : [0, 0, 0, 1];
14
+ }
15
+
16
+ export function generatePenroseTilingPixels(width = 800, height = 800, options) {
17
+ // Get parameters from options or use defaults
18
+ const {
19
+ divisions = 5,
20
+ zoomType = "in",
21
+ color1 = [255, 0, 0, 255], // Red for thin rhombi (0-255 range)
22
+ color2 = [0, 0, 255, 255], // Blue for thick rhombi (0-255 range)
23
+ color3 = [0, 0, 0, 255], // Black for outlines (0-255 range)
24
+ backgroundColor = [255, 255, 255, 255], // White background (0-255 range)
25
+ } = options || {};
26
+
27
+ // Create pixel data array (RGBA - 4 bytes per pixel)
28
+ const pixels = new Uint8ClampedArray(width * height * 4);
29
+
30
+ // Fill with background color initially
31
+ for (let i = 0; i < pixels.length; i += 4) {
32
+ pixels[i] = backgroundColor[0]; // R
33
+ pixels[i + 1] = backgroundColor[1]; // G
34
+ pixels[i + 2] = backgroundColor[2]; // B
35
+ pixels[i + 3] = backgroundColor[3] || 255; // A (default to 255 if not provided)
36
+ }
37
+
38
+ // Set up scale
39
+ const scale = zoomType === "in" ? 1 : 2;
40
+ const maxDim = Math.max(width, height);
41
+ const scaleX = maxDim / scale;
42
+ const scaleY = maxDim / scale;
43
+ const translateX = 0.5 * scale;
44
+ const translateY = 0.5 * scale;
45
+
46
+ // Golden ratio
47
+ const phi = (Math.sqrt(5) + 1) / 2;
48
+
49
+ // Base for the tiling pattern
50
+ const base = 5;
51
+
52
+ // Create initial triangles
53
+ let triangles = [];
54
+ for (let i = 0; i < base * 2; i++) {
55
+ const v2 = Complex.fromPolar(1, ((2 * i - 1) * Math.PI) / (base * 2));
56
+ const v3 = Complex.fromPolar(1, ((2 * i + 1) * Math.PI) / (base * 2));
57
+
58
+ if (i % 2 === 0) {
59
+ // Mirror every other triangle
60
+ triangles.push(["thin", new Complex(0), v3, v2]);
61
+ } else {
62
+ triangles.push(["thin", new Complex(0), v2, v3]);
63
+ }
64
+ }
65
+
66
+ // Subdivide triangles
67
+ for (let i = 0; i < divisions; i++) {
68
+ const newTriangles = [];
69
+
70
+ for (const [shape, v1, v2, v3] of triangles) {
71
+ if (shape === "thin") {
72
+ // Divide thin rhombus
73
+ const p1 = v1.add(v2.subtract(v1).scale(1 / phi));
74
+ newTriangles.push(["thin", v3, p1, v2]);
75
+ newTriangles.push(["thicc", p1, v3, v1]);
76
+ } else {
77
+ // Divide thicc rhombus
78
+ const p2 = v2.add(v1.subtract(v2).scale(1 / phi));
79
+ const p3 = v2.add(v3.subtract(v2).scale(1 / phi));
80
+ newTriangles.push(["thicc", p3, v3, v1]);
81
+ newTriangles.push(["thicc", p2, p3, v2]);
82
+ newTriangles.push(["thin", p3, p2, v1]);
83
+ }
84
+ }
85
+
86
+ triangles = newTriangles;
87
+ }
88
+
89
+ // Convert triangle coordinates to screen coordinates
90
+ function worldToScreen(point) {
91
+ const x = Math.floor(
92
+ ((point.real * scaleX + translateX * scaleX) * width) / maxDim
93
+ );
94
+ const y = Math.floor(
95
+ ((point.imag * scaleY + translateY * scaleY) * height) / maxDim
96
+ );
97
+ return { x, y };
98
+ }
99
+
100
+ // Draw triangles to pixel array using rasterization
101
+ for (const [shape, v1, v2, v3] of triangles) {
102
+ const p1 = worldToScreen(v1);
103
+ const p2 = worldToScreen(v2);
104
+ const p3 = worldToScreen(v3);
105
+
106
+ // Get color based on shape
107
+ const color = shape === "thin" ? color1 : color2;
108
+
109
+ // Rasterize triangle
110
+ fillTriangle(pixels, p1, p2, p3, color, width, height);
111
+ }
112
+
113
+ // Optional: Draw outlines (simplified)
114
+ if (color3 && color3[3] > 0) {
115
+ for (const [shape, v1, v2, v3] of triangles) {
116
+ const p1 = worldToScreen(v1);
117
+ const p2 = worldToScreen(v2);
118
+ const p3 = worldToScreen(v3);
119
+
120
+ // Draw outline with 1px width
121
+ drawLine(pixels, p1, p2, color3, width, height);
122
+ drawLine(pixels, p2, p3, color3, width, height);
123
+ drawLine(pixels, p3, p1, color3, width, height);
124
+ }
125
+ }
126
+
127
+ return pixels;
128
+ }
129
+
130
+ // Helper function to fill a triangle
131
+ function fillTriangle(pixels, p1, p2, p3, color, width, height) {
132
+ // Sort vertices by y-coordinate
133
+ if (p1.y > p2.y) [p1, p2] = [p2, p1];
134
+ if (p1.y > p3.y) [p1, p3] = [p3, p1];
135
+ if (p2.y > p3.y) [p2, p3] = [p3, p2];
136
+
137
+ // Get RGB values (now directly in 0-255 range)
138
+ const r = color[0];
139
+ const g = color[1];
140
+ const b = color[2];
141
+ const a = color[3] || 255; // Default alpha to 255 if not provided
142
+
143
+ // Flat bottom triangle
144
+ if (p2.y === p3.y) {
145
+ fillFlatBottomTriangle(pixels, p1, p2, p3, r, g, b, a, width, height);
146
+ }
147
+ // Flat top triangle
148
+ else if (p1.y === p2.y) {
149
+ fillFlatTopTriangle(pixels, p1, p2, p3, r, g, b, a, width, height);
150
+ }
151
+ // General triangle - split into flat-bottom and flat-top triangles
152
+ else {
153
+ // Calculate the new vertex
154
+ const p4 = {
155
+ x: Math.floor(p1.x + ((p2.y - p1.y) / (p3.y - p1.y)) * (p3.x - p1.x)),
156
+ y: p2.y
157
+ };
158
+
159
+ fillFlatBottomTriangle(pixels, p1, p2, p4, r, g, b, a, width, height);
160
+ fillFlatTopTriangle(pixels, p2, p4, p3, r, g, b, a, width, height);
161
+ }
162
+ }
163
+
164
+ // Helper to fill a flat-bottom triangle
165
+ function fillFlatBottomTriangle(pixels, p1, p2, p3, r, g, b, a, width, height) {
166
+ const invSlope1 = (p2.x - p1.x) / (p2.y - p1.y || 1); // Avoid division by zero
167
+ const invSlope2 = (p3.x - p1.x) / (p3.y - p1.y || 1);
168
+
169
+ let curx1 = p1.x;
170
+ let curx2 = p1.x;
171
+
172
+ for (let scanlineY = p1.y; scanlineY <= p2.y; scanlineY++) {
173
+ if (scanlineY >= 0 && scanlineY < height) {
174
+ const startX = Math.max(0, Math.min(Math.floor(curx1), width - 1));
175
+ const endX = Math.max(0, Math.min(Math.floor(curx2), width - 1));
176
+
177
+ for (let x = Math.min(startX, endX); x <= Math.max(startX, endX); x++) {
178
+ const index = (scanlineY * width + x) * 4;
179
+ if (index >= 0 && index < pixels.length - 3) {
180
+ pixels[index] = r;
181
+ pixels[index + 1] = g;
182
+ pixels[index + 2] = b;
183
+ pixels[index + 3] = a;
184
+ }
185
+ }
186
+ }
187
+
188
+ curx1 += invSlope1;
189
+ curx2 += invSlope2;
190
+ }
191
+ }
192
+
193
+ // Helper to fill a flat-top triangle
194
+ function fillFlatTopTriangle(pixels, p1, p2, p3, r, g, b, a, width, height) {
195
+ const invSlope1 = (p3.x - p1.x) / (p3.y - p1.y || 1);
196
+ const invSlope2 = (p3.x - p2.x) / (p3.y - p2.y || 1);
197
+
198
+ let curx1 = p3.x;
199
+ let curx2 = p3.x;
200
+
201
+ for (let scanlineY = p3.y; scanlineY > p1.y; scanlineY--) {
202
+ if (scanlineY >= 0 && scanlineY < height) {
203
+ curx1 -= invSlope1;
204
+ curx2 -= invSlope2;
205
+
206
+ const startX = Math.max(0, Math.min(Math.floor(curx1), width - 1));
207
+ const endX = Math.max(0, Math.min(Math.floor(curx2), width - 1));
208
+
209
+ for (let x = Math.min(startX, endX); x <= Math.max(startX, endX); x++) {
210
+ const index = (scanlineY * width + x) * 4;
211
+ if (index >= 0 && index < pixels.length - 3) {
212
+ pixels[index] = r;
213
+ pixels[index + 1] = g;
214
+ pixels[index + 2] = b;
215
+ pixels[index + 3] = a;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ // Helper to draw a line using Bresenham's algorithm
223
+ function drawLine(pixels, p1, p2, color, width, height) {
224
+ // Get RGB values (now directly in 0-255 range)
225
+ const r = color[0];
226
+ const g = color[1];
227
+ const b = color[2];
228
+ const a = color[3] || 255;
229
+
230
+ let x0 = p1.x;
231
+ let y0 = p1.y;
232
+ let x1 = p2.x;
233
+ let y1 = p2.y;
234
+
235
+ const dx = Math.abs(x1 - x0);
236
+ const dy = Math.abs(y1 - y0);
237
+ const sx = x0 < x1 ? 1 : -1;
238
+ const sy = y0 < y1 ? 1 : -1;
239
+ let err = dx - dy;
240
+
241
+ while (true) {
242
+ if (x0 >= 0 && x0 < width && y0 >= 0 && y0 < height) {
243
+ const index = (y0 * width + x0) * 4;
244
+ if (index >= 0 && index < pixels.length - 3) {
245
+ // Simple alpha blending
246
+ const alpha = a / 255;
247
+ pixels[index] = Math.round(pixels[index] * (1 - alpha) + r * alpha);
248
+ pixels[index + 1] = Math.round(pixels[index + 1] * (1 - alpha) + g * alpha);
249
+ pixels[index + 2] = Math.round(pixels[index + 2] * (1 - alpha) + b * alpha);
250
+ pixels[index + 3] = 255; // Full opacity for the line
251
+ }
252
+ }
253
+
254
+ if (x0 === x1 && y0 === y1) break;
255
+ const e2 = 2 * err;
256
+ if (e2 > -dy) { err -= dy; x0 += sx; }
257
+ if (e2 < dx) { err += dx; y0 += sy; }
258
+ }
259
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Quantum mechanics utilities for wave function visualization.
3
+ *
4
+ * @example
5
+ * import { gaussianWavePacket } from './quantum.js';
6
+ *
7
+ * const params = { amplitude: 1, sigma: 0.8, k: 8, omega: 4, velocity: 0.5 };
8
+ * const { psi, envelope } = gaussianWavePacket(x, t, params);
9
+ */
10
+
11
+ import { Complex } from "./complex.js";
12
+
13
+ // ─────────────────────────────────────────────────────────────────────────────
14
+ // WAVE PACKETS
15
+ // ─────────────────────────────────────────────────────────────────────────────
16
+
17
+ /**
18
+ * Gaussian wave packet: Ψ(x,t) = A * e^(-(x-vt)²/4σ²) * e^(i(kx-ωt))
19
+ *
20
+ * This is the standard form of a localized quantum mechanical wave packet.
21
+ * The Gaussian envelope moves with group velocity v, while the complex
22
+ * phase oscillates with wave number k and frequency ω.
23
+ *
24
+ * @param {number} x - Position
25
+ * @param {number} t - Time
26
+ * @param {Object} params - Wave packet parameters
27
+ * @param {number} params.amplitude - Amplitude A
28
+ * @param {number} params.sigma - Width of Gaussian envelope σ
29
+ * @param {number} params.k - Wave number (controls oscillation frequency)
30
+ * @param {number} params.omega - Angular frequency ω
31
+ * @param {number} params.velocity - Group velocity v
32
+ * @returns {{ psi: Complex, envelope: number }} Wave function and envelope
33
+ */
34
+ export function gaussianWavePacket(x, t, { amplitude, sigma, k, omega, velocity }) {
35
+ // Center of wave packet moves with group velocity
36
+ const x0 = velocity * t;
37
+
38
+ // Gaussian envelope: A * e^(-(x-x0)²/4σ²)
39
+ const dx = x - x0;
40
+ const envelope = amplitude * Math.exp(-(dx * dx) / (4 * sigma * sigma));
41
+
42
+ // Complex phase: e^(i(kx - ωt))
43
+ const phase = k * x - omega * t;
44
+ const psi = Complex.fromPolar(envelope, phase);
45
+
46
+ return { psi, envelope };
47
+ }
48
+
49
+ /**
50
+ * Compute just the phase for a plane wave: e^(i(kx - ωt))
51
+ *
52
+ * @param {number} x - Position
53
+ * @param {number} t - Time
54
+ * @param {number} k - Wave number
55
+ * @param {number} omega - Angular frequency
56
+ * @returns {number} Phase angle in radians
57
+ */
58
+ export function planeWavePhase(x, t, k, omega) {
59
+ return k * x - omega * t;
60
+ }
61
+
62
+ /**
63
+ * Gaussian envelope only (probability amplitude).
64
+ *
65
+ * |Ψ|² ∝ e^(-(x-x0)²/2σ²)
66
+ *
67
+ * @param {number} x - Position
68
+ * @param {number} x0 - Center of packet
69
+ * @param {number} sigma - Width parameter
70
+ * @param {number} [amplitude=1] - Amplitude
71
+ * @returns {number} Envelope value
72
+ */
73
+ export function gaussianEnvelope(x, x0, sigma, amplitude = 1) {
74
+ const dx = x - x0;
75
+ return amplitude * Math.exp(-(dx * dx) / (4 * sigma * sigma));
76
+ }
77
+
78
+ // ─────────────────────────────────────────────────────────────────────────────
79
+ // WAVE FUNCTION UTILITIES
80
+ // ─────────────────────────────────────────────────────────────────────────────
81
+
82
+ /**
83
+ * Probability density |Ψ|².
84
+ *
85
+ * @param {Complex} psi - Wave function value
86
+ * @returns {number} Probability density
87
+ */
88
+ export function probabilityDensity(psi) {
89
+ return psi.real * psi.real + psi.imag * psi.imag;
90
+ }
91
+
92
+ /**
93
+ * De Broglie wavelength: λ = 2π/k
94
+ *
95
+ * @param {number} k - Wave number
96
+ * @returns {number} Wavelength
97
+ */
98
+ export function deBroglieWavelength(k) {
99
+ return (2 * Math.PI) / k;
100
+ }
101
+
102
+ /**
103
+ * Group velocity from dispersion relation.
104
+ * For a free particle: v_g = dω/dk = ℏk/m
105
+ *
106
+ * @param {number} k - Wave number
107
+ * @param {number} omega - Angular frequency
108
+ * @param {number} [dk=0.01] - Step for numerical derivative
109
+ * @returns {number} Group velocity
110
+ */
111
+ export function groupVelocity(k, omega, dk = 0.01) {
112
+ // For visualization, we typically use v_g = ω/k (phase velocity)
113
+ // or a custom dispersion relation
114
+ return omega / k;
115
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * 🎲 Random - A utility class for game-friendly pseudorandom operations.
3
+ *
4
+ * Provides a variety of methods for positioning, noise, variance, and procedural randomness.
5
+ * Designed to simplify natural placement, movement, and behaviors across the GCanvas engine.
6
+ */
7
+ export class Random {
8
+ /**
9
+ * @typedef {'centered'|'topleft'} CoordinateSystem
10
+ */
11
+
12
+ /**
13
+ * Random point centered in the area with symmetric spread.
14
+ * Spawns across the whole area, not just positive quadrant.
15
+ * @param {number} x - X coordinate (center or top-left depending on coordSystem)
16
+ * @param {number} y - Y coordinate (center or top-left depending on coordSystem)
17
+ * @param {number} width - Area width
18
+ * @param {number} height - Area height
19
+ * @param {number} spread - Controls how far from center (default: 1 = full area)
20
+ * @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
21
+ * @returns {{x: number, y: number}}
22
+ */
23
+ static symmetric(x, y, width, height, spread = 1, coordSystem = 'topleft') {
24
+ const cx = coordSystem === 'centered' ? x : x + width / 2;
25
+ const cy = coordSystem === 'centered' ? y : y + height / 2;
26
+
27
+ return {
28
+ x: cx + (Math.random() - 0.5) * width * spread,
29
+ y: cy + (Math.random() - 0.5) * height * spread,
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Uniformly random point within a given area
35
+ * @param {number} x - X coordinate (center or top-left depending on coordSystem)
36
+ * @param {number} y - Y coordinate (center or top-left depending on coordSystem)
37
+ * @param {number} width - Width of the area
38
+ * @param {number} height - Height of the area
39
+ * @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
40
+ * @returns {{x: number, y: number}}
41
+ */
42
+ static pointInBox(x, y, width, height, coordSystem = 'topleft') {
43
+ if (coordSystem === 'centered') {
44
+ return {
45
+ x: x + (Math.random() - 0.5) * width,
46
+ y: y + (Math.random() - 0.5) * height,
47
+ };
48
+ } else {
49
+ return {
50
+ x: x + Math.random() * width,
51
+ y: y + Math.random() * height,
52
+ };
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Random point centered around specified point, with uniform variance
58
+ * @param {number} x - X coordinate (center or top-left depending on coordSystem)
59
+ * @param {number} y - Y coordinate (center or top-left depending on coordSystem)
60
+ * @param {number} width - Width of the area
61
+ * @param {number} height - Height of the area
62
+ * @param {number} variance - Max deviation from center
63
+ * @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
64
+ * @returns {{x: number, y: number}}
65
+ */
66
+ static centered(x, y, width, height, variance = 50, coordSystem = 'topleft') {
67
+ const cx = coordSystem === 'centered' ? x : x + width / 2;
68
+ const cy = coordSystem === 'centered' ? y : y + height / 2;
69
+
70
+ return {
71
+ x: cx + (Math.random() - 0.5) * 2 * variance,
72
+ y: cy + (Math.random() - 0.5) * 2 * variance,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Gaussian (bell-curve) distributed point around center
78
+ * @param {number} x - X coordinate (center or top-left depending on coordSystem)
79
+ * @param {number} y - Y coordinate (center or top-left depending on coordSystem)
80
+ * @param {number} width - Width of the area
81
+ * @param {number} height - Height of the area
82
+ * @param {number} stdDev - Standard deviation (spread)
83
+ * @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
84
+ * @returns {{x: number, y: number}}
85
+ */
86
+ static gaussian(x, y, width, height, stdDev = 40, coordSystem = 'topleft') {
87
+ const cx = coordSystem === 'centered' ? x : x + width / 2;
88
+ const cy = coordSystem === 'centered' ? y : y + height / 2;
89
+
90
+ return {
91
+ x: cx + Random._gaussian(0, stdDev),
92
+ y: cy + Random._gaussian(0, stdDev),
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Polar random point around center, within radius
98
+ * @param {number} x - X coordinate (center or top-left depending on coordSystem)
99
+ * @param {number} y - Y coordinate (center or top-left depending on coordSystem)
100
+ * @param {number} width - Width of area
101
+ * @param {number} height - Height of area
102
+ * @param {number} radius - Maximum radial distance
103
+ * @param {CoordinateSystem} coordSystem - 'centered' if x,y are the center point, 'topleft' if they're the top left corner
104
+ * @returns {{x: number, y: number}}
105
+ */
106
+ static radial(x, y, width, height, radius = 100, coordSystem = 'topleft') {
107
+ const cx = coordSystem === 'centered' ? x : x + width / 2;
108
+ const cy = coordSystem === 'centered' ? y : y + height / 2;
109
+
110
+ const angle = Math.random() * Math.PI * 2;
111
+ const r = Math.random() * radius;
112
+
113
+ return {
114
+ x: cx + Math.cos(angle) * r,
115
+ y: cy + Math.sin(angle) * r,
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Choose a random item from an array
121
+ * @template T
122
+ * @param {T[]} array
123
+ * @returns {T}
124
+ */
125
+ static pick(array) {
126
+ return array[Math.floor(Math.random() * array.length)];
127
+ }
128
+
129
+ /**
130
+ * Picks a random item from the array that is NOT equal to the excluded item.
131
+ * Returns `undefined` if no valid alternative exists.
132
+ * @template T
133
+ * @param {T[]} array - Source array
134
+ * @param {T} exclude - Value to exclude from selection
135
+ * @returns {T|undefined}
136
+ */
137
+ static pickOther(array, exclude) {
138
+ const filtered = array.filter((item) => item !== exclude);
139
+ if (filtered.length === 0) return undefined;
140
+ return Random.pick(filtered);
141
+ }
142
+
143
+ /**
144
+ * Random float between min and max
145
+ * @param {number} min
146
+ * @param {number} max
147
+ * @returns {number}
148
+ */
149
+ static float(min, max) {
150
+ return min + Math.random() * (max - min);
151
+ }
152
+
153
+ /**
154
+ * Random integer between min and max (inclusive)
155
+ * @param {number} min
156
+ * @param {number} max
157
+ * @returns {number}
158
+ */
159
+ static int(min, max) {
160
+ return Math.floor(Random.float(min, max + 1));
161
+ }
162
+
163
+ /**
164
+ * Chance roll: returns true with given probability (0 to 1)
165
+ * @param {number} probability
166
+ * @returns {boolean}
167
+ */
168
+ static chance(probability = 0.5) {
169
+ return Math.random() < probability;
170
+ }
171
+
172
+ /**
173
+ * Flip a coin (true or false, 50/50)
174
+ * @returns {boolean}
175
+ */
176
+ static coin() {
177
+ return Math.random() < 0.5;
178
+ }
179
+
180
+ /**
181
+ * Internal Gaussian random generator (Box-Muller transform)
182
+ * @param {number} mean
183
+ * @param {number} stdDev
184
+ * @returns {number}
185
+ * @private
186
+ */
187
+ static _gaussian(mean = 0, stdDev = 1) {
188
+ let u = 1 - Math.random();
189
+ let v = 1 - Math.random();
190
+ return (
191
+ mean +
192
+ stdDev * Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v)
193
+ );
194
+ }
195
+ }