diff --git a/CHANGELOG.md b/CHANGELOG.md index 168c9ba56..7bdfcb7b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/engine/Collision/BoundingBox.ts b/src/engine/Collision/BoundingBox.ts index 66bd30d4b..2c9ce5057 100644 --- a/src/engine/Collision/BoundingBox.ts +++ b/src/engine/Collision/BoundingBox.ts @@ -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'; @@ -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 + }); } /** @@ -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 diff --git a/src/engine/Math/vector.ts b/src/engine/Math/vector.ts index f99d5224a..b1c3be604 100644 --- a/src/engine/Math/vector.ts +++ b/src/engine/Math/vector.ts @@ -89,6 +89,14 @@ export class Vector implements Clonable { 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 diff --git a/src/spec/AlgebraSpec.ts b/src/spec/AlgebraSpec.ts index 69621949b..4d166fd4a 100644 --- a/src/spec/AlgebraSpec.ts +++ b/src/spec/AlgebraSpec.ts @@ -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', () => { diff --git a/src/spec/BoundingBoxSpec.ts b/src/spec/BoundingBoxSpec.ts index d22e00f6f..ecd8af858 100644 --- a/src/spec/BoundingBoxSpec.ts +++ b/src/spec/BoundingBoxSpec.ts @@ -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);