@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,303 @@
1
+ /**
2
+ * Collision - Static collision detection utilities
3
+ *
4
+ * Provides various collision detection algorithms for 2D game development.
5
+ * All methods work with bounds objects: { x, y, width, height }
6
+ *
7
+ * @example
8
+ * // Basic rectangle collision
9
+ * const playerBounds = player.getBounds();
10
+ * const enemyBounds = enemy.getBounds();
11
+ * if (Collision.rectRect(playerBounds, enemyBounds)) {
12
+ * console.log('Hit!');
13
+ * }
14
+ *
15
+ * @example
16
+ * // Point collision
17
+ * if (Collision.pointRect(mouseX, mouseY, button.getBounds())) {
18
+ * button.highlight();
19
+ * }
20
+ */
21
+ export class Collision {
22
+ /**
23
+ * Test if two axis-aligned rectangles intersect (AABB collision)
24
+ *
25
+ * @param {Object} a - First rectangle { x, y, width, height }
26
+ * @param {Object} b - Second rectangle { x, y, width, height }
27
+ * @returns {boolean} True if rectangles overlap
28
+ */
29
+ static rectRect(a, b) {
30
+ return (
31
+ a.x < b.x + b.width &&
32
+ a.x + a.width > b.x &&
33
+ a.y < b.y + b.height &&
34
+ a.y + a.height > b.y
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Alias for rectRect - matches common naming convention
40
+ * @param {Object} a - First rectangle
41
+ * @param {Object} b - Second rectangle
42
+ * @returns {boolean} True if rectangles overlap
43
+ */
44
+ static intersects(a, b) {
45
+ return Collision.rectRect(a, b);
46
+ }
47
+
48
+ /**
49
+ * Test if a point is inside a rectangle
50
+ *
51
+ * @param {number} px - Point X coordinate
52
+ * @param {number} py - Point Y coordinate
53
+ * @param {Object} rect - Rectangle { x, y, width, height }
54
+ * @returns {boolean} True if point is inside rectangle
55
+ */
56
+ static pointRect(px, py, rect) {
57
+ return (
58
+ px >= rect.x &&
59
+ px <= rect.x + rect.width &&
60
+ py >= rect.y &&
61
+ py <= rect.y + rect.height
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Test if two circles intersect
67
+ *
68
+ * @param {Object} a - First circle { x, y, radius }
69
+ * @param {Object} b - Second circle { x, y, radius }
70
+ * @returns {boolean} True if circles overlap
71
+ */
72
+ static circleCircle(a, b) {
73
+ const dx = a.x - b.x;
74
+ const dy = a.y - b.y;
75
+ const distSq = dx * dx + dy * dy;
76
+ const radii = a.radius + b.radius;
77
+ return distSq <= radii * radii;
78
+ }
79
+
80
+ /**
81
+ * Test if a point is inside a circle
82
+ *
83
+ * @param {number} px - Point X coordinate
84
+ * @param {number} py - Point Y coordinate
85
+ * @param {Object} circle - Circle { x, y, radius }
86
+ * @returns {boolean} True if point is inside circle
87
+ */
88
+ static pointCircle(px, py, circle) {
89
+ const dx = px - circle.x;
90
+ const dy = py - circle.y;
91
+ return dx * dx + dy * dy <= circle.radius * circle.radius;
92
+ }
93
+
94
+ /**
95
+ * Test if a circle and rectangle intersect
96
+ *
97
+ * @param {Object} circle - Circle { x, y, radius }
98
+ * @param {Object} rect - Rectangle { x, y, width, height }
99
+ * @returns {boolean} True if circle and rectangle overlap
100
+ */
101
+ static circleRect(circle, rect) {
102
+ // Find the closest point on the rectangle to the circle center
103
+ const closestX = Math.max(rect.x, Math.min(circle.x, rect.x + rect.width));
104
+ const closestY = Math.max(rect.y, Math.min(circle.y, rect.y + rect.height));
105
+
106
+ // Calculate distance from circle center to closest point
107
+ const dx = circle.x - closestX;
108
+ const dy = circle.y - closestY;
109
+
110
+ return dx * dx + dy * dy <= circle.radius * circle.radius;
111
+ }
112
+
113
+ /**
114
+ * Test if a line segment intersects a rectangle
115
+ * Useful for things like lightning bolts, lasers, or raycast-style collision
116
+ *
117
+ * @param {number} x1 - Line start X
118
+ * @param {number} y1 - Line start Y
119
+ * @param {number} x2 - Line end X
120
+ * @param {number} y2 - Line end Y
121
+ * @param {Object} rect - Rectangle { x, y, width, height }
122
+ * @param {number} [thickness=0] - Optional line thickness (expands rect check)
123
+ * @returns {boolean} True if line intersects rectangle
124
+ */
125
+ static lineRect(x1, y1, x2, y2, rect, thickness = 0) {
126
+ // Expand rect by half thickness if provided
127
+ const rx = rect.x - thickness / 2;
128
+ const ry = rect.y - thickness / 2;
129
+ const rw = rect.width + thickness;
130
+ const rh = rect.height + thickness;
131
+
132
+ // Check if either endpoint is inside the rectangle
133
+ if (Collision.pointRect(x1, y1, { x: rx, y: ry, width: rw, height: rh }) ||
134
+ Collision.pointRect(x2, y2, { x: rx, y: ry, width: rw, height: rh })) {
135
+ return true;
136
+ }
137
+
138
+ // Check line against all 4 edges of rectangle
139
+ const left = Collision.lineLine(x1, y1, x2, y2, rx, ry, rx, ry + rh);
140
+ const right = Collision.lineLine(x1, y1, x2, y2, rx + rw, ry, rx + rw, ry + rh);
141
+ const top = Collision.lineLine(x1, y1, x2, y2, rx, ry, rx + rw, ry);
142
+ const bottom = Collision.lineLine(x1, y1, x2, y2, rx, ry + rh, rx + rw, ry + rh);
143
+
144
+ return left || right || top || bottom;
145
+ }
146
+
147
+ /**
148
+ * Test if two line segments intersect
149
+ *
150
+ * @param {number} x1 - First line start X
151
+ * @param {number} y1 - First line start Y
152
+ * @param {number} x2 - First line end X
153
+ * @param {number} y2 - First line end Y
154
+ * @param {number} x3 - Second line start X
155
+ * @param {number} y3 - Second line start Y
156
+ * @param {number} x4 - Second line end X
157
+ * @param {number} y4 - Second line end Y
158
+ * @returns {boolean} True if lines intersect
159
+ */
160
+ static lineLine(x1, y1, x2, y2, x3, y3, x4, y4) {
161
+ // Calculate direction vectors
162
+ const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
163
+
164
+ // Parallel lines
165
+ if (denom === 0) return false;
166
+
167
+ const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
168
+ const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
169
+
170
+ // Check if intersection is within both line segments
171
+ return ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1;
172
+ }
173
+
174
+ /**
175
+ * Test if multiple line segments intersect a rectangle
176
+ * Useful for complex shapes like lightning bolts
177
+ *
178
+ * @param {Array<Object>} segments - Array of { x1, y1, x2, y2 } segments
179
+ * @param {Object} rect - Rectangle { x, y, width, height }
180
+ * @param {number} [thickness=0] - Optional line thickness
181
+ * @returns {boolean} True if any segment intersects rectangle
182
+ */
183
+ static segmentsRect(segments, rect, thickness = 0) {
184
+ for (const seg of segments) {
185
+ if (Collision.lineRect(seg.x1, seg.y1, seg.x2, seg.y2, rect, thickness)) {
186
+ return true;
187
+ }
188
+ }
189
+ return false;
190
+ }
191
+
192
+ /**
193
+ * Get the intersection depth between two rectangles (for collision response)
194
+ * Returns null if no collision, otherwise returns overlap amounts
195
+ *
196
+ * @param {Object} a - First rectangle { x, y, width, height }
197
+ * @param {Object} b - Second rectangle { x, y, width, height }
198
+ * @returns {Object|null} { x, y } overlap depths, or null if no collision
199
+ */
200
+ static getOverlap(a, b) {
201
+ if (!Collision.rectRect(a, b)) return null;
202
+
203
+ // Calculate overlap on each axis
204
+ const overlapX = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
205
+ const overlapY = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);
206
+
207
+ return { x: overlapX, y: overlapY };
208
+ }
209
+
210
+ /**
211
+ * Get the minimum translation vector to separate two rectangles
212
+ * Returns the smallest push needed to stop the collision
213
+ *
214
+ * @param {Object} a - Moving rectangle { x, y, width, height }
215
+ * @param {Object} b - Static rectangle { x, y, width, height }
216
+ * @returns {Object|null} { x, y } translation vector, or null if no collision
217
+ */
218
+ static getMTV(a, b) {
219
+ const overlap = Collision.getOverlap(a, b);
220
+ if (!overlap) return null;
221
+
222
+ // Find center of each rectangle
223
+ const aCenterX = a.x + a.width / 2;
224
+ const aCenterY = a.y + a.height / 2;
225
+ const bCenterX = b.x + b.width / 2;
226
+ const bCenterY = b.y + b.height / 2;
227
+
228
+ // Determine push direction
229
+ const pushX = aCenterX < bCenterX ? -overlap.x : overlap.x;
230
+ const pushY = aCenterY < bCenterY ? -overlap.y : overlap.y;
231
+
232
+ // Return the smaller translation (minimum translation vector)
233
+ if (Math.abs(overlap.x) < Math.abs(overlap.y)) {
234
+ return { x: pushX, y: 0 };
235
+ } else {
236
+ return { x: 0, y: pushY };
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Check if a moving rectangle will collide with a static one (sweep test)
242
+ * Useful for fast-moving objects like bullets
243
+ *
244
+ * @param {Object} rect - Moving rectangle { x, y, width, height }
245
+ * @param {number} vx - Velocity X
246
+ * @param {number} vy - Velocity Y
247
+ * @param {Object} target - Target rectangle { x, y, width, height }
248
+ * @returns {Object|null} { time, normalX, normalY } collision info, or null
249
+ */
250
+ static sweep(rect, vx, vy, target) {
251
+ // Expand target by moving rect size (Minkowski sum)
252
+ const expanded = {
253
+ x: target.x - rect.width / 2,
254
+ y: target.y - rect.height / 2,
255
+ width: target.width + rect.width,
256
+ height: target.height + rect.height,
257
+ };
258
+
259
+ // Ray-box intersection from center of moving rect
260
+ const originX = rect.x + rect.width / 2;
261
+ const originY = rect.y + rect.height / 2;
262
+
263
+ // Calculate entry and exit times for each axis
264
+ let tMinX, tMaxX, tMinY, tMaxY;
265
+
266
+ if (vx !== 0) {
267
+ tMinX = (expanded.x - originX) / vx;
268
+ tMaxX = (expanded.x + expanded.width - originX) / vx;
269
+ if (tMinX > tMaxX) [tMinX, tMaxX] = [tMaxX, tMinX];
270
+ } else {
271
+ tMinX = originX >= expanded.x && originX <= expanded.x + expanded.width ? -Infinity : Infinity;
272
+ tMaxX = originX >= expanded.x && originX <= expanded.x + expanded.width ? Infinity : -Infinity;
273
+ }
274
+
275
+ if (vy !== 0) {
276
+ tMinY = (expanded.y - originY) / vy;
277
+ tMaxY = (expanded.y + expanded.height - originY) / vy;
278
+ if (tMinY > tMaxY) [tMinY, tMaxY] = [tMaxY, tMinY];
279
+ } else {
280
+ tMinY = originY >= expanded.y && originY <= expanded.y + expanded.height ? -Infinity : Infinity;
281
+ tMaxY = originY >= expanded.y && originY <= expanded.y + expanded.height ? Infinity : -Infinity;
282
+ }
283
+
284
+ // Find overlap of intervals
285
+ const tEntry = Math.max(tMinX, tMinY);
286
+ const tExit = Math.min(tMaxX, tMaxY);
287
+
288
+ // No collision if exit before entry, or entry is after full movement
289
+ if (tEntry > tExit || tEntry < 0 || tEntry > 1) {
290
+ return null;
291
+ }
292
+
293
+ // Calculate collision normal
294
+ let normalX = 0, normalY = 0;
295
+ if (tMinX > tMinY) {
296
+ normalX = vx > 0 ? -1 : 1;
297
+ } else {
298
+ normalY = vy > 0 ? -1 : 1;
299
+ }
300
+
301
+ return { time: tEntry, normalX, normalY };
302
+ }
303
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Collision Detection Module
3
+ *
4
+ * Provides utilities for 2D collision detection and management.
5
+ *
6
+ * @module collision
7
+ */
8
+
9
+ export { Collision } from "./collision.js";
10
+ export { CollisionSystem } from "./collision-system.js";