Closest-Point Queries

Detecting a collision is only half the job. Once two things are touching, the engine has to respond: push them apart, slide one along the other, snap a character to the floor, find how far the cursor is from a wire. Every one of those needs the same primitive — the closest point on a shape to a given point \vec{p}. Once you have it, the distance to the shape is just \lVert \vec{p} - \text{closest}\rVert, and the direction to push is the line between them. Three shapes, all solved by the same move: project, then clamp.

Closest point on a segment, line by line

A segment runs from A to B; find the point on it nearest to \vec{p}.

Step 1 — drop a perpendicular onto the infinite line. The nearest point on the whole line through A and B is the foot of the perpendicular from \vec{p}. Write the line as A + t(B - A); the foot's parameter t comes from projecting \vec{p} - A onto the direction B - A:

t = \frac{(\vec{p} - A)\cdot(B - A)}{\lVert B - A\rVert^2}.

Step 2 — clamp t to the segment. A segment is not an infinite line — it stops at its endpoints. If the projection lands before A (t < 0) or past B (t > 1), the true nearest point on the segment is the endpoint it overshot. So pin t into [0, 1]:

t^\star = \min\big(1,\ \max(0,\ t)\big).

Step 3 — evaluate at the clamped parameter. The closest point on the segment is the line point at t^\star:

\vec{q} = A + t^\star (B - A).

That clamp is the whole story of segment distance: interior projections give a perpendicular foot, exterior ones fall back to an endpoint.

Closest point on a plane and on a box, line by line

Step 4 — closest point on a plane: subtract the signed distance. For a plane with unit normal \vec{n} and offset d, the signed distance of \vec{p} is s = \vec{n}\cdot\vec{p} - d. Stepping back along the normal by exactly that much lands on the plane:

\vec{q} = \vec{p} - (\vec{n}\cdot\vec{p} - d)\,\vec{n} = \vec{p} - s\,\vec{n}.

No clamping — a plane is unbounded, so the perpendicular foot is always valid.

Step 5 — closest point on an AABB: clamp each coordinate. Because the box [\vec{m}, \vec{M}] is a product of independent intervals, the nearest point is found one axis at a time — clamp each coordinate of \vec{p} into that axis's interval:

q_a = \min\big(M_a,\ \max(m_a,\ p_a)\big) \quad\text{for each axis } a \in \{x, y, z\}.

If \vec{p} is already inside the box every coordinate passes through and \vec{q} = \vec{p} (distance zero); if it is outside, each axis snaps to the nearer face. The same componentwise clamp as Step 2, three times over.

Step 6 — the distance falls out. For any of these shapes, once you have the closest point \vec{q}, the distance from \vec{p} to the shape is simply

\operatorname{dist}(\vec{p}, \text{shape}) = \lVert \vec{p} - \vec{q}\rVert,

and for comparisons you keep it squared. One query gives you both the distance and the point — which is exactly what collision response needs next.

The closest point \vec{q} to a point \vec{p} on:

The most common job a closest-point query does is undo an overlap. When the narrow phase reports that two shapes have interpenetrated, the engine must separate them, and it wants to move them the least distance that does so — along the direction that exits the collision fastest.

Take a ball that has sunk into a wall. The closest point on the wall to the ball's centre gives both numbers at once: the penetration depth is r - \lVert \vec{c} - \vec{q}\rVert (how far the surface is inside the ball), and the contact normal is the unit direction (\vec{c} - \vec{q})/\lVert \vec{c} - \vec{q}\rVert from the closest point back to the centre. Push the ball out along that normal by the depth and the overlap is gone — the minimal-translation fix. Box-on-box, capsule-on-mesh, character-on-terrain: all reduce to "find the closest point, push along the line to it by the overlap". The same little project-and-clamp that measured the distance is what resolves the collision — the backbone of every physics response, built on projection and a clamp.