53 lines
1.3 KiB
TypeScript
53 lines
1.3 KiB
TypeScript
// Finished in earlier steps — reuse, don't rewrite.
|
|
export type Vec = { x: number; y: number };
|
|
export const vec = (x: number, y: number): Vec => ({ x, y });
|
|
|
|
export type AABB = { x: number; y: number; w: number; h: number };
|
|
export type Span = { entry: number; exit: number };
|
|
export type Hit = { time: number; normal: Vec };
|
|
|
|
/** Step 05, finished. */
|
|
export function sweepInterval(
|
|
p: number,
|
|
v: number,
|
|
min: number,
|
|
max: number,
|
|
): Span | null {
|
|
if (v === 0) {
|
|
return min <= p && p <= max ? { entry: -Infinity, exit: Infinity } : null;
|
|
}
|
|
|
|
let t1 = (min - p) / v;
|
|
let t2 = (max - p) / v;
|
|
if (t1 > t2) {
|
|
[t1, t2] = [t2, t1];
|
|
}
|
|
|
|
return { entry: t1, exit: t2 };
|
|
}
|
|
|
|
/** Step 06, finished. A moving point vs a static box. */
|
|
export function rayVsAABB(p: Vec, v: Vec, box: AABB): Hit | null {
|
|
const spanX = sweepInterval(p.x, v.x, box.x, box.x + box.w);
|
|
const spanY = sweepInterval(p.y, v.y, box.y, box.y + box.h);
|
|
if (spanX === null || spanY === null) {
|
|
return null;
|
|
}
|
|
|
|
const entry = Math.max(spanX.entry, spanY.entry);
|
|
const exit = Math.min(spanX.exit, spanY.exit);
|
|
if (entry > exit) {
|
|
return null;
|
|
}
|
|
if (entry >= 1 || exit <= 0) {
|
|
return null;
|
|
}
|
|
|
|
const normal =
|
|
spanX.entry > spanY.entry
|
|
? { x: v.x > 0 ? -1 : 1, y: 0 }
|
|
: { x: 0, y: v.y > 0 ? -1 : 1 };
|
|
|
|
return { time: entry, normal };
|
|
}
|