cast_shapes / minkowski_ray_cast returns a time of impact that comes back short by an amount that scales with the target shape's extent. Casting a ball (r = 0.5166) straight down at a cuboid, measured max TOI distance error:
| cuboid half-extent |
max TOI error |
| 5 m |
0.246 mm |
| 50 m |
3.64 mm |
| 500 m |
139.45 mm |
The error is one-sided (always short), pose-discontinuous (deterministic per pose, jumping tick to tick), and the witness data (point1/normal1) stays exact even when the TOI is wrong.
Reproduction
Failing test on a branch of my fork (test commit only, no fix — based on current master):
git clone https://github.com/vikng-dev/parry && cd parry
git checkout 89eb49a
cargo test -p parry3d --test lib shape_cast_toi_accuracy
# FAILS — prints the error table above
Mechanism
At first I suspected the relative convergence tolerance (gjk.rs:775-786), but it never fires for this case — instrumented over the test's 2,400 casts: 0 hits. What actually happens: minkowski_ray_cast advances a lower TOI bound ltoi and tracks an upper bound. With large support coordinates orthogonal to the cast direction, float cancellation keeps the upper bound from decreasing; the "upper bounds inconsistencies" last-chance exit fires (gjk.rs:712-715) and returns the current ltoi as the hit (gjk.rs:721) — still short by the stagnated gap. All 2,278 erroneous TOIs in the instrumented run exited through this path.
Impact
Hit this in a 64 Hz networked tank sim: wheel sphere-casts against a 1000 m ground cuboid came back short by p50 33 mm / p99 134 mm / max 200 mm over 19,828 samples; through a 551 kN/m suspension spring that's 10-40 kN of per-tick force noise — a sustained at-rest hull limit cycle (~12 mm heave) and a standing client/server divergence amplifier. Looks like #414 hit the same exit from a Rapier DynamicShapeCastVehicleController (wheels vs a 10,000-unit ground backstop, TOI ~10× short) before self-closing. #180's knife-edge None returns may live in the same exit structure too.
I have a fix ready — PR follows right after this issue.
cast_shapes/minkowski_ray_castreturns a time of impact that comes back short by an amount that scales with the target shape's extent. Casting a ball (r = 0.5166) straight down at a cuboid, measured max TOI distance error:The error is one-sided (always short), pose-discontinuous (deterministic per pose, jumping tick to tick), and the witness data (
point1/normal1) stays exact even when the TOI is wrong.Reproduction
Failing test on a branch of my fork (test commit only, no fix — based on current master):
Mechanism
At first I suspected the relative convergence tolerance (gjk.rs:775-786), but it never fires for this case — instrumented over the test's 2,400 casts: 0 hits. What actually happens:
minkowski_ray_castadvances a lower TOI boundltoiand tracks an upper bound. With large support coordinates orthogonal to the cast direction, float cancellation keeps the upper bound from decreasing; the "upper bounds inconsistencies" last-chance exit fires (gjk.rs:712-715) and returns the currentltoias the hit (gjk.rs:721) — still short by the stagnated gap. All 2,278 erroneous TOIs in the instrumented run exited through this path.Impact
Hit this in a 64 Hz networked tank sim: wheel sphere-casts against a 1000 m ground cuboid came back short by p50 33 mm / p99 134 mm / max 200 mm over 19,828 samples; through a 551 kN/m suspension spring that's 10-40 kN of per-tick force noise — a sustained at-rest hull limit cycle (~12 mm heave) and a standing client/server divergence amplifier. Looks like #414 hit the same exit from a Rapier
DynamicShapeCastVehicleController(wheels vs a 10,000-unit ground backstop, TOI ~10× short) before self-closing. #180's knife-edgeNonereturns may live in the same exit structure too.I have a fix ready — PR follows right after this issue.