Matrix index out of bounds.
// Error / Panic --------------------------------------------------------------
parry2d-f64-0.18.0/src/query/clip/clip_aabb_line.rs:141:19: Matrix index out of bounds.
// What happened ---------------------------------------------------------
I accidentally created a triangle ColliderShape with:
point_a -> Vec2 { data: [40.0, 0.0] } point_b -> Vec2 { data: [ 0.0, 80.0] } // Same as point_c point_c -> Vec2 { data: [ 0.0, 80.0] } // Same as point_b
In clip_aabb_line.rs the origin parameter has NAN values -> pub fn clip_aabb_line(...);
In the loop in the else part: let mut inter_with_near_halfspace = (aabb.mins[i] - origin[i]) * denom; // Results in NaN (origin[i] == NaN) let mut inter_with_far_halfspace = (aabb.maxs[i] - origin[i]) * denom; // Results in NaN (origin[i] == NaN)
near_diag stays false near_side stays 0
in line 141 [0 - 1] Matrix index out of bounds.
// Solution ----------------------------------------------------------------
let near = if near_diag {
(tmin, -dir.normalize(), near_side)
} else {
let mut normal = Vector::zeros();
if near_side < 0 {
normal[(-near_side - 1) as usize] = 1.0;
} else if near_side > 0 { // <-------------------------------------- Here
normal[(near_side - 1) as usize] = -1.0;
}
else { // <--------------------------------------------------------- Here
return None;
}
(tmin, normal, near_side)
};
let far = if far_diag {
(tmax, -dir.normalize(), far_side)
} else {
let mut normal = Vector::zeros();
if far_side < 0 {
normal[(-far_side - 1) as usize] = -1.0;
} else if far_side > 0 { // <--------------------------------------- Here
normal[(far_side - 1) as usize] = 1.0;
}
else { // <--------------------------------------------------------- Here
return None;
}
(tmax, normal, far_side)
};
I'm getting this too, an only panic I've seen in a long time. Would be nice to get this fixed.
Problem:
If a triangle collider has two or all three points the same,
the code divides by zero inside project_local_point_and_get_location.
This makes the result NaN and breaks further calculations.
How to Reproduce: This example uses Rapier2D for demonstration, but the root cause of the error lies in Parry.
use rapier2d::prelude::*;
fn main() {
let mut rigid_body_set = RigidBodySet::new();
let mut collider_set = ColliderSet::new();
/* Create the ground. */
let collider = ColliderBuilder::ball(100.0).build();
collider_set.insert(collider);
/* Create the bouncing ball. */
let rigid_body = RigidBodyBuilder::dynamic()
.translation(vector![0.0, 0.0])
.build();
let a = point![40.0, 0.0];
let b = point![ 0.0, 80.0];
let c = point![ 0.0, 80.0];
let collider = ColliderBuilder::triangle(a, b, c).restitution(0.7).build();
let triangle_body_handle = rigid_body_set.insert(rigid_body);
collider_set.insert_with_parent(collider, triangle_body_handle, &mut rigid_body_set);
/* Create other structures necessary for the simulation. */
let gravity = vector![0.0, -9.81];
let integration_parameters = IntegrationParameters::default();
let mut physics_pipeline = PhysicsPipeline::new();
let mut island_manager = IslandManager::new();
let mut broad_phase = DefaultBroadPhase::new();
let mut narrow_phase = NarrowPhase::new();
let mut impulse_joint_set = ImpulseJointSet::new();
let mut multibody_joint_set = MultibodyJointSet::new();
let mut ccd_solver = CCDSolver::new();
let mut query_pipeline = QueryPipeline::new();
let physics_hooks = ();
let event_handler = ();
/* Run the game loop, stepping the simulation once per frame. */
for _ in 0..3 {
physics_pipeline.step(
&gravity,
&integration_parameters,
&mut island_manager,
&mut broad_phase,
&mut narrow_phase,
&mut rigid_body_set,
&mut collider_set,
&mut impulse_joint_set,
&mut multibody_joint_set,
&mut ccd_solver,
Some(&mut query_pipeline),
&physics_hooks,
&event_handler,
);
let triangle_body = &rigid_body_set[triangle_body_handle];
println!("Triangle altitude: {}", triangle_body.translation().y); // Prints NaN
}
}
Output: Triangle altitude: NaN Triangle altitude: NaN Triangle altitude: NaN
What Happens:
- In "src/query/point/point_triangle.rs" in the fn
project_local_point_and_get_location(around line 254), the code does:
let u = (ac_bp - ab_bp) / (ac_bp - ab_bp + ab_cp - ac_cp); // proj on bc = b + bc * u
This resolves in the NaN.
Propagation and Panic:
If a invalid triangle makes u become NaN, later code still uses these NaN values. For example, in clip_aabb_line:
let inter_with_near_halfspace = (aabb.mins[i] - origin[i]) * denom;
let inter_with_far_halfspace = (aabb.maxs[i] - origin[i]) * denom;
Both results are NaN because origin[i] is NaN. The flags near_diag and far_diag stay false, and near_side and far_side remain 0. Eventually the code does:
normal[(near_side - 1) as usize] = 1.0;
This becomes normal[-1], causing a panic: index out of bounds.
Note: I’m not sure how critical this is in practice, since you normally create triangles with valid, distinct points. I investigated this mainly to pinpoint the root cause of the matrix index out of bounds panic.*
Just to add: This was the first place I found where a triangle with duplicate points leads to issues, but I’m not sure if it’s the only one.
Thanks for the details! There's a few different approaches we can take:
- Avoid setting NaN to position for triangle
- This may be possible by detecting where NaN occurs first and avoid setting it.
- We have enough information here to start investigating.
- This may be possible by avoiding creating an incorrect triangle in the first place, would it be welcome to change
ColliderBuilder::triangle()to a Result ?
- This may be possible by detecting where NaN occurs first and avoid setting it.
- Fix in
clip_aabb_lineor up ; :warning: The issue is missing how exactly the call toclip_aabb_lineis triggered, I'm curious to see that.
I've managed to almost never get this by trying to ensure triangles don't share duplicate points. Exactly as was suggested above.
However, I've seen this even then (could be that there somewhere was some wrong triangle that I thought should no longer be the case).
Anyway, I'd appreciate the attached fix so parry wouldn't panic. We're so close to a panic free rapier (or parry) :)
I had accidentally deleted the original program and couldn’t remember how to reproduce the crash - but I finally managed to recreate it.
I'm spawning three dynamic rigid bodies at the same position:
- one with an invalid triangle collider,
- one with a valid ball collider,
- and one with a valid cuboid collider.
As soon as the simulation starts, it immediately panics.
Cargo.toml:
[package]
name = "clip_aabb_line"
version = "0.1.0"
edition = "2024"
[dependencies]
rapier2d = "0.26.1"
main.rs
use rapier2d::prelude::*;
fn main() {
let mut rigid_body_set = RigidBodySet::new();
let mut collider_set = ColliderSet::new();
/* Create ball. */
let rigid_body = RigidBodyBuilder::dynamic()
.translation(vector![0.0, 0.0])
.build();
let collider = ColliderBuilder::ball(10.0).restitution(0.7).build();
let ball_body_handle = rigid_body_set.insert(rigid_body);
collider_set.insert_with_parent(collider, ball_body_handle, &mut rigid_body_set);
/* Create cuboid. */
let rigid_body = RigidBodyBuilder::dynamic()
.translation(vector![0.0, 0.0])
.build();
let collider = ColliderBuilder::cuboid(10.0, 10.0).restitution(0.7).build();
let cuboid_body_handle = rigid_body_set.insert(rigid_body);
collider_set.insert_with_parent(collider, cuboid_body_handle, &mut rigid_body_set);
/* Create the invalid triangle. */
let rigid_body = RigidBodyBuilder::dynamic()
.translation(vector![0.0, 0.0])
.build();
let a = point![40.0, 0.0];
let b = point![ 0.0, 80.0];
let c = point![ 0.0, 80.0];
let collider = ColliderBuilder::triangle(a, b, c).restitution(0.7).build();
let triangle_body_handle = rigid_body_set.insert(rigid_body);
collider_set.insert_with_parent(collider, triangle_body_handle, &mut rigid_body_set);
/* Create other structures necessary for the simulation. */
let gravity = vector![0.0, -9.81];
let integration_parameters = IntegrationParameters::default();
let mut physics_pipeline = PhysicsPipeline::new();
let mut island_manager = IslandManager::new();
let mut broad_phase = DefaultBroadPhase::new();
let mut narrow_phase = NarrowPhase::new();
let mut impulse_joint_set = ImpulseJointSet::new();
let mut multibody_joint_set = MultibodyJointSet::new();
let mut ccd_solver = CCDSolver::new();
let mut query_pipeline = QueryPipeline::new();
let physics_hooks = ();
let event_handler = ();
/* Run the game loop, stepping the simulation once per frame. */
for _ in 0..3 {
physics_pipeline.step(
&gravity,
&integration_parameters,
&mut island_manager,
&mut broad_phase,
&mut narrow_phase,
&mut rigid_body_set,
&mut collider_set,
&mut impulse_joint_set,
&mut multibody_joint_set,
&mut ccd_solver,
Some(&mut query_pipeline),
&physics_hooks,
&event_handler,
);
let triangle_body = &rigid_body_set[triangle_body_handle];
println!("Triangle altitude: {}", triangle_body.translation().y); // Prints NaN
}
}
Error:
thread 'main' panicked at /home/johannes/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/parry2d-0.21.1/src/query/clip/clip_aabb_line.rs:141:19:
Matrix index out of bounds.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I tried to trace and summarize the call stack leading to the crash in clip_aabb_line. Let me know if you need the full stacktrace - I can send the entire thing.
- clip_aabb_line::main
- rapier2d::pipeline::physics_pipeline::PhysicsPipeline::step
- rapier2d::pipeline::physics_pipeline::PhysicsPipeline::detect_collisions
- rapier2d::geometry::narrow_phase::NarrowPhase::compute_contacts
- for_each closure
- parry2d::query::query_dispatcher::PersistentQueryDispatcher<ManifoldData,ContactData>>::contact_manifolds
- parry2d::query::query_dispatcher::PersistentQueryDispatcher<ManifoldData,ContactData>>::contact_manifold_convex_convex
- parry2d::query::contact_manifolds::contact_manifolds_convex_ball::contact_manifold_convex_ball_shapes
- parry2d::query::contact_manifolds::contact_manifolds_convex_ball::contact_manifold_convex_ball
- parry2d::shape::cuboid::Cuboid>::cast_local_ray_and_get_normal
- parry2d::bounding_volume::aabb::Aabb>::cast_local_ray_and_get_normal
- parry2d::query::ray::ray_aabb::ray_aabb
- parry2d::query::clip::clip_aabb_line::clip_aabb_line <- CRASH