Skip to content

Commit 225bc0a

Browse files
Fix zero length 2d segment raycast (#357)
* fix zero length segment always reporting intersection * add changelog * add test for segments with very close points * chore: remove unused import --------- Co-authored-by: Sébastien Crozet <sebcrozet@dimforge.com>
1 parent 3340d89 commit 225bc0a

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Fixed
44

55
- Fix `clip_aabb_line` crashing when given incorrect inputs (zero length direction or NAN).
6+
- Fix `Segment::intersects_ray` returning false-positive when the segment is zero-length. ([#31](https://github.com/dimforge/parry/issues/31)).
67

78
## 0.21.1
89

src/query/ray/ray_support_map.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,15 @@ impl RayCast for Segment {
229229
} else if s >= 0.0 && s <= max_time_of_impact && t >= 0.0 && t <= 1.0 {
230230
let normal = self.normal().map(|n| *n).unwrap_or_else(Vector::zeros);
231231

232-
if normal.dot(&ray.dir) > 0.0 {
232+
let dot = normal.dot(&ray.dir);
233+
if dot > 0.0 {
233234
Some(RayIntersection::new(s, -normal, FeatureId::Face(1)))
234-
} else {
235+
} else if dot < 0.0 {
235236
Some(RayIntersection::new(s, normal, FeatureId::Face(0)))
237+
} else {
238+
// dot == 0 happens when lines are parallel, which is normally handled before,
239+
// but this may happen if segment is zero length, as the ray is not considered parallel.
240+
None
236241
}
237242
} else {
238243
// The closest points are outside of

src/shape/segment.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,3 +359,86 @@ impl ConvexPolyhedron for Segment {
359359
}
360360
}
361361
*/
362+
363+
#[cfg(test)]
364+
mod test {
365+
use crate::query::{Ray, RayCast};
366+
367+
pub use super::*;
368+
#[test]
369+
fn segment_intersect_zero_length_issue_31() {
370+
// never intersect each other
371+
let ray = Ray::new(Point::origin(), Vector::x());
372+
let segment = Segment {
373+
a: Point::new(
374+
10.0,
375+
10.0,
376+
#[cfg(feature = "dim3")]
377+
10.0,
378+
),
379+
b: Point::new(
380+
10.0,
381+
10.0,
382+
#[cfg(feature = "dim3")]
383+
10.0,
384+
),
385+
};
386+
387+
let hit = segment.intersects_ray(&Isometry::identity(), &ray, Real::MAX);
388+
assert_eq!(hit, false);
389+
}
390+
#[test]
391+
fn segment_very_close_points_hit() {
392+
let epsilon = 1.1920929e-7;
393+
// intersect each other
394+
let ray = Ray::new(
395+
Point::new(
396+
epsilon * 0.5,
397+
0.3,
398+
#[cfg(feature = "dim3")]
399+
0.0,
400+
),
401+
-Vector::y(),
402+
);
403+
let segment = Segment {
404+
a: Point::origin(),
405+
b: Point::new(
406+
// Theoretically, epsilon would suffice but imprecisions force us to add some more offset.
407+
epsilon * 1.01,
408+
0.0,
409+
#[cfg(feature = "dim3")]
410+
0.0,
411+
),
412+
};
413+
414+
let hit = segment.intersects_ray(&Isometry::identity(), &ray, Real::MAX);
415+
assert_eq!(hit, true);
416+
}
417+
#[test]
418+
fn segment_very_close_points_no_hit() {
419+
let epsilon = 1.1920929e-7;
420+
// never intersect each other
421+
let ray = Ray::new(
422+
Point::new(
423+
// Theoretically, epsilon would suffice but imprecisions force us to add some more offset.
424+
epsilon * 11.0,
425+
0.1,
426+
#[cfg(feature = "dim3")]
427+
0.0,
428+
),
429+
-Vector::y(),
430+
);
431+
let segment = Segment {
432+
a: Point::origin(),
433+
b: Point::new(
434+
epsilon * 0.9,
435+
0.0,
436+
#[cfg(feature = "dim3")]
437+
0.0,
438+
),
439+
};
440+
441+
let hit = segment.intersects_ray(&Isometry::identity(), &ray, Real::MAX);
442+
assert_eq!(hit, false);
443+
}
444+
}

0 commit comments

Comments
 (0)