ehm
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import { type AABB, EPSILON, moveAndSlide, type Vec } from "./given.ts";
|
||||
|
||||
/**
|
||||
* How to push `a` OUT of `b`, by the smallest possible single-axis move.
|
||||
*
|
||||
* Returns the push as a vector to ADD to a's position, or null if the boxes
|
||||
* don't strictly overlap (merely touching edges is not overlapping).
|
||||
*
|
||||
* There are four ways out (left, right, up, down) — measure all four
|
||||
* distances and return the shortest one. See the README.
|
||||
*/
|
||||
export function penetrationVector(a: AABB, b: AABB): Vec | null {
|
||||
const outLeft = a.x + a.w - b.x;
|
||||
const outRight = b.x + b.w - a.x;
|
||||
const outUp = a.y + a.h - b.y;
|
||||
const outDown = b.y + b.h - a.y;
|
||||
|
||||
const minX = Math.min(outLeft, outRight);
|
||||
const minY = Math.min(outUp, outDown);
|
||||
|
||||
if (minX > 0 && minY > 0) {
|
||||
return minX > minY
|
||||
? { x: 0, y: outDown > outUp ? -minY : minY }
|
||||
: { x: outLeft > outRight ? minX : -minX, y: 0 };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* moveAndSlide, but immune to the overlap trap: first push `box` out of any
|
||||
* wall it is already inside (plus an EPSILON of slack, same idea as the
|
||||
* backoff in the loop), THEN run the normal sweep loop from the safe spot.
|
||||
*
|
||||
* Everything you need is penetrationVector above and given.ts.
|
||||
*/
|
||||
export function safeMoveAndSlide(box: AABB, v: Vec, walls: AABB[]): Vec {
|
||||
for (const wall of walls) {
|
||||
const pv = penetrationVector(box, wall);
|
||||
if (pv) {
|
||||
box.x += pv.x > 0 ? pv.x + EPSILON : pv.x - EPSILON;
|
||||
box.y += pv.y > 0 ? pv.y + EPSILON : pv.y - EPSILON;
|
||||
}
|
||||
}
|
||||
|
||||
return moveAndSlide(box, v, walls);
|
||||
}
|
||||
Reference in New Issue
Block a user