# Ball Physics — Iteration 2 Summary

**Game:** Positron Stakes, ball-toss panel.
**Scope:** What ball/hole/rim physics the game now implements, after iteration 2.

In v1, hole interaction was a single check: if the ball's center entered a hole circle and its speed was low enough, capture. Iteration 2 replaces that with a multi-stage physically-grounded model derived from the golf-putting literature[^1][^2][^3].

---

## 1. What was wrong with v1

The naive `if (dist < R) capture` model produced six specific failures:

1. **No skip-over.** A ball moving fast enough that it would, in real life, fly clean over the hole instead either got captured or got dragged into the pocket and visibly distorted. There was no regime where high velocity meant "the hole isn't there."
2. **Random-feeling rim bounces.** When bounces were added, they behaved like a hard wall — high restitution, no asymmetry between glancing and head-on contact. The signature golf-lip behaviors (rim-around, lip-out) were absent.
3. **Bounces carried too much energy.** A single rim contact left the ball moving fast enough to cross the entire panel and bounce a second time. Real wood-on-rubber loses ~90% of kinetic energy on a head-on hit.
4. **"Covering" captures.** At low speed, dropping the ball with its rim just touching the hole's rim — center of gravity clearly outside the hole — still scored. The capture criterion didn't respect the ball's footprint.
5. **Throw-speed range was too wide.** 0 → 1000 px/s mapped to the meter; the bottom 30% couldn't reach the top row and the top 20% always overshot. The useful band was narrow and surrounded by dead zones.
6. **Tangential brushes got sucked in.** A ball glancing the very edge of the rim — most of its body outside the hole — would enter the rim-roll state and spiral in. The geometry gate that lets a ball *actually* roll around a rim was missing.

---

## 2. What's actually happening physically

A ball of radius `r` approaching a hole of radius `R` on a flat surface partitions the (offset `δ`, speed `v`) plane into four regions. `δ` is the perpendicular distance from the ball's velocity line to the hole center (geometrically, the closest the ball center ever gets to the hole center on a straight-line trajectory); `v` is its speed at the rim. The Royal Society 2024 lip-out paper[^3] gives closed-form boundaries for all four:

![Regime map](figures/regime-map.png)

| Region | Behavior | Bounding equation |
|---|---|---|
| 1 — Skip-over | Ball flies over the hole; chord time < free-fall time. | `v > √(2gr/r) · √(R² − δ²)` |
| 2 — Mid-rim drop | Ball touches rim; normal force vanishes mid-arc; falls in. | `v ≈ √(2gr/(1+j)) / √(Rδ²/(R−r)³ − 1)` |
| 3 — Lip-out / rim-around | Ball rides the rim through some arc; either drops or pops back out. Can roll >180°. | Separatrix; numerically integrated |
| 4 — Clean entry | Drops straight in. | `δ ≲ R · √(1 − (v/V_max)²)` (Penner)[^2] |

For our game `R = 18 px`, `r = 11 px`, so `R/r ≈ 1.64`. This is *much* tighter than golf (`R/r ≈ 2.57`) — the hole is barely bigger than the ball, so the rim-interaction regions occupy most of the (δ, v) plane and clean entry (region 4) is a small wedge near `δ = 0`.

---

## 3. The six implementation stages

Each stage maps to a chunk of the (δ, v) plane and is independently shippable. Together they reproduce all four regions plus the physical loss/geometry constraints that make rim interactions feel realistic.

### Stage 1 — Speed-dependent capture window (Penner effective hole)

**Logic.** On the frame the ball center first enters a hole, compute its speed `v` and its line-offset `δ` from the hole center. Capture iff the offset is inside a speed-shrinking window.

**Physics.** The Penner "effective hole" insight[^2]: at low speed the ball can fall through anywhere inside the rim; at high speed only a perfectly-centered shot has time for the center of mass to drop past the lip. The leading-order behavior is

`δ_eff(v) = R · √(1 − (v/V_MAX)²)`

with `V_MAX = 600 px/s` tuned so high-power throws are above the curve for any δ > 0 — they cannot enter clean. Region 4 of the (δ, v) map.

### Stage 2 — Rim bounce (elastic + tangential friction)

**Logic.** When the capture test fails, the ball is in contact with the inner edge of the rim. Decompose its velocity into normal (into rim) and tangential (along rim) components at the contact point and reflect.

**Physics.** Standard rigid-body collision with a curved wall:

```
n̂  = (ball.pos − hole.center) / |…|
v_n = v · n̂                    (scalar)
v_t = v − v_n · n̂              (vector)
v'  = (1 − μ_t) · v_t  −  e · v_n · n̂
```

`e = RIM_RESTITUTION = 0.30`, `μ_t = RIM_FRICTION = 0.30`. Produces caroms off the back rim (head-on `v_n` dominant) and skim-offs at glancing angles (`v_t` dominant) from one rule.

### Stage 3 — Rim-around / lip-out

**Logic.** When a Stage-2 contact has tangential velocity high relative to normal — `|v_t| / |v_n| > RIM_ROLL_RATIO = 1.4` — instead of bouncing, constrain the ball's position to the rim arc and let tangential speed decay each frame. Exit when speed drops below `RIM_ROLL_DROP_SPEED = 110 px/s` (falls in) or when total arc traveled exceeds `RIM_ROLL_MAX_TURNS · 2π` (pops off the rim).

**Physics.** Sustained rolling contact along a concave arc: the ball's center is constrained to a circle of radius `R` around the hole center (ball balanced on the lip); tangential KE bleeds off through rolling/sliding friction, modeled as exponential decay `v_t ← v_t · exp(−μ_roll · dt)` with `μ_roll = RIM_ROLL_FRICTION = 5.0`. This is the Hubbard-Smith roll-around visualized: a ball traces a partial loop on the rim before resolving.

### Stage 4 — Rim energy tuning

**Logic.** Lower restitution and raise friction relative to defaults until bounce velocities match physical wood-on-hard-rubber. No structural change — just calibration.

**Physics.** Energy loss per collision is `1 − e²` along the normal and `1 − (1 − μ_t)²` along the tangent. With `e = 0.30, μ_t = 0.30`, a head-on hit loses `1 − 0.09 = 91%` of normal KE; a 45° hit loses 71% of total KE. Empirically this lands bounces at distances that look real (ball stops within a hole-width or two of impact) instead of crossing the panel.

### Stage 5 — Skip-over (chord-traversal projectile)

**Logic.** Before testing capture, check whether the ball is moving fast enough that its chord through the hole takes less time than it takes for the ball to fall by its own radius. If so, lock the hole into a "skip" state for this transit — no capture, no rim collision, no near-miss flash.

**Physics.** The ball is briefly airborne over the hole. Chord length at offset `δ` is `2√(R² − δ²)`; traversal time `t = 2√(R² − δ²) / v`; vertical drop in that time `Δh = ½ g t²`. The ball will *not* drop in if `Δh < r`, giving

`v_skip(δ) = V_SKIP_MAX · √(1 − δ²/R²),   V_SKIP_MAX = R · √(2g/r) = 640 px/s`

Same `√(1 − δ²/R²)` shape as the Stage 1 capture curve — they are parallel curves in (δ, v), and the band between them (`V_MAX = 600` to `V_SKIP_MAX = 640`) is where rim interactions live. Region 1 of the (δ, v) map.

![Skip-over geometry and the parallel capture/skip curves](figures/skip-over.png)

### Stage 6 — COG geometry gate for rim-roll

**Logic.** Rim-roll (Stage 3) only engages if the ball's center of gravity is far enough past the rim that more than half the ball is over the hole. Otherwise the contact is treated as a Stage-2 bounce and the ball deflects off rather than spiraling in.

**Physics.** A ball balanced on the rim with its center of gravity outside the hole has a restoring moment — friction/torque pushes it back out, not around. The threshold is

`δ_COG < R − r/2`

Concretely, `R = 18`, `r = 11` → `δ < 12.5 px`. Tangential brushes (`δ > 12.5`) deflect; only contacts where the ball is substantially over the hole get to ride the rim. This is the geometric criterion absent from v1 that fixes the "tangential brush gets pulled in" failure.

![COG geometry at five trajectory offsets](figures/cog-geometry.png)

---

## 4. Constants (current values, `BallTossPanel.jsx`)

| Symbol | Value | Role |
|---|---|---|
| `R` (HOLE_RADIUS) | 18 px | hole radius |
| `r` (BALL_RADIUS) | 11 px | ball radius (R/r ≈ 1.64) |
| `V_MAX` | 600 px/s | capture window collapse speed |
| `V_SKIP_MAX` | 640 px/s | dead-center skip threshold |
| `e` (RIM_RESTITUTION) | 0.30 | normal coefficient of restitution |
| `μ_t` (RIM_FRICTION) | 0.30 | tangential friction loss |
| `RIM_ROLL_RATIO` | 1.4 | `\|v_t\|/\|v_n\|` to enter rim-roll |
| `RIM_ROLL_FRICTION` | 5.0 | rim-roll exponential decay rate |
| `RIM_ROLL_DROP_SPEED` | 110 px/s | rim-roll → drop threshold |
| `MIN_VY` | 175 px/s | minimum throw speed |
| `MAX_VY` | 700 px/s | maximum throw speed |

The throw range `[MIN_VY, MAX_VY] = [175, 700]` is intentionally narrow relative to `V_MAX = 600` and `V_SKIP_MAX = 640`: the player can reach all the holes, the upper third of the meter is in skip-over for any aim error, and the sweet spot for top-row holes is roughly the middle of the meter.

---

## 5. Region → Stage mapping

| (δ, v) Region | Real-world behavior | Stage(s) | Gate equation |
|---|---|---|---|
| 1 Skip-over | Flies over hole | Stage 5 | `v > V_SKIP_MAX · √(1 − δ²/R²)` |
| 2 Mid-rim drop | Touches rim, falls in | Stage 1 (low v) + Stage 3 (mid v) | `δ < R · √(1 − (v/V_MAX)²)` ∨ rim-roll exits inward |
| 3 Lip-out / rim-around | Rides rim then exits | Stage 3 + Stage 6 | `\|v_t\|/\|v_n\| > 1.4` AND `δ < R − r/2` |
| 4 Clean entry | Drops straight in | Stage 1 | `δ ≤ R · √(1 − (v/V_MAX)²)` |
| Outside all | Bounces off rim | Stage 2 + Stage 4 | failed capture, failed rim-roll gate |

---

## References

[^1]: Holmes, B. W. (1991). *Putting: How a golf ball and hole interact.* American Journal of Physics 59(2):129–136.
[^2]: Penner, A. R. (2002). *The physics of putting.* Canadian Journal of Physics 80(2):83–96.
[^3]: Royal Society Open Science (2024). *Mechanics of the golf lip out.* 11:250907 — closed-form (δ, v) parameter-space map and four-region taxonomy.
