Skip to content

Commit

Permalink
feat: add contain method
Browse files Browse the repository at this point in the history
  • Loading branch information
yingpengsha committed Jan 18, 2024
1 parent 01f2f4f commit d0dc6bc
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
81 changes: 81 additions & 0 deletions src/AABB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export type Point<Is3D extends boolean = false> = Is3D extends true
? Point3D
: Point2D

export type Ray<Is3D extends boolean = false> = Is3D extends true
? { start: Point3D, end: Point3D }
: { start: Point2D, end: Point2D }

class AABB<P extends Point<boolean> = Point2D> {
min: P
max: P
Expand Down Expand Up @@ -99,6 +103,83 @@ class AABB<P extends Point<boolean> = Point2D> {
}
}

// ======================== collision detection ========================
// AABB-AABB collision detection
static collide (a: AABB<Point2D>, b: AABB<Point2D>): boolean
static collide (a: AABB<Point3D>, b: AABB<Point3D>): boolean
static collide<Shape extends AABB = AABB<Point2D>>(
a: Shape,
b: Shape
): boolean {
// whether intersect on x axis
if (a.max.x < b.min.x || a.min.x > b.max.x) {
return false
}

// whether intersect on y axis
if (a.max.y < b.min.y || a.min.y > b.max.y) {
return false
}

// whether intersect on z axis
if (AABB.is3D(a) && AABB.is3D(b)) {
if (a.max.z < b.min.z || a.min.z > b.max.z) {
return false
}
}

return true
}

collide (
other: this extends AABB<Point2D> ? AABB<Point2D> : AABB<Point3D>
): boolean {
// @ts-expect-error type derivation is ambiguous
return AABB.collide(this, other)
}

// ======================== contain ========================
contain (
AABB: this extends AABB<Point2D> ? AABB<Point2D> : AABB<Point3D>
): boolean
contain (point: this extends AABB<Point2D> ? Point2D : Point3D): boolean
contain (
input:
| (this extends AABB<Point2D> ? AABB<Point2D> : AABB<Point3D>)
| (this extends AABB<Point2D> ? Point2D : Point3D)
): boolean {
if (input instanceof AABB) {
if (
this.min.x >= input.min.x ||
this.min.y >= input.min.y ||
this.max.x <= input.max.x ||
this.max.y <= input.max.y
) {
return false
}
if (AABB.is3D(this) && AABB.is3D(input)) {
if (this.min.z >= input.min.z || this.max.z <= input.max.z) {
return false
}
}
} else {
if (
this.min.x >= input.x &&
this.min.y >= input.y &&
this.max.x <= input.x &&
this.max.y <= input.y
) {
return false
}
if (AABB.is3D(this) && 'z' in input) {
if (this.min.z <= input.z && this.max.z >= input.z) {
return false
}
}
}
return true
}

// ======================== factory ========================
static from (min: Point2D, max: Point2D): AABB<Point2D>
static from (min: Point3D, max: Point3D): AABB<Point3D>
Expand Down
14 changes: 14 additions & 0 deletions src/__test__/AABB.methods.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,18 @@ describe('AABB methods test', () => {
expectTypeOf(instance.is3D).toEqualTypeOf<false>()
})
})

describe('AABB.collide', () => {
test('2D AABB collide should accept 2D AABB', () => {
const instance = new AABB({ x: 1, y: 1 }, { x: 2, y: 2 })
const instance2 = new AABB({ x: 1, y: 1 }, { x: 2, y: 2 })
expectTypeOf(instance.collide).toBeCallableWith(instance2)
})

test('3D AABB collide should accept 3D AABB', () => {
const instance = new AABB({ x: 1, y: 1, z: 1 }, { x: 2, y: 2, z: 2 })
const instance2 = new AABB({ x: 1, y: 1, z: 1 }, { x: 2, y: 2, z: 2 })
expectTypeOf(instance.collide).toBeCallableWith(instance2)
})
})
})
Empty file added src/__test__/AABB.test.ts
Empty file.

0 comments on commit d0dc6bc

Please sign in to comment.