parry3d/query/clip/
clip_aabb_line.rs1use crate::bounding_volume::Aabb;
2use crate::math::{Point, Real, Vector, DIM};
3use crate::query::Ray;
4use crate::shape::Segment;
5use num::{Bounded, Zero};
6
7impl Aabb {
8 #[inline]
12 pub fn clip_segment(&self, pa: &Point<Real>, pb: &Point<Real>) -> Option<Segment> {
13 let ab = pb - pa;
14 clip_aabb_line(self, pa, &ab)
15 .map(|clip| Segment::new(pa + ab * (clip.0).0.max(0.0), pa + ab * (clip.1).0.min(1.0)))
16 }
17
18 #[inline]
23 pub fn clip_line_parameters(
24 &self,
25 orig: &Point<Real>,
26 dir: &Vector<Real>,
27 ) -> Option<(Real, Real)> {
28 clip_aabb_line(self, orig, dir).map(|clip| ((clip.0).0, (clip.1).0))
29 }
30
31 #[inline]
35 pub fn clip_line(&self, orig: &Point<Real>, dir: &Vector<Real>) -> Option<Segment> {
36 clip_aabb_line(self, orig, dir)
37 .map(|clip| Segment::new(orig + dir * (clip.0).0, orig + dir * (clip.1).0))
38 }
39
40 #[inline]
45 pub fn clip_ray_parameters(&self, ray: &Ray) -> Option<(Real, Real)> {
46 self.clip_line_parameters(&ray.origin, &ray.dir)
47 .and_then(|clip| {
48 let t0 = clip.0;
49 let t1 = clip.1;
50
51 if t1 < 0.0 {
52 None
53 } else {
54 Some((t0.max(0.0), t1))
55 }
56 })
57 }
58
59 #[inline]
63 pub fn clip_ray(&self, ray: &Ray) -> Option<Segment> {
64 self.clip_ray_parameters(ray)
65 .map(|clip| Segment::new(ray.point_at(clip.0), ray.point_at(clip.1)))
66 }
67}
68
69pub fn clip_aabb_line(
80 aabb: &Aabb,
81 origin: &Point<Real>,
82 dir: &Vector<Real>,
83) -> Option<((Real, Vector<Real>, isize), (Real, Vector<Real>, isize))> {
84 let mut tmax: Real = Bounded::max_value();
85 let mut tmin: Real = -tmax;
86 let mut near_side = 0;
87 let mut far_side = 0;
88 let mut near_diag = false;
89 let mut far_diag = false;
90
91 for i in 0usize..DIM {
92 if dir[i].is_zero() {
93 if origin[i] < aabb.mins[i] || origin[i] > aabb.maxs[i] {
94 return None;
95 }
96 } else {
97 let denom = 1.0 / dir[i];
98 let flip_sides;
99 let mut inter_with_near_halfspace = (aabb.mins[i] - origin[i]) * denom;
100 let mut inter_with_far_halfspace = (aabb.maxs[i] - origin[i]) * denom;
101
102 if inter_with_near_halfspace > inter_with_far_halfspace {
103 flip_sides = true;
104 core::mem::swap(
105 &mut inter_with_near_halfspace,
106 &mut inter_with_far_halfspace,
107 )
108 } else {
109 flip_sides = false;
110 }
111
112 if inter_with_near_halfspace > tmin {
113 tmin = inter_with_near_halfspace;
114 near_side = if flip_sides {
115 -(i as isize + 1)
116 } else {
117 i as isize + 1
118 };
119 near_diag = false;
120 } else if inter_with_near_halfspace == tmin {
121 near_diag = true;
122 }
123
124 if inter_with_far_halfspace < tmax {
125 tmax = inter_with_far_halfspace;
126 far_side = if !flip_sides {
127 -(i as isize + 1)
128 } else {
129 i as isize + 1
130 };
131 far_diag = false;
132 } else if inter_with_far_halfspace == tmax {
133 far_diag = true;
134 }
135
136 if tmax < 0.0 || tmin > tmax {
137 return None;
138 }
139 }
140 }
141
142 let near = if near_diag {
143 (tmin, -dir.normalize(), near_side)
144 } else {
145 if near_side == 0 {
149 let zero = (0.0, Vector::zeros(), 0);
150 return aabb.contains_local_point(origin).then_some((zero, zero));
151 }
152
153 let mut normal = Vector::zeros();
154
155 if near_side < 0 {
156 normal[(-near_side - 1) as usize] = 1.0;
157 } else {
158 normal[(near_side - 1) as usize] = -1.0;
159 }
160
161 (tmin, normal, near_side)
162 };
163
164 let far = if far_diag {
165 (tmax, -dir.normalize(), far_side)
166 } else {
167 if far_side == 0 {
171 let zero = (0.0, Vector::zeros(), 0);
172 return aabb.contains_local_point(origin).then_some((zero, zero));
173 }
174
175 let mut normal = Vector::zeros();
176
177 if far_side < 0 {
178 normal[(-far_side - 1) as usize] = -1.0;
179 } else {
180 normal[(far_side - 1) as usize] = 1.0;
181 }
182
183 (tmax, normal, far_side)
184 };
185
186 Some((near, far))
187}
188#[cfg(test)]
189mod test {
190 use super::*;
191
192 #[test]
193 pub fn clip_empty_aabb_line() {
194 assert!(clip_aabb_line(
195 &Aabb::new(Point::origin(), Point::origin()),
196 &Point::origin(),
197 &Vector::zeros(),
198 )
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 )
205 .is_none());
206 }
207
208 #[test]
209 pub fn clip_empty_aabb_segment() {
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
216 .clip_segment(&Point::origin(), &Point::from(Vector::repeat(Real::NAN)))
217 .is_none());
218 }
219}