@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.
- package/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- 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
|
+
}
|