From 9d1c445dcd2d130aef4132e03eea1944d01c2fb8 Mon Sep 17 00:00:00 2001 From: BurntWaffleCake Date: Tue, 12 Dec 2023 10:58:13 -0600 Subject: [PATCH] box collision ariticle added --- .../physicsEngineArticleNavObject.html | 1 + .../BoxCollision/images/AABB.drawio.svg | 4 + projects/Physics/BoxCollision/index.html | 96 +++++++++++++++++++ .../rotation}/index.html | 17 +++- .../rotation}/script.js | 56 +++++++---- 5 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 projects/Physics/BoxCollision/images/AABB.drawio.svg create mode 100644 projects/Physics/BoxCollision/index.html rename projects/Physics/{polygonCollision => BoxCollision/rotation}/index.html (78%) rename projects/Physics/{polygonCollision => BoxCollision/rotation}/script.js (92%) diff --git a/articleTemplateObjects/physicsEngineArticleNavObject.html b/articleTemplateObjects/physicsEngineArticleNavObject.html index 7138779..637cda8 100644 --- a/articleTemplateObjects/physicsEngineArticleNavObject.html +++ b/articleTemplateObjects/physicsEngineArticleNavObject.html @@ -69,6 +69,7 @@
Ball Collision Ball Collision Optimization + Box Collision Polygon Collision Physics Engine diff --git a/projects/Physics/BoxCollision/images/AABB.drawio.svg b/projects/Physics/BoxCollision/images/AABB.drawio.svg new file mode 100644 index 0000000..6114762 --- /dev/null +++ b/projects/Physics/BoxCollision/images/AABB.drawio.svg @@ -0,0 +1,4 @@ + + + +
Colliding
Colliding
Not Colliding
Not Colliding
Text is not SVG - cannot display
\ No newline at end of file diff --git a/projects/Physics/BoxCollision/index.html b/projects/Physics/BoxCollision/index.html new file mode 100644 index 0000000..151b686 --- /dev/null +++ b/projects/Physics/BoxCollision/index.html @@ -0,0 +1,96 @@ + + + + + + + Box Collision + + + + + + + + + + + + +
+ + +
+ +
+

Box Collision

+ +

Basic Polygon Collision

+

+ Balls are the simplest collision shape we need to handle. Unfortunately, most complex volumes are not made up of only balls and instead use complex polygons and shapes to fully or partially + represent itself. In our case with 2D collision, triangles are the most basic shape we can decompose these volumes into. This article will not go into triangle and other decomposition + algorithms and instead focus on collision between box or rectangular volumes. +

+ +

AABB Box Collision

+

+ AABB collision is the most simple way to calculate the collision between two boxes. All of the edges of the box we want to collide is mapped to the x and y axis of the coordinate frame we + are using. This means that all colliders do not have rotation allowing for every efficient and fast calculations to test for collision. In our case (with 2d collision), we only need to test + the x and y axis to see if two colliders are colliding which we can calculate using the overlap of the projection of these shapes onto said axis. If these projections do not overlap in any + of the tested axis, we can conclude that the two volumes are not colliding. This concept of using projections is further expanded in the SAT collision algorithm. +

+ +

calculating overlap between two lines

+ +

\( overlap =\max{(0, \min{(A_{max}, B_{max})}-\max{(A_{min}, B_{min})})}\)

+ +
+ +
+ +

Pros and Cons

+

+ AABB is extremely fast compared to other collision algorithms and its simple nature allows for efficient and fast calculations. This is why many games use AABB when testing for collision + using bounding boxes that encapsulate the entire body that needs collision. This, however, comes at the cost of precision as AABB is locked to the axis and does not allow any rotation. AABB + also only represents a box and is unable to test for collision between more complex geometry. AABB serves as an excellent approximation of a body's volume and is often used as an + optimization method for more complex collision algorithms. +

+ +

SAT Algorithm

+

+ SAT or Separating Axis Theorem is a method of finding collision using a set of axis and overlaps between projections. The overlap calculations from AABB is applicable here with the max and + min points being derived by projecting the colliding geometries onto an arbitrary axis instead of using the x and y axis. SAT allows for our boxes to have rotation and can be extended to any + convex polygon (any line drawn across a geometry can not have move than 2 points of intersection with that geometry). SAT uses an arbitrarily large number of axis to find whether two + geometries collide, however; we only need to test a set number of them in order to determine whether two geometries are colliding. These axis can be obtained by fetching all the normals of + both geometries, ignoring any normals that are parallel to existing axis. By projecting both geometries to these axis and making sure every axis has an overlap, we can guarantee a + collision/overlap between those two geometries (as long as they are both convex). +

+
+ +
+ + +
+ + diff --git a/projects/Physics/polygonCollision/index.html b/projects/Physics/BoxCollision/rotation/index.html similarity index 78% rename from projects/Physics/polygonCollision/index.html rename to projects/Physics/BoxCollision/rotation/index.html index 824cb88..59eb082 100644 --- a/projects/Physics/polygonCollision/index.html +++ b/projects/Physics/BoxCollision/rotation/index.html @@ -37,6 +37,10 @@ p { margin: 0; + } + + options { + margin: 0; position: absolute; color: white; top: 1rem; @@ -45,10 +49,15 @@ -

- Drag mouse to move boxes
- Press space to spawn new boxes -

+ +

+ Drag mouse to move boxes
+ Press space to spawn new boxes +

+
+ + +
diff --git a/projects/Physics/polygonCollision/script.js b/projects/Physics/BoxCollision/rotation/script.js similarity index 92% rename from projects/Physics/polygonCollision/script.js rename to projects/Physics/BoxCollision/rotation/script.js index 403af9e..2b0f540 100644 --- a/projects/Physics/polygonCollision/script.js +++ b/projects/Physics/BoxCollision/rotation/script.js @@ -392,7 +392,7 @@ let boxes = []; const gravity = 0; const restitution = 1; -var substeps = 1; +var substeps = 4; let time = 0.0; function loop(t) { @@ -402,20 +402,23 @@ function loop(t) { if (mouse1Down) { let point = getMousePos(canvas, mousePos); let closestBox = boxes[0]; - let distance = Math.sqrt((point.x - closestBox.x) ** 2 + (point.y - closestBox.y) ** 2); - for (let i = 1; i < boxes.length; i++) { - let nextBox = boxes[i]; - let nextDistance = Math.sqrt((point.x - nextBox.x) ** 2 + (point.y - nextBox.y) ** 2); - if (nextDistance < distance) { - closestBox = nextBox; - distance = nextDistance; + + if (closestBox !== undefined) { + let distance = Math.sqrt((point.x - closestBox.x) ** 2 + (point.y - closestBox.y) ** 2); + for (let i = 1; i < boxes.length; i++) { + let nextBox = boxes[i]; + let nextDistance = Math.sqrt((point.x - nextBox.x) ** 2 + (point.y - nextBox.y) ** 2); + if (nextDistance < distance) { + closestBox = nextBox; + distance = nextDistance; + } } - } - closestBox.x = point.x; - closestBox.y = point.y; + closestBox.x = point.x; + closestBox.y = point.y; - closestBox.dx = mouseDelta.x * 50; - closestBox.dy = mouseDelta.y * 50; + closestBox.dx = mouseDelta.x * 50; + closestBox.dy = mouseDelta.y * 50; + } } for (let c = 0; c < substeps; c++) { @@ -483,12 +486,12 @@ let mouse1Down = false; let mousePos = { x: 0, y: 0 }; let mouseDelta = { x: 0, y: 0 }; -document.addEventListener("mousedown", (event) => { - console.log("mousedown"); +canvas.addEventListener("mousedown", (event) => { if (event.buttons == 1) { mouse1Down = true; } }); + document.addEventListener("mouseup", (event) => { if (event.buttons == 0) { mouse1Down = false; @@ -504,7 +507,6 @@ canvas.addEventListener("mousemove", (event) => { document.onkeydown = function (event) { if (event.key == " ") { - console.log("spacePressed"); boxes.push( new Box( Math.random() * width, @@ -519,3 +521,25 @@ document.onkeydown = function (event) { ); } }; + +const clearButton = document.getElementById("clearButton"); +const flingButton = document.getElementById("flingButton"); + +clearButton.onkeydown = function (e) { + e.preventDefault(); +}; + +clearButton.onclick = function (e) { + boxes = []; +}; + +flingButton.onkeydown = function (e) { + e.preventDefault(); +}; + +flingButton.onclick = function (e) { + boxes.forEach((box) => { + box.dx += box.m / 2 - Math.random() * box.m; + box.dy += box.m / 2 - Math.random() * box.m; + }); +};