@inglorious/engine 0.1.0 → 0.2.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 (292) hide show
  1. package/README.md +39 -36
  2. package/package.json +20 -33
  3. package/src/engine/ai/movement/dynamic/align.js +63 -63
  4. package/src/engine/ai/movement/dynamic/arrive.js +42 -43
  5. package/src/engine/ai/movement/dynamic/evade.js +38 -38
  6. package/src/engine/ai/movement/dynamic/face.js +19 -20
  7. package/src/engine/ai/movement/dynamic/flee.js +45 -45
  8. package/src/engine/ai/movement/dynamic/look-where-youre-going.js +16 -17
  9. package/src/engine/ai/movement/dynamic/match-velocity.js +51 -50
  10. package/src/engine/ai/movement/dynamic/pursue.js +38 -38
  11. package/src/engine/ai/movement/dynamic/seek.js +44 -44
  12. package/src/engine/ai/movement/dynamic/wander.js +31 -32
  13. package/src/engine/ai/movement/kinematic/align.js +37 -37
  14. package/src/engine/ai/movement/kinematic/arrive.js +42 -42
  15. package/src/engine/ai/movement/kinematic/face.js +19 -20
  16. package/src/engine/ai/movement/kinematic/flee.js +26 -26
  17. package/src/engine/ai/movement/kinematic/seek.js +26 -26
  18. package/src/engine/ai/movement/kinematic/seek.test.js +42 -42
  19. package/src/engine/ai/movement/kinematic/wander-as-seek.js +31 -31
  20. package/src/engine/ai/movement/kinematic/wander.js +27 -27
  21. package/src/engine/animation/sprite.js +101 -0
  22. package/src/engine/animation/ticker.js +38 -0
  23. package/src/engine/behaviors/camera.js +68 -0
  24. package/src/engine/behaviors/controls/dynamic/modern.js +76 -0
  25. package/src/engine/behaviors/controls/dynamic/shooter.js +84 -0
  26. package/src/engine/behaviors/controls/dynamic/tank.js +69 -0
  27. package/src/engine/behaviors/controls/event-handlers.js +17 -0
  28. package/src/engine/behaviors/controls/kinematic/modern.js +76 -0
  29. package/src/engine/behaviors/controls/kinematic/shooter.js +82 -0
  30. package/src/engine/behaviors/controls/kinematic/tank.js +67 -0
  31. package/src/engine/behaviors/debug/collision.js +35 -0
  32. package/src/engine/behaviors/fps.js +29 -0
  33. package/src/engine/behaviors/fsm.js +33 -0
  34. package/src/{game/decorators → engine/behaviors}/fsm.test.js +49 -56
  35. package/src/engine/behaviors/game.js +15 -0
  36. package/src/engine/behaviors/input/controls.js +37 -0
  37. package/src/engine/behaviors/input/gamepad.js +114 -0
  38. package/src/engine/behaviors/input/input.js +48 -0
  39. package/src/engine/behaviors/input/keyboard.js +64 -0
  40. package/src/engine/behaviors/input/mouse.js +91 -0
  41. package/src/engine/behaviors/physics/bouncy.js +25 -0
  42. package/src/engine/behaviors/physics/clamped.js +36 -0
  43. package/src/{game/decorators/collisions.js → engine/behaviors/physics/collidable.js} +20 -24
  44. package/src/engine/behaviors/physics/jumpable.js +145 -0
  45. package/src/engine/behaviors/ui/button.js +17 -0
  46. package/src/engine/collision/detection.js +110 -115
  47. package/src/engine/core/api.js +34 -0
  48. package/src/engine/core/dev-tools.js +135 -0
  49. package/src/engine/core/engine.js +119 -0
  50. package/src/engine/core/loop.js +15 -0
  51. package/src/engine/{loop → core/loops}/animation-frame.js +25 -26
  52. package/src/engine/{loop → core/loops}/elapsed.js +22 -23
  53. package/src/engine/{loop → core/loops}/fixed.js +27 -28
  54. package/src/engine/{loop → core/loops}/flash.js +13 -14
  55. package/src/engine/{loop → core/loops}/lag.js +26 -27
  56. package/src/engine/core/select.js +26 -0
  57. package/src/engine/core/store.js +178 -0
  58. package/src/engine/core/store.test.js +110 -0
  59. package/src/engine/movement/dynamic/modern.js +21 -24
  60. package/src/engine/movement/dynamic/tank.js +43 -43
  61. package/src/engine/movement/kinematic/modern.js +16 -16
  62. package/src/engine/movement/kinematic/modern.test.js +27 -27
  63. package/src/engine/movement/kinematic/tank.js +27 -27
  64. package/src/engine/physics/bounds.js +138 -0
  65. package/src/engine/physics/position.js +43 -0
  66. package/src/engine/physics/position.test.js +80 -0
  67. package/src/engine/systems/sprite-animation.js +27 -0
  68. package/src/main.js +10 -5
  69. package/src/renderers/canvas/absolute-position.js +18 -0
  70. package/src/renderers/canvas/camera.js +13 -0
  71. package/src/renderers/canvas/canvas-renderer.js +68 -0
  72. package/src/{ui → renderers}/canvas/character.js +38 -35
  73. package/src/{ui → renderers}/canvas/form/button.js +25 -25
  74. package/src/{ui → renderers}/canvas/fps.js +18 -18
  75. package/src/renderers/canvas/image/hitmask.js +51 -0
  76. package/src/{ui → renderers}/canvas/image/image.js +34 -37
  77. package/src/{ui → renderers}/canvas/image/sprite.js +49 -49
  78. package/src/{ui → renderers}/canvas/image/tilemap.js +66 -64
  79. package/src/{ui → renderers}/canvas/mouse.js +37 -37
  80. package/src/renderers/canvas/rendering-system.js +79 -0
  81. package/src/{ui → renderers}/canvas/shapes/circle.js +29 -31
  82. package/src/{ui → renderers}/canvas/shapes/rectangle.js +27 -31
  83. package/src/renderers/react/game/character/index.jsx +20 -0
  84. package/src/{ui → renderers}/react/game/cursor/index.jsx +20 -20
  85. package/src/{ui → renderers}/react/game/form/fields/field/index.jsx +56 -56
  86. package/src/{ui → renderers}/react/game/form/fields/index.jsx +12 -12
  87. package/src/{ui → renderers}/react/game/form/index.jsx +22 -22
  88. package/src/{ui → renderers}/react/game/fps/index.jsx +16 -16
  89. package/src/{ui → renderers}/react/game/game.jsx +72 -71
  90. package/src/{ui → renderers}/react/game/index.jsx +29 -29
  91. package/src/{ui → renderers}/react/game/platform/index.jsx +30 -30
  92. package/src/{ui → renderers}/react/game/scene/index.jsx +27 -25
  93. package/src/{ui → renderers}/react/game/sprite/index.jsx +60 -58
  94. package/src/{ui → renderers}/react/game/stats/index.jsx +22 -22
  95. package/src/{ui → renderers}/react/hocs/with-absolute-position/index.jsx +20 -20
  96. package/src/{ui → renderers}/react/index.jsx +9 -9
  97. package/src/utils/algorithms/decision-tree.js +24 -24
  98. package/src/utils/algorithms/decision-tree.test.js +153 -102
  99. package/src/utils/algorithms/path-finding.js +155 -155
  100. package/src/utils/algorithms/path-finding.test.js +151 -151
  101. package/src/utils/data-structures/array.js +83 -83
  102. package/src/utils/data-structures/array.test.js +173 -173
  103. package/src/utils/data-structures/board.js +159 -159
  104. package/src/utils/data-structures/board.test.js +242 -242
  105. package/src/utils/data-structures/boolean.js +9 -9
  106. package/src/utils/data-structures/heap.js +164 -164
  107. package/src/utils/data-structures/heap.test.js +103 -103
  108. package/src/utils/data-structures/object.js +138 -102
  109. package/src/utils/data-structures/object.test.js +218 -121
  110. package/src/utils/data-structures/objects.js +66 -48
  111. package/src/utils/data-structures/objects.test.js +99 -99
  112. package/src/utils/data-structures/tree.js +36 -36
  113. package/src/utils/data-structures/tree.test.js +33 -33
  114. package/src/utils/functions/functions.js +19 -19
  115. package/src/utils/functions/functions.test.js +23 -23
  116. package/src/utils/math/geometry/circle.js +70 -117
  117. package/src/utils/math/geometry/circle.test.js +97 -97
  118. package/src/utils/math/geometry/hitmask.js +70 -39
  119. package/src/utils/math/geometry/hitmask.test.js +155 -84
  120. package/src/utils/math/geometry/line.js +35 -35
  121. package/src/utils/math/geometry/line.test.js +49 -49
  122. package/src/utils/math/geometry/point.js +78 -71
  123. package/src/utils/math/geometry/point.test.js +81 -81
  124. package/src/utils/math/geometry/rectangle.js +76 -45
  125. package/src/utils/math/geometry/rectangle.test.js +42 -42
  126. package/src/utils/math/geometry/segment.js +80 -80
  127. package/src/utils/math/geometry/segment.test.js +183 -183
  128. package/src/utils/math/geometry/triangle.js +15 -15
  129. package/src/utils/math/geometry/triangle.test.js +11 -11
  130. package/src/utils/math/linear-algebra/2d.js +28 -28
  131. package/src/utils/math/linear-algebra/2d.test.js +17 -17
  132. package/src/utils/math/linear-algebra/quaternion.js +22 -22
  133. package/src/utils/math/linear-algebra/quaternion.test.js +25 -25
  134. package/src/utils/math/linear-algebra/quaternions.js +20 -20
  135. package/src/utils/math/linear-algebra/quaternions.test.js +29 -29
  136. package/src/utils/math/linear-algebra/vector.js +327 -302
  137. package/src/utils/math/linear-algebra/vector.test.js +265 -257
  138. package/src/utils/math/linear-algebra/vectors.js +122 -122
  139. package/src/utils/math/linear-algebra/vectors.test.js +65 -65
  140. package/src/utils/math/linear-interpolation.js +9 -0
  141. package/src/utils/math/numbers.js +90 -90
  142. package/src/utils/math/numbers.test.js +137 -137
  143. package/src/utils/math/rng.js +44 -44
  144. package/src/utils/math/rng.test.js +39 -39
  145. package/src/utils/math/statistics.js +43 -43
  146. package/src/utils/math/statistics.test.js +47 -47
  147. package/src/utils/math/trigonometry.js +89 -89
  148. package/src/utils/math/trigonometry.test.js +52 -52
  149. package/src/utils/physics/acceleration.js +61 -63
  150. package/src/utils/physics/friction.js +28 -30
  151. package/src/utils/physics/friction.test.js +42 -44
  152. package/src/utils/physics/gravity.js +69 -71
  153. package/src/utils/physics/gravity.test.js +77 -80
  154. package/src/utils/physics/jump.js +31 -41
  155. package/src/utils/physics/velocity.js +36 -38
  156. package/src/docs/ai/movement/dynamic/align.js +0 -131
  157. package/src/docs/ai/movement/dynamic/arrive.js +0 -88
  158. package/src/docs/ai/movement/dynamic/dynamic.mdx +0 -99
  159. package/src/docs/ai/movement/dynamic/dynamic.stories.js +0 -58
  160. package/src/docs/ai/movement/dynamic/evade.js +0 -72
  161. package/src/docs/ai/movement/dynamic/face.js +0 -90
  162. package/src/docs/ai/movement/dynamic/flee.js +0 -38
  163. package/src/docs/ai/movement/dynamic/look-where-youre-going.js +0 -114
  164. package/src/docs/ai/movement/dynamic/match-velocity.js +0 -92
  165. package/src/docs/ai/movement/dynamic/pursue.js +0 -72
  166. package/src/docs/ai/movement/dynamic/seek.js +0 -37
  167. package/src/docs/ai/movement/dynamic/wander.js +0 -71
  168. package/src/docs/ai/movement/kinematic/align.js +0 -122
  169. package/src/docs/ai/movement/kinematic/arrive.js +0 -78
  170. package/src/docs/ai/movement/kinematic/face.js +0 -82
  171. package/src/docs/ai/movement/kinematic/flee.js +0 -36
  172. package/src/docs/ai/movement/kinematic/kinematic.mdx +0 -67
  173. package/src/docs/ai/movement/kinematic/kinematic.stories.js +0 -42
  174. package/src/docs/ai/movement/kinematic/seek.js +0 -34
  175. package/src/docs/ai/movement/kinematic/wander-as-seek.js +0 -62
  176. package/src/docs/ai/movement/kinematic/wander.js +0 -28
  177. package/src/docs/bounds.js +0 -7
  178. package/src/docs/code-reuse.js +0 -35
  179. package/src/docs/collision/circles.js +0 -58
  180. package/src/docs/collision/collision.mdx +0 -27
  181. package/src/docs/collision/collision.stories.js +0 -22
  182. package/src/docs/collision/platform.js +0 -76
  183. package/src/docs/collision/tilemap.js +0 -181
  184. package/src/docs/empty.js +0 -1
  185. package/src/docs/engine.mdx +0 -81
  186. package/src/docs/engine.stories.js +0 -37
  187. package/src/docs/event-handlers.js +0 -68
  188. package/src/docs/framerate.js +0 -37
  189. package/src/docs/game.jsx +0 -15
  190. package/src/docs/image/image.js +0 -19
  191. package/src/docs/image/image.stories.js +0 -22
  192. package/src/docs/image/sprite.js +0 -39
  193. package/src/docs/image/tilemap.js +0 -84
  194. package/src/docs/input/controls.js +0 -67
  195. package/src/docs/input/gamepad.js +0 -67
  196. package/src/docs/input/input.mdx +0 -55
  197. package/src/docs/input/input.stories.js +0 -27
  198. package/src/docs/input/keyboard.js +0 -58
  199. package/src/docs/input/mouse.js +0 -32
  200. package/src/docs/instances.js +0 -49
  201. package/src/docs/player/dynamic/double-jump.js +0 -90
  202. package/src/docs/player/dynamic/dynamic.stories.js +0 -32
  203. package/src/docs/player/dynamic/jump.js +0 -83
  204. package/src/docs/player/dynamic/modern-controls.js +0 -57
  205. package/src/docs/player/dynamic/shooter-controls.js +0 -51
  206. package/src/docs/player/dynamic/tank-controls.js +0 -44
  207. package/src/docs/player/kinematic/double-jump.js +0 -90
  208. package/src/docs/player/kinematic/jump.js +0 -82
  209. package/src/docs/player/kinematic/kinematic.stories.js +0 -32
  210. package/src/docs/player/kinematic/modern-controls.js +0 -56
  211. package/src/docs/player/kinematic/shooter-controls.js +0 -48
  212. package/src/docs/player/kinematic/tank-controls.js +0 -42
  213. package/src/docs/quick-start/first-game.js +0 -49
  214. package/src/docs/quick-start/hello-world.js +0 -1
  215. package/src/docs/quick-start.mdx +0 -127
  216. package/src/docs/quick-start.stories.js +0 -17
  217. package/src/docs/recipes/add-and-remove.js +0 -71
  218. package/src/docs/recipes/add-instance.js +0 -42
  219. package/src/docs/recipes/decision-tree.js +0 -169
  220. package/src/docs/recipes/random-instances.js +0 -25
  221. package/src/docs/recipes/recipes.mdx +0 -81
  222. package/src/docs/recipes/recipes.stories.js +0 -37
  223. package/src/docs/recipes/remove-instance.js +0 -52
  224. package/src/docs/recipes/states.js +0 -64
  225. package/src/docs/ui/button.js +0 -28
  226. package/src/docs/ui/form.stories.js +0 -55
  227. package/src/docs/ui-chooser.jsx +0 -6
  228. package/src/docs/utils/data-structures/object.mdx +0 -47
  229. package/src/docs/utils/data-structures/objects.mdx +0 -30
  230. package/src/docs/utils/functions/functions.mdx +0 -34
  231. package/src/docs/utils/math/geometry/circle.mdx +0 -55
  232. package/src/docs/utils/math/geometry/point.mdx +0 -38
  233. package/src/docs/utils/math/geometry/rectangle.mdx +0 -24
  234. package/src/docs/utils/math/geometry/segment.mdx +0 -55
  235. package/src/docs/utils/math/geometry/triangle.mdx +0 -22
  236. package/src/docs/utils/math/linear-algebra/2d.mdx +0 -22
  237. package/src/docs/utils/math/linear-algebra/quaternion.mdx +0 -21
  238. package/src/docs/utils/math/linear-algebra/quaternions.mdx +0 -22
  239. package/src/docs/utils/math/linear-algebra/vector.mdx +0 -177
  240. package/src/docs/utils/math/linear-algebra/vectors.mdx +0 -58
  241. package/src/docs/utils/math/numbers.mdx +0 -76
  242. package/src/docs/utils/math/random.mdx +0 -35
  243. package/src/docs/utils/math/statistics.mdx +0 -38
  244. package/src/docs/utils/math/trigonometry.mdx +0 -85
  245. package/src/docs/utils/physics/friction.mdx +0 -20
  246. package/src/docs/utils/physics/gravity.mdx +0 -28
  247. package/src/engine/loop.js +0 -15
  248. package/src/engine/store.js +0 -174
  249. package/src/engine/store.test.js +0 -256
  250. package/src/engine.js +0 -74
  251. package/src/game/animation.js +0 -26
  252. package/src/game/bounds.js +0 -66
  253. package/src/game/decorators/character.js +0 -5
  254. package/src/game/decorators/clamp-to-bounds.js +0 -15
  255. package/src/game/decorators/controls/dynamic/modern.js +0 -48
  256. package/src/game/decorators/controls/dynamic/shooter.js +0 -47
  257. package/src/game/decorators/controls/dynamic/tank.js +0 -55
  258. package/src/game/decorators/controls/kinematic/modern.js +0 -49
  259. package/src/game/decorators/controls/kinematic/shooter.js +0 -45
  260. package/src/game/decorators/controls/kinematic/tank.js +0 -52
  261. package/src/game/decorators/debug/collisions.js +0 -32
  262. package/src/game/decorators/double-jump.js +0 -70
  263. package/src/game/decorators/fps.js +0 -30
  264. package/src/game/decorators/fsm.js +0 -27
  265. package/src/game/decorators/game.js +0 -11
  266. package/src/game/decorators/image/image.js +0 -5
  267. package/src/game/decorators/image/sprite.js +0 -5
  268. package/src/game/decorators/image/tilemap.js +0 -5
  269. package/src/game/decorators/input/controls.js +0 -27
  270. package/src/game/decorators/input/gamepad.js +0 -74
  271. package/src/game/decorators/input/input.js +0 -41
  272. package/src/game/decorators/input/keyboard.js +0 -49
  273. package/src/game/decorators/input/mouse.js +0 -65
  274. package/src/game/decorators/jump.js +0 -72
  275. package/src/game/decorators/platform.js +0 -5
  276. package/src/game/decorators/ui/button.js +0 -21
  277. package/src/game/sprite.js +0 -119
  278. package/src/ui/canvas/absolute-position.js +0 -17
  279. package/src/ui/canvas/image/hitmask.js +0 -37
  280. package/src/ui/canvas.js +0 -81
  281. package/src/ui/react/game/character/index.jsx +0 -30
  282. package/src/utils/math/geometry/platform.js +0 -42
  283. package/src/utils/math/geometry/platform.test.js +0 -133
  284. /package/src/{ui → renderers}/react/game/character/character.module.scss +0 -0
  285. /package/src/{ui → renderers}/react/game/cursor/cursor.module.scss +0 -0
  286. /package/src/{ui → renderers}/react/game/form/fields/field/field.module.scss +0 -0
  287. /package/src/{ui → renderers}/react/game/form/fields/fields.module.scss +0 -0
  288. /package/src/{ui → renderers}/react/game/form/form.module.scss +0 -0
  289. /package/src/{ui → renderers}/react/game/platform/platform.module.scss +0 -0
  290. /package/src/{ui → renderers}/react/game/scene/scene.module.scss +0 -0
  291. /package/src/{ui → renderers}/react/game/sprite/sprite.module.css +0 -0
  292. /package/src/{ui → renderers}/react/hocs/with-absolute-position/with-absolute-position.module.scss +0 -0
@@ -1,117 +1,70 @@
1
- /**
2
- * @typedef {import("./types").Point} Point
3
- * @typedef {import("./types").Circle} Circle
4
- * @typedef {import("./types").Rectangle} Rectangle
5
- * @typedef {import("./types").Platform} Platform
6
- */
7
-
8
- import { subtract } from "@inglorious/utils/math/linear-algebra/vectors.js"
9
-
10
- import { clamp } from "../numbers.js"
11
- import { intersectsCircle as platformIntersectsCircle } from "./platform.js"
12
- import {
13
- intersectsCircle as pointIntersectsCircle,
14
- intersectsRectangle as pointIntersectsRectangle,
15
- } from "./point.js"
16
- import { intersectsCircle as segmentIntersectsCircle } from "./segment.js"
17
- import { hypothenuse } from "./triangle.js"
18
-
19
- const SQUARED = 2
20
- const INITIAL_SUM = 0
21
-
22
- /**
23
- * Checks if a circle intersects with a point.
24
- * @param {Circle} circle - The circle to check.
25
- * @param {Point} point - The point to check.
26
- * @returns {boolean} True if the point intersects the circle, false otherwise.
27
- */
28
- export function intersectsPoint(circle, point) {
29
- return pointIntersectsCircle(point, circle)
30
- }
31
-
32
- /**
33
- * Checks if two circles intersect.
34
- * @param {Circle} circle1 - The first circle.
35
- * @param {Circle} circle2 - The second circle.
36
- * @returns {boolean} True if the circles intersect, false otherwise.
37
- */
38
- export function intersectsCircle(circle1, circle2) {
39
- return (
40
- hypothenuse(...subtract(circle1.position, circle2.position)) <=
41
- circle1.radius + circle2.radius
42
- )
43
- }
44
-
45
- /**
46
- * Checks if a circle intersects with a rectangle.
47
- * @param {Circle} circle - The circle to check.
48
- * @param {Rectangle} rectangle - The rectangle to check.
49
- * @returns {boolean} True if the circle intersects the rectangle, false otherwise.
50
- */
51
- export function intersectsRectangle(circle, rectangle) {
52
- const [left, top, front] = rectangle.position
53
- const [width, height, depth] = rectangle.size
54
-
55
- const ltf = [left, top, front]
56
- const rtf = [left + width, top, front]
57
- const lbf = [left, top, front + depth]
58
- const rbf = [left + width, top, front + depth]
59
-
60
- const ltb = [left, top + height, front]
61
- const rtb = [left + width, top + height, front]
62
- const lbb = [left, top + height, front + depth]
63
- const rbb = [left + width, top + height, front + depth]
64
-
65
- return (
66
- // Center
67
- pointIntersectsRectangle(circle.position, rectangle) ||
68
- // Front face
69
- segmentIntersectsCircle({ from: ltf, to: rtf }, circle) ||
70
- segmentIntersectsCircle({ from: rtf, to: rbf }, circle) ||
71
- segmentIntersectsCircle({ from: rbf, to: lbf }, circle) ||
72
- segmentIntersectsCircle({ from: lbf, to: ltf }, circle) ||
73
- // Back face
74
- segmentIntersectsCircle({ from: ltb, to: rtb }, circle) ||
75
- segmentIntersectsCircle({ from: rtb, to: rbb }, circle) ||
76
- segmentIntersectsCircle({ from: rbb, to: lbb }, circle) ||
77
- segmentIntersectsCircle({ from: lbb, to: ltb }, circle) ||
78
- // Connecting edges
79
- segmentIntersectsCircle({ from: ltf, to: ltb }, circle) ||
80
- segmentIntersectsCircle({ from: rtf, to: rtb }, circle) ||
81
- segmentIntersectsCircle({ from: lbf, to: lbb }, circle) ||
82
- segmentIntersectsCircle({ from: rbf, to: rbb }, circle) ||
83
- // Corners
84
- isCircleWithinRectangleRadius(circle, rectangle)
85
- )
86
- }
87
-
88
- /**
89
- * Checks if a circle intersects with a platform.
90
- * @param {Circle} circle - The circle to check.
91
- * @param {Platform} platform - The platform to check.
92
- * @returns {boolean} True if the circle intersects the platform, false otherwise.
93
- */
94
- export function intersectsPlatform(circle, platform) {
95
- return platformIntersectsCircle(platform, circle)
96
- }
97
-
98
- function isCircleWithinRectangleRadius(circle, rectangle) {
99
- const [left, top, front] = rectangle.position
100
- const [width, height, depth] = rectangle.size
101
-
102
- // Find the closest point on the rectangle to the circle's center
103
- const [x, y, z] = circle.position
104
- const closestPoint = [
105
- clamp(x, left, left + width),
106
- clamp(y, top, top + height),
107
- clamp(z, front, front + depth),
108
- ]
109
-
110
- // Calculate the distance from the circle's center to the closest point
111
- const distanceSquared = subtract(circle.position, closestPoint).reduce(
112
- (sum, value) => sum + value ** SQUARED,
113
- INITIAL_SUM,
114
- )
115
-
116
- return distanceSquared <= circle.radius ** SQUARED
117
- }
1
+ /**
2
+ * @typedef {import("./types").Point} Point
3
+ * @typedef {import("./types").Circle} Circle
4
+ * @typedef {import("./types").Rectangle} Rectangle
5
+ * @typedef {import("./types").Platform} Platform
6
+ */
7
+
8
+ import { clamp } from "@inglorious/utils/math/linear-algebra/vector.js"
9
+ import { subtract } from "@inglorious/utils/math/linear-algebra/vectors.js"
10
+
11
+ import { intersectsCircle as pointIntersectsCircle } from "./point.js"
12
+ import { hypothenuse } from "./triangle.js"
13
+
14
+ const HALF = 2
15
+ const SQUARED = 2
16
+ const INITIAL_SUM = 0
17
+
18
+ /**
19
+ * Checks if a circle intersects with a point.
20
+ * @param {Circle} circle - The circle to check.
21
+ * @param {Point} point - The point to check.
22
+ * @returns {boolean} True if the point intersects the circle, false otherwise.
23
+ */
24
+ export function intersectsPoint(circle, point) {
25
+ return pointIntersectsCircle(point, circle)
26
+ }
27
+
28
+ /**
29
+ * Checks if two circles intersect.
30
+ * @param {Circle} circle1 - The first circle.
31
+ * @param {Circle} circle2 - The second circle.
32
+ * @returns {boolean} True if the circles intersect, false otherwise.
33
+ */
34
+ export function intersectsCircle(circle1, circle2) {
35
+ return (
36
+ hypothenuse(...subtract(circle1.position, circle2.position)) <=
37
+ circle1.radius + circle2.radius
38
+ )
39
+ }
40
+
41
+ /**
42
+ * Checks if a circle intersects with a rectangle.
43
+ * @param {Circle} circle - The circle to check.
44
+ * @param {Rectangle} rectangle - The rectangle to check.
45
+ * @returns {boolean} True if the circle intersects the rectangle, false otherwise.
46
+ */
47
+ export function intersectsRectangle(circle, rectangle) {
48
+ const [rectX, rectY, rectZ] = rectangle.position
49
+ const [width, height, depth] = rectangle.size
50
+
51
+ const left = rectX - width / HALF
52
+ const right = rectX + width / HALF
53
+ const bottom = rectY - height / HALF
54
+ const top = rectY + height / HALF
55
+ const back = rectZ - depth / HALF
56
+ const front = rectZ + depth / HALF
57
+
58
+ const closestPoint = clamp(
59
+ circle.position,
60
+ [left, bottom, back],
61
+ [right, top, front],
62
+ )
63
+
64
+ const distanceSquared = subtract(circle.position, closestPoint).reduce(
65
+ (sum, value) => sum + value ** SQUARED,
66
+ INITIAL_SUM,
67
+ )
68
+
69
+ return distanceSquared <= circle.radius ** SQUARED
70
+ }
@@ -1,97 +1,97 @@
1
- import { expect, test } from "vitest"
2
-
3
- import {
4
- intersectsCircle,
5
- intersectsPoint,
6
- intersectsRectangle,
7
- } from "./circle.js"
8
-
9
- test("it should prove that a circle around a point intersects with it", () => {
10
- const circle = {
11
- position: [1, 1, 0],
12
- radius: 1,
13
- }
14
- const point = [1.5, 1.5, 0]
15
-
16
- expect(intersectsPoint(circle, point)).toBe(true)
17
- })
18
-
19
- test("it should prove that two equal circles intersect", () => {
20
- const circle1 = { position: [1, 1, 0], radius: 1 }
21
- const circle2 = { position: [1, 1, 0], radius: 1 }
22
-
23
- expect(intersectsCircle(circle1, circle2)).toBe(true)
24
- })
25
-
26
- test("it should prove that two circles shifted by a small x intersect", () => {
27
- const circle1 = { position: [1, 1, 0], radius: 1 }
28
- const circle2 = { position: [2, 1, 0], radius: 1 }
29
-
30
- expect(intersectsCircle(circle1, circle2)).toBe(true)
31
- })
32
-
33
- test("it should prove that two touching circles intersect", () => {
34
- const circle1 = { position: [1, 1, 0], radius: 1 }
35
- const circle2 = { position: [3, 1, 0], radius: 1 }
36
-
37
- expect(intersectsCircle(circle1, circle2)).toBe(true)
38
- })
39
-
40
- test("it should prove that two non-touching circles do not intersect", () => {
41
- const circle1 = { position: [1, 1, 0], radius: 1 }
42
- const circle2 = { position: [4, 1, 0], radius: 1 }
43
-
44
- expect(intersectsCircle(circle1, circle2)).toBe(false)
45
- })
46
-
47
- test("it should prove that a circle inside of a rectangle intersects with it", () => {
48
- const circle = {
49
- position: [1, 1, 0],
50
- radius: 1,
51
- }
52
- const rectangle = {
53
- position: [-1, -1, 0],
54
- size: [4, 4, 0],
55
- }
56
-
57
- expect(intersectsRectangle(circle, rectangle)).toBe(true)
58
- })
59
-
60
- test("it should prove that a circle on the border of a rectangle intersects with it", () => {
61
- const circle = {
62
- position: [1, 1, 0],
63
- radius: 1,
64
- }
65
- const rectangle = {
66
- position: [-2, 0, 0],
67
- size: [2, 2, 0],
68
- }
69
-
70
- expect(intersectsRectangle(circle, rectangle)).toBe(true)
71
- })
72
-
73
- test("it should prove that a circle crossing a rectangle intersects with it", () => {
74
- const circle = {
75
- position: [1, 1, 0],
76
- radius: 1,
77
- }
78
- const rectangle = {
79
- position: [-1, -1, 0],
80
- size: [2, 2, 0],
81
- }
82
-
83
- expect(intersectsRectangle(circle, rectangle)).toBe(true)
84
- })
85
-
86
- test("it should prove that a circle outside of a rectangle does not intersect with it", () => {
87
- const circle = {
88
- position: [1, 1, 0],
89
- radius: 1,
90
- }
91
- const rectangle = {
92
- position: [-3, 0, 0],
93
- size: [2, 2, 0],
94
- }
95
-
96
- expect(intersectsRectangle(circle, rectangle)).toBe(false)
97
- })
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ intersectsCircle,
5
+ intersectsPoint,
6
+ intersectsRectangle,
7
+ } from "./circle.js"
8
+
9
+ test("it should prove that a circle around a point intersects with it", () => {
10
+ const circle = {
11
+ position: [1, 1, 0],
12
+ radius: 1,
13
+ }
14
+ const point = [1.5, 1.5, 0]
15
+
16
+ expect(intersectsPoint(circle, point)).toBe(true)
17
+ })
18
+
19
+ test("it should prove that two equal circles intersect", () => {
20
+ const circle1 = { position: [1, 1, 0], radius: 1 }
21
+ const circle2 = { position: [1, 1, 0], radius: 1 }
22
+
23
+ expect(intersectsCircle(circle1, circle2)).toBe(true)
24
+ })
25
+
26
+ test("it should prove that two circles shifted by a small x intersect", () => {
27
+ const circle1 = { position: [1, 1, 0], radius: 1 }
28
+ const circle2 = { position: [2, 1, 0], radius: 1 }
29
+
30
+ expect(intersectsCircle(circle1, circle2)).toBe(true)
31
+ })
32
+
33
+ test("it should prove that two touching circles intersect", () => {
34
+ const circle1 = { position: [1, 1, 0], radius: 1 }
35
+ const circle2 = { position: [3, 1, 0], radius: 1 }
36
+
37
+ expect(intersectsCircle(circle1, circle2)).toBe(true)
38
+ })
39
+
40
+ test("it should prove that two non-touching circles do not intersect", () => {
41
+ const circle1 = { position: [1, 1, 0], radius: 1 }
42
+ const circle2 = { position: [4, 1, 0], radius: 1 }
43
+
44
+ expect(intersectsCircle(circle1, circle2)).toBe(false)
45
+ })
46
+
47
+ test("it should prove that a circle inside of a rectangle intersects with it", () => {
48
+ const circle = {
49
+ position: [1, 1, 0],
50
+ radius: 1,
51
+ }
52
+ const rectangle = {
53
+ position: [-1, -1, 0],
54
+ size: [4, 4, 0],
55
+ }
56
+
57
+ expect(intersectsRectangle(circle, rectangle)).toBe(true)
58
+ })
59
+
60
+ test("it should prove that a circle touching the border of a rectangle intersects with it", () => {
61
+ const circle = {
62
+ position: [1, 1, 0],
63
+ radius: 1,
64
+ }
65
+ const rectangle = {
66
+ position: [-1, 1, 0], // Rectangle's right edge is at x=0, touching the circle's left edge.
67
+ size: [2, 2, 0],
68
+ }
69
+
70
+ expect(intersectsRectangle(circle, rectangle)).toBe(true)
71
+ })
72
+
73
+ test("it should prove that a circle crossing a rectangle intersects with it", () => {
74
+ const circle = {
75
+ position: [1, 1, 0],
76
+ radius: 1,
77
+ }
78
+ const rectangle = {
79
+ position: [0, 0, 0],
80
+ size: [2, 2, 0],
81
+ }
82
+
83
+ expect(intersectsRectangle(circle, rectangle)).toBe(true)
84
+ })
85
+
86
+ test("it should prove that a circle outside of a rectangle does not intersect with it", () => {
87
+ const circle = {
88
+ position: [1, 1, 0],
89
+ radius: 1,
90
+ }
91
+ const rectangle = {
92
+ position: [-3, 0, 0],
93
+ size: [2, 2, 0],
94
+ }
95
+
96
+ expect(intersectsRectangle(circle, rectangle)).toBe(false)
97
+ })
@@ -1,39 +1,70 @@
1
- // @see https://jonathanwhiting.com/tutorial/collision/
2
-
3
- const LOWER_BOUND = 0
4
- const TOP_TILE_OFFSET = 1 // Offset to include the top boundary tile
5
-
6
- export function findCollisions(hitmask, target) {
7
- const { position, tileSize, columns, heights } = hitmask
8
- const [left, top, front] = position
9
- const [tileWidth, tileHeight] = tileSize
10
- const rows = heights.length / columns
11
-
12
- const [x, y, z] = target.position
13
- const [width, height, depth] = target.size
14
-
15
- const leftTile = Math.floor((x - left) / tileWidth)
16
- const rightTile = Math.floor((x - left + width) / tileWidth)
17
- const bottomTile = Math.floor((z - front) / tileHeight)
18
- const topTile = Math.floor((z - front + depth) / tileHeight)
19
-
20
- if (
21
- leftTile < LOWER_BOUND ||
22
- rightTile > columns ||
23
- bottomTile < LOWER_BOUND ||
24
- topTile > rows
25
- ) {
26
- return false
27
- }
28
-
29
- for (let i = leftTile; i < rightTile; i++) {
30
- for (let j = bottomTile; j <= topTile + TOP_TILE_OFFSET; j++) {
31
- const heightAtTile = heights[i * columns + j]
32
- if (y + height <= top + heightAtTile) {
33
- return true
34
- }
35
- }
36
- }
37
-
38
- return false
39
- }
1
+ // @see https://jonathanwhiting.com/tutorial/collision/
2
+
3
+ import { intersectsRectangle } from "./rectangle.js"
4
+
5
+ const FIRST_TILE = 0
6
+ const HALF = 2
7
+ const LAST_ROW = 1
8
+
9
+ export function findCollisions(hitmask, target) {
10
+ const [tilemapX, tilemapY, tilemapZ] = hitmask.position
11
+ const [tileWidth, tileDepth] = hitmask.tileSize
12
+ const halfTileWidth = tileWidth / HALF
13
+ const halfTileDepth = tileDepth / HALF
14
+
15
+ const dRows = Math.ceil(hitmask.heights.length / hitmask.columns)
16
+ const tilemapWidth = hitmask.columns * tileWidth
17
+ const tilemapDepth = dRows * tileDepth
18
+
19
+ const tilemapLeft = tilemapX - tilemapWidth / HALF
20
+ const tilemapBack = tilemapZ - tilemapDepth / HALF
21
+
22
+ const [targetX, , targetZ] = target.position
23
+ const [targetWidth, , targetDepth] = target.size
24
+ const targetHalfWidth = targetWidth / HALF
25
+ const targetHalfDepth = targetDepth / HALF
26
+
27
+ const targetLeft = targetX - targetHalfWidth
28
+ const targetRight = targetX + targetHalfWidth
29
+ const targetBack = targetZ - targetHalfDepth
30
+ const targetFront = targetZ + targetHalfDepth
31
+
32
+ const minTileX = Math.floor((targetLeft - tilemapLeft) / tileWidth)
33
+ const maxTileX = Math.floor((targetRight - tilemapLeft) / tileWidth)
34
+ const minTileZ = Math.floor((targetBack - tilemapBack) / tileDepth)
35
+ const maxTileZ = Math.floor((targetFront - tilemapBack) / tileDepth)
36
+
37
+ for (let i = minTileX; i <= maxTileX; i++) {
38
+ for (let j = minTileZ; j <= maxTileZ; j++) {
39
+ if (
40
+ i < FIRST_TILE ||
41
+ i >= hitmask.columns ||
42
+ j < FIRST_TILE ||
43
+ j >= dRows
44
+ ) {
45
+ continue
46
+ }
47
+
48
+ const invertedRow = dRows - LAST_ROW - j
49
+ const tileIndex = invertedRow * hitmask.columns + i
50
+ const tileHeightValue = hitmask.heights[tileIndex]
51
+
52
+ if (tileHeightValue) {
53
+ const tileRectangle = {
54
+ position: [
55
+ tilemapLeft + i * tileWidth + halfTileWidth,
56
+ tilemapY,
57
+ tilemapBack + j * tileDepth + halfTileDepth,
58
+ ],
59
+ size: [tileWidth, tileHeightValue, tileDepth],
60
+ }
61
+
62
+ if (intersectsRectangle(target, tileRectangle)) {
63
+ return true
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ return false
70
+ }