114 lines
3.8 KiB
TypeScript
114 lines
3.8 KiB
TypeScript
import { Polygon } from "../geometry/polygon.ts";
|
|
import { Point, Vector } from "../geometry/vector.ts";
|
|
import { CircleLike } from "./circular.ts";
|
|
|
|
export const satCollision = (s1: Polygon, s2: Polygon) => {
|
|
const shape1 = s1.points.map((p) => new Vector(p).add(s1.center));
|
|
const shape2 = s2.points.map((p) => new Vector(p).add(s2.center));
|
|
|
|
if (shape1.length < 2 || shape2.length < 2) {
|
|
throw "Insufficient shape data in satCollision";
|
|
}
|
|
for (let i = 0; i < shape1.length; i++) {
|
|
const axis = shape1[i].normal(shape1.at(i - 1)!);
|
|
let [_, p1minDot] = Vector.vectorProjectionAndDot(shape1[0], axis);
|
|
let p1maxDot = p1minDot;
|
|
for (const point of shape1) {
|
|
const [_, dot] = Vector.vectorProjectionAndDot(point, axis);
|
|
p1minDot = Math.min(dot, p1minDot);
|
|
p1maxDot = Math.max(dot, p1maxDot);
|
|
}
|
|
let [__, p2minDot] = Vector.vectorProjectionAndDot(shape2[0], axis);
|
|
let p2maxDot = p2minDot;
|
|
for (const point of shape2) {
|
|
const [_, dot] = Vector.vectorProjectionAndDot(point, axis);
|
|
p2minDot = Math.min(dot, p2minDot);
|
|
p2maxDot = Math.max(dot, p2maxDot);
|
|
}
|
|
|
|
if (p1minDot - p2maxDot > 0 || p2minDot - p1maxDot > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
for (let i = 0; i < shape2.length; i++) {
|
|
const axis = shape2[i].normal(shape2.at(i - 1)!);
|
|
let [_, p1minDot] = Vector.vectorProjectionAndDot(shape2[0], axis);
|
|
let p1maxDot = p1minDot;
|
|
for (const point of shape2) {
|
|
const [_, dot] = Vector.vectorProjectionAndDot(point, axis);
|
|
// p1min = dot < p1minDot ? projected : p1min;
|
|
p1minDot = Math.min(dot, p1minDot);
|
|
// p1max = dot > p1maxDot ? projected : p1max;
|
|
p1maxDot = Math.max(dot, p1maxDot);
|
|
}
|
|
let [__, p2minDot] = Vector.vectorProjectionAndDot(shape1[0], axis);
|
|
let p2maxDot = p2minDot;
|
|
for (const point of shape1) {
|
|
const [_, dot] = Vector.vectorProjectionAndDot(point, axis);
|
|
// p2min = dot < p2minDot ? projected : p2min;
|
|
p2minDot = Math.min(dot, p2minDot);
|
|
// p2max = dot > p2maxDot ? projected : p2max;
|
|
p2maxDot = Math.max(dot, p2maxDot);
|
|
}
|
|
|
|
if (p1minDot - p2maxDot > 0 || p2minDot - p1maxDot > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
export const satCollisionCircle = (s: Polygon, c: CircleLike) => {
|
|
const shape = s.points.map((p) => new Vector(p).add(s.center));
|
|
|
|
if (shape.length < 2) {
|
|
throw "Insufficient shape data in satCollisionCircle";
|
|
}
|
|
for (let i = 0; i < shape.length; i++) {
|
|
const axis = shape[i].normal(shape.at(i - 1)!);
|
|
let [_, p1minDot] = Vector.vectorProjectionAndDot(shape[0], axis);
|
|
let p1maxDot = p1minDot;
|
|
for (const point of shape) {
|
|
const [_, dot] = Vector.vectorProjectionAndDot(point, axis);
|
|
p1minDot = Math.min(dot, p1minDot);
|
|
p1maxDot = Math.max(dot, p1maxDot);
|
|
}
|
|
const [__, circleDot] = Vector.vectorProjectionAndDot(
|
|
new Vector(c.center),
|
|
axis,
|
|
);
|
|
const p2minDot = circleDot - c.radius;
|
|
const p2maxDot = circleDot + c.radius;
|
|
|
|
if (p1minDot - p2maxDot > 0 || p2minDot - p1maxDot > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
const center = new Vector(c.center);
|
|
let nearest = shape[0];
|
|
for (const p of shape) {
|
|
if (center.dist(p) < center.dist(nearest)) nearest = p;
|
|
}
|
|
const axis = center.sub(nearest);
|
|
let [_, p1minDot] = Vector.vectorProjectionAndDot(shape[0], axis);
|
|
let p1maxDot = p1minDot;
|
|
for (const point of shape) {
|
|
const [_, dot] = Vector.vectorProjectionAndDot(point, axis);
|
|
p1minDot = Math.min(dot, p1minDot);
|
|
p1maxDot = Math.max(dot, p1maxDot);
|
|
}
|
|
const [__, circleDot] = Vector.vectorProjectionAndDot(
|
|
new Vector(c.center),
|
|
axis,
|
|
);
|
|
const p2minDot = circleDot - c.radius;
|
|
const p2maxDot = circleDot + c.radius;
|
|
|
|
if (p1minDot - p2maxDot > 0 || p2minDot - p1maxDot > 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|