diff --git a/bundle.js b/bundle.js index 95dbaac..9b401a2 100644 --- a/bundle.js +++ b/bundle.js @@ -2,8 +2,8 @@ // deno-lint-ignore-file // This code was bundled using `deno bundle` and it's not recommended to edit it manually -const axisAlignedCollision = (aa1, aa2)=>{ - return aa1.x < aa2.x + aa2.w && aa1.x + aa1.w > aa2.x && aa1.y < aa2.y + aa2.h && aa1.y + aa1.h > aa2.y; +const axisAlignedContains = (aa1, aa2)=>{ + return aa1.x < aa2.x && aa1.y < aa2.y && aa1.x + aa1.w > aa2.x + aa2.w && aa1.y + aa1.h > aa2.y + aa2.h; }; const Constants = { TWO_PI: Math.PI * 2 @@ -293,6 +293,89 @@ class OriginVector extends Vector { return new OriginVector(origin, v); } } +const satCollision = (s1, s2)=>{ + const shape1 = s1.points.map((p)=>new Vector(p).add(s1.center)); + const shape2 = s2.points.map((p)=>new Vector(p).add(s2.center)); + if (shape1.length < 2 || shape2.length < 2) { + throw "Insufficient shape data in satCollision"; + } + new Vector(s1.center).sub(s2.center); + for(let i = 0; i < shape1.length; i++){ + const axis = shape1[i].normal(shape1.at(i - 1)); + let [p1min, p1minDot] = Vector.vectorProjectionAndDot(shape1[0], axis); + let [p1max, p1maxDot] = [ + p1min, + p1minDot + ]; + for (const point of shape1){ + const [projected, dot] = Vector.vectorProjectionAndDot(point, axis); + p1min = dot < p1minDot ? projected : p1min; + p1minDot = Math.min(dot, p1minDot); + p1max = dot > p1maxDot ? projected : p1max; + p1maxDot = Math.max(dot, p1maxDot); + } + let [p2min, p2minDot] = Vector.vectorProjectionAndDot(shape2[0], axis); + let [p2max, p2maxDot] = [ + p2min, + p2minDot + ]; + for (const point of shape2){ + const [projected, dot] = Vector.vectorProjectionAndDot(point, axis); + p2min = dot < p2minDot ? projected : p2min; + p2minDot = Math.min(dot, p2minDot); + p2max = dot > p2maxDot ? projected : p2max; + p2maxDot = Math.max(dot, p2maxDot); + } + if (p1minDot - p2maxDot > 0 || p2minDot - p1maxDot > 0) { + return false; + } + [ + axis, + p1max, + p1min, + p2max, + p2min + ]; + } + for(let i = 0; i < shape2.length; i++){ + const axis = shape2[i].normal(shape2.at(i - 1)); + let [p1min, p1minDot] = Vector.vectorProjectionAndDot(shape2[0], axis); + let [p1max, p1maxDot] = [ + p1min, + p1minDot + ]; + for (const point of shape2){ + const [projected, dot] = Vector.vectorProjectionAndDot(point, axis); + p1min = dot < p1minDot ? projected : p1min; + p1minDot = Math.min(dot, p1minDot); + p1max = dot > p1maxDot ? projected : p1max; + p1maxDot = Math.max(dot, p1maxDot); + } + let [p2min, p2minDot] = Vector.vectorProjectionAndDot(shape1[0], axis); + let [p2max, p2maxDot] = [ + p2min, + p2minDot + ]; + for (const point of shape1){ + const [projected, dot] = Vector.vectorProjectionAndDot(point, axis); + p2min = dot < p2minDot ? projected : p2min; + p2minDot = Math.min(dot, p2minDot); + p2max = dot > p2maxDot ? projected : p2max; + p2maxDot = Math.max(dot, p2maxDot); + } + if (p1minDot - p2maxDot > 0 || p2minDot - p1maxDot > 0) { + return false; + } + [ + axis, + p1max, + p1min, + p2max, + p2min + ]; + } + return true; +}; const easeInOut = (x)=>x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2; const map = (value, x1, y1, x2, y2)=>(value - x1) * (y2 - x2) / (y1 - x1) + x2; class Doodler { @@ -879,10 +962,6 @@ class Polygon { doodler.line(p1.copy().add(this.center), p2.copy().add(this.center), { color }); - const { x, y, w, h } = this.aaHitbox(); - doodler.drawRect(new Vector(x, y), w, h, { - color: "lime" - }); } } calcCenter() { @@ -894,7 +973,7 @@ class Polygon { center.div(this.points.length); return center; } - circularHitbox() { + get circularHitbox() { let greatestDistance = 0; for (const p of this.points){ greatestDistance = Math.max(p.copy().add(this.center).dist(this.center), greatestDistance); @@ -904,7 +983,7 @@ class Polygon { radius: greatestDistance }; } - aaHitbox() { + get aaHitbox() { let smallestX, biggestX, smallestY, biggestY; smallestX = smallestY = Infinity; biggestX = biggestY = -Infinity; @@ -953,12 +1032,12 @@ document.body.append(img); const p = new Vector(500, 500); const poly = new Polygon([ { - x: -50, + x: -25, y: -25 }, { x: 25, - y: -50 + y: -25 }, { x: 25, @@ -970,7 +1049,7 @@ const poly = new Polygon([ } ]); const poly2 = Polygon.createPolygon(5, 75); -poly2.center = p.copy(); +poly2.center = p.copy().add(100, 100); poly.center.add(p); doodler.createLayer((c)=>{ for(let i = 0; i < c.canvas.width; i += 50){ @@ -980,16 +1059,20 @@ doodler.createLayer((c)=>{ }); } } - const color = axisAlignedCollision(poly.aaHitbox(), poly2.aaHitbox()) ? "red" : "black"; + const color = satCollision(poly, poly2) ? "red" : "aqua"; poly.draw(color); poly2.draw(color); const [gamepad] = navigator.getGamepads(); if (gamepad) { const leftX = gamepad.axes[0]; const leftY = gamepad.axes[1]; - poly.center.add(new Vector(Math.min(Math.max(leftX - 0.05, 0), leftX + 0.05), Math.min(Math.max(leftY - 0.05, 0), leftY + 0.05)).mult(10)); const rightX = gamepad.axes[2]; const rightY = gamepad.axes[3]; + if (axisAlignedContains(poly2.aaHitbox, poly.aaHitbox)) { + poly.center.add(new Vector(Math.min(Math.max(rightX - 0.05, 0), rightX + 0.05), Math.min(Math.max(rightY - 0.05, 0), rightY + 0.05)).mult(10)); + poly2.center.add(new Vector(Math.min(Math.max(leftX - 0.05, 0), leftX + 0.05), Math.min(Math.max(leftY - 0.05, 0), leftY + 0.05)).mult(10)); + } + poly.center.add(new Vector(Math.min(Math.max(leftX - 0.05, 0), leftX + 0.05), Math.min(Math.max(leftY - 0.05, 0), leftY + 0.05)).mult(10)); poly2.center.add(new Vector(Math.min(Math.max(rightX - 0.05, 0), rightX + 0.05), Math.min(Math.max(rightY - 0.05, 0), rightY + 0.05)).mult(10)); } }); diff --git a/collision/aa.ts b/collision/aa.ts index 7387e69..b34f1ca 100644 --- a/collision/aa.ts +++ b/collision/aa.ts @@ -14,3 +14,13 @@ export const axisAlignedCollision = ( aa1.y < aa2.y + aa2.h && aa1.y + aa1.h > aa2.y; }; + +export const axisAlignedContains = ( + aa1: axisAlignedBoundingBox, + aa2: axisAlignedBoundingBox, +) => { + return aa1.x < aa2.x && + aa1.y < aa2.y && + aa1.x + aa1.w > aa2.x + aa2.w && + aa1.y + aa1.h > aa2.y + aa2.h; +}; diff --git a/geometry/polygon.ts b/geometry/polygon.ts index d2927a1..0d9c031 100644 --- a/geometry/polygon.ts +++ b/geometry/polygon.ts @@ -19,8 +19,6 @@ export class Polygon { doodler.line(p1.copy().add(this.center), p2.copy().add(this.center), { color, }); - const { x, y, w, h } = this.aaHitbox(); - doodler.drawRect(new Vector(x, y), w, h, { color: "lime" }); } } @@ -35,7 +33,7 @@ export class Polygon { return center; } - circularHitbox(): CircleLike { + get circularHitbox(): CircleLike { let greatestDistance = 0; for (const p of this.points) { greatestDistance = Math.max( @@ -50,7 +48,7 @@ export class Polygon { }; } - aaHitbox(): axisAlignedBoundingBox { + get aaHitbox(): axisAlignedBoundingBox { let smallestX, biggestX, smallestY, biggestY; smallestX = smallestY = diff --git a/main.ts b/main.ts index 400be28..90ac638 100644 --- a/main.ts +++ b/main.ts @@ -1,6 +1,6 @@ /// -import { axisAlignedCollision } from "./collision/aa.ts"; +import { axisAlignedCollision, axisAlignedContains } from "./collision/aa.ts"; import { circularCollision } from "./collision/circular.ts"; import { satCollision } from "./collision/sat.ts"; import { Polygon } from "./geometry/polygon.ts"; @@ -32,15 +32,15 @@ document.body.append(img); const p = new Vector(500, 500); const poly = new Polygon([ - { x: -50, y: -25 }, - { x: 25, y: -50 }, + { x: -25, y: -25 }, + { x: 25, y: -25 }, { x: 25, y: 25 }, { x: -25, y: 25 }, ]); // poly.center = p.copy(); const poly2 = Polygon.createPolygon(5, 75); -poly2.center = p.copy(); +poly2.center = p.copy().add(100, 100); poly.center.add(p); @@ -50,12 +50,12 @@ doodler.createLayer((c) => { doodler.drawSquare(new Vector(i, j), 50, { color: "#00000010" }); } } - const color = axisAlignedCollision( - poly.aaHitbox(), - poly2.aaHitbox(), + const color = satCollision( + poly, + poly2, ) ? "red" - : "black"; + : "aqua"; // console.log(satCollision( // )); @@ -70,21 +70,37 @@ doodler.createLayer((c) => { if (gamepad) { const leftX = gamepad.axes[0]; const leftY = gamepad.axes[1]; + const rightX = gamepad.axes[2]; + const rightY = gamepad.axes[3]; + + if (axisAlignedContains(poly2.aaHitbox, poly.aaHitbox)) { + poly.center.add( + new Vector( + Math.min(Math.max(rightX - deadzone, 0), rightX + deadzone), + Math.min(Math.max(rightY - deadzone, 0), rightY + deadzone), + ).mult(10), + ); + poly2.center.add( + new Vector( + Math.min(Math.max(leftX - deadzone, 0), leftX + deadzone), + Math.min(Math.max(leftY - deadzone, 0), leftY + deadzone), + ).mult(10), + ); + } + poly.center.add( new Vector( Math.min(Math.max(leftX - deadzone, 0), leftX + deadzone), Math.min(Math.max(leftY - deadzone, 0), leftY + deadzone), ).mult(10), ); - - const rightX = gamepad.axes[2]; - const rightY = gamepad.axes[3]; poly2.center.add( new Vector( Math.min(Math.max(rightX - deadzone, 0), rightX + deadzone), Math.min(Math.max(rightY - deadzone, 0), rightY + deadzone), ).mult(10), ); + // (doodler as ZoomableDoodler).moveOrigin({ x: -rigthX * 5, y: -rigthY * 5 }); // if (gamepad.buttons[7].value) {