Files
nage/workshop/steps/06-ray-vs-aabb/ray.ts
T
2026-07-11 17:48:28 +02:00

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,
};
}