Files
nage/workshop/steps/10-move-and-slide/given.ts
T
Schluffe 196d87eaea
CI / test (push) Failing after 1m18s
CI / benchmark (bun) (push) Skipped
CI / benchmark (deno) (push) Skipped
Add CI stuff
2026-08-27 12:46:42 +02:00

64 lines
1.9 KiB
TypeScript

// The whole kernel you've built, steps 01-09, finished. Reuse it.
export type Vec = { x: number; y: number };
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 };
/** A hair of slack so we stop just short of a wall instead of inside it. */
export const EPSILON = 1e-4;
export const vec = (x: number, y: number): Vec => ({ x, y });
export const add = (a: Vec, b: Vec): Vec => ({ x: a.x + b.x, y: a.y + b.y });
export const sub = (a: Vec, b: Vec): Vec => ({ x: a.x - b.x, y: a.y - b.y });
export const scale = (a: Vec, s: number): Vec => ({ x: a.x * s, y: a.y * s });
export const dot = (a: Vec, b: Vec): number => a.x * b.x + a.y * b.y;
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 };
}
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 || 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 };
}
export function sweptAABB(a: AABB, v: Vec, b: AABB): Hit | null {
const inflated: AABB = {
x: b.x - a.w,
y: b.y - a.h,
w: b.w + a.w,
h: b.h + a.h,
};
return rayVsAABB({ x: a.x, y: a.y }, v, inflated);
}
export function slide(v: Vec, normal: Vec): Vec {
return sub(v, scale(normal, dot(v, normal)));
}