1use rand;
4
5use crate::*;
6use super::{intersect, shape};
7use super::mesh::VertexEdgeTriangleMesh;
8
9#[cfg(feature = "derive_serdes")]
10use serde::{Deserialize, Serialize};
11
12pub mod integer;
14
15mod hull;
16mod simplex;
17
18#[macro_use]
19mod macro_def;
20
21pub use self::hull::*;
22pub use self::simplex::{simplex2, simplex3, Simplex2, Simplex3};
23pub use simplex2::{
24 Segment as Segment2,
25 Triangle as Triangle2,
26 SegmentPoint as Segment2Point,
27 TrianglePoint as Triangle2Point
28};
29pub use simplex3::{
30 Segment as Segment3,
31 Triangle as Triangle3,
32 Tetrahedron as Tetrahedron3,
33 SegmentPoint as Segment3Point,
34 TrianglePoint as Triangle3Point,
35 TetrahedronPoint as Tetrahedron3Point
36};
37
38pub type Line2Point <S> = (S, Point2 <S>);
40pub type Line3Point <S> = (S, Point3 <S>);
42pub type Ray2Point <S> = (NonNegative <S>, Point2 <S>);
44pub type Ray3Point <S> = (NonNegative <S>, Point3 <S>);
46
47pub trait Primitive <S, P> : std::fmt::Debug where
49 S : Field,
50 P : AffineSpace <S>
51{
52 fn translate (&mut self, displacement : P::Translation);
53 fn scale (&mut self, scale : Positive <S>);
54 fn support (&self, direction : <P::Translation as VectorSpace <S>>::NonZero)
56 -> (P, S);
57}
58
59#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
65#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub struct Interval <S> {
67 min : S,
68 max : S
69}
70
71#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
92pub struct Aabb2 <S> {
93 min : Point2 <S>,
94 max : Point2 <S>
95}
96
97#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124pub struct Aabb3 <S> {
125 min : Point3 <S>,
126 max : Point3 <S>
127}
128
129#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
132pub struct Obb2 <S> {
133 pub center : Point2 <S>,
134 pub extents : Vector2 <Positive <S>>,
135 pub orientation : AngleWrapped <S>
136}
137
138#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
141pub struct Obb3 <S> {
142 pub center : Point3 <S>,
143 pub extents : Vector3 <Positive <S>>,
144 pub orientation : Angles3 <S>
145}
146
147#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
149#[derive(Clone, Copy, Debug, Eq, PartialEq)]
150pub struct Cylinder3 <S> {
151 pub center : Point3 <S>,
152 pub half_height : Positive <S>,
153 pub radius : Positive <S>
154}
155
156#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
158#[derive(Clone, Copy, Debug, Eq, PartialEq)]
159pub struct Capsule3 <S> {
160 pub center : Point3 <S>,
161 pub half_height : Positive <S>,
162 pub radius : Positive <S>
163}
164
165#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
168pub struct Cone3 <S> {
169 pub center : Point3 <S>,
170 pub half_height : Positive <S>,
171 pub radius : Positive <S>
172}
173
174#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
177#[derive(Clone, Copy, Debug, Eq, PartialEq)]
178pub struct Line2 <S> {
179 pub base : Point2 <S>,
180 pub direction : Unit2 <S>
181}
182
183#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
186#[derive(Clone, Copy, Debug, Eq, PartialEq)]
187pub struct Ray2 <S> {
188 pub base : Point2 <S>,
189 pub direction : Unit2 <S>
190}
191
192#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
198#[derive(Clone, Copy, Debug, Eq, PartialEq)]
199pub struct Line3 <S> {
200 pub base : Point3 <S>,
201 pub direction : Unit3 <S>
202}
203
204#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
207#[derive(Clone, Copy, Debug, Eq, PartialEq)]
208pub struct Ray3 <S> {
209 pub base : Point3 <S>,
210 pub direction : Unit3 <S>
211}
212
213#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
215#[derive(Clone, Copy, Debug, Eq, PartialEq)]
216pub struct Plane3 <S> {
217 pub base : Point3 <S>,
218 pub normal : Unit3 <S>
219}
220
221#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
223#[derive(Clone, Copy, Debug, Eq, PartialEq)]
224pub struct Sphere2 <S> {
225 pub center : Point2 <S>,
226 pub radius : Positive <S>
227}
228
229#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
231#[derive(Clone, Copy, Debug, Eq, PartialEq)]
232pub struct Sphere3 <S> {
233 pub center : Point3 <S>,
234 pub radius : Positive <S>
235}
236
237#[derive(Clone, Debug)]
240struct Rect <S> {
241 pub edge : Vector2 <S>,
242 pub perp : Vector2 <S>,
243 pub indices : [u32; 4],
245 pub area : S,
246 pub edge_len_squared : S
247}
248
249pub fn colinear_2 <S> (a : Point2 <S>, b : Point2 <S>, c : Point2 <S>) -> bool where
269 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
270{
271 let epsilon = adaptive_epsilon (&[a.0, b.0, c.0], &[b - a, c - a], None);
272 colinear_2_epsilon (a, b, c, epsilon)
273}
274
275pub fn colinear_2_epsilon <S> (
276 a : Point2 <S>, b : Point2 <S>, c : Point2 <S>, epsilon : S
277) -> bool where S : OrderedField {
278 if a == b || a == c || b == c {
280 return true
281 }
282 let e1 = b - a;
283 let e2 = c - a;
284 let e1_max = e1.norm_max();
286 let e2_max = e2.norm_max();
287 if *e1_max == S::zero() || *e2_max == S::zero() {
288 return true
289 }
290 let u = e1 / *e1_max;
291 let v = e2 / *e2_max;
292 let u_mag_sq = u.magnitude_squared();
293 let v_mag_sq = v.magnitude_squared();
294 let cross = u.exterior_product (v);
295 cross * cross <= epsilon * epsilon * u_mag_sq * v_mag_sq
296}
297
298pub fn colinear_3 <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>) -> bool where
309 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
310{
311 let max_epsilon = S::two().powi (-3);
312 let epsilon = adaptive_epsilon (&[a.0, b.0, c.0], &[b - a, c - a], Some (max_epsilon));
313 colinear_3_epsilon (a, b, c, epsilon)
314}
315
316pub fn colinear_3_epsilon <S> (
317 a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, epsilon : S
318) -> bool where
319 S : OrderedField
320{
321 if a == b || a == c || b == c {
323 return true
324 }
325 let e1 = b - a;
326 let e2 = c - a;
327 let e1_max = e1.norm_max();
329 let e2_max = e2.norm_max();
330 if *e1_max == S::zero() || *e2_max == S::zero() {
331 return true
332 }
333 let u = e1 / *e1_max;
334 let v = e2 / *e2_max;
335 let u_mag_sq = u.magnitude_squared();
336 let v_mag_sq = v.magnitude_squared();
337 let cross_sq = u.cross (v).magnitude_squared();
338 cross_sq <= epsilon * epsilon * u_mag_sq * v_mag_sq
339}
340
341pub fn coplanar_3 <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>)
353 -> bool
354where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
355 let max_epsilon = S::two().powi (-2);
356 let epsilon = adaptive_epsilon (
357 &[a.0, b.0, c.0, d.0], &[b - a, c - a, d - a], Some (max_epsilon));
358 coplanar_3_epsilon (a, b, c, d, epsilon)
359}
360
361pub fn coplanar_3_epsilon <S> (
362 a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>, epsilon : S
363) -> bool where
364 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
365{
366 let ab = b - a;
367 let ac = c - a;
368 let ad = d - a;
369 let ab_max_norm = ab.norm_max();
370 let ac_max_norm = ac.norm_max();
371 let ad_max_norm = ad.norm_max();
372 if *ab_max_norm == S::zero() || *ac_max_norm == S::zero()
373 || *ad_max_norm == S::zero()
374 {
375 return true
377 }
378 let u = ab / *ab_max_norm;
379 let v = ac / *ac_max_norm;
380 let w = ad / *ad_max_norm;
381 let n = u.cross (v);
382 let n_sq = n.magnitude_squared();
383 if n_sq == S::zero() {
384 return true
386 }
387 let tp = n.dot (w);
388 let w_sq = w.magnitude_squared();
389 tp * tp <= epsilon * epsilon * n_sq * w_sq
390}
391
392pub fn plane_side_3 <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>)
397 -> S
398where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
399 let max_epsilon = S::two().powi (-2);
400 let epsilon = adaptive_epsilon (
401 &[a.0, b.0, c.0, d.0], &[b - a, c - a, d - a], Some (max_epsilon));
402 plane_side_3_epsilon (a, b, c, d, epsilon)
403}
404
405pub fn plane_side_3_epsilon <S> (
406 a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>, epsilon : S
407) -> S where
408 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
409{
410 let ab = b - a;
411 let ac = c - a;
412 let ad = d - a;
413 let ab_max_norm = ab.norm_max();
414 let ac_max_norm = ac.norm_max();
415 let ad_max_norm = ad.norm_max();
416 if *ab_max_norm == S::zero() || *ac_max_norm == S::zero()
417 || *ad_max_norm == S::zero()
418 {
419 return S::zero()
421 }
422 let u = ab / *ab_max_norm;
423 let v = ac / *ac_max_norm;
424 let w = ad / *ad_max_norm;
425 let n = u.cross (v);
426 let n_sq = n.magnitude_squared();
427 if n_sq == S::zero() {
428 return S::zero()
430 }
431 let tp = n.dot (w);
432 let w_sq = w.magnitude_squared();
433 if tp * tp <= epsilon * epsilon * n_sq * w_sq {
434 S::zero()
435 } else if tp > S::zero() {
436 S::one()
437 } else {
438 -S::one()
439 }
440}
441
442#[inline]
444pub fn determinant_3 <S : Ring> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>) -> S {
445 Matrix3::from_col_arrays ([a.0.into_array(), b.0.into_array(), c.0.into_array()])
446 .determinant()
447}
448
449pub fn orthonormal_basis <S> (e1 : Unit3 <S>) -> [Unit3 <S>; 3] where S : Real {
451 use num::Zero;
452 let u = Unit3::axis_x();
453 let e2 = {
454 let mut e2 = (*e1).cross (*u);
455 e2.z += if e2.is_zero() {
456 S::one()
457 } else {
458 S::zero()
459 };
460 Unit3::new (e2).unwrap()
461 };
462 let e3 = Unit3::new ((*e1).cross (*e2)).unwrap();
463 [e1, e2, e3]
464}
465
466#[inline]
468pub fn point2_min <S : PartialOrd> (a : Point2 <S>, b : Point2 <S>) -> Point2 <S> {
469 Vector2::partial_min (a.0, b.0).into()
470}
471
472#[inline]
474pub fn point2_max <S : PartialOrd> (a : Point2 <S>, b : Point2 <S>) -> Point2 <S> {
475 Vector2::partial_max (a.0, b.0).into()
476}
477
478#[inline]
480pub fn point3_min <S : PartialOrd> (a : Point3 <S>, b : Point3 <S>) -> Point3 <S> {
481 Vector3::partial_min (a.0, b.0).into()
482}
483
484#[inline]
486pub fn point3_max <S : PartialOrd> (a : Point3 <S>, b : Point3 <S>) -> Point3 <S> {
487 Vector3::partial_max (a.0, b.0).into()
488}
489
490#[inline]
493pub fn project_point2_on_line2 <S : OrderedRing> (point : Point2 <S>, line : Line2 <S>)
494 -> Line2Point <S>
495{
496 let dot_dir = line.direction.dot (point - line.base);
497 (dot_dir, line.point (dot_dir))
498}
499
500#[inline]
503pub fn project_point3_on_line3 <S : OrderedRing> (point : Point3 <S>, line : Line3 <S>)
504 -> Line3Point <S>
505{
506 let dot_dir = line.direction.dot (point - line.base);
507 (dot_dir, line.point (dot_dir))
508}
509
510#[inline]
513pub fn project_point3_on_plane3 <S : Real> (point : Point3 <S>, plane : Plane3 <S>)
514 -> Point3 <S>
515{
516 let v = point - plane.base;
517 let distance = (*plane.normal).dot (v);
518 point - *plane.normal * distance
519}
520
521#[inline]
524pub fn support_2 <S> (points : &[Point2 <S>], direction : NonZero2 <S>)
525 -> (Point2 <S>, S)
526where S : Ring + PartialOrd {
527 debug_assert!(!points.is_empty());
528 points.iter()
529 .map (|point| (*point, direction.dot (point.0)))
530 .max_by (|(_, dot_a), (_, dot_b)| dot_a.partial_cmp (dot_b).unwrap()).unwrap()
531}
532
533#[inline]
536pub fn support_3 <S> (points : &[Point3 <S>], direction : NonZero3 <S>)
537 -> (Point3 <S>, S)
538where S : Ring + PartialOrd {
539 debug_assert!(!points.is_empty());
540 points.iter()
541 .map (|point| (*point, direction.dot (point.0)))
542 .max_by (|(_, dot_a), (_, dot_b)| dot_a.partial_cmp (dot_b).unwrap()).unwrap()
543}
544
545pub fn triangle_3d_area_squared <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>)
575 -> NonNegative <S>
576where
577 S : OrderedField + Sqrt + approx::AbsDiffEq <Epsilon = S>
578{
579 use num::Zero;
580 if a == b || a == c || b == c {
581 return NonNegative::zero()
582 }
583 let ab_mag = *(b-a).norm();
585 let ac_mag = *(c-a).norm();
586 let bc_mag = *(c-b).norm();
587 let min_ab_ac = S::min (ab_mag, ac_mag);
589 let max_ab_ac = S::max (ab_mag, ac_mag);
590 let (min, mid, max) = if bc_mag <= min_ab_ac {
591 (bc_mag, min_ab_ac, max_ab_ac)
592 } else if bc_mag >= max_ab_ac {
593 (min_ab_ac, max_ab_ac, bc_mag)
594 } else {
595 (min_ab_ac, bc_mag, max_ab_ac)
596 };
597 let frac = S::two().powi (-4);
599 let mut area_squared = frac
600 * (max + (mid + min))
601 * (min - (max - mid))
602 * (min + (max - mid))
603 * (max + (mid - min));
604 if area_squared < S::zero() {
608 if cfg!(debug_assertions) {
609 let lengths_squared = (b - a).magnitude_squared() + (c - b).magnitude_squared()
610 + (a - c).magnitude_squared();
611 approx::assert_abs_diff_eq!(area_squared, S::zero(),
612 epsilon = S::default_epsilon() * S::two().powi (44) * lengths_squared)
613 }
614 area_squared = S::zero();
615 }
616 NonNegative::unchecked (area_squared)
617}
618
619fn smallest_rect <S : Real> (i0 : u32, i1 : u32, hull : &Hull2 <S>) -> Rect <S> {
623 let points = hull.points();
627 let edge = points[i1 as usize] - points[i0 as usize];
628 let perp = vector2 (-edge.y, edge.x);
629 let edge_len_squared = edge.magnitude_squared();
630 let origin = points[i1 as usize];
631 let mut support = [Point2::<S>::origin(); 4];
632 let mut rect_index = [i1, i1, i1, i1];
633 #[expect(clippy::cast_possible_truncation)]
634 for (i, point) in points.iter().enumerate() {
635 let diff = point - origin;
636 let v = point2 (edge.dot (diff), perp.dot (diff));
637 if v.x() > support[1].x() || v.x() == support[1].x() && v.y() > support[1].y() {
638 rect_index[1] = i as u32;
639 support[1] = v;
640 }
641 if v.y() > support[2].y() || v.y() == support[2].y() && v.x() < support[2].x() {
642 rect_index[2] = i as u32;
643 support[2] = v;
644 }
645 if v.x() < support[3].x() || v.x() == support[3].x() && v.y() < support[3].y() {
646 rect_index[3] = i as u32;
647 support[3] = v;
648 }
649 }
650 let scaled_width = support[1].x() - support[3].x();
651 let scaled_height = support[2].y();
652 let area = scaled_width * scaled_height / edge_len_squared;
653
654 Rect {
655 edge, perp, area, edge_len_squared,
656 indices: [rect_index[0], rect_index[1], rect_index[2], rect_index[3]]
657 }
658}
659
660fn adaptive_epsilon <S, V> (points : &[V], edges : &[V], max_epsilon : Option <S>)
661 -> S
662where
663 S : OrderedField + approx::AbsDiffEq <Epsilon = S>,
664 V : NormedVectorSpace <S> + Copy
665{
666 use num::Zero;
667 debug_assert!(!points.is_empty());
668 debug_assert!(!edges.is_empty());
669 let max_point_max_norm = points.iter().copied().map (NormedVectorSpace::norm_max)
670 .max_by (|a, b| a.partial_cmp (b).unwrap()).unwrap();
671 let min_edge_max_norm = edges.iter().copied().map (NormedVectorSpace::norm_max)
672 .min_by (|a, b| a.partial_cmp (b).unwrap()).unwrap();
673 let ratio = if min_edge_max_norm == NonNegative::zero() {
674 NonNegative::zero()
675 } else {
676 max_point_max_norm / min_edge_max_norm
677 };
678 let epsilon = S::default_epsilon() * S::eight() * (S::one() + *ratio);
679 max_epsilon.map_or (epsilon, |max_epsilon| epsilon.min (max_epsilon))
680}
681
682impl <S> Interval <S> where S : Copy {
687 #[inline]
688 pub const fn min (self) -> S {
689 self.min
690 }
691 #[inline]
692 pub const fn max (self) -> S {
693 self.max
694 }
695}
696impl <S> Interval <S> where
697 S : MinMax + Copy + PartialOrd + std::fmt::Debug
698{
699 #[inline]
700 pub fn from_points (a : S, b : S) -> Self {
701 let min = S::min (a, b);
702 let max = S::max (a, b);
703 Interval { min, max }
704 }
705 #[inline]
707 pub fn with_minmax (min : S, max : S) -> Option <Self> {
708 if min > max {
709 None
710 } else {
711 Some (Interval { min, max })
712 }
713 }
714 #[inline]
723 pub fn with_minmax_unchecked (min : S, max : S) -> Self {
724 debug_assert_eq!(min, S::min (min, max));
725 debug_assert_eq!(max, S::max (min, max));
726 Interval { min, max }
727 }
728 #[inline]
737 pub fn containing (points : &[S]) -> Option <Self> {
738 if points.len() < 2 {
739 None
740 } else {
741 debug_assert!(points.len() >= 2);
742 let mut min = S::min (points[0], points[1]);
743 let mut max = S::max (points[0], points[1]);
744 for point in points.iter().skip (2) {
745 min = S::min (min, *point);
746 max = S::max (max, *point);
747 }
748 Interval::with_minmax (min, max)
749 }
750 }
751 #[inline]
752 pub fn numcast <T> (self) -> Option <Interval <T>> where
753 S : num::ToPrimitive,
754 T : num::NumCast
755 {
756 Some (Interval {
757 min: T::from (self.min)?,
758 max: T::from (self.max)?
759 })
760 }
761 #[inline]
763 pub fn union (a : Interval <S>, b : Interval <S>) -> Self {
764 Interval::with_minmax_unchecked (
765 S::min (a.min(), b.min()), S::max (a.max(), b.max()))
766 }
767 #[inline]
768 pub fn length (self) -> NonNegative <S> where
769 S : num::Zero + std::ops::Sub <Output=S>
770 {
771 self.width()
772 }
773 #[inline]
774 pub fn width (self) -> NonNegative <S> where
775 S : num::Zero + std::ops::Sub <Output=S>
776 {
777 NonNegative::unchecked (self.max - self.min)
778 }
779 #[inline]
780 pub fn contains (self, point : S) -> bool {
781 self.min < point && point < self.max
782 }
783 #[inline]
793 pub fn clamp (self, point : S) -> S {
794 point.clamp (self.min, self.max)
795 }
796 #[inline]
821 pub fn rand_point <R> (self, rng : &mut R) -> S where
822 S : rand::distr::uniform::SampleUniform,
823 R : rand::RngExt
824 {
825 rng.random_range (self.min..self.max)
826 }
827 #[inline]
828 pub fn intersects (self, other : Interval <S>) -> bool {
829 intersect::overlaps_interval (self, other)
830 }
831 #[inline]
832 pub fn intersection (self, other : Interval <S>) -> Option <Interval <S>> {
833 intersect::intersect_interval (self, other)
834 }
835
836 pub fn dilate (mut self, amount : S) -> Option <Self> where
846 S : std::ops::AddAssign + std::ops::SubAssign
847 {
848 self.min -= amount;
849 self.max += amount;
850 if self.min > self.max {
851 None
852 } else {
853 Some (self)
854 }
855 }
856}
857
858impl <S> Primitive <S, S> for Interval <S> where
859 S : OrderedField + AffineSpace <S, Translation=S>
860 + VectorSpace <S, NonZero=NonZero <S>>
861{
862 fn translate (&mut self, displacement : S) {
863 self.min += displacement;
864 self.max += displacement;
865 }
866 fn scale (&mut self, scale : Positive <S>) {
867 let old_width = *self.width();
868 let new_width = old_width * *scale;
869 let half_difference = (new_width - old_width) / S::two();
870 self.min -= half_difference;
871 self.max += half_difference;
872 }
873 fn support (&self, direction : NonZero <S>) -> (S, S) {
874 if *direction > S::zero() {
875 (self.max, self.max * *direction)
876 } else {
877 (self.min, self.min * *direction)
878 }
879 }
880}
881
882impl <S> std::ops::Add <S> for Interval <S> where
883 S : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
884 Self : Primitive <S, S>
885{
886 type Output = Self;
887 #[expect(clippy::renamed_function_params)]
888 fn add (mut self, displacement : S) -> Self {
889 self.translate (displacement);
890 self
891 }
892}
893
894impl <S> std::ops::Sub <S> for Interval <S> where
895 S : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
896 Self : Primitive <S, S>
897{
898 type Output = Self;
899 #[expect(clippy::renamed_function_params)]
900 fn sub (mut self, displacement : S) -> Self {
901 self.translate (-displacement);
902 self
903 }
904}
905
906impl_numcast_fields!(Aabb2, min, max);
907impl <S> Aabb2 <S> where S : Copy {
908 #[inline]
909 pub const fn min (self) -> Point2 <S> {
910 self.min
911 }
912 #[inline]
913 pub const fn max (self) -> Point2 <S> {
914 self.max
915 }
916}
917impl <S> Aabb2 <S> where
918 S : Copy + PartialOrd + std::fmt::Debug
919{
920 #[inline]
922 pub fn with_minmax (min : Point2 <S>, max : Point2 <S>) -> Option <Self> {
923 if min == max || min != point2_min (min, max) || max != point2_max (min, max) {
924 None
925 } else {
926 Some (Aabb2 { min, max })
927 }
928 }
929 #[inline]
931 pub fn from_points (a : Point2 <S>, b : Point2 <S>) -> Option <Self> {
932 if a == b {
933 None
934 } else {
935 let min = point2_min (a, b);
936 let max = point2_max (a, b);
937 Some (Aabb2 { min, max })
938 }
939 }
940 #[inline]
960 pub fn with_minmax_unchecked (min : Point2 <S>, max : Point2 <S>) -> Self {
961 debug_assert_ne!(min, max);
962 debug_assert_eq!(min, point2_min (min, max));
963 debug_assert_eq!(max, point2_max (min, max));
964 Aabb2 { min, max }
965 }
966 #[inline]
977 pub fn from_points_unchecked (a : Point2 <S>, b : Point2 <S>) -> Self {
978 debug_assert_ne!(a, b);
979 let min = point2_min (a, b);
980 let max = point2_max (a, b);
981 Aabb2 { min, max }
982 }
983 #[inline]
997 pub fn containing (points : &[Point2 <S>]) -> Option <Self> {
998 if points.len() < 2 {
999 None
1000 } else {
1001 debug_assert!(points.len() >= 2);
1002 let mut min = point2_min (points[0], points[1]);
1003 let mut max = point2_max (points[0], points[1]);
1004 for point in points.iter().skip (2) {
1005 min = point2_min (min, *point);
1006 max = point2_max (max, *point);
1007 }
1008 Aabb2::with_minmax (min, max)
1009 }
1010 }
1011 #[inline]
1013 pub fn union (a : Aabb2 <S>, b : Aabb2 <S>) -> Self {
1014 Aabb2::with_minmax_unchecked (
1015 point2_min (a.min(), b.min()), point2_max (a.max(), b.max()))
1016 }
1017 #[inline]
1018 pub fn center (self) -> Point2 <S> where S : Ring + std::ops::Div <Output=S> {
1019 Point2 ((self.min.0 + self.max.0) / S::two())
1020 }
1021 #[inline]
1022 pub fn dimensions (self) -> Vector2 <NonNegative <S>> where
1023 S : num::Zero + std::ops::Sub <Output=S>
1024 {
1025 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1026 }
1027 #[inline]
1028 pub fn extents (self) -> Vector2 <NonNegative <S>> where S : Field {
1029 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1030 }
1031 #[inline]
1033 pub fn width (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1034 NonNegative::unchecked (self.max.x() - self.min.x())
1035 }
1036 #[inline]
1038 pub fn height (self) -> NonNegative <S> where
1039 S : num::Zero + std::ops::Sub <Output=S>
1040 {
1041 NonNegative::unchecked (self.max.y() - self.min.y())
1042 }
1043 #[inline]
1044 pub fn area (self) -> NonNegative <S> where
1045 S : num::Zero + std::ops::Sub <Output=S> + std::ops::Mul <Output=S>
1046 {
1047 self.width() * self.height()
1048 }
1049 #[inline]
1050 pub fn contains (self, point : Point2 <S>) -> bool {
1051 self.min.x() < point.x() && point.x() < self.max.x() &&
1052 self.min.y() < point.y() && point.y() < self.max.y()
1053 }
1054 pub fn clamp (self, point : Point2 <S>) -> Point2 <S> where S : MinMax {
1064 [ point.x().clamp (self.min.x(), self.max.x()),
1065 point.y().clamp (self.min.y(), self.max.y())
1066 ].into()
1067 }
1068 #[inline]
1096 pub fn rand_point <R> (self, rng : &mut R) -> Point2 <S> where
1097 S : rand::distr::uniform::SampleUniform,
1098 R : rand::RngExt
1099 {
1100 let x_range = self.min.x()..self.max.x();
1101 let y_range = self.min.y()..self.max.y();
1102 let x = if x_range.is_empty() {
1103 self.min.x()
1104 } else {
1105 rng.random_range (x_range)
1106 };
1107 let y = if y_range.is_empty() {
1108 self.min.y()
1109 } else {
1110 rng.random_range (y_range)
1111 };
1112 [x, y].into()
1113 }
1114 #[inline]
1115 pub fn intersects (self, other : Aabb2 <S>) -> bool {
1116 intersect::overlaps_aabb2_aabb2 (self, other)
1117 }
1118 #[inline]
1119 pub fn intersection (self, other : Aabb2 <S>) -> Option <Aabb2 <S>> {
1120 intersect::intersect_aabb2_aabb2 (self, other)
1121 }
1122
1123 pub fn corner (self, quadrant : Quadrant) -> Point2 <S> {
1124 match quadrant {
1125 Quadrant::PosPos => self.max,
1127 Quadrant::PosNeg => [self.max.x(), self.min.y()].into(),
1129 Quadrant::NegPos => [self.min.x(), self.max.y()].into(),
1131 Quadrant::NegNeg => self.min
1133 }
1134 }
1135
1136 pub fn corners (self) -> [Point2 <S>; 4] {
1138 [ self.min, [self.min.x(), self.max.y()].into(),
1139 self.max, [self.max.x(), self.min.y()].into()
1140 ]
1141 }
1142
1143 pub fn edge (self, direction : SignedAxis2) -> Self {
1144 let (min, max) = match direction {
1145 SignedAxis2::PosX => (
1146 self.corner (Quadrant::PosNeg),
1147 self.corner (Quadrant::PosPos) ),
1148 SignedAxis2::NegX => (
1149 self.corner (Quadrant::NegNeg),
1150 self.corner (Quadrant::NegPos) ),
1151 SignedAxis2::PosY => (
1152 self.corner (Quadrant::NegPos),
1153 self.corner (Quadrant::PosPos) ),
1154 SignedAxis2::NegY => (
1155 self.corner (Quadrant::NegNeg),
1156 self.corner (Quadrant::PosNeg) )
1157 };
1158 Aabb2::with_minmax_unchecked (min, max)
1159 }
1160
1161 #[must_use]
1174 pub fn extrude (self, axis : SignedAxis2, amount : S) -> Option <Self> where S : Ring {
1175 let (p1, p2) = match axis {
1176 SignedAxis2::PosX => (
1177 self.min + Vector2::zero().with_x (*self.width()),
1178 self.max + Vector2::zero().with_x (amount) ),
1179 SignedAxis2::NegX => (
1180 self.min - Vector2::zero().with_x (amount),
1181 self.max - Vector2::zero().with_x (*self.height()) ),
1182 SignedAxis2::PosY => (
1183 self.min + Vector2::zero().with_y (*self.height()),
1184 self.max + Vector2::zero().with_y (amount) ),
1185 SignedAxis2::NegY => (
1186 self.min - Vector2::zero().with_y (amount),
1187 self.max - Vector2::zero().with_y (*self.height()) )
1188 };
1189 Self::from_points (p1, p2)
1190 }
1191
1192 #[must_use]
1194 pub fn extend (mut self, axis : SignedAxis2, amount : S) -> Option <Self>
1195 where S : std::ops::AddAssign + std::ops::SubAssign
1196 {
1197 match axis {
1198 SignedAxis2::PosX => self.max.0.x += amount,
1199 SignedAxis2::NegX => self.min.0.x -= amount,
1200 SignedAxis2::PosY => self.max.0.y += amount,
1201 SignedAxis2::NegY => self.min.0.y -= amount
1202 }
1203 Self::with_minmax (self.min, self.max)
1204 }
1205
1206 pub fn with_z (self, z : Interval <S>) -> Aabb3 <S> {
1207 Aabb3::with_minmax_unchecked (
1208 self.min.0.with_z (z.min()).into(),
1209 self.max.0.with_z (z.max()).into()
1210 )
1211 }
1212
1213 #[must_use]
1223 pub fn dilate (mut self, amount : S) -> Option <Self> where S : Ring {
1224 self.min -= Vector2::broadcast (amount);
1225 self.max += Vector2::broadcast (amount);
1226 if self.min == self.max || self.min != point2_min (self.min, self.max)
1227 || self.max != point2_max (self.min, self.max)
1228 {
1229 None
1230 } else {
1231 Some (self)
1232 }
1233 }
1234
1235 pub fn project (self, axis : Axis2) -> Interval <S> where S : MinMax {
1239 let (min, max) = match axis {
1240 Axis2::X => (self.min.y(), self.max.y()),
1241 Axis2::Y => (self.min.x(), self.max.x())
1242 };
1243 Interval::with_minmax_unchecked (min, max)
1244 }
1245}
1246
1247impl <S> Primitive <S, Point2 <S>> for Aabb2 <S> where S : OrderedField {
1248 fn translate (&mut self, displacement : Vector2 <S>) {
1249 self.min += displacement;
1250 self.max += displacement;
1251 }
1252 fn scale (&mut self, scale : Positive <S>) {
1253 let center = self.center();
1254 self.translate (-center.0);
1255 self.min.0 *= *scale;
1256 self.max.0 *= *scale;
1257 self.translate (center.0)
1258 }
1259 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
1260 let out = if let Some (quadrant) = Quadrant::from_vec_strict (*direction) {
1261 self.corner (quadrant)
1262 } else if direction.x > S::zero() {
1263 debug_assert_eq!(direction.y, S::zero());
1264 point2 (self.max.x(), (self.min.y() + self.max.y()) / S::two())
1265 } else if direction.x < S::zero() {
1266 debug_assert_eq!(direction.y, S::zero());
1267 point2 (self.min.x(), (self.min.y() + self.max.y()) / S::two())
1268 } else {
1269 debug_assert_eq!(direction.x, S::zero());
1270 if direction.y > S::zero() {
1271 point2 ((self.min.x() + self.max.x()) / S::two(), self.max.y())
1272 } else {
1273 debug_assert!(direction.y < S::zero());
1274 point2 ((self.min.x() + self.max.x()) / S::two(), self.min.y())
1275 }
1276 };
1277 (out, out.0.dot (*direction))
1278 }
1279}
1280
1281impl <S> std::ops::Add <Vector2 <S>> for Aabb2 <S> where
1282 S : Field,
1283 Self : Primitive <S, Point2 <S>>
1284{
1285 type Output = Self;
1286 #[expect(clippy::renamed_function_params)]
1287 fn add (mut self, displacement : Vector2 <S>) -> Self {
1288 self.translate (displacement);
1289 self
1290 }
1291}
1292
1293impl <S> std::ops::Sub <Vector2 <S>> for Aabb2 <S> where
1294 S : Field,
1295 Self : Primitive <S, Point2 <S>>
1296{
1297 type Output = Self;
1298 #[expect(clippy::renamed_function_params)]
1299 fn sub (mut self, displacement : Vector2 <S>) -> Self {
1300 self.translate (-displacement);
1301 self
1302 }
1303}
1304
1305impl_numcast_fields!(Aabb3, min, max);
1306impl <S> Aabb3 <S> where S : Copy {
1307 #[inline]
1308 pub const fn min (self) -> Point3 <S> {
1309 self.min
1310 }
1311 #[inline]
1312 pub const fn max (self) -> Point3 <S> {
1313 self.max
1314 }
1315}
1316impl <S> Aabb3 <S> where S : Copy + PartialOrd + std::fmt::Debug {
1317 #[inline]
1319 pub fn with_minmax (min : Point3 <S>, max : Point3 <S>) -> Option <Self> {
1320 if min == max || min != point3_min (min, max) || max != point3_max (min, max) {
1321 None
1322 } else {
1323 Some (Aabb3 { min, max })
1324 }
1325 }
1326 #[inline]
1328 pub fn from_points (a : Point3 <S>, b : Point3 <S>) -> Option <Self> {
1329 if a == b {
1330 None
1331 } else {
1332 let min = point3_min (a, b);
1333 let max = point3_max (a, b);
1334 Some (Aabb3 { min, max })
1335 }
1336 }
1337 #[inline]
1357 pub fn with_minmax_unchecked (min : Point3 <S>, max : Point3 <S>) -> Self {
1358 debug_assert_ne!(min, max);
1359 debug_assert_eq!(min, point3_min (min, max));
1360 debug_assert_eq!(max, point3_max (min, max));
1361 Aabb3 { min, max }
1362 }
1363 #[inline]
1374 pub fn from_points_unchecked (a : Point3 <S>, b : Point3 <S>) -> Self {
1375 debug_assert_ne!(a, b);
1376 let min = point3_min (a, b);
1377 let max = point3_max (a, b);
1378 Aabb3 { min, max }
1379 }
1380 #[inline]
1394 pub fn containing (points : &[Point3 <S>]) -> Option <Self> {
1395 debug_assert!(points.len() >= 2);
1396 let mut min = point3_min (points[0], points[1]);
1397 let mut max = point3_max (points[0], points[1]);
1398 for point in points.iter().skip (2) {
1399 min = point3_min (min, *point);
1400 max = point3_max (max, *point);
1401 }
1402 Aabb3::with_minmax (min, max)
1403 }
1404 #[inline]
1406 pub fn union (a : Aabb3 <S>, b : Aabb3 <S>) -> Self {
1407 Aabb3::with_minmax_unchecked (
1408 point3_min (a.min(), b.min()), point3_max (a.max(), b.max()))
1409 }
1410 #[inline]
1411 pub fn center (self) -> Point3 <S> where S : Ring + std::ops::Div <Output=S> {
1412 Point3 ((self.min.0 + self.max.0) / S::two())
1413 }
1414 #[inline]
1415 pub fn dimensions (self) -> Vector3 <NonNegative <S>> where
1416 S : num::Zero + std::ops::Sub <Output=S>
1417 {
1418 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1419 }
1420 #[inline]
1421 pub fn extents (self) -> Vector3 <NonNegative <S>> where S : Field {
1422 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1423 }
1424 #[inline]
1426 pub fn width (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1427 NonNegative::unchecked (self.max.x() - self.min.x())
1428 }
1429 #[inline]
1431 pub fn depth (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1432 NonNegative::unchecked (self.max.y() - self.min.y())
1433 }
1434 #[inline]
1436 pub fn height (self) -> NonNegative <S> where
1437 S : num::Zero + std::ops::Sub <Output=S>
1438 {
1439 NonNegative::unchecked (self.max.z() - self.min.z())
1440 }
1441 #[inline]
1442 pub fn volume (self) -> NonNegative <S> where
1443 S : num::Zero + std::ops::Sub <Output=S> + std::ops::Mul <Output=S>
1444 {
1445 self.width() * self.depth() * self.height()
1446 }
1447 #[inline]
1448 pub fn contains (self, point : Point3 <S>) -> bool {
1449 self.min.x() < point.x() && point.x() < self.max.x() &&
1450 self.min.y() < point.y() && point.y() < self.max.y() &&
1451 self.min.z() < point.z() && point.z() < self.max.z()
1452 }
1453 pub fn clamp (self, point : Point3 <S>) -> Point3 <S> where S : MinMax {
1465 [ point.x().clamp (self.min.x(), self.max.x()),
1466 point.y().clamp (self.min.y(), self.max.y()),
1467 point.z().clamp (self.min.z(), self.max.z())
1468 ].into()
1469 }
1470 #[inline]
1498 pub fn rand_point <R> (self, rng : &mut R) -> Point3 <S> where
1499 S : rand::distr::uniform::SampleUniform,
1500 R : rand::RngExt
1501 {
1502 let x_range = self.min.x()..self.max.x();
1503 let y_range = self.min.y()..self.max.y();
1504 let z_range = self.min.z()..self.max.z();
1505 let x = if x_range.is_empty() {
1506 self.min.x()
1507 } else {
1508 rng.random_range (x_range)
1509 };
1510 let y = if y_range.is_empty() {
1511 self.min.y()
1512 } else {
1513 rng.random_range (y_range)
1514 };
1515 let z = if z_range.is_empty() {
1516 self.min.z()
1517 } else {
1518 rng.random_range (z_range)
1519 };
1520 [x, y, z].into()
1521 }
1522 #[inline]
1523 pub fn intersects (self, other : Aabb3 <S>) -> bool {
1524 intersect::overlaps_aabb3_aabb3 (self, other)
1525 }
1526 #[inline]
1527 pub fn intersection (self, other : Aabb3 <S>) -> Option <Aabb3 <S>> {
1528 intersect::intersect_aabb3_aabb3 (self, other)
1529 }
1530
1531 pub fn corner (self, octant : Octant) -> Point3 <S> {
1532 match octant {
1533 Octant::PosPosPos => self.max,
1534 Octant::NegPosPos => [self.min.x(), self.max.y(), self.max.z()].into(),
1535 Octant::PosNegPos => [self.max.x(), self.min.y(), self.max.z()].into(),
1536 Octant::NegNegPos => [self.min.x(), self.min.y(), self.max.z()].into(),
1537 Octant::PosPosNeg => [self.max.x(), self.max.y(), self.min.z()].into(),
1538 Octant::NegPosNeg => [self.min.x(), self.max.y(), self.min.z()].into(),
1539 Octant::PosNegNeg => [self.max.x(), self.min.y(), self.min.z()].into(),
1540 Octant::NegNegNeg => self.min
1541 }
1542 }
1543
1544 pub fn corners (self) -> [Point3 <S>; 8] {
1545 [ self.min,
1546 [self.min.x(), self.max.y(), self.max.z()].into(),
1547 [self.max.x(), self.min.y(), self.max.z()].into(),
1548 [self.min.x(), self.min.y(), self.max.z()].into(),
1549 [self.max.x(), self.max.y(), self.min.z()].into(),
1550 [self.min.x(), self.max.y(), self.min.z()].into(),
1551 [self.max.x(), self.min.y(), self.min.z()].into(),
1552 self.max
1553 ]
1554 }
1555
1556 pub fn face (self, direction : SignedAxis3) -> Self {
1557 let (min, max) = match direction {
1558 SignedAxis3::PosX => (
1559 self.corner (Octant::PosNegNeg),
1560 self.corner (Octant::PosPosPos) ),
1561 SignedAxis3::NegX => (
1562 self.corner (Octant::NegNegNeg),
1563 self.corner (Octant::NegPosPos) ),
1564 SignedAxis3::PosY => (
1565 self.corner (Octant::NegPosNeg),
1566 self.corner (Octant::PosPosPos) ),
1567 SignedAxis3::NegY => (
1568 self.corner (Octant::NegNegNeg),
1569 self.corner (Octant::PosNegPos) ),
1570 SignedAxis3::PosZ => (
1571 self.corner (Octant::NegNegPos),
1572 self.corner (Octant::PosPosPos) ),
1573 SignedAxis3::NegZ => (
1574 self.corner (Octant::NegNegNeg),
1575 self.corner (Octant::PosPosNeg) )
1576 };
1577 Aabb3::with_minmax_unchecked (min, max)
1578 }
1579
1580 #[must_use]
1593 pub fn extrude (self, axis : SignedAxis3, amount : S) -> Option <Self> where S : Ring {
1594 let (p1, p2) = match axis {
1595 SignedAxis3::PosX => (
1596 self.min + Vector3::zero().with_x (*self.width()),
1597 self.max + Vector3::zero().with_x (amount)
1598 ),
1599 SignedAxis3::NegX => (
1600 self.min - Vector3::zero().with_x (amount),
1601 self.max - Vector3::zero().with_x (*self.width())
1602 ),
1603 SignedAxis3::PosY => (
1604 self.min + Vector3::zero().with_y (*self.depth()),
1605 self.max + Vector3::zero().with_y (amount)
1606 ),
1607 SignedAxis3::NegY => (
1608 self.min - Vector3::zero().with_y (amount),
1609 self.max - Vector3::zero().with_y (*self.depth())
1610 ),
1611 SignedAxis3::PosZ => (
1612 self.min + Vector3::zero().with_z (*self.height()),
1613 self.max + Vector3::zero().with_z (amount)
1614 ),
1615 SignedAxis3::NegZ => (
1616 self.min - Vector3::zero().with_z (amount),
1617 self.max - Vector3::zero().with_z (*self.height())
1618 )
1619 };
1620 Aabb3::from_points (p1, p2)
1621 }
1622
1623 #[must_use]
1625 pub fn extend (mut self, axis : SignedAxis3, amount : S) -> Option <Self>
1626 where S : std::ops::AddAssign + std::ops::SubAssign
1627 {
1628 match axis {
1629 SignedAxis3::PosX => self.max.0.x += amount,
1630 SignedAxis3::NegX => self.min.0.x -= amount,
1631 SignedAxis3::PosY => self.max.0.y += amount,
1632 SignedAxis3::NegY => self.min.0.y -= amount,
1633 SignedAxis3::PosZ => self.max.0.z += amount,
1634 SignedAxis3::NegZ => self.min.0.z -= amount
1635 }
1636 Self::with_minmax (self.min, self.max)
1637 }
1638
1639 #[must_use]
1649 pub fn dilate (mut self, amount : S) -> Option <Self> where S : Ring {
1650 self.min -= Vector3::broadcast (amount);
1651 self.max += Vector3::broadcast (amount);
1652 if self.min == self.max || self.min != point3_min (self.min, self.max)
1653 || self.max != point3_max (self.min, self.max)
1654 {
1655 None
1656 } else {
1657 Some (self)
1658 }
1659 }
1660
1661 pub fn project (self, axis : Axis3) -> Aabb2 <S> {
1665 let (min, max) = match axis {
1666 Axis3::X => ([self.min.y(), self.min.z()], [self.max.y(), self.max.z()]),
1667 Axis3::Y => ([self.min.x(), self.min.z()], [self.max.x(), self.max.z()]),
1668 Axis3::Z => ([self.min.x(), self.min.y()], [self.max.x(), self.max.y()]),
1669 };
1670 Aabb2::with_minmax_unchecked (min.into(), max.into())
1671 }
1672}
1673
1674impl <S> Primitive <S, Point3 <S>> for Aabb3 <S> where S : OrderedField {
1675 fn translate (&mut self, displacement : Vector3 <S>) {
1676 self.min += displacement;
1677 self.max += displacement;
1678 }
1679 fn scale (&mut self, scale : Positive <S>) {
1680 let center = self.center();
1681 self.translate (-center.0);
1682 self.min.0 *= *scale;
1683 self.max.0 *= *scale;
1684 self.translate (center.0);
1685 }
1686 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
1687 let out = if let Some (octant) = Octant::from_vec_strict (*direction) {
1688 self.corner (octant)
1689 } else {
1690 use Sign::*;
1691 let center = self.center();
1692 match direction.map (S::sign).into_array() {
1693 [Positive, Zero, Zero] => center.0.with_x (self.max.x()).into(),
1695 [Negative, Zero, Zero] => center.0.with_x (self.min.x()).into(),
1696 [Zero, Positive, Zero] => center.0.with_y (self.max.y()).into(),
1697 [Zero, Negative, Zero] => center.0.with_y (self.min.y()).into(),
1698 [Zero, Zero, Positive] => center.0.with_z (self.max.z()).into(),
1699 [Zero, Zero, Negative] => center.0.with_z (self.min.z()).into(),
1700 [Positive, Positive, Zero] =>
1702 center.0.with_x (self.max.x()).with_y (self.max.y()).into(),
1703 [Positive, Negative, Zero] =>
1704 center.0.with_x (self.max.x()).with_y (self.min.y()).into(),
1705 [Negative, Positive, Zero] =>
1706 center.0.with_x (self.min.x()).with_y (self.max.y()).into(),
1707 [Negative, Negative, Zero] =>
1708 center.0.with_x (self.min.x()).with_y (self.min.y()).into(),
1709
1710 [Positive, Zero, Positive] =>
1711 center.0.with_x (self.max.x()).with_z (self.max.z()).into(),
1712 [Positive, Zero, Negative] =>
1713 center.0.with_x (self.max.x()).with_z (self.min.z()).into(),
1714 [Negative, Zero, Positive] =>
1715 center.0.with_x (self.min.x()).with_z (self.max.z()).into(),
1716 [Negative, Zero, Negative] =>
1717 center.0.with_x (self.min.x()).with_z (self.min.z()).into(),
1718
1719 [Zero, Positive, Positive] =>
1720 center.0.with_y (self.max.y()).with_z (self.max.z()).into(),
1721 [Zero, Positive, Negative] =>
1722 center.0.with_y (self.max.y()).with_z (self.min.z()).into(),
1723 [Zero, Negative, Positive] =>
1724 center.0.with_y (self.min.y()).with_z (self.max.z()).into(),
1725 [Zero, Negative, Negative] =>
1726 center.0.with_y (self.min.y()).with_z (self.min.z()).into(),
1727 [_, _, _] => unreachable!()
1728 }
1729 };
1730 (out, out.0.dot (*direction))
1731 }
1732}
1733
1734impl <S> std::ops::Add <Vector3 <S>> for Aabb3 <S> where
1735 S : Field,
1736 Self : Primitive <S, Point3 <S>>
1737{
1738 type Output = Self;
1739 #[expect(clippy::renamed_function_params)]
1740 fn add (mut self, displacement : Vector3 <S>) -> Self {
1741 self.translate (displacement);
1742 self
1743 }
1744}
1745
1746impl <S> std::ops::Sub <Vector3 <S>> for Aabb3 <S> where
1747 S : Field,
1748 Self : Primitive <S, Point3 <S>>
1749{
1750 type Output = Self;
1751 #[expect(clippy::renamed_function_params)]
1752 fn sub (mut self, displacement : Vector3 <S>) -> Self {
1753 self.translate (-displacement);
1754 self
1755 }
1756}
1757
1758impl_numcast_fields!(Obb2, center, extents, orientation);
1759impl <S : OrderedRing> Obb2 <S> {
1760 #[inline]
1762 pub const fn new (
1763 center : Point2 <S>,
1764 extents : Vector2 <Positive <S>>,
1765 orientation : AngleWrapped <S>
1766 ) -> Self {
1767 Obb2 { center, extents, orientation }
1768 }
1769 pub fn containing_points_with_orientation (
1773 points : &[Point2 <S>], orientation : AngleWrapped <S>
1774 ) -> Option <Self> where
1775 S : Real + num::real::Real + MaybeSerDes
1776 {
1777 let [x, y] = Rotation2::from_angle (orientation.angle()).cols
1778 .map (NonZero2::unchecked).into_array();
1779 let min_dot_x = -support_2 (points, -x).1;
1780 let max_dot_x = support_2 (points, x).1;
1781 let min_dot_y = -support_2 (points, -y).1;
1782 let max_dot_y = support_2 (points, y).1;
1783 let center = Point2 (
1784 (*x * min_dot_x + *x * max_dot_x) * S::half() +
1785 (*y * min_dot_y + *y * max_dot_y) * S::half());
1786 let extents = vector2 (
1787 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
1788 Positive::new (S::half() * (max_dot_y - min_dot_y))?);
1789 Some (Obb2 { center, extents, orientation })
1790 }
1791 pub fn containing_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
1809 let hull = Hull2::from_points (points)?;
1810 Self::containing (&hull)
1811 }
1812 pub fn containing (hull : &Hull2 <S>) -> Option <Self> where S : Real {
1831 let points = hull.points();
1832 if points.len() < 3 {
1833 return None
1834 }
1835 let mut visited = vec![false; points.len()];
1836 #[expect(clippy::cast_possible_truncation)]
1837 let mut min_rect = smallest_rect (points.len() as u32 - 1, 0, hull);
1838 visited[min_rect.indices[0] as usize] = true;
1839 let mut rect = min_rect.clone();
1841 for _ in 0..points.len() {
1842 let mut angles = [(S::zero(), u32::MAX); 4];
1843 let mut nangles = u32::MAX;
1844 if !compute_angles (hull, &rect, &mut angles, &mut nangles) {
1845 break
1846 }
1847 let sorted = sort_angles (&angles, nangles);
1848 if !update_support (&angles, nangles, &sorted, hull, &mut visited, &mut rect) {
1849 break
1850 }
1851 if rect.area < min_rect.area {
1852 min_rect = rect.clone();
1853 }
1854 }
1855 let sum = [
1857 points[min_rect.indices[1] as usize].0 + points[min_rect.indices[3] as usize].0,
1858 points[min_rect.indices[2] as usize].0 + points[min_rect.indices[0] as usize].0
1859 ];
1860 let diff = [
1861 points[min_rect.indices[1] as usize].0 - points[min_rect.indices[3] as usize].0,
1862 points[min_rect.indices[2] as usize].0 - points[min_rect.indices[0] as usize].0
1863 ];
1864 let obb = {
1865 let center = Point2::from ((min_rect.edge * min_rect.edge.dot (sum[0]) +
1866 min_rect.perp * min_rect.perp.dot (sum[1]))
1867 * S::half() / min_rect.edge_len_squared);
1868 let extents = {
1869 let extent_x = Positive::unchecked (S::sqrt (
1870 (S::half() * min_rect.edge.dot (diff[0])).squared()
1871 / min_rect.edge_len_squared));
1872 let extent_y = Positive::unchecked (S::sqrt (
1873 (S::half() * min_rect.perp.dot (diff[1])).squared()
1874 / min_rect.edge_len_squared));
1875 vector2 (extent_x, extent_y)
1876 };
1877 let orientation =
1878 AngleWrapped::wrap (Rad (min_rect.edge.y.atan2 (min_rect.edge.x)));
1879 Obb2 { center, extents, orientation }
1880 };
1881 fn compute_angles <S : Real> (
1882 hull : &Hull2 <S>,
1883 rect : &Rect <S>,
1884 angles : &mut [(S, u32); 4],
1885 nangles : &mut u32
1886 ) -> bool {
1887 *nangles = 0;
1888 let points = hull.points();
1889 let mut k0 = 3;
1890 let mut k1 = 0;
1891 while k1 < 4 {
1892 if rect.indices[k0] != rect.indices[k1] {
1893 let d = {
1894 let mut d = if k0 & 1 != 0 {
1895 rect.perp
1896 } else {
1897 rect.edge
1898 };
1899 if k0 & 2 != 0 {
1900 d = -d;
1901 }
1902 d
1903 };
1904 let j0 = rect.indices[k0];
1905 let mut j1 = j0 + 1;
1906 #[expect(clippy::cast_possible_truncation)]
1907 if j1 == points.len() as u32 {
1908 j1 = 0;
1909 }
1910 let e = points[j1 as usize] - points[j0 as usize];
1911 let dp = d.dot ([e.y, -e.x].into());
1912 let e_lensq = e.magnitude_squared();
1913 let sin_theta_squared = (dp * dp) / e_lensq;
1914 angles[*nangles as usize] =
1915 #[expect(clippy::cast_possible_truncation)]
1916 (sin_theta_squared, k0 as u32);
1917 *nangles += 1;
1918 }
1919 k0 = k1;
1920 k1 += 1;
1921 }
1922 *nangles > 0
1923 }
1924 fn sort_angles <S : PartialOrd> (angles : &[(S, u32); 4], nangles : u32)
1925 -> [u32; 4]
1926 {
1927 let mut sorted = [0u32, 1, 2, 3];
1928 match nangles {
1929 0 | 1 => {}
1930 2 => if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1931 sorted.swap (0, 1)
1932 }
1933 3 => {
1934 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1935 sorted.swap (0, 1)
1936 }
1937 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1938 sorted.swap (0, 2)
1939 }
1940 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1941 sorted.swap (1, 2)
1942 }
1943 }
1944 4 => {
1945 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1946 sorted.swap (0, 1)
1947 }
1948 if angles[sorted[2] as usize].0 > angles[sorted[3] as usize].0 {
1949 sorted.swap (2, 3)
1950 }
1951 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1952 sorted.swap (0, 2)
1953 }
1954 if angles[sorted[1] as usize].0 > angles[sorted[3] as usize].0 {
1955 sorted.swap (1, 3)
1956 }
1957 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1958 sorted.swap (1, 2)
1959 }
1960 }
1961 _ => unreachable!()
1962 }
1963 sorted
1964 }
1965 fn update_support <S : Real> (
1966 angles : &[(S, u32); 4],
1967 nangles : u32,
1968 sorted : &[u32; 4],
1969 hull : &Hull2 <S>,
1970 visited : &mut [bool],
1971 rect : &mut Rect <S>
1972 ) -> bool {
1973 let points = hull.points();
1974 let min_angle = angles[sorted[0] as usize];
1975 for k in 0..nangles as usize {
1976 let (angle, index) = angles[sorted[k] as usize];
1977 if angle == min_angle.0 {
1978 rect.indices[index as usize] += 1;
1979 #[expect(clippy::cast_possible_truncation)]
1980 if rect.indices[index as usize] == points.len() as u32 {
1981 rect.indices[index as usize] = 0;
1982 }
1983 }
1984 }
1985 let bottom = rect.indices[min_angle.1 as usize];
1986 if visited[bottom as usize] {
1987 return false
1988 }
1989 visited[bottom as usize] = true;
1990 let mut next_index = [u32::MAX; 4];
1991 for k in 0u32..4 {
1992 next_index[k as usize] = rect.indices[((min_angle.1 + k) % 4) as usize];
1993 }
1994 rect.indices = next_index;
1995 let j1 = rect.indices[0] as usize;
1996 let j0 = if j1 == 0 {
1997 points.len() - 1
1998 } else {
1999 j1 - 1
2000 };
2001 rect.edge = points[j1] - points[j0];
2002 rect.perp = vector2 (-rect.edge.y, rect.edge.x);
2003 let edge_len_squared = rect.edge.magnitude_squared();
2004 let diff = [
2005 points[rect.indices[1] as usize] - points[rect.indices[3] as usize],
2006 points[rect.indices[2] as usize] - points[rect.indices[0] as usize]
2007 ];
2008 rect.area = rect.edge.dot (diff[0]) * rect.perp.dot (diff[1]) / edge_len_squared;
2009 true
2010 }
2011 Some (obb)
2012 }
2013 #[inline]
2014 pub fn dimensions (self) -> Vector2 <Positive <S>> {
2015 self.extents * Positive::unchecked (S::two())
2016 }
2017 #[inline]
2019 pub fn width (self) -> Positive <S> {
2020 self.extents.x * Positive::unchecked (S::two())
2021 }
2022 #[inline]
2024 pub fn height (self) -> Positive <S> {
2025 self.extents.y * Positive::unchecked (S::two())
2026 }
2027 #[inline]
2028 pub fn area (self) -> Positive <S> {
2029 self.width() * self.height()
2030 }
2031 #[inline]
2032 pub fn corners (self) -> [Point2 <S>; 4] where
2033 S : Real + num::real::Real + MaybeSerDes
2034 {
2035 let mut points = [Point2::origin(); 4];
2036 for i in 0..2 {
2037 for j in 0..2 {
2038 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2039 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2040 points[i * 2 + j] = [*self.extents.x * sign_x, *self.extents.y * sign_y].into();
2041 }
2042 }
2043 let rotation = Rotation2::from_angle (self.orientation.angle());
2044 points.map (|point| rotation.rotate (point) + self.center.0)
2045 }
2046
2047 pub fn aabb (self) -> Aabb2 <S> where S : Real + num::real::Real + MaybeSerDes {
2048 Aabb2::containing (&self.corners()).unwrap()
2049 }
2050
2051 pub fn contains (self, point : Point2 <S>) -> bool where
2064 S : Real + num::real::Real + MaybeSerDes
2065 {
2066 let p = point - self.center.0;
2068 let p = Rotation2::from_angle (-self.orientation.angle()).rotate (p);
2070 p.x().abs() < *self.extents.x && p.y().abs() < *self.extents.y
2071 }
2072
2073 pub fn dilate (mut self, amount : S) -> Self {
2103 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2104 self
2105 }
2106}
2107impl <S : Real> From <Aabb2 <S>> for Obb2 <S> {
2108 fn from (aabb : Aabb2 <S>) -> Self {
2121 use num::Zero;
2122 let center = aabb.center();
2123 let extents = vector2 (
2124 Positive::noisy (*aabb.width() / S::two()),
2125 Positive::noisy (*aabb.height() / S::two()));
2126 let orientation = AngleWrapped::zero();
2127 Obb2::new (center, extents, orientation)
2128 }
2129}
2130impl <S> Primitive <S, Point2 <S>> for Obb2 <S> where
2131 S : Real + num::real::Real + MaybeSerDes
2132{
2133 fn translate (&mut self, displacement : Vector2 <S>) {
2134 self.center += displacement;
2135 }
2136 fn scale (&mut self, scale : Positive <S>) {
2137 self.extents *= scale;
2138 }
2139 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2140 use num::Inv;
2141 let rotation = Rotation2::from_angle (self.orientation.angle());
2142 let aabb = Aabb2::with_minmax_unchecked (
2143 self.center - self.extents.map (|e| *e),
2144 self.center + self.extents.map (|e| *e));
2145 let aabb_direction = rotation.inv() * direction;
2146 let (aabb_support, _) = aabb.support (aabb_direction);
2147 let out = rotation.rotate_around (aabb_support, self.center);
2148 (out, out.0.dot (*direction))
2149 }
2150}
2151
2152impl_numcast_fields!(Obb3, center, extents, orientation);
2153impl <S : OrderedRing> Obb3 <S> {
2154 #[inline]
2156 pub const fn new (
2157 center : Point3 <S>,
2158 extents : Vector3 <Positive <S>>,
2159 orientation : Angles3 <S>
2160 ) -> Self {
2161 Obb3 { center, extents, orientation }
2162 }
2163 pub fn containing_points_with_orientation (
2167 points : &[Point3 <S>], orientation : Angles3 <S>
2168 ) -> Option <Self> where
2169 S : Real + num::real::Real + MaybeSerDes
2170 {
2171 let [x, y, z] = Rotation3::from (orientation).cols.map (NonZero3::unchecked)
2172 .into_array();
2173 let min_dot_x = -support_3 (points, -x).1;
2174 let max_dot_x = support_3 (points, x).1;
2175 let min_dot_y = -support_3 (points, -y).1;
2176 let max_dot_y = support_3 (points, y).1;
2177 let min_dot_z = -support_3 (points, -z).1;
2178 let max_dot_z = support_3 (points, z).1;
2179 let center = Point3 (
2180 (*x * min_dot_x + *x * max_dot_x) * S::half() +
2181 (*y * min_dot_y + *y * max_dot_y) * S::half() +
2182 (*z * min_dot_z + *z * max_dot_z) * S::half());
2183 let extents = vector3 (
2184 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
2185 Positive::new (S::half() * (max_dot_y - min_dot_y))?,
2186 Positive::new (S::half() * (max_dot_z - min_dot_z))?);
2187 Some (Obb3 { center, extents, orientation })
2188 }
2189 pub fn containing_points_approx (points : &[Point3 <S>]) -> Option <Self> where
2206 S : Real + num::real::Real + approx::RelativeEq <Epsilon=S> + MaybeSerDes
2207 {
2208 if points.len() < 4 {
2209 return None
2210 }
2211 let (hull, mesh) = Hull3::from_points_with_mesh (points)?;
2212 Self::containing_approx (&hull, &mesh)
2213 }
2214 pub fn containing_approx (hull : &Hull3 <S>, mesh : &VertexEdgeTriangleMesh)
2233 -> Option <Self>
2234 where S : Real + num::NumCast + approx::RelativeEq <Epsilon=S> + MaybeSerDes {
2235 use num::Inv;
2238 if hull.points().len() < 4 {
2239 return None
2240 }
2241 let mut min_obb : Obb3 <S> = Aabb3::containing (hull.points()).unwrap().into();
2242 let mut min_vol = min_obb.volume();
2243 for triangle in mesh.triangles().values() {
2244 let [a, b, c] = triangle.vertices().map (|i| hull.points()[i as usize]);
2245 let mut u = b - a;
2246 let mut v = c - a;
2247 let mut w = u.cross (v);
2248 v = w.cross (u);
2249 u = *u.normalize();
2250 v = *v.normalize();
2251 w = *w.normalize();
2252 let m = Matrix3::from_row_arrays (std::array::from_fn (|i| [u[i], v[i], w[i]]));
2253 let m_rot = Rotation3::new_approx (m).unwrap();
2254 let rot_inv = m_rot.inv();
2255 let center = a;
2256 let rotated = hull.points().iter().copied()
2257 .map (|p| rot_inv.rotate_around (p, center))
2258 .collect::<Vec <_>>();
2259 let aabb = Aabb3::containing (rotated.as_slice()).unwrap();
2260 let aabb_volume = aabb.volume();
2261 if approx::abs_diff_eq!(*aabb_volume, S::zero()) {
2262 return None
2264 }
2265 let volume = Positive::new (*aabb_volume).unwrap();
2266 if volume < min_vol {
2267 min_vol = volume;
2268 min_obb = aabb.into();
2269 min_obb.orientation = m_rot.into();
2270 min_obb.center = m_rot.rotate_around (min_obb.center, center);
2271 }
2272 }
2273 Some (min_obb)
2274 }
2275 pub fn containing_approx_pca (hull : &Hull3 <S>) -> Option <Self> where
2294 S : Real + num::real::Real + num::NumCast + approx::RelativeEq <Epsilon=S>
2295 + MaybeSerDes
2296 {
2297 if hull.points().len() < 4 {
2298 return None
2299 }
2300 let [p1, p2, p3] = &hull.points()[..3] else { unreachable!() };
2304 let mut coplanar = true;
2305 for p in &hull.points()[3..] {
2306 if !coplanar_3 (*p1, *p2, *p3, *p) {
2307 coplanar = false;
2308 break
2309 }
2310 }
2311 if coplanar {
2312 return None
2313 }
2314 let orientation = data::pca_3 (hull.points()).into();
2316 Some (Self::containing_points_with_orientation (hull.points(), orientation).unwrap())
2317 }
2318 #[inline]
2319 pub fn dimensions (self) -> Vector3 <Positive <S>> {
2320 self.extents * Positive::unchecked (S::two())
2321 }
2322 #[inline]
2324 pub fn width (self) -> Positive <S> {
2325 self.extents.x * Positive::unchecked (S::two())
2326 }
2327 #[inline]
2329 pub fn depth (self) -> Positive <S> {
2330 self.extents.y * Positive::unchecked (S::two())
2331 }
2332 #[inline]
2334 pub fn height (self) -> Positive <S> {
2335 self.extents.z * Positive::unchecked (S::two())
2336 }
2337 #[inline]
2338 pub fn volume (self) -> Positive <S> {
2339 self.width() * self.depth() * self.height()
2340 }
2341 #[inline]
2342 pub fn corners (self) -> [Point3 <S>; 8] where
2343 S : Real + num::real::Real + MaybeSerDes
2344 {
2345 let mut points = [Point3::origin(); 8];
2346 for i in 0..2 {
2347 for j in 0..2 {
2348 for k in 0..2 {
2349 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2350 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2351 let sign_z = if k % 2 == 0 { -S::one() } else { S::one() };
2352 points[i * 4 + j * 2 + k] = [
2353 *self.extents.x * sign_x,
2354 *self.extents.y * sign_y,
2355 *self.extents.z * sign_z
2356 ].into();
2357 }
2358 }
2359 }
2360 let rotation = Rotation3::from (self.orientation);
2361 points.map (|point| rotation.rotate (point) + self.center.0)
2362 }
2363
2364 pub fn aabb (self) -> Aabb3 <S> where S : Real + num::real::Real + MaybeSerDes {
2365 Aabb3::containing (&self.corners()).unwrap()
2366 }
2367
2368 pub fn contains (self, point : Point3 <S>) -> bool where
2370 S : Real + num::real::Real + MaybeSerDes
2371 {
2372 use num::Inv;
2373 let p = point - self.center.0;
2375 let p = Rotation3::from (self.orientation).inv().rotate (p);
2377 p.x().abs() < *self.extents.x &&
2378 p.y().abs() < *self.extents.y &&
2379 p.z().abs() < *self.extents.z
2380 }
2381
2382 pub fn dilate (mut self, amount : S) -> Self {
2397 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2398 self
2399 }
2400}
2401impl <S : Real> From <Aabb3 <S>> for Obb3 <S> {
2402 fn from (aabb : Aabb3 <S>) -> Self {
2415 let center = aabb.center();
2416 let extents = aabb.extents().map (|d| Positive::noisy (*d));
2417 let orientation = Angles3::zero();
2418 Obb3::new (center, extents, orientation)
2419 }
2420}
2421impl <S> Primitive <S, Point3 <S>> for Obb3 <S> where
2422 S : Real + num::real::Real + MaybeSerDes
2423{
2424 fn translate (&mut self, displacement : Vector3 <S>) {
2425 self.center += displacement;
2426 }
2427 fn scale (&mut self, scale : Positive <S>) {
2428 self.extents *= scale;
2429 }
2430 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2431 use num::Inv;
2432 let rotation = Rotation3::from (self.orientation);
2433 let aabb = Aabb3::with_minmax_unchecked (
2434 self.center - self.extents.map (|e| *e),
2435 self.center + self.extents.map (|e| *e));
2436 let aabb_direction = rotation.inv() * direction;
2437 let (aabb_support, _) = aabb.support (aabb_direction);
2438 let out = rotation.rotate_around (aabb_support, self.center);
2439 (out, out.0.dot (*direction))
2440 }
2441}
2442
2443impl_numcast_fields!(Capsule3, center, half_height, radius);
2444impl <S : OrderedRing> Capsule3 <S> {
2445 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2446 use shape::Aabb;
2447 let shape_aabb = shape::Capsule {
2448 radius: self.radius,
2449 half_height: self.half_height
2450 }.aabb();
2451 let center_vec = self.center.0;
2452 let min = shape_aabb.min() + center_vec;
2453 let max = shape_aabb.max() + center_vec;
2454 Aabb3::with_minmax_unchecked (min, max)
2455 }
2456 pub fn decompose (self) -> (Sphere3 <S>, Option <Cylinder3 <S>>, Sphere3 <S>) {
2458 let cylinder = if *self.half_height > S::zero() {
2459 Some (Cylinder3 {
2460 center: self.center,
2461 radius: self.radius,
2462 half_height: Positive::unchecked (*self.half_height)
2463 })
2464 } else {
2465 None
2466 };
2467 let upper_sphere = Sphere3 {
2468 center: self.center + (Vector3::unit_z() * *self.half_height),
2469 radius: self.radius
2470 };
2471 let lower_sphere = Sphere3 {
2472 center: self.center - (Vector3::unit_z() * *self.half_height),
2473 radius: self.radius
2474 };
2475 (upper_sphere, cylinder, lower_sphere)
2476 }
2477}
2478impl <S> Primitive <S, Point3 <S>> for Capsule3 <S> where
2479 S : Real + num::real::Real + MaybeSerDes
2480{
2481 fn translate (&mut self, displacement : Vector3 <S>) {
2482 self.center += displacement;
2483 }
2484 fn scale (&mut self, scale : Positive <S>) {
2485 self.half_height *= scale;
2486 self.radius *= scale;
2487 }
2488 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2489 let dir_unit = direction.normalized();
2490 let out = self.center + if direction.z > S::zero() {
2491 Vector3::unit_z() * *self.half_height + dir_unit * self.radius
2492 } else if direction.z < S::zero() {
2493 -Vector3::unit_z() * *self.half_height + dir_unit * self.radius
2494 } else {
2495 dir_unit * self.radius
2496 };
2497 (out, out.0.dot (*direction))
2498 }
2499}
2500
2501impl_numcast_fields!(Cylinder3, center, half_height, radius);
2502impl <S : OrderedRing> Cylinder3 <S> {
2503 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2504 use shape::Aabb;
2505 let shape_aabb = shape::Cylinder::unchecked (*self.radius, *self.half_height).aabb();
2506 let center_vec = self.center.0;
2507 let min = shape_aabb.min() + center_vec;
2508 let max = shape_aabb.max() + center_vec;
2509 Aabb3::with_minmax_unchecked (min, max)
2510 }
2511}
2512impl <S> Primitive <S, Point3 <S>> for Cylinder3 <S> where
2513 S : Real + num::real::Real + MaybeSerDes
2514{
2515 fn translate (&mut self, displacement : Vector3 <S>) {
2516 self.center += displacement;
2517 }
2518 fn scale (&mut self, scale : Positive <S>) {
2519 self.half_height *= scale;
2520 self.radius *= scale;
2521 }
2522 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2523 let out = self.center + if *direction == Vector3::unit_z() {
2524 Vector3::unit_z() * *self.half_height
2525 } else if *direction == -Vector3::unit_z() {
2526 -Vector3::unit_z() * *self.half_height
2527 } else if direction.z == S::zero() {
2528 direction.normalized() * self.radius
2529 } else if direction.z > S::zero() {
2530 Vector3::unit_z() * *self.half_height
2531 + vector3 (direction.x, direction.y, S::zero()).normalized() * self.radius
2532 } else {
2533 debug_assert!(direction.z < S::zero());
2534 -Vector3::unit_z() * *self.half_height
2535 + vector3 (direction.x, direction.y, S::zero()).normalized() * self.radius
2536 };
2537 (out, out.0.dot (*direction))
2538 }
2539}
2540
2541impl_numcast_fields!(Cone3, center, half_height, radius);
2542impl <S : OrderedRing> Cone3 <S> {
2543 pub fn height (self) -> Positive <S> {
2544 self.half_height * Positive::unchecked (S::two())
2545 }
2546 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2547 use shape::Aabb;
2548 let shape_aabb = shape::Cone {
2549 radius: self.radius,
2550 half_height: self.half_height
2551 }.aabb();
2552 let center_vec = self.center.0;
2553 let min = shape_aabb.min() + center_vec;
2554 let max = shape_aabb.max() + center_vec;
2555 Aabb3::with_minmax_unchecked (min, max)
2556 }
2557}
2558impl <S> Primitive <S, Point3 <S>> for Cone3 <S> where
2559 S : Real + num::real::Real + approx::RelativeEq + MaybeSerDes
2560{
2561 fn translate (&mut self, displacement : Vector3 <S>) {
2562 self.center += displacement;
2563 }
2564 fn scale (&mut self, scale : Positive <S>) {
2565 self.half_height *= scale;
2566 self.radius *= scale;
2567 }
2568 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2569 let top = || Vector3::unit_z() * *self.half_height;
2570 let bottom = || -top();
2571 let edge = || bottom()
2572 + vector3 (direction.x, direction.y, S::zero()).normalized() * self.radius;
2573 let out = self.center + if *direction == Vector3::unit_z() {
2574 top()
2575 } else if *direction == -Vector3::unit_z() {
2576 bottom()
2577 } else if direction.z <= S::zero() {
2578 edge()
2579 } else {
2580 let angle_dir =
2582 Trig::atan2 (direction.z, Sqrt::sqrt (S::one() - direction.z.squared()));
2583 debug_assert!(angle_dir > S::zero());
2584 let angle_cone = Trig::atan2 (-*self.height(), *self.radius);
2585 debug_assert!(angle_cone < S::zero());
2586 let pi_2 = S::pi() / S::two();
2587 let angle_total = angle_dir - angle_cone;
2588 if approx::relative_eq!(angle_total, pi_2) {
2589 (top() + edge()) * S::half()
2590 } else if angle_total > pi_2 {
2591 top()
2592 } else {
2593 debug_assert!(angle_total < pi_2);
2594 edge()
2595 }
2596 };
2597 (out, out.0.dot (*direction))
2598 }
2599}
2600
2601impl_line_ray_common!(Line2, Point2, Unit2, Line2, Ray2, S);
2602impl_numcast_fields!(Line2, base, direction);
2603impl <S : OrderedRing> Line2 <S> {
2604 #[inline]
2605 pub fn nearest_point (self, point : Point2 <S>) -> Line2Point <S> {
2606 project_point2_on_line2 (point, self)
2607 }
2608 #[inline]
2609 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2610 -> Option <(Line2Point <S>, Line2Point <S>)>
2611 {
2612 intersect::intersect_line2_aabb2 (self, aabb)
2613 }
2614 #[inline]
2615 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2616 -> Option <(Line2Point <S>, Line2Point <S>)>
2617 where S : Field + Sqrt {
2618 intersect::intersect_line2_sphere2 (self, sphere)
2619 }
2620}
2621impl <S> Primitive <S, Point2 <S>> for Line2 <S> where
2622 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2623{
2624 fn translate (&mut self, displacement : Vector2 <S>) {
2625 self.base += displacement;
2626 }
2627 #[expect(unused_variables)]
2628 fn scale (&mut self, scale : Positive <S>) {
2629 }
2631 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2632 let dot = self.direction.dot (*direction);
2633 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2634 self.base
2635 } else if dot > S::zero() {
2636 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2637 S::zero()
2638 } else {
2639 x * S::infinity()
2640 }))
2641 } else {
2642 debug_assert!(dot < S::zero());
2643 Point2 ((-self.direction).sigvec().map (|x| if x == S::zero() {
2644 S::zero()
2645 } else {
2646 x * S::infinity()
2647 }))
2648 };
2649 (out, out.0.dot (*direction))
2650 }
2651}
2652
2653impl_line_ray_common!(Ray2, Point2, Unit2, Line2, Line2, NonNegative <S>);
2654impl_numcast_fields!(Ray2, base, direction);
2655impl <S : OrderedRing> Ray2 <S> {
2656 pub fn nearest_point (self, point : Point2 <S>) -> Ray2Point <S> {
2657 use num::Zero;
2658 let (t, point) = Line2::from (self).nearest_point (point);
2659 if t < S::zero() {
2660 (NonNegative::zero(), self.base)
2661 } else {
2662 (NonNegative::unchecked (t), point)
2663 }
2664 }
2665 #[inline]
2666 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2667 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2668 {
2669 intersect::intersect_ray2_aabb2 (self, aabb)
2670 }
2671 #[inline]
2672 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2673 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2674 {
2675 intersect::intersect_ray2_sphere2 (self, sphere)
2676 }
2677}
2678impl <S> Primitive <S, Point2 <S>> for Ray2 <S> where
2679 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2680{
2681 fn translate (&mut self, displacement : Vector2 <S>) {
2682 self.base += displacement;
2683 }
2684 #[expect(unused_variables)]
2685 fn scale (&mut self, scale : Positive <S>) {
2686 }
2688 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2689 let dot = self.direction.dot (*direction);
2690 let out = if dot < S::default_epsilon() {
2691 self.base
2692 } else {
2693 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2694 S::zero()
2695 } else {
2696 x * S::infinity()
2697 }))
2698 };
2699 (out, out.0.dot (*direction))
2700 }
2701}
2702
2703impl_line_ray_common!(Line3, Point3, Unit3, Line3, Ray3, S);
2704impl_numcast_fields!(Line3, base, direction);
2705impl <S : OrderedRing> Line3 <S> {
2706 #[inline]
2707 pub fn nearest_point (self, point : Point3 <S>) -> Line3Point <S> {
2708 project_point3_on_line3 (point, self)
2709 }
2710 #[inline]
2711 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Line3Point <S>> where
2712 S : Field + approx::RelativeEq
2713 {
2714 intersect::intersect_line3_plane3 (self, plane)
2715 }
2716 #[inline]
2717 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2718 -> Option <(Line3Point <S>, Line3Point <S>)>
2719 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2720 intersect::intersect_line3_aabb3 (self, aabb)
2721 }
2722 #[inline]
2723 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2724 -> Option <(Line3Point <S>, Line3Point <S>)>
2725 where S : Field + Sqrt {
2726 intersect::intersect_line3_sphere3 (self, sphere)
2727 }
2728 #[inline]
2729 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2730 -> Option <Line3Point <S>>
2731 where S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2732 intersect::intersect_line3_triangle3 (self, triangle)
2733 }
2734}
2735impl <S> Primitive <S, Point3 <S>> for Line3 <S> where
2736 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2737{
2738 fn translate (&mut self, displacement : Vector3 <S>) {
2739 self.base += displacement;
2740 }
2741 #[expect(unused_variables)]
2742 fn scale (&mut self, scale : Positive <S>) {
2743 }
2745 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2746 let dot = self.direction.dot (*direction);
2747 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2748 self.base
2749 } else if dot > S::zero() {
2750 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2751 S::zero()
2752 } else {
2753 x * S::infinity()
2754 }))
2755 } else {
2756 debug_assert!(dot < S::zero());
2757 Point3 (-self.direction.sigvec().map (|x| if x == S::zero() {
2758 S::zero()
2759 } else {
2760 x * S::infinity()
2761 }))
2762 };
2763 (out, out.0.dot (*direction))
2764 }
2765}
2766
2767impl_line_ray_common!(Ray3, Point3, Unit3, Line3, Line3, NonNegative <S>);
2768impl_numcast_fields!(Ray3, base, direction);
2769impl <S : OrderedRing> Ray3 <S> {
2770 pub fn nearest_point (self, point : Point3 <S>) -> Ray3Point <S> {
2771 use num::Zero;
2772 let (t, point) = Line3::from (self).nearest_point (point);
2773 if t < S::zero() {
2774 (NonNegative::zero(), self.base)
2775 } else {
2776 (NonNegative::unchecked (t), point)
2777 }
2778 }
2779 #[inline]
2780 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Ray3Point <S>> where
2781 S : approx::RelativeEq
2782 {
2783 intersect::intersect_ray3_plane3 (self, plane)
2784 }
2785 #[inline]
2786 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2787 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2788 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2789 intersect::intersect_ray3_aabb3 (self, aabb)
2790 }
2791 #[inline]
2792 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2793 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2794 {
2795 intersect::intersect_ray3_sphere3 (self, sphere)
2796 }
2797 #[inline]
2798 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2799 -> Option <Ray3Point <S>>
2800 where S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2801 intersect::intersect_ray3_triangle3 (self, triangle)
2802 }
2803}
2804impl <S> Primitive <S, Point3 <S>> for Ray3 <S> where
2805 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2806{
2807 fn translate (&mut self, displacement : Vector3 <S>) {
2808 self.base += displacement;
2809 }
2810 #[expect(unused_variables)]
2811 fn scale (&mut self, scale : Positive <S>) {
2812 }
2814 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2815 let dot = self.direction.dot (*direction);
2816 let out = if dot < S::default_epsilon() {
2817 self.base
2818 } else {
2819 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2820 S::zero()
2821 } else {
2822 x * S::infinity()
2823 }))
2824 };
2825 (out, out.0.dot (*direction))
2826 }
2827}
2828
2829impl_numcast_fields!(Plane3, base, normal);
2830impl <S> Plane3 <S> {
2831 #[inline]
2833 pub const fn new (base : Point3 <S>, normal : Unit3 <S>) -> Self {
2834 Plane3 { base, normal }
2835 }
2836}
2837impl <S : Real> Default for Plane3 <S> {
2838 fn default() -> Self {
2839 Plane3 {
2840 base: Point3::origin(),
2841 normal: Unit3::axis_z()
2842 }
2843 }
2844}
2845impl <S> Primitive <S, Point3 <S>> for Plane3 <S> where
2846 S : Real + num::float::FloatCore + approx::RelativeEq
2847{
2848 fn translate (&mut self, displacement : Vector3 <S>) {
2849 self.base += displacement;
2850 }
2851 #[expect(unused_variables)]
2852 fn scale (&mut self, scale : Positive <S>) {
2853 }
2855 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2856 let dot = self.normal.dot (*direction);
2857 let out = if approx::relative_eq!(dot, S::one()) {
2858 self.base
2859 } else {
2860 let projected =
2861 project_point3_on_plane3 (Point3 (self.base.0 + *direction), *self).0;
2862 Point3 (projected.sigvec().map (|x| if x == S::zero() {
2863 S::zero()
2864 } else {
2865 x * S::infinity()
2866 }))
2867 };
2868 (out, out.0.dot (*direction))
2869 }
2870}
2871
2872impl_numcast_fields!(Sphere2, center, radius);
2873impl <S : OrderedRing> Sphere2 <S> {
2874 #[inline]
2876 pub fn unit() -> Self {
2877 Sphere2 {
2878 center: Point2::origin(),
2879 radius: num::One::one()
2880 }
2881 }
2882 #[inline]
2884 pub fn intersects (self, other : Self) -> bool {
2885 intersect::overlaps_sphere2_sphere2 (self, other)
2886 }
2887}
2888impl <S> Primitive <S, Point2 <S>> for Sphere2 <S> where
2889 S : Real + num::real::Real + MaybeSerDes
2890{
2891 fn translate (&mut self, displacement : Vector2 <S>) {
2892 self.center += displacement;
2893 }
2894 fn scale (&mut self, scale : Positive <S>) {
2895 self.radius *= scale;
2896 }
2897 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2898 let out = self.center + direction.normalized() * self.radius;
2899 (out, out.0.dot (*direction))
2900 }
2901}
2902
2903impl_numcast_fields!(Sphere3, center, radius);
2904impl <S : OrderedRing> Sphere3 <S> {
2905 #[inline]
2907 pub fn unit() -> Self where S : Field {
2908 Sphere3 {
2909 center: Point3::origin(),
2910 radius: num::One::one()
2911 }
2912 }
2913 #[inline]
2915 pub fn intersects (self, other : Self) -> bool {
2916 intersect::overlaps_sphere3_sphere3 (self, other)
2917 }
2918}
2919impl <S> Primitive <S, Point3 <S>> for Sphere3 <S> where
2920 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes
2921{
2922 fn translate (&mut self, displacement : Vector3 <S>) {
2923 self.center += displacement;
2924 }
2925 fn scale (&mut self, scale : Positive <S>) {
2926 self.radius *= scale;
2927 }
2928 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2929 let out = self.center + direction.normalized() * self.radius;
2930 (out, out.0.dot (*direction))
2931 }
2932}
2933
2934#[cfg(test)]
2939mod tests {
2940 use approx::{assert_relative_eq, assert_ulps_eq};
2941 use rand;
2942 use rand_distr;
2943 use rand_xorshift;
2944 use sorted_vec::partial::SortedSet;
2945 use super::*;
2946
2947 #[test]
2948 fn colinear_2d() {
2949 use rand::{RngExt, SeedableRng};
2950 use rand_xorshift::XorShiftRng;
2951 use rand_distr::Distribution;
2952 macro test ($t:ty) {
2953 let mut rng = XorShiftRng::seed_from_u64 (0);
2954 let normal = rand_distr::StandardNormal;
2955 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
2956 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
2957 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
2958 for _ in 0..100000 {
2960 let dir = Unit2::<$t>::normalize ([randn (&mut rng), randn (&mut rng)].into());
2961 let base = point2 (randf (&mut rng), randf (&mut rng));
2962 let line = Line2::new (base, dir);
2963 let p1 = line.point (randf (&mut rng));
2964 let p2 = line.point (randf (&mut rng));
2965 let p3 = line.point (randf (&mut rng));
2966 assert!(colinear_2 (p1, p2, p3));
2967 }
2968 for _ in 0..100000 {
2970 let p1 = point2 (randf (&mut rng), randf (&mut rng));
2971 let v = p1.0;
2972 let len = 0.1 + randf (&mut rng).abs();
2973 let angle = Deg (rng.random_range (-180.0..180.0)).into();
2974 let rotation = Rotation2::from_angle (angle);
2975 let p2 = p1 + rotation * (v.normalized() * len);
2976 let v = p2 - p1;
2977 let len = 0.1 + randf (&mut rng).abs();
2978 let mut angle = rng.random_range (5.0..175.0);
2979 if rng.random_bool (0.5) {
2980 angle = -angle;
2981 }
2982 let rotation = Rotation2::from_angle (Deg (angle).into());
2983 let p3 = p2 + rotation * v * len;
2984 assert!(!colinear_2 (p1, p2, p3));
2985 }
2986 }
2987 test!(f32);
2988 test!(f64);
2989 }
2990
2991 #[test]
2992 fn colinear_3d() {
2993 use rand::{RngExt, SeedableRng};
2994 use rand_xorshift::XorShiftRng;
2995 use rand_distr::Distribution;
2996 macro test ($t:ty) {
2997 let mut rng = XorShiftRng::seed_from_u64 (0);
2998 let normal = rand_distr::StandardNormal;
2999 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3000 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3001 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3002 for _ in 0..50000 {
3004 let dir = Unit3::<$t>::normalize (
3005 [randn (&mut rng), randn (&mut rng), randn (&mut rng)].into());
3006 let base = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3007 let line = Line3::new (base, dir);
3008 let p1 = line.point (randf (&mut rng));
3009 let p2 = line.point (randf (&mut rng));
3010 let p3 = line.point (randf (&mut rng));
3011 assert!(colinear_3 (p1, p2, p3));
3012 }
3013 for _ in 0..50000 {
3015 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3016 let v = p1.0;
3017 let len = 0.1 + randf (&mut rng).abs();
3018 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3019 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3020 let rotation = Rotation3::from_axis_angle (axis, angle);
3021 let p2 = p1 + rotation * (v.normalized() * len);
3022 let v = p2 - p1;
3023 let len = 0.1 + randf (&mut rng).abs();
3024 let axis = loop {
3025 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3026 if axis.normalized().cross (v.normalized()).magnitude() > 0.1 {
3027 break axis
3028 }
3029 };
3030 let mut angle = rng.random_range (5.0..175.0);
3031 if rng.random_bool (0.5) {
3032 angle = -angle;
3033 }
3034 let rotation = Rotation3::from_axis_angle (axis, Deg (angle).into());
3035 let p3 = p2 + rotation * v * len;
3036 assert!(!colinear_3 (p1, p2, p3));
3037 }
3038 }
3039 test!(f32);
3040 test!(f64);
3041 }
3042
3043 #[test]
3044 fn coplanar_3d() {
3045 use rand::SeedableRng;
3046 use rand_xorshift::XorShiftRng;
3047 use rand_distr::Distribution;
3048 macro test ($t:ty) {
3049 let mut rng = XorShiftRng::seed_from_u64 (0);
3050 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3051 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3052 for _ in 0..50000 {
3054 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3055 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3056 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3057 let s = randf (&mut rng);
3058 let t = randf (&mut rng);
3059 let p4 = p1 + (p2 - p1) * s + (p3 - p1) * t;
3060 assert!(coplanar_3::<$t> (p1, p2, p3, p4));
3061 }
3062 for _ in 0..50000 {
3064 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3065 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3066 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3067 let s = randf (&mut rng);
3068 let t = randf (&mut rng);
3069 let e1 = p2 - p1;
3070 let e2 = p3 - p1;
3071 let n = e1.cross (e2).normalized();
3072 let rn = randf (&mut rng);
3073 let pnew = p1 + e1 * s + e2 * t;
3074 let n_len = (0.3 * (pnew - p1).magnitude()).mul_add (rn.signum(), rn);
3075 let p4 = pnew + n * n_len;
3077 assert!(!coplanar_3::<$t> (p1, p2, p3, p4));
3078 }
3079 }
3080 test!(f32);
3081 test!(f64);
3082 }
3083
3084 #[test]
3085 fn triangle_area_squared() {
3086 assert_relative_eq!(
3087 81.0,
3088 *triangle_3d_area_squared (
3089 [-2.0, -1.0, 0.0].into(),
3090 [ 1.0, -1.0, 0.0].into(),
3091 [ 1.0, 5.0, 0.0].into())
3092 );
3093 assert_relative_eq!(
3094 20.25,
3095 *triangle_3d_area_squared (
3096 [-2.0, -1.0, 0.0].into(),
3097 [ 1.0, -1.0, 0.0].into(),
3098 [ 1.0, 2.0, 0.0].into())
3099 );
3100 assert_relative_eq!(
3101 20.25,
3102 *triangle_3d_area_squared (
3103 [ 1.0, -1.0, 0.0].into(),
3104 [-2.0, -1.0, 0.0].into(),
3105 [ 1.0, 2.0, 0.0].into())
3106 );
3107 assert_relative_eq!(
3108 20.25,
3109 *triangle_3d_area_squared (
3110 [ 1.0, -1.0, 0.0].into(),
3111 [ 1.0, 2.0, 0.0].into(),
3112 [-2.0, -1.0, 0.0].into())
3113 );
3114 }
3115
3116 #[test]
3117 fn project_point_on_line_2d() {
3118 use super::project_point2_on_line2;
3119 use Unit2;
3120 let point : Point2 <f64> = [2.0, 2.0].into();
3121 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_x());
3122 assert_eq!(project_point2_on_line2 (point, line).1, [2.0, 0.0].into());
3123 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_y());
3124 assert_eq!(project_point2_on_line2 (point, line).1, [0.0, 2.0].into());
3125 let point : Point2 <f64> = [0.0, 1.0].into();
3126 let line = Line2::<f64>::new (
3127 [0.0, -1.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3128 assert_relative_eq!(
3129 project_point2_on_line2 (point, line).1, [1.0, 0.0].into());
3130 let point : Point2 <f64> = [1.0, 3.0].into();
3132 let line_a = Line2::<f64>::new (
3133 [0.0, -1.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3134 let line_b = Line2::<f64>::new (
3135 [2.0, 0.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3136 assert_relative_eq!(
3137 project_point2_on_line2 (point, line_a).1,
3138 project_point2_on_line2 (point, line_b).1);
3139 let line_a = Line2::<f64>::new (
3140 [0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3141 let line_b = Line2::<f64>::new (
3142 [-2.0, -2.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3143 assert_ulps_eq!(
3144 project_point2_on_line2 (point, line_a).1,
3145 project_point2_on_line2 (point, line_b).1
3146 );
3147 assert_relative_eq!(
3148 project_point2_on_line2 (point, line_a).1,
3149 [2.0, 2.0].into());
3150 }
3151
3152 #[test]
3153 fn project_point_on_line_3d() {
3154 use Unit3;
3155 use super::project_point3_on_line3;
3156 let point : Point3 <f64> = [2.0, 2.0, 0.0].into();
3158 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
3159 assert_eq!(project_point3_on_line3 (point, line).1, [2.0, 0.0, 0.0].into());
3160 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
3161 assert_eq!(project_point3_on_line3 (point, line).1, [0.0, 2.0, 0.0].into());
3162 let point : Point3 <f64> = [0.0, 1.0, 0.0].into();
3163 let line = Line3::<f64>::new (
3164 [0.0, -1.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3165 assert_relative_eq!(
3166 project_point3_on_line3 (point, line).1, [1.0, 0.0, 0.0].into());
3167 let point : Point3 <f64> = [1.0, 3.0, 0.0].into();
3169 let line_a = Line3::<f64>::new (
3170 [0.0, -1.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3171 let line_b = Line3::<f64>::new (
3172 [2.0, 0.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3173 assert_relative_eq!(
3174 project_point3_on_line3 (point, line_a).1,
3175 project_point3_on_line3 (point, line_b).1);
3176 let line_a = Line3::<f64>::new (
3177 [0.0, 0.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3178 let line_b = Line3::<f64>::new (
3179 [2.0, 2.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3180 assert_relative_eq!(
3181 project_point3_on_line3 (point, line_a).1,
3182 project_point3_on_line3 (point, line_b).1);
3183 assert_relative_eq!(
3184 project_point3_on_line3 (point, line_a).1,
3185 [2.0, 2.0, 0.0].into());
3186 let point : Point3 <f64> = [0.0, 0.0, 2.0].into();
3188 let line_a = Line3::<f64>::new (
3189 [-4.0, -4.0, -4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3190 let line_b = Line3::<f64>::new (
3191 [4.0, 4.0, 4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3192 assert_relative_eq!(
3193 project_point3_on_line3 (point, line_a).1,
3194 project_point3_on_line3 (point, line_b).1,
3195 epsilon = 0.000_000_000_000_01
3196 );
3197 assert_relative_eq!(
3198 project_point3_on_line3 (point, line_a).1,
3199 [2.0/3.0, 2.0/3.0, 2.0/3.0].into(),
3200 epsilon = 0.000_000_000_000_01
3201 );
3202 }
3203
3204 #[test]
3205 fn smallest_rect() {
3206 use super::smallest_rect;
3207 let points : Vec <Point2 <f32>> = [
3209 [-3.0, -3.0],
3210 [ 3.0, -3.0],
3211 [ 3.0, 3.0],
3212 [ 0.0, 6.0],
3213 [-3.0, 3.0]
3214 ].map (Point2::from).into_iter().collect();
3215 let hull = Hull2::from_points (points.as_slice()).unwrap();
3216 assert_eq!(hull.points(), points);
3217 let rect = smallest_rect (0, 1, &hull);
3218 assert_eq!(rect.area, 54.0);
3219 let points : Vec <Point2 <f32>> = [
3221 [-1.0, -4.0],
3222 [ 2.0, 2.0],
3223 [-4.0, -1.0]
3224 ].map (Point2::from).into_iter().collect();
3225 let hull = Hull2::from_points (points.as_slice()).unwrap();
3226 assert_eq!(hull.points(), points);
3227 let rect = smallest_rect (0, 1, &hull);
3228 assert_eq!(rect.area, 27.0);
3229 }
3230
3231 #[test]
3232 fn capsule3() {
3233 use num::One;
3234 let capsule : Capsule3 <f32> = Capsule3 {
3235 center: Point3::origin(),
3236 radius: Positive::noisy (2.0),
3237 half_height: Positive::one()
3238 };
3239 let support = capsule.support (Unit3::axis_z().into()).0;
3240 assert_eq!(support, point3 (0.0, 0.0, 3.0));
3241 let support = capsule.support (Unit3::axis_x().into()).0;
3242 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3243 let support = capsule.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3244 assert_eq!(support,
3245 point3 (0.0, 0.0, 1.0) + *Unit3::normalize (vector3 (1.0, 0.0, 1.0)) * 2.0);
3246 }
3247
3248 #[test]
3249 fn cylinder3() {
3250 use num::One;
3251 let cylinder : Cylinder3 <f32> = Cylinder3 {
3252 center: Point3::origin(),
3253 radius: Positive::noisy (2.0),
3254 half_height: Positive::one()
3255 };
3256 let support = cylinder.support (Unit3::axis_z().into()).0;
3257 assert_eq!(support, point3 (0.0, 0.0, 1.0));
3258 let support = cylinder.support (Unit3::axis_x().into()).0;
3259 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3260 let support = cylinder.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3261 assert_eq!(support, point3 (2.0, 0.0, 1.0));
3262 }
3263
3264 #[test]
3265 fn obb2() {
3266 let points : Vec <Point2 <f32>> = [
3267 [-1.0, -4.0],
3268 [ 2.0, 2.0],
3269 [-4.0, -1.0]
3270 ].map (Point2::from).into_iter().collect();
3271 let obb = Obb2::containing_points (&points).unwrap();
3272 let corners = obb.corners();
3273 assert_eq!(obb.center, [-0.25, -0.25].into());
3274 approx::assert_relative_eq!(point2 (-4.0, -1.0), corners[0], max_relative = 0.00001);
3275 approx::assert_relative_eq!(point2 ( 0.5, 3.5), corners[1], max_relative = 0.00001);
3276 approx::assert_relative_eq!(point2 (-1.0, -4.0), corners[2], max_relative = 0.00001);
3277 approx::assert_relative_eq!(point2 ( 3.5, 0.5), corners[3], max_relative = 0.00001);
3278 let points : Vec <Point2 <f32>> = [
3280 [-2.0, 0.0],
3281 [ 0.0, 2.0],
3282 [ 2.0, 0.0],
3283 [ 0.0, -2.0],
3284 ].map (Point2::from).into_iter().collect();
3285 let obb = Obb2::containing_points (&points).unwrap();
3286 let support = obb.support (Unit2::axis_y().into()).0;
3287 approx::assert_relative_eq!(support, point2 (0.0, 2.0), epsilon = 0.000_001);
3288 }
3289
3290 #[test]
3291 #[expect(clippy::suboptimal_flops)]
3292 fn obb3() {
3293 use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
3294 use approx::AbsDiffEq;
3295
3296 let points = [
3297 [ 1.0, -1.0, -1.0],
3298 [ 1.0, -1.0, 1.0],
3299 [ 1.0, 1.0, -1.0],
3300 [ 1.0, 1.0, 1.0],
3301 [-1.0, -1.0, -1.0],
3302 [-1.0, -1.0, 1.0],
3303 [-1.0, 1.0, -1.0],
3304 [-1.0, 1.0, 1.0]
3305 ].map (Point3::<f32>::from);
3306 let obb1 = Obb3::containing_points_with_orientation (&points, Angles3::zero())
3307 .unwrap();
3308 assert_eq!(
3309 SortedSet::from_unsorted (obb1.corners().to_vec()),
3310 SortedSet::from_unsorted (
3311 Aabb3::containing (&points).unwrap().corners().to_vec()));
3312 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3313 assert_eq!(obb1, obb2);
3314
3315 let rotation = Rotation3::from_angle_y (FRAC_PI_4.into());
3316 let points = points.map (|p| rotation.rotate (p));
3317 let obb1 = Obb3::containing_points_with_orientation (&points,
3318 Angles3::wrap (
3320 (3.0 * FRAC_PI_2).into(),
3321 (2.0 * PI - FRAC_PI_4).into(),
3322 FRAC_PI_2.into())
3323 ).unwrap();
3324 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3325 approx::assert_abs_diff_eq!(obb1.center, obb2.center);
3326 assert_eq!(obb1.orientation, obb2.orientation);
3327 approx::assert_abs_diff_eq!(*obb1.extents.x, *obb2.extents.x,
3328 epsilon=4.0 * f32::default_epsilon());
3329 approx::assert_abs_diff_eq!(*obb1.extents.y, *obb2.extents.y,
3330 epsilon=4.0 * f32::default_epsilon());
3331 approx::assert_abs_diff_eq!(*obb1.extents.z, *obb2.extents.z,
3332 epsilon=2.0 * f32::default_epsilon());
3333 let corners = obb2.corners();
3335 approx::assert_abs_diff_eq!(corners[0], points[7],
3336 epsilon=8.0 * f32::default_epsilon());
3337 approx::assert_abs_diff_eq!(corners[1], points[5],
3338 epsilon=8.0 * f32::default_epsilon());
3339 approx::assert_abs_diff_eq!(corners[2], points[3],
3340 epsilon=8.0 * f32::default_epsilon());
3341 approx::assert_abs_diff_eq!(corners[3], points[1],
3342 epsilon=8.0 * f32::default_epsilon());
3343 approx::assert_abs_diff_eq!(corners[4], points[6],
3344 epsilon=8.0 * f32::default_epsilon());
3345 approx::assert_abs_diff_eq!(corners[5], points[4],
3346 epsilon=8.0 * f32::default_epsilon());
3347 approx::assert_abs_diff_eq!(corners[6], points[2],
3348 epsilon=8.0 * f32::default_epsilon());
3349 approx::assert_abs_diff_eq!(corners[7], points[0],
3350 epsilon=8.0 * f32::default_epsilon());
3351
3352 let points : Vec <Point3 <f32>> = [
3354 [-2.0, 0.0, -2.0],
3355 [ 0.0, 2.0, -2.0],
3356 [ 2.0, 0.0, -2.0],
3357 [ 0.0, -2.0, -2.0],
3358 [-2.0, 0.0, 2.0],
3359 [ 0.0, 2.0, 2.0],
3360 [ 2.0, 0.0, 2.0],
3361 [ 0.0, -2.0, 2.0]
3362 ].map (Point3::from).into_iter().collect();
3363 let obb = Obb3::containing_points_approx (&points).unwrap();
3364 let support = obb.support (Unit3::axis_y().into()).0;
3365 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 0.0), epsilon = 0.000_001);
3366 let support = obb.support (NonZero3::noisy (vector3 (0.0, 1.0, 1.0))).0;
3367 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 2.0), epsilon = 0.000_001);
3368 }
3369
3370 #[test]
3371 fn support_aabb3() {
3372 let aabb = Aabb3::with_minmax (
3373 [-1.0, -2.0, -3.0].into(),
3374 [ 1.0, 2.0, 3.0].into()).unwrap();
3375 assert_eq!(
3376 [-1.0, -2.0, -3.0],
3377 aabb.support (NonZero3::noisy ([-1.0, -1.0, -1.0].into())).0.0.into_array());
3378 assert_eq!(
3379 [-1.0, -2.0, 3.0],
3380 aabb.support (NonZero3::noisy ([-1.0, -1.0, 1.0].into())).0.0.into_array());
3381 assert_eq!(
3382 [-1.0, 2.0, -3.0],
3383 aabb.support (NonZero3::noisy ([-1.0, 1.0, -1.0].into())).0.0.into_array());
3384 assert_eq!(
3385 [-1.0, 2.0, 3.0],
3386 aabb.support (NonZero3::noisy ([-1.0, 1.0, 1.0].into())).0.0.into_array());
3387 assert_eq!(
3388 [ 1.0, -2.0, -3.0],
3389 aabb.support (NonZero3::noisy ([ 1.0, -1.0, -1.0].into())).0.0.into_array());
3390 assert_eq!(
3391 [ 1.0, -2.0, 3.0],
3392 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 1.0].into())).0.0.into_array());
3393 assert_eq!(
3394 [ 1.0, 2.0, -3.0],
3395 aabb.support (NonZero3::noisy ([ 1.0, 1.0, -1.0].into())).0.0.into_array());
3396 assert_eq!(
3397 [ 1.0, 2.0, 3.0],
3398 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 1.0].into())).0.0.into_array());
3399
3400 assert_eq!(
3401 [-1.0, -2.0, 0.0],
3402 aabb.support (NonZero3::noisy ([-1.0, -1.0, 0.0].into())).0.0.into_array());
3403 assert_eq!(
3404 [-1.0, 2.0, 0.0],
3405 aabb.support (NonZero3::noisy ([-1.0, 1.0, 0.0].into())).0.0.into_array());
3406 assert_eq!(
3407 [ 1.0, -2.0, 0.0],
3408 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 0.0].into())).0.0.into_array());
3409 assert_eq!(
3410 [ 1.0, 2.0, 0.0],
3411 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 0.0].into())).0.0.into_array());
3412
3413 assert_eq!(
3414 [-1.0, 0.0, -3.0],
3415 aabb.support (NonZero3::noisy ([-1.0, 0.0, -1.0].into())).0.0.into_array());
3416 assert_eq!(
3417 [-1.0, 0.0, 3.0],
3418 aabb.support (NonZero3::noisy ([-1.0, 0.0, 1.0].into())).0.0.into_array());
3419 assert_eq!(
3420 [ 1.0, 0.0, -3.0],
3421 aabb.support (NonZero3::noisy ([ 1.0, 0.0, -1.0].into())).0.0.into_array());
3422 assert_eq!(
3423 [ 1.0, 0.0, 3.0],
3424 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 1.0].into())).0.0.into_array());
3425
3426 assert_eq!(
3427 [ 0.0, -2.0, -3.0],
3428 aabb.support (NonZero3::noisy ([ 0.0, -1.0, -1.0].into())).0.0.into_array());
3429 assert_eq!(
3430 [ 0.0, -2.0, 3.0],
3431 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 1.0].into())).0.0.into_array());
3432 assert_eq!(
3433 [ 0.0, 2.0, -3.0],
3434 aabb.support (NonZero3::noisy ([ 0.0, 1.0, -1.0].into())).0.0.into_array());
3435 assert_eq!(
3436 [ 0.0, 2.0, 3.0],
3437 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 1.0].into())).0.0.into_array());
3438
3439 assert_eq!(
3440 [-1.0, 0.0, 0.0],
3441 aabb.support (NonZero3::noisy ([-1.0, 0.0, 0.0].into())).0.0.into_array());
3442 assert_eq!(
3443 [ 1.0, 0.0, 0.0],
3444 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 0.0].into())).0.0.into_array());
3445 assert_eq!(
3446 [ 0.0, -2.0, 0.0],
3447 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 0.0].into())).0.0.into_array());
3448 assert_eq!(
3449 [ 0.0, 2.0, 0.0],
3450 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 0.0].into())).0.0.into_array());
3451 assert_eq!(
3452 [ 0.0, 0.0, -3.0],
3453 aabb.support (NonZero3::noisy ([ 0.0, 0.0, -1.0].into())).0.0.into_array());
3454 assert_eq!(
3455 [ 0.0, 0.0, 3.0],
3456 aabb.support (NonZero3::noisy ([ 0.0, 0.0, 1.0].into())).0.0.into_array());
3457 }
3458}