diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bc35d41..f195608a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Fixed - Fix `clip_aabb_line` crashing when given incorrect inputs (zero length direction or NAN). +- Fix `Segment::intersects_ray` returning false-positive when the segment is zero-length. ([#31](https://github.com/dimforge/parry/issues/31)). ## 0.21.1 diff --git a/src/query/ray/ray_support_map.rs b/src/query/ray/ray_support_map.rs index 577c4e1b..5311e63c 100644 --- a/src/query/ray/ray_support_map.rs +++ b/src/query/ray/ray_support_map.rs @@ -229,10 +229,15 @@ impl RayCast for Segment { } else if s >= 0.0 && s <= max_time_of_impact && t >= 0.0 && t <= 1.0 { let normal = self.normal().map(|n| *n).unwrap_or_else(Vector::zeros); - if normal.dot(&ray.dir) > 0.0 { + let dot = normal.dot(&ray.dir); + if dot > 0.0 { Some(RayIntersection::new(s, -normal, FeatureId::Face(1))) - } else { + } else if dot < 0.0 { Some(RayIntersection::new(s, normal, FeatureId::Face(0))) + } else { + // dot == 0 happens when lines are parallel, which is normally handled before, + // but this may happen if segment is zero length, as the ray is not considered parallel. + None } } else { // The closest points are outside of diff --git a/src/shape/segment.rs b/src/shape/segment.rs index 4089a416..58a8502d 100644 --- a/src/shape/segment.rs +++ b/src/shape/segment.rs @@ -359,3 +359,86 @@ impl ConvexPolyhedron for Segment { } } */ + +#[cfg(test)] +mod test { + use crate::query::{Ray, RayCast}; + + pub use super::*; + #[test] + fn segment_intersect_zero_length_issue_31() { + // never intersect each other + let ray = Ray::new(Point::origin(), Vector::x()); + let segment = Segment { + a: Point::new( + 10.0, + 10.0, + #[cfg(feature = "dim3")] + 10.0, + ), + b: Point::new( + 10.0, + 10.0, + #[cfg(feature = "dim3")] + 10.0, + ), + }; + + let hit = segment.intersects_ray(&Isometry::identity(), &ray, Real::MAX); + assert_eq!(hit, false); + } + #[test] + fn segment_very_close_points_hit() { + let epsilon = 1.1920929e-7; + // intersect each other + let ray = Ray::new( + Point::new( + epsilon * 0.5, + 0.3, + #[cfg(feature = "dim3")] + 0.0, + ), + -Vector::y(), + ); + let segment = Segment { + a: Point::origin(), + b: Point::new( + // Theoretically, epsilon would suffice but imprecisions force us to add some more offset. + epsilon * 1.01, + 0.0, + #[cfg(feature = "dim3")] + 0.0, + ), + }; + + let hit = segment.intersects_ray(&Isometry::identity(), &ray, Real::MAX); + assert_eq!(hit, true); + } + #[test] + fn segment_very_close_points_no_hit() { + let epsilon = 1.1920929e-7; + // never intersect each other + let ray = Ray::new( + Point::new( + // Theoretically, epsilon would suffice but imprecisions force us to add some more offset. + epsilon * 11.0, + 0.1, + #[cfg(feature = "dim3")] + 0.0, + ), + -Vector::y(), + ); + let segment = Segment { + a: Point::origin(), + b: Point::new( + epsilon * 0.9, + 0.0, + #[cfg(feature = "dim3")] + 0.0, + ), + }; + + let hit = segment.intersects_ray(&Isometry::identity(), &ray, Real::MAX); + assert_eq!(hit, false); + } +}