Skip to content

Commit

Permalink
Update AABB algorithms (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
jthomperoo committed Oct 18, 2020
1 parent 0462888 commit 48e9bb9
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 88 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ object creation.
must be implemented on a per `Component` basis by overriding this function.
- The game loop now ensures all game logic messages are processed using `DispatchUntilEmpty` before executing any
render logic.
- `AABBAlgorithm` more efficient algorithm involving sweeping horizontal then vertical.
- `AABB` method `FarthestPointInDirection` faster and using less `Vector` objects.

## [v0.9.0] - 2020-09-05
### Added
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/classes/aabb.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ requiring less calculations than a fully defined polygon.
Name | Type | Default |
------ | ------ | ------ |
`size` | [Vector](vector.md) | - |
`center` | [Vector](vector.md) | Vector.New(0,0) |
`center` | [Vector](vector.md) | Vector.New(0, 0) |

**Returns:** *[AABB](aabb.md)*

Expand Down
16 changes: 0 additions & 16 deletions docs/reference/classes/aabbalgorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ This algorithm sacrifices some collision accuracy for performance.
### Methods

* [CalculateCollisions](aabbalgorithm.md#calculatecollisions)
* [aabb](aabbalgorithm.md#private-aabb)

## Methods

Expand All @@ -35,18 +34,3 @@ Name | Type |
`shapes` | [IShape](../interfaces/ishape.md)[] |

**Returns:** *[CollisionInfo](collisioninfo.md)[]*

___

### `Private` aabb

**aabb**(`a`: [IShape](../interfaces/ishape.md), `b`: [IShape](../interfaces/ishape.md)): *[CollisionInfo](collisioninfo.md) | undefined*

**Parameters:**

Name | Type |
------ | ------ |
`a` | [IShape](../interfaces/ishape.md) |
`b` | [IShape](../interfaces/ishape.md) |

**Returns:** *[CollisionInfo](collisioninfo.md) | undefined*
56 changes: 28 additions & 28 deletions src/shape/aabb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,27 @@ describe("AABB - FarthestPointInDirection", () => {
type TestTuple = [string, Vector, AABB, Vector];
test.each<TestTuple>([
[
"Square - up - top left point",
new Vector(-1.5,1.5),
new AABB(new Vector(3,3)),
"Square - up - top right point",
new Vector(1.5, 1.5),
new AABB(new Vector(3, 3)),
new Vector(0, 1)
],
[
"Square - down - bottom left point",
new Vector(-1.5,-1.5),
new AABB(new Vector(3,3)),
"Square - down - bottom right point",
new Vector(1.5, -1.5),
new AABB(new Vector(3, 3)),
new Vector(0, -1)
],
[
"Square - left - top left point",
new Vector(-1.5,1.5),
new AABB(new Vector(3,3)),
new Vector(-1.5, 1.5),
new AABB(new Vector(3, 3)),
new Vector(-1, 0)
],
[
"Square - right - top right point",
new Vector(1.5,1.5),
new AABB(new Vector(3,3)),
new Vector(1.5, 1.5),
new AABB(new Vector(3, 3)),
new Vector(1, 0)
],
])("%p", (description: string, expected: Vector, aabb: AABB, direction: Vector) => {
Expand All @@ -58,20 +58,20 @@ describe("AABB - Transform", () => {
test.each<TestTuple>([
[
"Square, move up by 2",
new AABB(new Vector(2,2), new Vector(0, 2)),
new AABB(new Vector(2,2)),
new AABB(new Vector(2, 2), new Vector(0, 2)),
new AABB(new Vector(2, 2)),
new Transform(new Vector(0, 2))
],
[
"Rectangle around point, move up by 5, left 8",
new AABB(new Vector(2,6), new Vector(-5, 11)),
new AABB(new Vector(2,6), new Vector(3,6)),
new AABB(new Vector(2, 6), new Vector(-5, 11)),
new AABB(new Vector(2, 6), new Vector(3, 6)),
new Transform(new Vector(-8, 5))
],
[
"Rectangle around point, move up by 5, left 8, scale by 2 width and 4 height, ignore rotation",
new AABB(new Vector(4,12), new Vector(-5, 11)),
new AABB(new Vector(2,3), new Vector(3,6)),
new AABB(new Vector(4, 12), new Vector(-5, 11)),
new AABB(new Vector(2, 3), new Vector(3, 6)),
new Transform(new Vector(-8, 5), new Vector(2, 4), 3)
],
])("%p", (description: string, expected: AABB, aabb: AABB, transform: Transform) => {
Expand All @@ -86,37 +86,37 @@ describe("AABB - PointInside", () => {
"Square around origin, point inside",
true,
new AABB(new Vector(1, 1)),
new Vector(0.2,0.2)
new Vector(0.2, 0.2)
],
[
"Square around origin, point left",
false,
new AABB(new Vector(2, 2)),
new Vector(-3,0)
new Vector(-3, 0)
],
[
"Square around origin, point right",
false,
new AABB(new Vector(2, 2)),
new Vector(3,0)
new Vector(3, 0)
],
[
"Rectangle around origin, point outside",
false,
new AABB(new Vector(2, 3)),
new Vector(5,3)
new Vector(5, 3)
],
[
"Rectangle around arbitrary point, point within",
true,
new AABB(new Vector(2, 3), new Vector(5,3)),
new Vector(5,4)
new AABB(new Vector(2, 3), new Vector(5, 3)),
new Vector(5, 4)
],
[
"Rectangle around arbitrary point, point outside",
false,
new AABB(new Vector(2, 3), new Vector(5,3)),
new Vector(2,1)
new AABB(new Vector(2, 3), new Vector(5, 3)),
new Vector(2, 1)
],

])("%p", (description: string, expected: boolean, aabb: AABB, point: Vector) => {
Expand All @@ -130,13 +130,13 @@ describe("AABB - Center", () => {
test.each<TestTuple>([
[
"Rectangle around origin",
new Vector(0,0),
new AABB(new Vector(2, 6), new Vector(0,0))
new Vector(0, 0),
new AABB(new Vector(2, 6), new Vector(0, 0))
],
[
"Rectangle around point",
new Vector(5,-3),
new AABB(new Vector(2, 3), new Vector(5,-3))
new Vector(5, -3),
new AABB(new Vector(2, 3), new Vector(5, -3))
],
])("%p", (description: string, expected: Vector, aabb: AABB) => {
expect(aabb.Center()).toEqual(expected);
Expand Down
37 changes: 14 additions & 23 deletions src/shape/aabb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class AABB implements IShape {
public center: Vector;
public size: Vector;

constructor(size: Vector, center: Vector = Vector.New(0,0)) {
constructor(size: Vector, center: Vector = Vector.New(0, 0)) {
this.center = center;
this.size = size;
}
Expand All @@ -46,30 +46,21 @@ class AABB implements IShape {
}

public FarthestPointInDirection(direction: Vector): Vector {
const left = this.center.x - this.size.x / 2;
const right = this.center.x + this.size.x / 2;
const top = this.center.y + this.size.y / 2;
const bottom = this.center.y - this.size.y / 2;

const points = [
Vector.New(left, top),
Vector.New(right, top),
Vector.New(left, bottom),
Vector.New(right, bottom)
];

let farthestDistance = points[0].Dot(direction);
let farthestPoint = points[0];

for (let i = 1; i < points.length; i++) {
const distanceInDirection = points[i].Dot(direction);
if (distanceInDirection > farthestDistance) {
farthestPoint = points[i];
farthestDistance = distanceInDirection;
if (direction.x >= 0) {
const right = this.center.x + this.size.x / 2;
if (direction.y >= 0) {
return new Vector(right, this.center.y + this.size.y / 2); // top right
} else {
return new Vector(right, this.center.y - this.size.y / 2); // bottom right
}
} else {
const left = this.center.x - this.size.x / 2;
if (direction.y >= 0) {
return new Vector(left, this.center.y + this.size.y / 2); // top left
} else {
return new Vector(left, this.center.y - this.size.y / 2); // bottom left
}
}

return farthestPoint;
}

public PointInside(point: Vector): boolean {
Expand Down
58 changes: 38 additions & 20 deletions src/standard/collision/algorithm/aabb_algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,52 @@ import Vector from "../../../geometry/vector";
*/
class AABBAlgorithm implements ICollisionAlgorithm {
public CalculateCollisions(shapes: IShape[]): CollisionInfo[] {
const collisions: CollisionInfo[] = [];
// Iterate over collision pairs

const horizontalOverlap: [IShape, IShape][] = [];

const leftDir = Vector.New(-1, 0);
const rightDir = Vector.New(1, 0);

for (let i = 0; i < shapes.length; i++) {
const a = shapes[i];
const aLeft = a.FarthestPointInDirection(leftDir);
const aRight = a.FarthestPointInDirection(rightDir);
for (let j = i + 1; j < shapes.length; j++) {
const a = shapes[i];
const b = shapes[j];
// Detect collision
const collision = this.aabb(a,b);
if (collision !== undefined) {
collisions.push(collision);
const bLeft = b.FarthestPointInDirection(leftDir);
const bRight = b.FarthestPointInDirection(rightDir);
if (aLeft.x < bRight.x &&
aRight.x > bLeft.x) {
horizontalOverlap.push([a, b]);
}
}
}
return collisions;
}

private aabb(a: IShape, b: IShape): CollisionInfo | undefined {
const aTopLeft = a.FarthestPointInDirection(Vector.New(-1, 1));
const aBottomRight = a.FarthestPointInDirection(Vector.New(1, -1));
const bTopLeft = b.FarthestPointInDirection(Vector.New(-1, 1));
const bBottomRight = b.FarthestPointInDirection(Vector.New(1, -1));
if (aTopLeft.x < bBottomRight.x &&
aBottomRight.x > bTopLeft.x &&
aBottomRight.y < bTopLeft.y &&
aTopLeft.y > bBottomRight.y) {
return new CollisionInfo(a, b);
leftDir.Free();
rightDir.Free();

const collisions: CollisionInfo[] = [];

const upDir = Vector.New(0, 1);
const downDir = Vector.New(0, -1);

for (let i = 0; i < horizontalOverlap.length; i++) {
const a = horizontalOverlap[i][0];
const b = horizontalOverlap[i][1];
const aUp = a.FarthestPointInDirection(upDir);
const aDown = a.FarthestPointInDirection(downDir);
const bUp = b.FarthestPointInDirection(upDir);
const bDown = b.FarthestPointInDirection(downDir);
if (aDown.y < bUp.y &&
aUp.y > bDown.y) {
collisions.push(new CollisionInfo(a, b));
}
}
return;

upDir.Free();
downDir.Free();

return collisions;
}
}

Expand Down

0 comments on commit 48e9bb9

Please sign in to comment.