1mod sat;
16mod trim;
17
18pub use trim::trim_according_overlapping;
19
20use crate::math::*;
21use crate::Aabb3;
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct OrientedBoundingBox3 {
26 pub center: Point3,
28 pub orientation: UnitQuat,
30 extents: Vec3,
31}
32
33impl OrientedBoundingBox3 {
34 #[inline]
36 #[must_use]
37 pub fn new(center: Point3, orientation: UnitQuat, extents: Vec3) -> Self {
38 Self {
39 center,
40 orientation,
41 extents,
42 }
43 }
44
45 #[inline]
47 #[must_use]
48 pub fn extents(&self) -> Vec3 {
49 self.extents
50 }
51
52 #[inline]
54 #[must_use]
55 pub fn size(&self) -> Vec3 {
56 self.extents * 2.0
57 }
58
59 #[inline]
61 #[must_use]
62 pub fn x_axis(&self) -> UnitVec3 {
63 self.orientation * Vec3::x_axis()
64 }
65
66 #[inline]
68 #[must_use]
69 pub fn y_axis(&self) -> UnitVec3 {
70 self.orientation * Vec3::y_axis()
71 }
72
73 #[inline]
75 #[must_use]
76 pub fn z_axis(&self) -> UnitVec3 {
77 self.orientation * Vec3::z_axis()
78 }
79
80 #[inline]
82 pub fn translate(&mut self, translation: Vec3) {
83 self.center += translation;
84 }
85
86 #[must_use]
88 pub fn axis_aligned_bounding_box(&self) -> Aabb3 {
89 let x_axis = self.x_axis().into_inner();
91 let y_axis = self.y_axis().into_inner();
92 let z_axis = self.z_axis().into_inner();
93
94 let mut points = [Vec3::zeros(); 8];
95 points[0] = x_axis * self.extents.x + y_axis * self.extents.y + z_axis * self.extents.z;
96 points[1] = x_axis * self.extents.x + y_axis * self.extents.y - z_axis * self.extents.z;
97 points[2] = x_axis * self.extents.x - y_axis * self.extents.y + z_axis * self.extents.z;
98 points[3] = x_axis * self.extents.x - y_axis * self.extents.y - z_axis * self.extents.z;
99 points[4] = -x_axis * self.extents.x + y_axis * self.extents.y + z_axis * self.extents.z;
100 points[5] = -x_axis * self.extents.x + y_axis * self.extents.y - z_axis * self.extents.z;
101 points[6] = -x_axis * self.extents.x - y_axis * self.extents.y + z_axis * self.extents.z;
102 points[7] = -x_axis * self.extents.x - y_axis * self.extents.y - z_axis * self.extents.z;
103
104 let mut min = points[0];
105 let mut max = points[0];
106 for p in points.iter().copied().skip(1) {
107 min = min.inf(&p);
108 max = max.sup(&p);
109 }
110
111 min += self.center.coords;
112 max += self.center.coords;
113 Aabb3::new_unchecked(Point3::from(min), Point3::from(max))
114 }
115
116 #[must_use]
136 pub fn vertices(&self) -> [Point3; 8] {
137 let x_axis = self.x_axis().into_inner();
139 let y_axis = self.y_axis().into_inner();
140 let z_axis = self.z_axis().into_inner();
141
142 let x_extent = x_axis * self.extents.x;
144 let y_extent = y_axis * self.extents.y;
145 let z_extent = z_axis * self.extents.z;
146
147 [
148 self.center + (-x_extent - y_extent - z_extent), self.center + (-x_extent - y_extent + z_extent), self.center + (-x_extent + y_extent - z_extent), self.center + (-x_extent + y_extent + z_extent), self.center + (x_extent - y_extent - z_extent), self.center + (x_extent - y_extent + z_extent), self.center + (x_extent + y_extent - z_extent), self.center + (x_extent + y_extent + z_extent), ]
157 }
158
159 pub fn overlaps(&self, other: &Self) -> bool {
161 let (a, b) = (self, other);
162
163 let axes_a = [a.x_axis(), a.y_axis(), a.z_axis()];
165
166 let vertices_b = b.vertices();
167
168 for (index, axis) in axes_a.iter().enumerate() {
169 let o = a.center.coords.dot(axis);
170 let overlap = sat::is_vertices_overlap_inerval_on_axis(
171 *axis,
172 vertices_b.iter().map(|p| p.coords),
173 o - a.extents()[index],
174 o + a.extents()[index],
175 );
176 if !overlap {
177 return false;
178 }
179 }
180
181 let axes_b = [b.x_axis(), b.y_axis(), b.z_axis()];
183
184 let vertices_a = a.vertices();
185
186 for (index, axis) in axes_b.iter().enumerate() {
187 let o = b.center.coords.dot(axis);
188 let overlap = sat::is_vertices_overlap_inerval_on_axis(
189 *axis,
190 vertices_a.iter().map(|p| p.coords),
191 o - b.extents()[index],
192 o + b.extents()[index],
193 );
194 if !overlap {
195 return false;
196 }
197 }
198
199 let axes_cross = [
201 axes_a[0].cross(&axes_b[0]),
202 axes_a[0].cross(&axes_b[1]),
203 axes_a[0].cross(&axes_b[2]),
204 axes_a[1].cross(&axes_b[0]),
205 axes_a[1].cross(&axes_b[1]),
206 axes_a[1].cross(&axes_b[2]),
207 axes_a[2].cross(&axes_b[0]),
208 axes_a[2].cross(&axes_b[1]),
209 axes_a[2].cross(&axes_b[2]),
210 ];
211 let axes_cross = axes_cross.map(|v| UnitVec3::try_new(v, 1e-5));
212
213 for axis in axes_cross.iter().copied().flatten() {
214 let (min_a, max_a) =
215 sat::compute_vertices_interval_on_axis(axis, vertices_a.iter().map(|v| v.coords));
216 let (min_b, max_b) =
217 sat::compute_vertices_interval_on_axis(axis, vertices_b.iter().map(|v| v.coords));
218
219 let overlap = min_a <= max_b && min_b <= max_a;
220 if !overlap {
221 return false;
222 }
223 }
224
225 true
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use approx::assert_relative_eq;
232 use wasm_bindgen_test::*;
233
234 use super::*;
235
236 wasm_bindgen_test_configure!(run_in_browser);
237
238 #[test]
239 #[wasm_bindgen_test]
240 fn test_obb3() {
241 let rotation = UnitQuat::from_axis_angle(&Vec3::y_axis(), (30.0 as Real).to_radians());
242 let extents = Vec3::new(2.0, 3.0, 4.0);
243 let obb = OrientedBoundingBox3::new(Point3::new(1.0, 2.0, 3.0), rotation, extents);
244
245 assert_relative_eq!(obb.extents(), extents);
246 assert_relative_eq!(obb.size(), extents * 2.0);
247
248 assert_relative_eq!(obb.x_axis(), rotation * Vec3::x_axis());
249 assert_relative_eq!(obb.y_axis(), rotation * Vec3::y_axis());
250 assert_relative_eq!(obb.z_axis(), rotation * Vec3::z_axis());
251
252 let aabb = obb.axis_aligned_bounding_box();
253 assert_relative_eq!(
254 aabb.min(),
255 Point3::new(-2.732051, -1.000000, -1.464102),
256 epsilon = 1e-5
257 );
258 assert_relative_eq!(
259 aabb.max(),
260 Point3::new(4.732051, 5.000000, 7.464102),
261 epsilon = 1e-5
262 );
263
264 let vertices = obb.vertices();
265
266 let expect_vertices = [
267 Point3::new(-2.7321, -1.0000, 0.5359),
268 Point3::new(1.2680, -1.0000, 7.4641),
269 Point3::new(-2.7321, 5.0000, 0.5359),
270 Point3::new(1.2680, 5.0000, 7.4641),
271 Point3::new(0.7321, -1.0000, -1.4641),
272 Point3::new(4.7321, -1.0000, 5.4641),
273 Point3::new(0.7321, 5.0000, -1.4641),
274 Point3::new(4.7321, 5.0000, 5.4641),
275 ];
276
277 for (actual, expect) in vertices.iter().zip(expect_vertices.iter()) {
278 assert_relative_eq!(actual, expect, epsilon = 1e-4);
279 }
280 }
281
282 #[test]
283 #[wasm_bindgen_test]
284 fn test_obb3_overlaps() {
285 let obb_1 = OrientedBoundingBox3::new(
286 point3(1.0, 2.0, 3.0),
287 UnitQuat::from_axis_angle(&Vec3::y_axis(), 30f32.to_radians()),
288 vec3(2.0, 3.0, 4.0),
289 );
290 let mut obb_2 = OrientedBoundingBox3::new(
291 point3(-1.46, 0.0, -1.11),
292 UnitQuat::from_axis_angle(
293 &vec3(0.679, 0.679, -0.281).normalize_to_unit(),
294 62.79941f32.to_radians(),
295 ),
296 Vec3::repeat(0.5),
297 );
298
299 assert!(obb_1.overlaps(&obb_2));
300 obb_2.translate(-obb_2.z_axis().into_inner() * 0.1);
301 assert!(!obb_1.overlaps(&obb_2));
302
303 let mut obb_3 = OrientedBoundingBox3::new(
304 point3(1.07, -1.42, -1.567),
305 UnitQuat::from_axis_angle(
306 &vec3(0.580, 0.760, -0.293).normalize_to_unit(),
307 126.9181f32.to_radians(),
308 ),
309 Vec3::repeat(0.5),
310 );
311
312 assert!(!obb_1.overlaps(&obb_3));
313
314 obb_3.translate(-obb_3.z_axis().into_inner() * 0.1);
315 assert!(obb_1.overlaps(&obb_3));
316
317 let mut obb_4 = OrientedBoundingBox3::new(
318 point3(0.96, -0.085, -2.128),
319 UnitQuat::from_axis_angle(
320 &vec3(0.580, 0.760, -0.293).normalize_to_unit(),
321 126.9181f32.to_radians(),
322 ),
323 Vec3::repeat(0.5),
324 );
325
326 assert!(!obb_1.overlaps(&obb_4));
327 obb_4.translate(-obb_4.x_axis().into_inner() * 0.1);
328 assert!(obb_1.overlaps(&obb_4));
329 }
330}