Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Fast BoundingBox overlap and transform #2241

Merged
merged 2 commits into from
Feb 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added

Added arbitrary non-convex polygon support (only non-self intersecting) with `ex.PolygonCollider(...).triangulate()` which builds a new `ex.CompositeCollider` composed of triangles.
- Added arbitrary non-convex polygon support (only non-self intersecting) with `ex.PolygonCollider(...).triangulate()` which builds a new `ex.CompositeCollider` composed of triangles.
- Added faster `ex.BoundingBox.transform(...)` implementation.
- Added faster `ex.BoundingBox.overlap(...)` implementation.
- Added `ex.Vector.min(...)` and `ex.Vector.max(...)` to find the min/max of each vector component between 2 vectors.

### Fixed

Expand Down
41 changes: 38 additions & 3 deletions src/engine/Collision/BoundingBox.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Vector } from '../Math/vector';
import { vec, Vector } from '../Math/vector';
import { Ray } from '../Math/ray';
import { Color } from '../Color';
import { Side } from './Side';
Expand Down Expand Up @@ -140,14 +140,39 @@ export class BoundingBox {
return BoundingBox.fromPoints(points);
}

/**
* Scale a bounding box by a scale factor, optionally provide a point
* @param scale
* @param point
*/
public scale(scale: Vector, point: Vector = Vector.Zero): BoundingBox {
const shifted = this.translate(point);
return new BoundingBox(shifted.left * scale.x, shifted.top * scale.y, shifted.right * scale.x, shifted.bottom * scale.y);
}

/**
* Transform the axis aligned bounding box by a [[Matrix]], producing a new axis aligned bounding box
* @param matrix
*/
public transform(matrix: Matrix) {
const points = this.getPoints().map((p) => matrix.multiply(p));
return BoundingBox.fromPoints(points);
const matFirstColumn = vec(matrix.data[0], matrix.data[1]);
const xa = matFirstColumn.scale(this.left);
const xb = matFirstColumn.scale(this.right);

const matSecondColumn = vec(matrix.data[4], matrix.data[5]);
const ya = matSecondColumn.scale(this.top);
const yb = matSecondColumn.scale(this.bottom);

const matrixPos = matrix.getPosition();
const topLeft = Vector.min(xa, xb).add(Vector.min(ya, yb)).add(matrixPos);
const bottomRight = Vector.max(xa, xb).add(Vector.max(ya, yb)).add(matrixPos);

return new BoundingBox({
left: topLeft.x,
top: topLeft.y,
right: bottomRight.x,
bottom: bottomRight.y
});
}

/**
Expand Down Expand Up @@ -257,6 +282,16 @@ export class BoundingBox {
return new Vector(this.width, this.height);
}

/**
* Returns true if the bounding boxes overlap.
* @param other
*/
public overlaps(other: BoundingBox): boolean {
const totalBoundingBox = this.combine(other);
return totalBoundingBox.width < other.width + this.width &&
totalBoundingBox.height < other.height + this.height;
}

/**
* Test wether this bounding box intersects with another returning
* the intersection vector that can be used to resolve the collision. If there
Expand Down
8 changes: 8 additions & 0 deletions src/engine/Math/vector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ export class Vector implements Clonable<Vector> {
return Math.sqrt(Math.pow(vec1.x - vec2.x, 2) + Math.pow(vec1.y - vec2.y, 2));
}

public static min(vec1: Vector, vec2: Vector) {
return new Vector(Math.min(vec1.x, vec2.x), Math.min(vec1.y, vec2.y));
}

public static max(vec1: Vector, vec2: Vector) {
return new Vector(Math.max(vec1.x, vec2.x), Math.max(vec1.y, vec2.y));
}

/**
* @param x X component of the Vector
* @param y Y component of the Vector
Expand Down
18 changes: 18 additions & 0 deletions src/spec/AlgebraSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,24 @@ describe('Vectors', () => {

expect(v.toString(2)).toBe('(1.23, 2.35)');
});

it('can find the min between vectors', () => {
const v1 = new ex.Vector(0, 1);
const v2 = new ex.Vector(2, 0);

const sut = ex.Vector.min(v1, v2);

expect(sut).toBeVector(ex.vec(0, 0));
});

it('can find the max between vectors', () => {
const v1 = new ex.Vector(0, 1);
const v2 = new ex.Vector(2, 0);

const sut = ex.Vector.max(v1, v2);

expect(sut).toBeVector(ex.vec(2, 1));
});
});

describe('Rays', () => {
Expand Down
10 changes: 10 additions & 0 deletions src/spec/BoundingBoxSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ function runBoundingBoxTests(creationType: string, createBoundingBox: Function)
expect(bb.contains(new ex.Vector(10, 20))).toBe(true);
});

it('can overlap with other bounding boxes', () => {
const b1 = new ex.BoundingBox({left: 100, right: 110, top: 100, bottom: 110});
const b2 = new ex.BoundingBox(2, 0, 20, 10);
const b3 = new ex.BoundingBox(12, 0, 28, 10);
expect(b2.overlaps(b3)).toBe(true);
expect(b3.overlaps(b2)).toBe(true);
expect(b1.overlaps(b2)).toBe(false);
expect(b1.overlaps(b3)).toBe(false);
});

it('can collide with other bounding boxes', () => {
const b2 = new ex.BoundingBox(2, 0, 20, 10);
const b3 = new ex.BoundingBox(12, 0, 28, 10);
Expand Down