fix(gjk): shape-cast TOI error scales with target shape extent#430
fix(gjk): shape-cast TOI error scales with target shape extent#430vikng-dev wants to merge 3 commits into
Conversation
|
In the test you added, before and after what is the error? And/or at extreme distances, what is the error? (I see in example you put before it was 139mm, and after? 0mm or?) And this seems to change the gjk algorithm (for the case of error? So it doesn't converge enough or?), is this the only source of error? (eg. there is also a path with epa for eg. in some cases as I remember). EDIT: Oh, ok, so after your PR 0.00024 mm error, before 139mm error. |
|
Aye, 139mm -> 0.00024mm. |
Fixes #429.
What
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 witness data (
point1/normal1) is exact even when the TOI is wrong.Reproduction
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.Fix
On the last-chance path, refine
ltoifrom the simplex witnesses already in hand before accepting the impact: project the witness separation along the cast direction ((witness1 - witness2 - ray.origin) · dir). The witnesses stay precise exactly where the upper bound stagnates, so this recovers the accuracy the bound lost. Witness and normal generation are untouched — the returned contact data is bit-identical to before; only the TOI scalar changes. Added cost is confined to the rare stagnation exit (≤ dim+1 weighted witness accumulations via the existingresult()).Two intentional behavior changes to be aware of:
max_time_of_impactnow returnsNone, where the old code returned a wrongly-short hit inside the limit.Real-world impact
Found 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 was 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. #414 hit the same exit independently from a Rapier
DynamicShapeCastVehicleController(wheels vs a 10,000-unit ground backstop, TOI ~10× short) — this PR is the same diagnosis with a regression test and the refine kept inside the existing exit. Possibly related: #180's pose-knife-edgeNonereturns come from this same function's exit structure, though this PR does not address that case.Tests
shape_cast_toi_accuracy_does_not_scale_with_shape_extent(tests/geometry/time_of_impact3.rs) pins max error ≤ 0.2 mm across half-extents 5/50/500 m and exact witnesses. Full workspace--testssuites pass for parry3d, parry2d, parry3d-f64, parry2d-f64;cargo fmt --checkclean.