Files
esengine/packages/math/tests/CollisionDetector.test.ts
2025-08-10 16:00:02 +08:00

212 lines
7.4 KiB
TypeScript

import { CollisionDetector } from '../src/Collision/CollisionDetector';
import { Vector2 } from '../src/Vector2';
import { Rectangle } from '../src/Rectangle';
import { Circle } from '../src/Circle';
declare global {
var expectFloatsEqual: (actual: number, expected: number, epsilon?: number) => void;
}
describe('CollisionDetector', () => {
describe('点与几何体碰撞', () => {
test('点与圆形碰撞检测', () => {
const circle = new Circle(0, 0, 5);
const pointInside = new Vector2(3, 0);
const pointOutside = new Vector2(10, 0);
const pointOnBoundary = new Vector2(5, 0);
const collision1 = CollisionDetector.pointCircle(pointInside, circle);
expect(collision1.collided).toBe(true);
expect(collision1.penetration).toBe(2);
const collision2 = CollisionDetector.pointCircle(pointOutside, circle);
expect(collision2.collided).toBe(false);
const collision3 = CollisionDetector.pointCircle(pointOnBoundary, circle);
expect(collision3.collided).toBe(true);
expectFloatsEqual(collision3.penetration!, 0, 1e-10);
});
test('点与矩形碰撞检测', () => {
const rect = new Rectangle(10, 10, 20, 20);
const pointInside = new Vector2(15, 15);
const pointOutside = new Vector2(5, 5);
const collision1 = CollisionDetector.pointRect(pointInside, rect);
expect(collision1.collided).toBe(true);
expect(collision1.normal).toBeDefined();
const collision2 = CollisionDetector.pointRect(pointOutside, rect);
expect(collision2.collided).toBe(false);
});
});
describe('圆形碰撞检测', () => {
test('圆形与圆形碰撞检测', () => {
const circle1 = new Circle(0, 0, 5);
const circle2 = new Circle(8, 0, 5); // 相交
const circle3 = new Circle(15, 0, 5); // 不相交
const circle4 = new Circle(10, 0, 5); // 边界接触
const collision1 = CollisionDetector.circleCircle(circle1, circle2);
expect(collision1.collided).toBe(true);
expect(collision1.penetration).toBe(2);
const collision2 = CollisionDetector.circleCircle(circle1, circle3);
expect(collision2.collided).toBe(false);
const collision3 = CollisionDetector.circleCircle(circle1, circle4);
expect(collision3.collided).toBe(true);
expectFloatsEqual(collision3.penetration!, 0, 1e-10);
});
test('圆形与矩形碰撞检测', () => {
const circle = new Circle(15, 15, 5);
const rect1 = new Rectangle(10, 10, 20, 20); // 相交
const rect2 = new Rectangle(30, 30, 10, 10); // 不相交
const collision1 = CollisionDetector.circleRect(circle, rect1);
expect(collision1.collided).toBe(true);
const collision2 = CollisionDetector.circleRect(circle, rect2);
expect(collision2.collided).toBe(false);
});
});
describe('矩形碰撞检测', () => {
test('矩形与矩形碰撞检测', () => {
const rect1 = new Rectangle(10, 10, 20, 20);
const rect2 = new Rectangle(15, 15, 20, 20); // 相交
const rect3 = new Rectangle(40, 40, 10, 10); // 不相交
const collision1 = CollisionDetector.rectRect(rect1, rect2);
expect(collision1.collided).toBe(true);
expect(collision1.penetration).toBeGreaterThan(0);
expect(collision1.normal).toBeDefined();
const collision2 = CollisionDetector.rectRect(rect1, rect3);
expect(collision2.collided).toBe(false);
});
});
describe('射线投射', () => {
test('射线与圆形相交', () => {
const rayOrigin = new Vector2(-10, 0);
const rayDirection = new Vector2(1, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.rayCircle(rayOrigin, rayDirection, circle);
expect(collision.collided).toBe(true);
expect(collision.distance).toBe(5);
expect(collision.contactPoint!.x).toBe(-5);
expect(collision.contactPoint!.y).toBe(0);
});
test('射线与圆形不相交', () => {
const rayOrigin = new Vector2(-10, 10);
const rayDirection = new Vector2(1, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.rayCircle(rayOrigin, rayDirection, circle);
expect(collision.collided).toBe(false);
});
test('射线与矩形相交', () => {
const rayOrigin = new Vector2(-5, 15);
const rayDirection = new Vector2(1, 0);
const rect = new Rectangle(10, 10, 20, 20);
const collision = CollisionDetector.rayRect(rayOrigin, rayDirection, rect);
expect(collision.collided).toBe(true);
expect(collision.distance).toBe(15);
});
test('射线距离限制', () => {
const rayOrigin = new Vector2(-10, 0);
const rayDirection = new Vector2(1, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.rayCircle(rayOrigin, rayDirection, circle, 3);
expect(collision.collided).toBe(false);
});
});
describe('线段相交', () => {
test('线段与线段相交', () => {
const p1 = new Vector2(0, 0);
const p2 = new Vector2(10, 10);
const p3 = new Vector2(0, 10);
const p4 = new Vector2(10, 0);
const collision = CollisionDetector.lineSegmentLineSegment(p1, p2, p3, p4);
expect(collision.collided).toBe(true);
expect(collision.contactPoint!.x).toBe(5);
expect(collision.contactPoint!.y).toBe(5);
});
test('线段与线段不相交', () => {
const p1 = new Vector2(0, 0);
const p2 = new Vector2(5, 5);
const p3 = new Vector2(10, 0);
const p4 = new Vector2(15, 5);
const collision = CollisionDetector.lineSegmentLineSegment(p1, p2, p3, p4);
expect(collision.collided).toBe(false);
});
test('线段与圆形相交', () => {
const lineStart = new Vector2(-10, 0);
const lineEnd = new Vector2(10, 0);
const circle = new Circle(0, 0, 5);
const collision = CollisionDetector.lineSegmentCircle(lineStart, lineEnd, circle);
expect(collision.collided).toBe(true);
});
});
describe('快速排斥测试', () => {
test('AABB包围盒测试', () => {
const bounds1 = new Rectangle(10, 10, 20, 20);
const bounds2 = new Rectangle(15, 15, 20, 20);
const bounds3 = new Rectangle(40, 40, 10, 10);
expect(CollisionDetector.aabbTest(bounds1, bounds2)).toBe(true);
expect(CollisionDetector.aabbTest(bounds1, bounds3)).toBe(false);
});
test('圆形包围盒测试', () => {
const center1 = new Vector2(0, 0);
const center2 = new Vector2(8, 0);
const center3 = new Vector2(15, 0);
expect(CollisionDetector.circleTest(center1, 5, center2, 5)).toBe(true);
expect(CollisionDetector.circleTest(center1, 5, center3, 5)).toBe(false);
});
});
describe('边界情况', () => {
test('零半径圆形', () => {
const point = new Vector2(0, 0);
const circle = new Circle(0, 0, 0);
const collision = CollisionDetector.pointCircle(point, circle);
expect(collision.collided).toBe(true);
});
test('零面积矩形', () => {
const point = new Vector2(10, 10);
const rect = new Rectangle(10, 10, 0, 0);
const collision = CollisionDetector.pointRect(point, rect);
expect(collision.collided).toBe(true);
});
test('同心圆形', () => {
const circle1 = new Circle(0, 0, 5);
const circle2 = new Circle(0, 0, 3);
const collision = CollisionDetector.circleCircle(circle1, circle2);
expect(collision.collided).toBe(true);
});
});
});