3. Keeping a rigid ball above the floor#

3.1. A rule for valid positions#

Gravity alone can move a body through a floor. Add a constraint: a rule that the stored state must satisfy. A constraint solver finds values satisfying that rule, instead of accepting an impossible overlap.

Consider one rigid circle in a vertical plane above a fixed horizontal floor. Rigid means its shape and radius do not change. It does not mean every collision preserves energy. Let \(x,y\) be the circle’s center coordinates in metres, with upward-positive \(y\) and the floor at \(y=0\). Its radius \(R=0.20\) m is constant.

The circle’s bottom is \(y-R\). Call that signed gap \(d\), in metres. Positive means separated, zero means touching, and negative means penetrating. The sign \(\geq\) means greater than or equal to. The nonpenetration constraint is

\[d=y-R\geq0.\]

This is the simplest flat-floor version of the contact inequality in Erin Catto’s constraint notes, sections on inequality and contact constraints. The floor is fixed: only the ball is moved to satisfy it. There is no need to distribute a correction between two movable bodies.

Suppose \(y_p\) is a predicted center height. The subscript p means prediction, not an accepted state. max selects the larger of its two arguments. Its position correction is

\[y_{\mathrm{valid}}=\max(R,y_p).\]

A prediction at 0.18 m with radius 0.20 m is corrected upward by 0.02 m. A prediction at 0.50 m stays there. This operation is called projection onto valid positions. One correction solves this single constraint exactly. With several interacting constraints, correcting one can invalidate another; then repeated passes can be needed. Those passes improve the same state and do not each advance time.

3.2. What happens to velocity at impact?#

Changing position alone does not explain a bounce. Let \(u\) be the vertical velocity immediately before impact, in m/s. An approaching ball has \(u<0\). The floor’s normal is its perpendicular direction, upward here. Let \(v\) be vertical velocity immediately after impact.

The coefficient of restitution \(e\) is the ratio of outgoing to incoming normal speed. Choose \(e=0.65\), between zero and one:

\[v=-e u.\]

The minus sign reverses direction; multiplication by \(e\) reduces speed. For \(u=-4\) m/s, \(v=2.6\) m/s. A ball already moving away is not bounced again. The horizontal component is unchanged by this frictionless floor. The contact and restitution rule is described in Catto’s restitution notes; this example uses only its vertical case.

Let mass \(m=1\) kg. Momentum is mass times velocity, in kg m/s. Impulse \(J\) is the collision’s change of momentum, in N s, equivalent to kg m/s. It is not a sustained force added every frame:

\[J=m(v-u)=-m(1+e)u.\]

The example gives an upward impulse of 6.6 N s. The idealized instantaneous velocity change represents a short contact event without modeling the force’s detailed time history. OpenStax’s impulse explanation distinguishes this accumulated effect from a force in N.

For this vertical impact, incoming kinetic energy \(K_{\mathrm{in}}\) and outgoing kinetic energy \(K_{\mathrm{out}}\), both in joules (J), are one-half mass times speed squared:

\[K_{\mathrm{in}}=\frac{1}{2}m u^2, \qquad K_{\mathrm{out}}=\frac{1}{2}m v^2=e^2K_{\mathrm{in}}.\]

Here \(e^2=0.4225\): 42.25% of vertical kinetic energy remains and 57.75% is lost. The 8 J incoming example leaves 3.38 J. The ideal rigid picture never squashes, while its restitution law represents unresolved energy loss during real contact. Perfect rigidity and perfect elasticity are different assumptions.

3.3. Find contact before finishing the tick#

Let \(g=9.81\) m/s² be a chosen constant near-surface downward gravity magnitude. It is positive; vertical acceleration is \(-g\). Let \(h\) be the tick’s duration in physical seconds and \(u\) now the starting vertical velocity. With starting height \(y\), free flight is

\[y_p=y+u h-\frac{1}{2}g h^2, \qquad v_p=u-g h.\]

These are the exact constant-acceleration relations from OpenStax’s free-flight derivation, not a large Euler approximation. Horizontal position advances by horizontal velocity times \(h\). They also agree with RK4 for constant gravity, but require no trial slopes.

Clamping an end-of-tick prediction and reversing its end velocity can make the bounce too energetic: the velocity may include gravity from time spent below the floor. Instead calculate the time of impact \(\tau\), the time from the start until the bottom first reaches the floor. Using starting gap \(d=y-R\), solve

\[d+u\tau-\frac{1}{2}g\tau^2=0, \qquad \tau=\frac{u+\sqrt{u^2+2gd}}{g}.\]

For a separated ball this is the positive root. A touching ball with downward or zero velocity has immediate contact; a touching ball moving upward returns after \(2u/g\). The code uses an algebraically equivalent root when downward motion and a tiny gap would make subtraction inaccurate.

If \(\tau>h\), accept ordinary free flight. Otherwise advance exactly to \(\tau\), set height to \(R\), apply restitution to the impact velocity \(u-g\tau\), then integrate the remaining \(h-\tau\) seconds. Apply the same rule again if another impact occurs in that remainder. This is one tick containing contact events, not extra elapsed time per correction. The final height is checked against the constraint.

For example, start at 2.20 m with zero velocity and radius 0.20 m. The gap is 2 m. Contact occurs after \(\sqrt{4/9.81}=0.63855\) s, at approximately -6.2642 m/s. Restitution gives +4.0717 m/s. The next rise above contact is \(v^2/(2g)=0.845\) m: 0.4225 times the original 2 m drop. Detecting contact at the right time avoids increasing that height.

3.4. Rest is a supported state#

Constant restitution would produce smaller and smaller bounces. To stop the tiny ones explicitly, choose a cutoff \(c=0.20\) m/s for incoming impact speed. The bars in \(|u|\) mean absolute value, or speed without its sign. If \(|u|\leq c\) at impact, accept \(y=R,v=0\) and mark the ball resting. At 1 kg the discarded incoming kinetic energy is at most \(m c^2/2=0.02\) J. This is a documented numerical resolution choice, not an airborne damping force. Low-speed inelastic contact is also used to avoid jitter in Box2D’s contact implementation.

While resting, the fixed floor’s upward reaction balances weight: \(m g=9.81\) N. Position and velocity remain unchanged; gravity is not allowed to push the ball down and then be corrected endlessly. A grabbed ball instead has a prescribed position supplied by the hand, within the valid domain. That input can add gravitational potential energy. Releasing sets both velocity components to zero deliberately: no inferred throwing speed, no extra impulse. A release at the floor starts resting immediately.