1use crate::bounding_volume::{BoundingSphere, BoundingVolume};
4use crate::math::{Isometry, Point, Real, UnitVector, Vector, DIM, TWO_DIM};
5use crate::shape::{Cuboid, SupportMap};
6use crate::utils::IsometryOps;
7use arrayvec::ArrayVec;
8use na;
9use num::Bounded;
10
11#[cfg(all(feature = "dim3", not(feature = "std")))]
12use na::ComplexField;
13use crate::query::{Ray, RayCast};
16#[cfg(feature = "rkyv")]
17use rkyv::{bytecheck, CheckBytes};
18
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
22#[cfg_attr(
23 feature = "rkyv",
24 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
25 archive(as = "Self")
26)]
27#[derive(Debug, PartialEq, Copy, Clone)]
28#[repr(C)]
29pub struct Aabb {
30 pub mins: Point<Real>,
31 pub maxs: Point<Real>,
32}
33
34impl Aabb {
35 #[cfg(feature = "dim3")]
50 pub const EDGES_VERTEX_IDS: [(usize, usize); 12] = [
51 (0, 1),
52 (1, 2),
53 (3, 2),
54 (0, 3),
55 (4, 5),
56 (5, 6),
57 (7, 6),
58 (4, 7),
59 (0, 4),
60 (1, 5),
61 (2, 6),
62 (3, 7),
63 ];
64
65 #[cfg(feature = "dim3")]
80 pub const FACES_VERTEX_IDS: [(usize, usize, usize, usize); 6] = [
81 (1, 2, 6, 5),
83 (0, 3, 7, 4),
85 (2, 3, 7, 6),
87 (1, 0, 4, 5),
89 (4, 5, 6, 7),
91 (0, 1, 2, 3),
93 ];
94
95 #[cfg(feature = "dim2")]
108 pub const FACES_VERTEX_IDS: [(usize, usize); 4] = [
109 (1, 2),
111 (3, 0),
113 (2, 3),
115 (0, 1),
117 ];
118
119 #[inline]
126 pub fn new(mins: Point<Real>, maxs: Point<Real>) -> Aabb {
127 Aabb { mins, maxs }
128 }
129
130 #[inline]
134 pub fn new_invalid() -> Self {
135 Self::new(
136 Vector::repeat(Real::max_value()).into(),
137 Vector::repeat(-Real::max_value()).into(),
138 )
139 }
140
141 #[inline]
143 pub fn from_half_extents(center: Point<Real>, half_extents: Vector<Real>) -> Self {
144 Self::new(center - half_extents, center + half_extents)
145 }
146
147 pub fn from_points_ref<'a, I>(pts: I) -> Self
149 where
150 I: IntoIterator<Item = &'a Point<Real>>,
151 {
152 super::aabb_utils::local_point_cloud_aabb(pts.into_iter().copied())
153 }
154
155 pub fn from_points<I>(pts: I) -> Self
157 where
158 I: IntoIterator<Item = Point<Real>>,
159 {
160 super::aabb_utils::local_point_cloud_aabb(pts)
161 }
162
163 #[inline]
165 pub fn center(&self) -> Point<Real> {
166 na::center(&self.mins, &self.maxs)
167 }
168
169 #[inline]
171 pub fn half_extents(&self) -> Vector<Real> {
172 let half: Real = na::convert::<f64, Real>(0.5);
173 (self.maxs - self.mins) * half
174 }
175
176 #[inline]
178 pub fn volume(&self) -> Real {
179 let extents = self.extents();
180 #[cfg(feature = "dim2")]
181 return extents.x * extents.y;
182 #[cfg(feature = "dim3")]
183 return extents.x * extents.y * extents.z;
184 }
185
186 pub fn half_area_or_perimeter(&self) -> Real {
188 #[cfg(feature = "dim2")]
189 return self.half_perimeter();
190 #[cfg(feature = "dim3")]
191 return self.half_area();
192 }
193
194 #[cfg(feature = "dim2")]
196 pub fn half_perimeter(&self) -> Real {
197 let extents = self.extents();
198 extents.x + extents.y
199 }
200
201 #[cfg(feature = "dim3")]
203 pub fn half_area(&self) -> Real {
204 let extents = self.extents();
205 extents.x * (extents.y + extents.z) + extents.y * extents.z
206 }
207
208 #[inline]
210 pub fn extents(&self) -> Vector<Real> {
211 self.maxs - self.mins
212 }
213
214 pub fn take_point(&mut self, pt: Point<Real>) {
216 self.mins = self.mins.coords.inf(&pt.coords).into();
217 self.maxs = self.maxs.coords.sup(&pt.coords).into();
218 }
219
220 #[inline]
222 pub fn transform_by(&self, m: &Isometry<Real>) -> Self {
223 let ls_center = self.center();
224 let center = m * ls_center;
225 let ws_half_extents = m.absolute_transform_vector(&self.half_extents());
226
227 Aabb::new(center + (-ws_half_extents), center + ws_half_extents)
228 }
229
230 #[inline]
232 pub fn translated(mut self, translation: &Vector<Real>) -> Self {
233 self.mins += translation;
234 self.maxs += translation;
235 self
236 }
237
238 #[inline]
239 pub fn scaled(self, scale: &Vector<Real>) -> Self {
240 let a = self.mins.coords.component_mul(scale);
241 let b = self.maxs.coords.component_mul(scale);
242 Self {
243 mins: a.inf(&b).into(),
244 maxs: a.sup(&b).into(),
245 }
246 }
247
248 #[inline]
255 #[must_use]
256 pub fn scaled_wrt_center(self, scale: &Vector<Real>) -> Self {
257 let center = self.center();
258 let half_extents = self.half_extents().component_mul(scale).abs();
262 Self::from_half_extents(center, half_extents)
263 }
264
265 #[inline]
267 pub fn bounding_sphere(&self) -> BoundingSphere {
268 let center = self.center();
269 let radius = na::distance(&self.mins, &self.maxs) * 0.5;
270 BoundingSphere::new(center, radius)
271 }
272
273 #[inline]
275 pub fn contains_local_point(&self, point: &Point<Real>) -> bool {
276 for i in 0..DIM {
277 if point[i] < self.mins[i] || point[i] > self.maxs[i] {
278 return false;
279 }
280 }
281
282 true
283 }
284
285 pub fn distance_to_origin(&self) -> Real {
287 self.mins
288 .coords
289 .sup(&-self.maxs.coords)
290 .sup(&Vector::zeros())
291 .norm()
292 }
293
294 #[inline]
296 pub fn intersects_moving_aabb(&self, aabb2: &Self, vel12: Vector<Real>) -> bool {
297 let msum = Aabb {
299 mins: self.mins - aabb2.maxs.coords,
300 maxs: self.maxs - aabb2.mins.coords,
301 };
302 let ray = Ray::new(Point::origin(), vel12);
303
304 msum.intersects_local_ray(&ray, 1.0)
305 }
306
307 pub fn intersection(&self, other: &Aabb) -> Option<Aabb> {
309 let result = Aabb {
310 mins: Point::from(self.mins.coords.sup(&other.mins.coords)),
311 maxs: Point::from(self.maxs.coords.inf(&other.maxs.coords)),
312 };
313
314 for i in 0..DIM {
315 if result.mins[i] > result.maxs[i] {
316 return None;
317 }
318 }
319
320 Some(result)
321 }
322
323 pub fn aligned_intersections(
328 &self,
329 pos12: &Isometry<Real>,
330 aabb2: &Self,
331 ) -> Option<(Aabb, Aabb)> {
332 let pos21 = pos12.inverse();
333
334 let aabb2_1 = aabb2.transform_by(pos12);
335 let inter1_1 = self.intersection(&aabb2_1)?;
336 let inter1_2 = inter1_1.transform_by(&pos21);
337
338 let aabb1_2 = self.transform_by(&pos21);
339 let inter2_2 = aabb2.intersection(&aabb1_2)?;
340 let inter2_1 = inter2_2.transform_by(pos12);
341
342 Some((
343 inter1_1.intersection(&inter2_1)?,
344 inter1_2.intersection(&inter2_2)?,
345 ))
346 }
347
348 pub fn difference(&self, rhs: &Aabb) -> ArrayVec<Self, TWO_DIM> {
353 self.difference_with_cut_sequence(rhs).0
354 }
355
356 pub fn difference_with_cut_sequence(
375 &self,
376 rhs: &Aabb,
377 ) -> (ArrayVec<Self, TWO_DIM>, ArrayVec<(i8, Real), TWO_DIM>) {
378 let mut result = ArrayVec::new();
379 let mut cut_sequence = ArrayVec::new();
380
381 for i in 0..DIM {
385 if self.mins[i] >= rhs.maxs[i] || self.maxs[i] <= rhs.mins[i] {
386 result.push(*self);
387 return (result, cut_sequence);
388 }
389 }
390
391 let mut rest = *self;
392
393 for i in 0..DIM {
394 if rhs.mins[i] > rest.mins[i] {
395 let mut fragment = rest;
396 fragment.maxs[i] = rhs.mins[i];
397 rest.mins[i] = rhs.mins[i];
398 result.push(fragment);
399 cut_sequence.push((i as i8 + 1, rhs.mins[i]));
400 }
401
402 if rhs.maxs[i] < rest.maxs[i] {
403 let mut fragment = rest;
404 fragment.mins[i] = rhs.maxs[i];
405 rest.maxs[i] = rhs.maxs[i];
406 result.push(fragment);
407 cut_sequence.push((-(i as i8 + 1), -rhs.maxs[i]));
408 }
409 }
410
411 (result, cut_sequence)
412 }
413
414 #[inline]
423 #[cfg(feature = "dim2")]
424 pub fn vertices(&self) -> [Point<Real>; 4] {
425 [
426 Point::new(self.mins.x, self.mins.y),
427 Point::new(self.maxs.x, self.mins.y),
428 Point::new(self.maxs.x, self.maxs.y),
429 Point::new(self.mins.x, self.maxs.y),
430 ]
431 }
432
433 #[inline]
444 #[cfg(feature = "dim3")]
445 pub fn vertices(&self) -> [Point<Real>; 8] {
446 [
447 Point::new(self.mins.x, self.mins.y, self.mins.z),
448 Point::new(self.maxs.x, self.mins.y, self.mins.z),
449 Point::new(self.maxs.x, self.maxs.y, self.mins.z),
450 Point::new(self.mins.x, self.maxs.y, self.mins.z),
451 Point::new(self.mins.x, self.mins.y, self.maxs.z),
452 Point::new(self.maxs.x, self.mins.y, self.maxs.z),
453 Point::new(self.maxs.x, self.maxs.y, self.maxs.z),
454 Point::new(self.mins.x, self.maxs.y, self.maxs.z),
455 ]
456 }
457
458 #[inline]
460 #[cfg(feature = "dim2")]
461 pub fn split_at_center(&self) -> [Aabb; 4] {
462 let center = self.center();
463
464 [
465 Aabb::new(self.mins, center),
466 Aabb::new(
467 Point::new(center.x, self.mins.y),
468 Point::new(self.maxs.x, center.y),
469 ),
470 Aabb::new(center, self.maxs),
471 Aabb::new(
472 Point::new(self.mins.x, center.y),
473 Point::new(center.x, self.maxs.y),
474 ),
475 ]
476 }
477
478 #[inline]
480 #[cfg(feature = "dim3")]
481 pub fn split_at_center(&self) -> [Aabb; 8] {
482 let center = self.center();
483
484 [
485 Aabb::new(
486 Point::new(self.mins.x, self.mins.y, self.mins.z),
487 Point::new(center.x, center.y, center.z),
488 ),
489 Aabb::new(
490 Point::new(center.x, self.mins.y, self.mins.z),
491 Point::new(self.maxs.x, center.y, center.z),
492 ),
493 Aabb::new(
494 Point::new(center.x, center.y, self.mins.z),
495 Point::new(self.maxs.x, self.maxs.y, center.z),
496 ),
497 Aabb::new(
498 Point::new(self.mins.x, center.y, self.mins.z),
499 Point::new(center.x, self.maxs.y, center.z),
500 ),
501 Aabb::new(
502 Point::new(self.mins.x, self.mins.y, center.z),
503 Point::new(center.x, center.y, self.maxs.z),
504 ),
505 Aabb::new(
506 Point::new(center.x, self.mins.y, center.z),
507 Point::new(self.maxs.x, center.y, self.maxs.z),
508 ),
509 Aabb::new(
510 Point::new(center.x, center.y, center.z),
511 Point::new(self.maxs.x, self.maxs.y, self.maxs.z),
512 ),
513 Aabb::new(
514 Point::new(self.mins.x, center.y, center.z),
515 Point::new(center.x, self.maxs.y, self.maxs.z),
516 ),
517 ]
518 }
519
520 #[must_use]
522 pub fn add_half_extents(&self, half_extents: &Vector<Real>) -> Self {
523 Self {
524 mins: self.mins - half_extents,
525 maxs: self.maxs + half_extents,
526 }
527 }
528
529 pub fn project_on_axis(&self, axis: &UnitVector<Real>) -> (Real, Real) {
531 let cuboid = Cuboid::new(self.half_extents());
532 let shift = cuboid
533 .local_support_point_toward(axis)
534 .coords
535 .dot(axis)
536 .abs();
537 let center = self.center().coords.dot(axis);
538 (center - shift, center + shift)
539 }
540
541 #[cfg(feature = "dim3")]
542 #[cfg(feature = "alloc")]
543 pub fn intersects_spiral(
544 &self,
545 point: &Point<Real>,
546 center: &Point<Real>,
547 axis: &UnitVector<Real>,
548 linvel: &Vector<Real>,
549 angvel: Real,
550 ) -> bool {
551 use crate::utils::WBasis;
552 use crate::utils::{Interval, IntervalFunction};
553 use alloc::vec;
554
555 struct SpiralPlaneDistance {
556 center: Point<Real>,
557 tangents: [Vector<Real>; 2],
558 linvel: Vector<Real>,
559 angvel: Real,
560 point: na::Vector2<Real>,
561 plane: Vector<Real>,
562 bias: Real,
563 }
564
565 impl SpiralPlaneDistance {
566 fn spiral_pt_at(&self, t: Real) -> Point<Real> {
567 let angle = t * self.angvel;
568
569 let (sin, cos) = angle.sin_cos();
573 let rotmat = na::Matrix2::new(cos, -sin, sin, cos);
574
575 let rotated_pt = rotmat * self.point;
576 let shift = self.tangents[0] * rotated_pt.x + self.tangents[1] * rotated_pt.y;
577 self.center + self.linvel * t + shift
578 }
579 }
580
581 impl IntervalFunction<Real> for SpiralPlaneDistance {
582 fn eval(&self, t: Real) -> Real {
583 let point_pos = self.spiral_pt_at(t);
584 point_pos.coords.dot(&self.plane) - self.bias
585 }
586
587 fn eval_interval(&self, t: Interval<Real>) -> Interval<Real> {
588 let angle = t * self.angvel;
590 let (sin, cos) = angle.sin_cos();
591 let rotmat = na::Matrix2::new(cos, -sin, sin, cos);
592
593 let rotated_pt = rotmat * self.point.map(Interval::splat);
594 let shift = self.tangents[0].map(Interval::splat) * rotated_pt.x
595 + self.tangents[1].map(Interval::splat) * rotated_pt.y;
596 let point_pos =
597 self.center.map(Interval::splat) + self.linvel.map(Interval::splat) * t + shift;
598 point_pos.coords.dot(&self.plane.map(Interval::splat)) - Interval::splat(self.bias)
599 }
600
601 fn eval_interval_gradient(&self, t: Interval<Real>) -> Interval<Real> {
602 let angle = t * self.angvel;
603 let (sin, cos) = angle.sin_cos();
604 let rotmat = na::Matrix2::new(-sin, -cos, cos, -sin) * Interval::splat(self.angvel);
605
606 let rotated_pt = rotmat * self.point.map(Interval::splat);
607 let shift = self.tangents[0].map(Interval::splat) * rotated_pt.x
608 + self.tangents[1].map(Interval::splat) * rotated_pt.y;
609 let point_vel = shift + self.linvel.map(Interval::splat);
610 point_vel.dot(&self.plane.map(Interval::splat))
611 }
612 }
613
614 let tangents = axis.orthonormal_basis();
615 let dpos = point - center;
616 let mut distance_fn = SpiralPlaneDistance {
617 center: *center,
618 tangents,
619 linvel: *linvel,
620 angvel,
621 point: na::Vector2::new(dpos.dot(&tangents[0]), dpos.dot(&tangents[1])),
622 plane: Vector::x(),
623 bias: 0.0,
624 };
625
626 let mut roots = vec![];
628 let mut candidates = vec![];
629
630 let planes = [
631 (-self.mins[0], -Vector::x(), 0),
632 (self.maxs[0], Vector::x(), 0),
633 (-self.mins[1], -Vector::y(), 1),
634 (self.maxs[1], Vector::y(), 1),
635 (-self.mins[2], -Vector::z(), 2),
636 (self.maxs[2], Vector::z(), 2),
637 ];
638
639 let range = self.project_on_axis(axis);
640 let range_bias = center.coords.dot(axis);
641 let interval = Interval::sort(range.0, range.1) - range_bias;
642
643 for (bias, axis, i) in &planes {
644 distance_fn.plane = *axis;
645 distance_fn.bias = *bias;
646
647 crate::utils::find_root_intervals_to(
648 &distance_fn,
649 interval,
650 1.0e-5,
651 1.0e-5,
652 100,
653 &mut roots,
654 &mut candidates,
655 );
656
657 for root in roots.drain(..) {
658 let point = distance_fn.spiral_pt_at(root.midpoint());
659 let (j, k) = ((i + 1) % 3, (i + 2) % 3);
660 if point[j] >= self.mins[j]
661 && point[j] <= self.maxs[j]
662 && point[k] >= self.mins[k]
663 && point[k] <= self.maxs[k]
664 {
665 return true;
666 }
667 }
668 }
669
670 false
671 }
672}
673
674impl BoundingVolume for Aabb {
675 #[inline]
676 fn center(&self) -> Point<Real> {
677 self.center()
678 }
679
680 #[inline]
681 fn intersects(&self, other: &Aabb) -> bool {
682 na::partial_le(&self.mins, &other.maxs) && na::partial_ge(&self.maxs, &other.mins)
683 }
684
685 #[inline]
686 fn contains(&self, other: &Aabb) -> bool {
687 na::partial_le(&self.mins, &other.mins) && na::partial_ge(&self.maxs, &other.maxs)
688 }
689
690 #[inline]
691 fn merge(&mut self, other: &Aabb) {
692 self.mins = self.mins.inf(&other.mins);
693 self.maxs = self.maxs.sup(&other.maxs);
694 }
695
696 #[inline]
697 fn merged(&self, other: &Aabb) -> Aabb {
698 Aabb {
699 mins: self.mins.inf(&other.mins),
700 maxs: self.maxs.sup(&other.maxs),
701 }
702 }
703
704 #[inline]
705 fn loosen(&mut self, amount: Real) {
706 assert!(amount >= 0.0, "The loosening margin must be positive.");
707 self.mins += Vector::repeat(-amount);
708 self.maxs += Vector::repeat(amount);
709 }
710
711 #[inline]
712 fn loosened(&self, amount: Real) -> Aabb {
713 assert!(amount >= 0.0, "The loosening margin must be positive.");
714 Aabb {
715 mins: self.mins + Vector::repeat(-amount),
716 maxs: self.maxs + Vector::repeat(amount),
717 }
718 }
719
720 #[inline]
721 fn tighten(&mut self, amount: Real) {
722 assert!(amount >= 0.0, "The tightening margin must be positive.");
723 self.mins += Vector::repeat(amount);
724 self.maxs += Vector::repeat(-amount);
725 assert!(
726 na::partial_le(&self.mins, &self.maxs),
727 "The tightening margin is to large."
728 );
729 }
730
731 #[inline]
732 fn tightened(&self, amount: Real) -> Aabb {
733 assert!(amount >= 0.0, "The tightening margin must be positive.");
734
735 Aabb::new(
736 self.mins + Vector::repeat(amount),
737 self.maxs + Vector::repeat(-amount),
738 )
739 }
740}