Part III · Interaction · Chapter 09
available

Contact mechanics

Collision detection reports overlap. Contact mechanics turns that geometry into impulses that prevent interpenetration, create bounce, and support resting bodies without gluing them to the floor.

Level
Advanced
Hands-on
150–210 minutes
Before you start
Chapters 1, 7–8; vectors and impulse–momentum

By the end, you can…

  • State the unilateral contact and complementarity conditions.
  • Derive a one-dimensional normal impulse with restitution.
  • Compute rigid-body effective mass at a contact point.
  • Distinguish restitution, stabilization, and compliance.
  • Explain projected sequential impulses and warm starting.
  • Read force and penetration from LavenderSim telemetry.

Contact is a one-sided constraint

Let ϕ\phi be signed separation: positive apart, zero touching, negative penetrating. Let λn\lambda_n be the normal impulse. A normal contact may push, never pull:

ϕ0,qquadλnge0,qquadϕλn=0\phi\ge0,qquad \lambda_nge0,qquad \phi\lambda_n=0

This is complementarity. Separated bodies need no impulse; an active contact may produce a nonnegative impulse. A practical time-stepping solver applies the same idea to predicted normal velocity and projects every trial impulse onto [0,)[0,\infty).

Geometry versus response: Chapter 8 produced body IDs, a point, a normal, and penetration. This chapter consumes that record. The renderer is not part of either proof.

Experiment 1: derive the bounce impulse

An impulse jnj n changes linear momentum instantly. For two point masses, relative normal speed changes by:

vn+=vn+(mA1+mB1)jv_n^+=v_n^-+(m_A^{-1}+m_B^{-1})j

Newton's restitution law asks for vn+=e,vnv_n^+=-e,v_n^- when bodies approach. Therefore:

j=(1+e)vnmA1+mB1,qquadjmax(0,j)j=-\frac{(1+e)v_n^-}{m_A^{-1}+m_B^{-1}},qquad j\leftarrow\max(0,j)

The projection is essential. If vn0v_n^-\ge0, the bodies already separate, and a negative impulse would pull them back together.

Rigid bodies can also rotate. With offsets rA,rBr_A,r_B from centers of mass to the contact, the scalar effective inverse mass becomes:

kn=mA1+mB1+n[(IA1(rA×n))×rA+(IB1(rB×n))×rB]k_n=m_A^{-1}+m_B^{-1}+n\cdot\left[(I_A^{-1}(r_A\times n))\times r_A+(I_B^{-1}(r_B\times n))\times r_B\right]

Replace the denominator with knk_n. A push near a body's center is easier to translate; an off-center push must also create angular velocity.

Experiment 01verified

Implement a non-pulling impulse

What should the solver do for approaching and separating velocities?

Normal impulse and post impact speed against incoming relative velocity
Approaching speeds are negative and receive a positive impulse. Separating speeds receive exactly zero—contact is not an invisible spring tying bodies together.
python
def normal_impulse(vn, inv_mass_a, inv_mass_b, restitution):
    if vn >= 0:                 # already separating
        return 0.0
    k = inv_mass_a + inv_mass_b
    return -(1 + restitution) * vn / k

Failure modes: omit relative velocity, reverse the normal without swapping body order, forget rotational effective mass, or allow j<0j<0.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

Experiment 2: predict a rebound

Drop from height hh without air resistance. Just before impact, v2=2ghv^2=2gh. Restitution multiplies speed by ee, so energy—and therefore rebound height—scales with its square:

hrebound=(ev)22g=e2hh_{rebound}=\frac{(ev)^2}{2g}=e^2h
Experiment 02verified

Sweep restitution

Does restitution 0.8 return 80% of height?

Ideal and measured rebound height across restitution
No: ideal height is 64% because speed is squared. LavenderSim follows the trend; its finite-step stabilization adds visible impact error at 240 Hz.
0.64 mideal height at e = 0.8
0.659 mnative result at 480 Hz
0 N·simpulse while separating

Failure modes: compare center height with surface clearance, include a later bounce in the first-apex measurement, expect eheh, or use restitution to repair resting penetration.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

Experiment 3: separate three jobs

Restitution targets impact velocity. Stabilization adds separating velocity when numerical integration has already produced penetration. A common bias is:

b=βΔtmax(ddslop,0)b=-\frac{\beta}{\Delta t}\max(d-d_{slop},0)

where d=min(ϕ,0)d=-\min(\phi,0) is penetration. Compliance regularizes a rigid constraint into a soft one. At velocity level, LavenderSim uses γ=c/Δt2\gamma=c/\Delta t^2:

Δλ=vn+b+brest+γλkn+γ,qquadλmax(0,λ+Δλ)\Delta\lambda=-\frac{v_n+b+b_{rest}+\gamma\lambda}{k_n+\gamma},qquad \lambda\leftarrow\max(0,\lambda+\Delta\lambda)

These knobs interact. Larger steps permit deeper first overlap. Stronger stabilization may inject energy. Compliance improves conditioning and softness, but allows load-dependent deflection.

Experiment 03verified

Sweep timestep and compliance

Which error belongs to discretization, and which belongs to the chosen soft-contact model?

Native rebound height by timestep and resting penetration by compliance
Finer stepping moves rebound toward the ideal result. Increasing per-surface compliance increases steady penetration while the normal force still converges to the body's weight.

Failure modes: treat compliance as an iteration count, tune bounce with penetration bias, divide compliance by Δt\Delta t instead of Δt2\Delta t^2, or compare timesteps while changing simulated duration.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

Experiment 4: inspect the contact rather than the pixels

At impact the one-kilogram sphere receives a short, large force. Once settled, its normal force approaches mg9.81mg\approx9.81 N. LavenderSim exposes aggregate body contact data and Chapter 8's individual contact trace:

python
observation, reward, terminated, truncated, info = sim.step()
contacts = observation["collision"]["contacts"]
normal_force = sim.body_contact_data()[ball_id, 1]
penetration = contacts[:, 8].max() if len(contacts) else 0.0
Experiment 04verified

Plot force beside penetration

Can telemetry distinguish an impact from resting support?

LavenderSim penetration and normal contact force around sphere impact
The impact force spikes above 1 kN for one 240 Hz step. Penetration is projected down, and steady force approaches 9.81 N.

A force reported per discrete step is an average Fj/ΔtF\approx j/\Delta t, not a resolved microsecond material-force curve. Shorter timesteps can produce taller-looking spikes while representing a similar impulse.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

Experiment 5: remember yesterday's answer

A stack has coupled contacts: changing one impulse changes another contact's velocity. Projected Gauss–Seidel visits rows repeatedly. A cold start begins with zero impulses every step. A warm start matches persistent contacts and begins from the previous step's impulses, usually close to the new solution.

Experiment 05verified

Compare cold and warm contact solves

With the same iteration budget, which initial guess leaves the smaller residual?

Projected Gauss Seidel residual for cold and warm contact impulses
A slightly changed six-block stack starts about twenty times closer with cached impulses and retains that advantage at every fixed-budget sweep.

Production matching uses body pair, nearby contact point, and compatible normal. Bad matches can inject stale impulses, so caches need distance thresholds, normal checks, timestep scaling, and invalidation when topology changes.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

The experiment found—and fixed—a real solver bug

The first native sweep initially showed identical rebound for every restitution. The contact solver recomputed the restitution condition inside each of its 22 iterations. Iteration one created a bounce; iteration two observed separation, set the bounce target to zero, and removed most of that impulse.

LavenderSim now captures the pre-solve impact velocity once when the contact is created and holds that restitution target fixed throughout the solve. The native rebound regression test protects this behavior. This is precisely why a simulator tutorial should contain quantitative experiments rather than screenshots alone.

Implemented by LavenderSim: append_contact() captures the restitution bias, solve_contact_velocity() performs the projected compliant impulse update, and the persistent cache warm-starts matched contacts.

Paw-check: make contact earn its force

  1. Derive the impulse for two movable masses instead of a ball and static floor.
  2. Move a contact away from a box's center and compute its rotational effective mass.
  3. Set e=1e=1 and plot numerical energy over five bounces.
  4. Sweep stabilization gain and mark where rebound becomes artificially energetic.
  5. Change the stack's load slightly and quantify the warm-start advantage.
  6. Disable positional projection and measure steady penetration.
  7. Compare impulse jj across timesteps instead of peak force.
What should I be able to say now?

Normal contact is a unilateral constraint: it may push but not pull. Effective mass converts a desired change in relative point velocity into impulse. Restitution targets impact speed; stabilization repairs overlap; compliance softens and regularizes the constraint; projection enforces nonnegative impulses; and caching gives an iterative solver a better initial guess. Native force and penetration telemetry let us test each claim quantitatively.