53 lines
1.2 KiB
TypeScript
53 lines
1.2 KiB
TypeScript
import { type AABB, type Span, sweepInterval, type Vec } from "./given.ts";
|
|
|
|
/** A collision: the fraction of the frame at impact, and the surface normal. */
|
|
export type Hit = { time: number; normal: Vec };
|
|
|
|
/**
|
|
* A point at `p` moving by `v` over this frame, against a static box.
|
|
* Returns the entry Hit, or null if there is no collision this frame.
|
|
*
|
|
* Reuse sweepInterval twice (x and y), then combine:
|
|
* entry = max of the two entries, exit = min of the two exits.
|
|
*/
|
|
export function rayVsAABB(p: Vec, v: Vec, box: AABB): Hit | null {
|
|
const spanX = sweepInterval(p.x, v.x, box.x, box.x + box.w);
|
|
if (spanX === null) {
|
|
return null;
|
|
}
|
|
const spanY = sweepInterval(p.y, v.y, box.y, box.y + box.h);
|
|
if (spanY === null) {
|
|
return null;
|
|
}
|
|
|
|
const span: Span = {
|
|
entry: Math.max(spanX.entry, spanY.entry),
|
|
exit: Math.min(spanX.exit, spanY.exit),
|
|
};
|
|
|
|
if (span.entry > span.exit) {
|
|
return null;
|
|
}
|
|
|
|
if (span.entry >= 1 || span.exit <= 0) {
|
|
return null;
|
|
}
|
|
|
|
const time = span.entry;
|
|
const normal = {
|
|
x: 0,
|
|
y: 0,
|
|
};
|
|
|
|
if (spanX.entry > spanY.entry) {
|
|
normal.x = v.x > 0 ? -1 : 1;
|
|
} else {
|
|
normal.y = v.y > 0 ? -1 : 1;
|
|
}
|
|
|
|
return {
|
|
time,
|
|
normal,
|
|
};
|
|
}
|