Skip to content

Commit 98792f7

Browse files
committed
fix: make clip_aabb_line return a result if the line’s starting point is inside the AABB but its direction is zero.
This allows other functions like segment intersection to return a valid result if the segment degenerates to a point.
1 parent 092c009 commit 98792f7

File tree

1 file changed

+35
-10
lines changed

1 file changed

+35
-10
lines changed

src/query/clip/clip_aabb_line.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use num::{Bounded, Zero};
77
impl Aabb {
88
/// Computes the intersection of a segment with this Aabb.
99
///
10-
/// Returns `None` if there is no intersection.
10+
/// Returns `None` if there is no intersection or if `pa` is invalid (contains `NaN`).
1111
#[inline]
1212
pub fn clip_segment(&self, pa: &Point<Real>, pb: &Point<Real>) -> Option<Segment> {
1313
let ab = pb - pa;
@@ -18,7 +18,7 @@ impl Aabb {
1818
/// Computes the parameters of the two intersection points between a line and this Aabb.
1919
///
2020
/// The parameters are such that the point are given by `orig + dir * parameter`.
21-
/// Returns `None` if there is no intersection.
21+
/// Returns `None` if there is no intersection or if `orig` is invalid (contains `NaN`).
2222
#[inline]
2323
pub fn clip_line_parameters(
2424
&self,
@@ -30,7 +30,7 @@ impl Aabb {
3030

3131
/// Computes the intersection segment between a line and this Aabb.
3232
///
33-
/// Returns `None` if there is no intersection.
33+
/// Returns `None` if there is no intersection or if `orig` is invalid (contains `NaN`).
3434
#[inline]
3535
pub fn clip_line(&self, orig: &Point<Real>, dir: &Vector<Real>) -> Option<Segment> {
3636
clip_aabb_line(self, orig, dir)
@@ -40,7 +40,7 @@ impl Aabb {
4040
/// Computes the parameters of the two intersection points between a ray and this Aabb.
4141
///
4242
/// The parameters are such that the point are given by `ray.orig + ray.dir * parameter`.
43-
/// Returns `None` if there is no intersection.
43+
/// Returns `None` if there is no intersection or if `ray.orig` is invalid (contains `NaN`).
4444
#[inline]
4545
pub fn clip_ray_parameters(&self, ray: &Ray) -> Option<(Real, Real)> {
4646
self.clip_line_parameters(&ray.origin, &ray.dir)
@@ -67,6 +67,15 @@ impl Aabb {
6767
}
6868

6969
/// Computes the segment given by the intersection of a line and an Aabb.
70+
///
71+
/// Returns the two intersections represented as `(t, normal, side)` such that:
72+
/// - `origin + dir * t` gives the intersection points.
73+
/// - `normal` is the face normal at the intersection. This is equal to the zero vector if `dir`
74+
/// is invalid (a zero vector or NaN) and `origin` is inside the AABB.
75+
/// - `side` is the side of the AABB that was hit. This is an integer in [-3, 3] where `1` represents
76+
/// the `+X` axis, `2` the `+Y` axis, etc., and negative values represent the corresponding
77+
/// negative axis. The special value of `0` indicates that the provided `dir` is zero or `NaN`
78+
/// and the line origin is inside the AABB.
7079
pub fn clip_aabb_line(
7180
aabb: &Aabb,
7281
origin: &Point<Real>,
@@ -133,9 +142,12 @@ pub fn clip_aabb_line(
133142
let near = if near_diag {
134143
(tmin, -dir.normalize(), near_side)
135144
} else {
136-
// If near_side is 0, the index calculation would be invalid (-1).
145+
// If near_side is 0, we are in a special case where `dir` is
146+
// zero or NaN. Return `Some` only if the ray starts inside the
147+
// aabb.
137148
if near_side == 0 {
138-
return None;
149+
let zero = (0.0, Vector::zeros(), 0);
150+
return aabb.contains_local_point(origin).then_some((zero, zero));
139151
}
140152

141153
let mut normal = Vector::zeros();
@@ -152,9 +164,12 @@ pub fn clip_aabb_line(
152164
let far = if far_diag {
153165
(tmax, -dir.normalize(), far_side)
154166
} else {
155-
// If far_side is 0, the index calculation would be invalid (-1).
167+
// If far_side is 0, we are in a special case where `dir` is
168+
// zero or NaN. Return `Some` only if the ray starts inside the
169+
// aabb.
156170
if far_side == 0 {
157-
return None;
171+
let zero = (0.0, Vector::zeros(), 0);
172+
return aabb.contains_local_point(origin).then_some((zero, zero));
158173
}
159174

160175
let mut normal = Vector::zeros();
@@ -181,13 +196,23 @@ mod test {
181196
&Point::origin(),
182197
&Vector::zeros(),
183198
)
199+
.is_some());
200+
assert!(clip_aabb_line(
201+
&Aabb::new(Vector::repeat(1.0).into(), Vector::repeat(2.0).into()),
202+
&Point::origin(),
203+
&Vector::zeros(),
204+
)
184205
.is_none());
185206
}
186207

187208
#[test]
188209
pub fn clip_empty_aabb_segment() {
189-
let aabb_empty = Aabb::new(Point::origin(), Point::origin());
190-
assert!(aabb_empty
210+
let aabb_origin = Aabb::new(Point::origin(), Point::origin());
211+
let aabb_shifted = Aabb::new(Vector::repeat(1.0).into(), Vector::repeat(2.0).into());
212+
assert!(aabb_origin
213+
.clip_segment(&Point::origin(), &Point::from(Vector::repeat(Real::NAN)))
214+
.is_some());
215+
assert!(aabb_shifted
191216
.clip_segment(&Point::origin(), &Point::from(Vector::repeat(Real::NAN)))
192217
.is_none());
193218
}

0 commit comments

Comments
 (0)