1use approx;
4use num_traits as num;
5use rand;
6
7use crate::*;
8use super::{intersect, shape};
9
10#[cfg(feature = "derive_serdes")]
11use serde::{Deserialize, Serialize};
12
13pub mod integer;
15
16mod hull;
17mod simplex;
18
19pub use self::hull::*;
20pub use self::simplex::{simplex2, simplex3, Simplex2, Simplex3};
21pub use simplex2::Segment as Segment2;
22pub use simplex3::Segment as Segment3;
23pub use simplex2::Triangle as Triangle2;
24pub use simplex3::Triangle as Triangle3;
25pub use simplex3::Tetrahedron as Tetrahedron3;
26
27pub trait Primitive <S, V> where
28 S : Field,
29 V : VectorSpace <S>
30{
31 fn translate (self, displacement : V) -> Self;
32 fn scale (self, scale : NonZero <S>) -> Self;
33}
34
35#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
41#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct Interval <S> {
43 min : S,
44 max : S
45}
46
47#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
67#[derive(Clone, Copy, Debug, Eq, PartialEq)]
68pub struct Aabb2 <S> {
69 min : Point2 <S>,
70 max : Point2 <S>
71}
72
73#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub struct Aabb3 <S> {
101 min : Point3 <S>,
102 max : Point3 <S>
103}
104
105#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
107#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108pub struct Obb2 <S> {
109 center : Point2 <S>,
110 half_extent_x : Positive <S>,
111 half_extent_y : Positive <S>,
112 orientation : AngleWrapped <S>
113}
114
115#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub struct Obb3 <S> {
119 center : Point3 <S>,
120 half_extent_x : Positive <S>,
121 half_extent_y : Positive <S>,
122 half_extent_z : Positive <S>,
123 orientation : Angles3 <S>
124}
125
126#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129pub struct Cylinder3 <S> {
130 pub center : Point3 <S>,
131 pub half_height : Positive <S>,
132 pub radius : Positive <S>
133}
134
135#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
138pub struct Capsule3 <S> {
139 pub center : Point3 <S>,
140 pub half_height : Positive <S>,
141 pub radius : Positive <S>
142}
143
144#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
147#[derive(Clone, Copy, Debug, Eq, PartialEq)]
148pub struct Line2 <S> {
149 pub base : Point2 <S>,
150 pub direction : Unit2 <S>
151}
152
153#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
156#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157pub struct Line3 <S> {
158 pub base : Point3 <S>,
159 pub direction : Unit3 <S>
160}
161
162#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
164#[derive(Clone, Copy, Debug, Eq, PartialEq)]
165pub struct Plane3 <S> {
166 pub base : Point3 <S>,
167 pub normal : Unit3 <S>
168}
169
170#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
172#[derive(Clone, Copy, Debug, Eq, PartialEq)]
173pub struct Sphere2 <S> {
174 pub center : Point2 <S>,
175 pub radius : Positive <S>
176}
177
178#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
180#[derive(Clone, Copy, Debug, Eq, PartialEq)]
181pub struct Sphere3 <S> {
182 pub center : Point3 <S>,
183 pub radius : Positive <S>
184}
185
186#[derive(Clone, Debug)]
189struct Rect <S> {
190 pub edge : Vector2 <S>,
191 pub perp : Vector2 <S>,
192 pub indices : [u32; 4],
194 pub area : S,
195 pub edge_len_squared : S
196}
197
198pub fn colinear_2d <S> (a : &Point2 <S>, b : &Point2 <S>, c : &Point2 <S>) -> bool where
220 S : Field + approx::RelativeEq
221{
222 if a == b || a == c || b == c {
224 true
225 } else {
226 let a = Vector3::from_point_2d (a.0).into();
227 let b = Vector3::from_point_2d (b.0).into();
228 let c = Vector3::from_point_2d (c.0).into();
229 let determinant = determinant_3d (&a, &b, &c);
230 approx::relative_eq!(S::zero(), determinant)
232 }
233}
234
235pub fn colinear_3d <S> (a : &Point3 <S>, b : &Point3 <S>, c : &Point3 <S>) -> bool where
246 S : Field + Sqrt + approx::RelativeEq
247{
248 if a == b || a == c || b == c {
250 true
251 } else {
252 let determinant = determinant_3d (a, b, c);
253 if approx::relative_eq!(S::zero(), determinant) {
255 approx::relative_eq!(S::zero(), triangle_3d_area2 (a, b, c))
256 } else {
257 false
258 }
259 }
260}
261
262pub fn coplanar_3d <S> (
274 a : &Point3 <S>, b : &Point3 <S>, c : &Point3 <S>, d : &Point3 <S>
275) -> bool where
276 S : Ring + approx::RelativeEq
277{
278 if a == b || a == c || a == d || b == c || b == d || c == d {
280 true
281 } else {
282 let ab_cross_ac_dot_ad = (*b-a).cross (*c-a).dot (*d-a);
283 approx::relative_eq!(S::zero(), ab_cross_ac_dot_ad)
284 }
285}
286
287pub fn triangle_3d_area2 <S> (
317 a : &Point3 <S>, b : &Point3 <S>, c : &Point3 <S>
318) -> S where
319 S : Field + Sqrt
320{
321 if a == b || a == c || b == c {
322 return S::zero()
323 }
324 let ab_mag = (*b-a).norm();
326 let ac_mag = (*c-a).norm();
327 let bc_mag = (*c-b).norm();
328 let max = S::max (S::max (ab_mag, ac_mag), bc_mag);
330 let min = S::min (S::min (ab_mag, ac_mag), bc_mag);
331 let mid = if min <= ab_mag && ab_mag <= max {
332 ab_mag
333 } else if min <= ac_mag && ac_mag <= max {
334 ac_mag
335 } else {
336 bc_mag
337 };
338 let two = S::one() + S::one();
339 let sixteen = two * two * two * two;
340 let frac = S::one() / sixteen;
342 frac
343 * (max + (mid + min))
344 * (min - (max - mid))
345 * (min + (max - mid))
346 * (max + (mid - min))
347}
348
349#[inline]
351pub fn determinant_3d <S : Ring> (a : &Point3 <S>, b : &Point3 <S>, c : &Point3 <S>)
352 -> S
353{
354 Matrix3::from_col_arrays ([a.0.into_array(), b.0.into_array(), c.0.into_array()])
355 .determinant()
356}
357
358pub fn point2_min <S : Ring> (a : &Point2 <S>, b : &Point2 <S>) -> Point2 <S> {
360 Vector2::partial_min (a.0, b.0).into()
361}
362
363pub fn point2_max <S : Ring> (a : &Point2 <S>, b : &Point2 <S>) -> Point2 <S> {
365 Vector2::partial_max (a.0, b.0).into()
366}
367
368pub fn point3_min <S : Ring> (a : &Point3 <S>, b : &Point3 <S>) -> Point3 <S> {
370 Vector3::partial_min (a.0, b.0).into()
371}
372
373pub fn point3_max <S : Ring> (a : &Point3 <S>, b : &Point3 <S>) -> Point3 <S> {
375 Vector3::partial_max (a.0, b.0).into()
376}
377
378pub fn project_2d_point_on_line <S : Real>
381 (point : &Point2 <S>, line : &Line2 <S>) -> Point2 <S>
382{
383 let dot_dir = line.direction.dot (*point - line.base);
384 line.point (dot_dir)
385}
386
387pub fn project_3d_point_on_line <S : Real> (point : &Point3 <S>, line : &Line3 <S>)
390 -> Point3 <S>
391{
392 let dot_dir = line.direction.dot (*point - line.base);
393 line.point (dot_dir)
394}
395
396fn smallest_rect <S : Real> (i0 : u32, i1 : u32, hull : &Hull2 <S>) -> Rect <S> {
400 let points = hull.points();
404 let edge = points[i1 as usize] - points[i0 as usize];
405 let perp = vector2 (-edge.y, edge.x);
406 let edge_len_squared = edge.norm_squared();
407 let origin = points[i1 as usize];
408 let mut support = [Point2::<S>::origin(); 4];
409 let mut rect_index = [i1, i1, i1, i1];
410 #[expect(clippy::cast_possible_truncation)]
411 for (i, point) in points.iter().enumerate() {
412 let diff = point - origin;
413 let v = point2 (edge.dot (diff), perp.dot (diff));
414 if v.0.x > support[1].0.x || v.0.x == support[1].0.x && v.0.y > support[1].0.y {
415 rect_index[1] = i as u32;
416 support[1] = v;
417 }
418 if v.0.y > support[2].0.y || v.0.y == support[2].0.y && v.0.x < support[2].0.x {
419 rect_index[2] = i as u32;
420 support[2] = v;
421 }
422 if v.0.x < support[3].0.x || v.0.x == support[3].0.x && v.0.y < support[3].0.y {
423 rect_index[3] = i as u32;
424 support[3] = v;
425 }
426 }
427 let scaled_width = support[1].0.x - support[3].0.x;
428 let scaled_height = support[2].0.y;
429 let area = scaled_width * scaled_height / edge_len_squared;
430
431 Rect {
432 edge, perp, area, edge_len_squared,
433 indices: [rect_index[0], rect_index[1], rect_index[2], rect_index[3]]
434 }
435}
436
437impl <S : Ring> Interval <S> {
442 #[inline]
458 pub fn with_minmax (min : S, max : S) -> Self where S : std::fmt::Debug {
459 debug_assert_ne!(min, max);
460 debug_assert_eq!(min, S::min (min, max));
461 debug_assert_eq!(max, S::max (min, max));
462 Interval { min, max }
463 }
464 #[inline]
473 pub fn from_points (a : S, b : S) -> Self where S : std::fmt::Debug {
474 debug_assert_ne!(a, b);
475 let min = S::min (a, b);
476 let max = S::max (a, b);
477 Interval { min, max }
478 }
479 #[inline]
493 pub fn containing (points : &[S]) -> Self where S : std::fmt::Debug {
494 debug_assert!(points.len() >= 2);
495 let mut min = S::min (points[0], points[1]);
496 let mut max = S::max (points[0], points[1]);
497 for point in points.iter().skip (2) {
498 min = S::min (min, *point);
499 max = S::max (max, *point);
500 }
501 Interval::with_minmax (min, max)
502 }
503 #[inline]
504 pub fn numcast <T> (self) -> Option <Interval <T>> where
505 S : num::ToPrimitive,
506 T : num::NumCast
507 {
508 Some (Interval {
509 min: T::from (self.min)?,
510 max: T::from (self.max)?
511 })
512 }
513 #[inline]
515 pub fn union (a : &Interval <S>, b : &Interval <S>) -> Self where
516 S : std::fmt::Debug
517 {
518 Interval::with_minmax (S::min (a.min(), b.min()), S::max (a.max(), b.max()))
519 }
520 #[inline]
521 pub const fn min (&self) -> S {
522 self.min
523 }
524 #[inline]
525 pub const fn max (&self) -> S {
526 self.max
527 }
528 #[inline]
529 pub fn length (&self) -> NonNegative <S> {
530 self.width()
531 }
532 #[inline]
533 pub fn width (&self) -> NonNegative <S> {
534 NonNegative::unchecked (self.max - self.min)
535 }
536 #[inline]
537 pub fn contains (&self, point : &S) -> bool {
538 self.min < *point && *point < self.max
539 }
540 pub fn clamp (&self, point : &S) -> S {
550 S::max (S::min (self.max, *point), self.min)
551 }
552 #[inline]
577 pub fn rand_point <R> (&self, rng : &mut R) -> S where
578 S : rand::distr::uniform::SampleUniform,
579 R : rand::Rng
580 {
581 rng.random_range (self.min..self.max)
582 }
583 #[inline]
584 pub fn intersects (&self, other : &Interval <S>) -> bool {
585 intersect::discrete_interval (self, other)
586 }
587 #[inline]
588 pub fn intersection (self, other : Interval <S>) -> Option <Interval <S>>
589 where S : std::fmt::Debug
590 {
591 intersect::continuous_interval (&self, &other)
592 }
593
594 pub fn dilate (mut self, amount : S) -> Self {
603 self.min -= amount;
604 self.max += amount;
605 debug_assert!(self.min < self.max);
606 self
607 }
608}
609
610impl <S> Primitive <S, S> for Interval <S> where S : Field + VectorSpace <S> {
611 fn translate (mut self, displacement : S) -> Self {
612 self.min += displacement;
613 self.max += displacement;
614 self
615 }
616 fn scale (mut self, scale : NonZero <S>) -> Self {
617 let old_width = *self.width();
618 let new_width = old_width * *scale;
619 let half_difference = (new_width - old_width) / S::two();
620 self.min -= half_difference;
621 self.max += half_difference;
622 self
623 }
624}
625
626impl <S> std::ops::Add <S> for Interval <S> where
627 S : Field + VectorSpace <S>,
628 Self : Primitive <S, S>
629{
630 type Output = Self;
631 #[expect(clippy::renamed_function_params)]
632 fn add (self, displacement : S) -> Self {
633 self.translate (displacement)
634 }
635}
636
637impl <S> std::ops::Sub <S> for Interval <S> where
638 S : Field + VectorSpace <S>,
639 Self : Primitive <S, S>
640{
641 type Output = Self;
642 #[expect(clippy::renamed_function_params)]
643 fn sub (self, displacement : S) -> Self {
644 self.translate (-displacement)
645 }
646}
647
648impl_numcast_fields!(Aabb2, min, max);
649impl <S : Ring> Aabb2 <S> {
650 #[inline]
666 pub fn with_minmax (min : Point2 <S>, max : Point2 <S>) -> Self where
667 S : std::fmt::Debug
668 {
669 debug_assert_ne!(min, max);
670 debug_assert_eq!(min, point2_min (&min, &max));
671 debug_assert_eq!(max, point2_max (&min, &max));
672 Aabb2 { min, max }
673 }
674 #[inline]
683 pub fn from_points (a : Point2 <S>, b : Point2 <S>) -> Self where
684 S : std::fmt::Debug
685 {
686 debug_assert_ne!(a, b);
687 let min = point2_min (&a, &b);
688 let max = point2_max (&a, &b);
689 Aabb2 { min, max }
690 }
691 #[inline]
705 pub fn containing (points : &[Point2 <S>]) -> Self where S : std::fmt::Debug {
706 debug_assert!(points.len() >= 2);
707 let mut min = point2_min (&points[0], &points[1]);
708 let mut max = point2_max (&points[0], &points[1]);
709 for point in points.iter().skip (2) {
710 min = point2_min (&min, point);
711 max = point2_max (&max, point);
712 }
713 Aabb2::with_minmax (min, max)
714 }
715 #[inline]
717 pub fn union (a : &Aabb2 <S>, b : &Aabb2 <S>) -> Self where S : std::fmt::Debug {
718 Aabb2::with_minmax (point2_min (a.min(), b.min()), point2_max (a.max(), b.max()))
719 }
720 #[inline]
721 pub const fn min (&self) -> &Point2 <S> {
722 &self.min
723 }
724 #[inline]
725 pub const fn max (&self) -> &Point2 <S> {
726 &self.max
727 }
728 #[inline]
729 pub fn center (&self) -> Point2 <S> where S : Field {
730 Point2 ((self.min.0 + self.max.0) / S::two())
731 }
732 #[inline]
733 pub fn dimensions (&self) -> Vector2 <S> {
734 self.max.0 - self.min.0
735 }
736 #[inline]
737 pub fn extents (&self) -> Vector2 <S> where S : Field {
738 self.dimensions() * S::half()
739 }
740 #[inline]
742 pub fn width (&self) -> NonNegative <S> {
743 NonNegative::unchecked (self.max.0.x - self.min.0.x)
744 }
745 #[inline]
747 pub fn height (&self) -> NonNegative <S> {
748 NonNegative::unchecked (self.max.0.y - self.min.0.y)
749 }
750 #[inline]
751 pub fn area (&self) -> NonNegative <S> {
752 self.width() * self.height()
753 }
754 #[inline]
755 pub fn contains (&self, point : &Point2 <S>) -> bool {
756 self.min.0.x < point.0.x && point.0.x < self.max.0.x &&
757 self.min.0.y < point.0.y && point.0.y < self.max.0.y
758 }
759 pub fn clamp (&self, point : &Point2 <S>) -> Point2 <S> {
769 [ S::max (S::min (self.max.0.x, point.0.x), self.min.0.x),
770 S::max (S::min (self.max.0.y, point.0.y), self.min.0.y),
771 ].into()
772 }
773 #[inline]
800 pub fn rand_point <R> (&self, rng : &mut R) -> Point2 <S> where
801 S : rand::distr::uniform::SampleUniform,
802 R : rand::Rng
803 {
804 let x_range = self.min.0.x..self.max.0.x;
805 let y_range = self.min.0.y..self.max.0.y;
806 let x = if x_range.is_empty() {
807 self.min.0.x
808 } else {
809 rng.random_range (x_range)
810 };
811 let y = if y_range.is_empty() {
812 self.min.0.y
813 } else {
814 rng.random_range (y_range)
815 };
816 [x, y].into()
817 }
818 #[inline]
819 pub fn intersects (&self, other : &Aabb2 <S>) -> bool {
820 intersect::discrete_aabb2_aabb2 (self, other)
821 }
822 #[inline]
823 pub fn intersection (self, other : Aabb2 <S>) -> Option <Aabb2 <S>> where
824 S : std::fmt::Debug
825 {
826 intersect::continuous_aabb2_aabb2 (&self, &other)
827 }
828
829 pub fn corner (&self, quadrant : Quadrant) -> Point2 <S> {
830 match quadrant {
831 Quadrant::PosPos => self.max,
833 Quadrant::PosNeg => [self.max.0.x, self.min.0.y].into(),
835 Quadrant::NegPos => [self.min.0.x, self.max.0.y].into(),
837 Quadrant::NegNeg => self.min
839 }
840 }
841
842 pub fn edge (&self, direction : SignedAxis2) -> Self where S : std::fmt::Debug {
843 let (min, max) = match direction {
844 SignedAxis2::PosX => (
845 self.corner (Quadrant::PosNeg),
846 self.corner (Quadrant::PosPos) ),
847 SignedAxis2::NegX => (
848 self.corner (Quadrant::NegNeg),
849 self.corner (Quadrant::NegPos) ),
850 SignedAxis2::PosY => (
851 self.corner (Quadrant::NegPos),
852 self.corner (Quadrant::PosPos) ),
853 SignedAxis2::NegY => (
854 self.corner (Quadrant::NegNeg),
855 self.corner (Quadrant::PosNeg) )
856 };
857 Aabb2::with_minmax (min, max)
858 }
859
860 pub fn extrude (&self, axis : SignedAxis2, amount : Positive <S>) -> Self {
861 let (min, max) = match axis {
862 SignedAxis2::PosX => (
863 self.min + Vector2::zero().with_x (*self.width()),
864 self.max + Vector2::zero().with_x (*amount)
865 ),
866 SignedAxis2::NegX => (
867 self.min - Vector2::zero().with_x (*amount),
868 self.max - Vector2::zero().with_x (*self.height())
869 ),
870 SignedAxis2::PosY => (
871 self.min + Vector2::zero().with_y (*self.height()),
872 self.max + Vector2::zero().with_y (*amount)
873 ),
874 SignedAxis2::NegY => (
875 self.min - Vector2::zero().with_y (*amount),
876 self.max - Vector2::zero().with_y (*self.height())
877 )
878 };
879 Aabb2 { min, max }
880 }
881
882 pub fn extend (&mut self, axis : SignedAxis2, amount : Positive <S>) {
883 match axis {
884 SignedAxis2::PosX => self.max.0.x += *amount,
885 SignedAxis2::NegX => self.min.0.x -= *amount,
886 SignedAxis2::PosY => self.max.0.y += *amount,
887 SignedAxis2::NegY => self.min.0.y -= *amount
888 }
889 }
890
891 pub fn with_z (self, z : Interval <S>) -> Aabb3 <S> where S : std::fmt::Debug {
892 Aabb3::with_minmax (
893 self.min.0.with_z (z.min()).into(),
894 self.max.0.with_z (z.max()).into()
895 )
896 }
897
898 pub fn dilate (mut self, amount : S) -> Self where S : std::fmt::Debug {
907 self.min -= Vector2::broadcast (amount);
908 self.max += Vector2::broadcast (amount);
909 debug_assert_ne!(self.min, self.max);
910 debug_assert_eq!(self.min, point2_min (&self.min, &self.max));
911 debug_assert_eq!(self.max, point2_max (&self.min, &self.max));
912 self
913 }
914
915 pub fn project (self, axis : Axis2) -> Interval <S> where S : std::fmt::Debug {
919 let (min, max) = match axis {
920 Axis2::X => (self.min.0.y, self.max.0.y),
921 Axis2::Y => (self.min.0.x, self.max.0.x)
922 };
923 Interval::with_minmax (min, max)
924 }
925}
926
927impl <S : Field> Primitive <S, Vector2 <S>> for Aabb2 <S> {
928 fn translate (mut self, displacement : Vector2 <S>) -> Self {
929 self.min.0 += displacement;
930 self.max.0 += displacement;
931 self
932 }
933 fn scale (mut self, scale : NonZero <S>) -> Self {
934 let center = self.center();
935 self = self.translate (-center.0);
936 self.min.0 *= *scale;
937 self.max.0 *= *scale;
938 self.translate (center.0)
939 }
940}
941
942impl <S> std::ops::Add <Vector2 <S>> for Aabb2 <S> where
943 S : Field,
944 Self : Primitive <S, Vector2 <S>>
945{
946 type Output = Self;
947 #[expect(clippy::renamed_function_params)]
948 fn add (self, displacement : Vector2 <S>) -> Self {
949 self.translate (displacement)
950 }
951}
952
953impl <S> std::ops::Sub <Vector2 <S>> for Aabb2 <S> where
954 S : Field,
955 Self : Primitive <S, Vector2 <S>>
956{
957 type Output = Self;
958 #[expect(clippy::renamed_function_params)]
959 fn sub (self, displacement : Vector2 <S>) -> Self {
960 self.translate (-displacement)
961 }
962}
963
964impl_numcast_fields!(Aabb3, min, max);
965impl <S : Ring> Aabb3 <S> {
966 #[inline]
982 pub fn with_minmax (min : Point3 <S>, max : Point3 <S>) -> Self where
983 S : std::fmt::Debug
984 {
985 debug_assert_ne!(min, max);
986 debug_assert_eq!(min, point3_min (&min, &max));
987 debug_assert_eq!(max, point3_max (&min, &max));
988 Aabb3 { min, max }
989 }
990 #[inline]
1001 pub fn from_points (a : Point3 <S>, b : Point3 <S>) -> Self where S : std::fmt::Debug {
1002 debug_assert_ne!(a, b);
1003 let min = point3_min (&a, &b);
1004 let max = point3_max (&a, &b);
1005 Aabb3 { min, max }
1006 }
1007 #[inline]
1021 pub fn containing (points : &[Point3 <S>]) -> Self where S : std::fmt::Debug {
1022 debug_assert!(points.len() >= 2);
1023 let mut min = point3_min (&points[0], &points[1]);
1024 let mut max = point3_max (&points[0], &points[1]);
1025 for point in points.iter().skip (2) {
1026 min = point3_min (&min, point);
1027 max = point3_max (&max, point);
1028 }
1029 Aabb3::with_minmax (min, max)
1030 }
1031 #[inline]
1033 pub fn union (a : &Aabb3 <S>, b : &Aabb3 <S>) -> Self where S : std::fmt::Debug {
1034 Aabb3::with_minmax (point3_min (a.min(), b.min()), point3_max (a.max(), b.max())
1035 )
1036 }
1037 #[inline]
1038 pub const fn min (&self) -> &Point3 <S> {
1039 &self.min
1040 }
1041 #[inline]
1042 pub const fn max (&self) -> &Point3 <S> {
1043 &self.max
1044 }
1045 #[inline]
1046 pub fn center (&self) -> Point3 <S> where S : Field {
1047 Point3 ((self.min.0 + self.max.0) / S::two())
1048 }
1049 #[inline]
1050 pub fn dimensions (&self) -> Vector3 <S> {
1051 self.max.0 - self.min.0
1052 }
1053 #[inline]
1054 pub fn extents (&self) -> Vector3 <S> where S : Field {
1055 self.dimensions() * S::half()
1056 }
1057 #[inline]
1059 pub fn width (&self) -> NonNegative <S> {
1060 NonNegative::unchecked (self.max.0.x - self.min.0.x)
1061 }
1062 #[inline]
1064 pub fn depth (&self) -> NonNegative <S> {
1065 NonNegative::unchecked (self.max.0.y - self.min.0.y)
1066 }
1067 #[inline]
1069 pub fn height (&self) -> NonNegative <S> {
1070 NonNegative::unchecked (self.max.0.z - self.min.0.z)
1071 }
1072 #[inline]
1073 pub fn volume (&self) -> NonNegative <S> {
1074 self.width() * self.depth() * self.height()
1075 }
1076 #[inline]
1077 pub fn contains (&self, point : &Point3 <S>) -> bool {
1078 self.min.0.x < point.0.x && point.0.x < self.max.0.x &&
1079 self.min.0.y < point.0.y && point.0.y < self.max.0.y &&
1080 self.min.0.z < point.0.z && point.0.z < self.max.0.z
1081 }
1082 pub fn clamp (&self, point : &Point3 <S>) -> Point3 <S> {
1093 [ S::max (S::min (self.max.0.x, point.0.x), self.min.0.x),
1094 S::max (S::min (self.max.0.y, point.0.y), self.min.0.y),
1095 S::max (S::min (self.max.0.z, point.0.z), self.min.0.z)
1096 ].into()
1097 }
1098 #[inline]
1125 pub fn rand_point <R> (&self, rng : &mut R) -> Point3 <S> where
1126 S : rand::distr::uniform::SampleUniform,
1127 R : rand::Rng
1128 {
1129 let x_range = self.min.0.x..self.max.0.x;
1130 let y_range = self.min.0.y..self.max.0.y;
1131 let z_range = self.min.0.z..self.max.0.z;
1132 let x = if x_range.is_empty() {
1133 self.min.0.x
1134 } else {
1135 rng.random_range (x_range)
1136 };
1137 let y = if y_range.is_empty() {
1138 self.min.0.y
1139 } else {
1140 rng.random_range (y_range)
1141 };
1142 let z = if z_range.is_empty() {
1143 self.min.0.z
1144 } else {
1145 rng.random_range (z_range)
1146 };
1147 [x, y, z].into()
1148 }
1149 #[inline]
1150 pub fn intersects (&self, other : &Aabb3 <S>) -> bool {
1151 intersect::discrete_aabb3_aabb3 (self, other)
1152 }
1153 #[inline]
1154 pub fn intersection (self, other : Aabb3 <S>) -> Option <Aabb3 <S>> where
1155 S : std::fmt::Debug
1156 {
1157 intersect::continuous_aabb3_aabb3 (&self, &other)
1158 }
1159
1160 pub fn corner (&self, octant : Octant) -> Point3 <S> {
1161 match octant {
1162 Octant::PosPosPos => self.max,
1163 Octant::NegPosPos => [self.min.0.x, self.max.0.y, self.max.0.z].into(),
1164 Octant::PosNegPos => [self.max.0.x, self.min.0.y, self.max.0.z].into(),
1165 Octant::NegNegPos => [self.min.0.x, self.min.0.y, self.max.0.z].into(),
1166 Octant::PosPosNeg => [self.max.0.x, self.max.0.y, self.min.0.z].into(),
1167 Octant::NegPosNeg => [self.min.0.x, self.max.0.y, self.min.0.z].into(),
1168 Octant::PosNegNeg => [self.max.0.x, self.min.0.y, self.min.0.z].into(),
1169 Octant::NegNegNeg => self.min
1170 }
1171 }
1172
1173 pub fn face (&self, direction : SignedAxis3) -> Self where S : std::fmt::Debug {
1174 let (min, max) = match direction {
1175 SignedAxis3::PosX => (
1176 self.corner (Octant::PosNegNeg),
1177 self.corner (Octant::PosPosPos) ),
1178 SignedAxis3::NegX => (
1179 self.corner (Octant::NegNegNeg),
1180 self.corner (Octant::NegPosPos) ),
1181 SignedAxis3::PosY => (
1182 self.corner (Octant::NegPosNeg),
1183 self.corner (Octant::PosPosPos) ),
1184 SignedAxis3::NegY => (
1185 self.corner (Octant::NegNegNeg),
1186 self.corner (Octant::PosNegPos) ),
1187 SignedAxis3::PosZ => (
1188 self.corner (Octant::NegNegPos),
1189 self.corner (Octant::PosPosPos) ),
1190 SignedAxis3::NegZ => (
1191 self.corner (Octant::NegNegNeg),
1192 self.corner (Octant::PosPosNeg) )
1193 };
1194 Aabb3::with_minmax (min, max)
1195 }
1196
1197 pub fn extrude (&self, axis : SignedAxis3, amount : Positive <S>) -> Self {
1198 let (min, max) = match axis {
1199 SignedAxis3::PosX => (
1200 self.min + Vector3::zero().with_x (*self.width()),
1201 self.max + Vector3::zero().with_x (*amount)
1202 ),
1203 SignedAxis3::NegX => (
1204 self.min - Vector3::zero().with_x (*amount),
1205 self.max - Vector3::zero().with_x (*self.width())
1206 ),
1207 SignedAxis3::PosY => (
1208 self.min + Vector3::zero().with_y (*self.depth()),
1209 self.max + Vector3::zero().with_y (*amount)
1210 ),
1211 SignedAxis3::NegY => (
1212 self.min - Vector3::zero().with_y (*amount),
1213 self.max - Vector3::zero().with_y (*self.depth())
1214 ),
1215 SignedAxis3::PosZ => (
1216 self.min + Vector3::zero().with_z (*self.height()),
1217 self.max + Vector3::zero().with_z (*amount)
1218 ),
1219 SignedAxis3::NegZ => (
1220 self.min - Vector3::zero().with_z (*amount),
1221 self.max - Vector3::zero().with_z (*self.height())
1222 )
1223 };
1224 Aabb3 { min, max }
1225 }
1226
1227 pub fn extend (&mut self, axis : SignedAxis3, amount : Positive <S>) {
1228 match axis {
1229 SignedAxis3::PosX => self.max.0.x += *amount,
1230 SignedAxis3::NegX => self.min.0.x -= *amount,
1231 SignedAxis3::PosY => self.max.0.y += *amount,
1232 SignedAxis3::NegY => self.min.0.y -= *amount,
1233 SignedAxis3::PosZ => self.max.0.z += *amount,
1234 SignedAxis3::NegZ => self.min.0.z -= *amount
1235 }
1236 }
1237
1238 pub fn dilate (mut self, amount : S) -> Self where S : std::fmt::Debug {
1247 self.min -= Vector3::broadcast (amount);
1248 self.max += Vector3::broadcast (amount);
1249 debug_assert_ne!(self.min, self.max);
1250 debug_assert_eq!(self.min, point3_min (&self.min, &self.max));
1251 debug_assert_eq!(self.max, point3_max (&self.min, &self.max));
1252 self
1253 }
1254
1255 pub fn project (self, axis : Axis3) -> Aabb2 <S> where S : std::fmt::Debug {
1259 let (min, max) = match axis {
1260 Axis3::X => ([self.min.0.y, self.min.0.z], [self.max.0.y, self.max.0.z]),
1261 Axis3::Y => ([self.min.0.x, self.min.0.z], [self.max.0.x, self.max.0.z]),
1262 Axis3::Z => ([self.min.0.x, self.min.0.y], [self.max.0.x, self.max.0.y]),
1263 };
1264 Aabb2::with_minmax (min.into(), max.into())
1265 }
1266}
1267
1268impl <S : Field> Primitive <S, Vector3 <S>> for Aabb3 <S> {
1269 fn translate (mut self, displacement : Vector3 <S>) -> Self {
1270 self.min.0 += displacement;
1271 self.max.0 += displacement;
1272 self
1273 }
1274 fn scale (mut self, scale : NonZero <S>) -> Self {
1275 let center = self.center();
1276 self = self.translate (-center.0);
1277 self.min.0 *= *scale;
1278 self.max.0 *= *scale;
1279 self.translate (center.0)
1280 }
1281}
1282
1283impl <S> std::ops::Add <Vector3 <S>> for Aabb3 <S> where
1284 S : Field,
1285 Self : Primitive <S, Vector3 <S>>
1286{
1287 type Output = Self;
1288 #[expect(clippy::renamed_function_params)]
1289 fn add (self, displacement : Vector3 <S>) -> Self {
1290 self.translate (displacement)
1291 }
1292}
1293
1294impl <S> std::ops::Sub <Vector3 <S>> for Aabb3 <S> where
1295 S : Field,
1296 Self : Primitive <S, Vector3 <S>>
1297{
1298 type Output = Self;
1299 #[expect(clippy::renamed_function_params)]
1300 fn sub (self, displacement : Vector3 <S>) -> Self {
1301 self.translate (-displacement)
1302 }
1303}
1304
1305impl_numcast_fields!(Obb2, center, half_extent_x, half_extent_y, orientation);
1306impl <S : Ring> Obb2 <S> {
1307 #[inline]
1309 pub const fn new (
1310 center : Point2 <S>,
1311 half_extent_x : Positive <S>,
1312 half_extent_y : Positive <S>,
1313 orientation : AngleWrapped <S>
1314 ) -> Self {
1315 Obb2 { center, half_extent_x, half_extent_y, orientation }
1316 }
1317 pub fn containing_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
1335 if points.len() < 3 {
1336 return None
1337 }
1338 let hull = Hull2::from_points (points)?;
1339 Self::containing (&hull)
1340 }
1341 pub fn containing (hull : &Hull2 <S>) -> Option <Self> where S : Real {
1356 let points = hull.points();
1357 if points.len() < 3 {
1358 return None
1359 }
1360 let mut visited = vec![false; points.len()];
1361 #[expect(clippy::cast_possible_truncation)]
1362 let mut min_rect = smallest_rect (points.len() as u32 - 1, 0, hull);
1363 visited[min_rect.indices[0] as usize] = true;
1364 let mut rect = min_rect.clone();
1366 for _ in 0..points.len() {
1367 let mut angles = [(S::zero(), u32::MAX); 4];
1368 let mut nangles = u32::MAX;
1369 if !compute_angles (hull, &rect, &mut angles, &mut nangles) {
1370 break
1371 }
1372 let sorted = sort_angles (&angles, nangles);
1373 if !update_support (&angles, nangles, &sorted, hull, &mut visited, &mut rect) {
1374 break
1375 }
1376 if rect.area < min_rect.area {
1377 min_rect = rect.clone();
1378 }
1379 }
1380 let sum = [
1382 points[min_rect.indices[1] as usize].0 + points[min_rect.indices[3] as usize].0,
1383 points[min_rect.indices[2] as usize].0 + points[min_rect.indices[0] as usize].0
1384 ];
1385 let diff = [
1386 points[min_rect.indices[1] as usize].0 - points[min_rect.indices[3] as usize].0,
1387 points[min_rect.indices[2] as usize].0 - points[min_rect.indices[0] as usize].0
1388 ];
1389 let center = Point2::from ((min_rect.edge * min_rect.edge.dot (sum[0]) +
1390 min_rect.perp * min_rect.perp.dot (sum[1]))
1391 * S::half() / min_rect.edge_len_squared);
1392 let half_extent_x = Positive::unchecked (S::sqrt (
1393 (S::half() * min_rect.edge.dot (diff[0])).squared()
1394 / min_rect.edge_len_squared));
1395 let half_extent_y = Positive::unchecked (S::sqrt (
1396 (S::half() * min_rect.perp.dot (diff[1])).squared()
1397 / min_rect.edge_len_squared));
1398 let orientation = AngleWrapped::wrap (Rad (min_rect.edge.y.atan2 (min_rect.edge.x)));
1399 let obb = Obb2 { center, half_extent_x, half_extent_y, orientation };
1400 fn compute_angles <S : Real> (
1401 hull : &Hull2 <S>,
1402 rect : &Rect <S>,
1403 angles : &mut [(S, u32); 4],
1404 nangles : &mut u32
1405 ) -> bool {
1406 *nangles = 0;
1407 let points = hull.points();
1408 let mut k0 = 3;
1409 let mut k1 = 0;
1410 while k1 < 4 {
1411 if rect.indices[k0] != rect.indices[k1] {
1412 let d = {
1413 let mut d = if k0 & 1 != 0 {
1414 rect.perp
1415 } else {
1416 rect.edge
1417 };
1418 if k0 & 2 != 0 {
1419 d = -d;
1420 }
1421 d
1422 };
1423 let j0 = rect.indices[k0];
1424 let mut j1 = j0 + 1;
1425 #[expect(clippy::cast_possible_truncation)]
1426 if j1 == points.len() as u32 {
1427 j1 = 0;
1428 }
1429 let e = points[j1 as usize] - points[j0 as usize];
1430 let dp = d.dot ([e.y, -e.x].into());
1431 let e_lensq = e.norm_squared();
1432 let sin_theta_squared = (dp * dp) / e_lensq;
1433 angles[*nangles as usize] =
1434 #[expect(clippy::cast_possible_truncation)]
1435 (sin_theta_squared, k0 as u32);
1436 *nangles += 1;
1437 }
1438 k0 = k1;
1439 k1 += 1;
1440 }
1441 *nangles > 0
1442 }
1443 fn sort_angles <S : PartialOrd> (angles : &[(S, u32); 4], nangles : u32)
1444 -> [u32; 4]
1445 {
1446 let mut sorted = [0u32, 1, 2, 3];
1447 match nangles {
1448 0 | 1 => {}
1449 2 => if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1450 sorted.swap (0, 1)
1451 }
1452 3 => {
1453 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1454 sorted.swap (0, 1)
1455 }
1456 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1457 sorted.swap (0, 2)
1458 }
1459 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1460 sorted.swap (1, 2)
1461 }
1462 }
1463 4 => {
1464 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1465 sorted.swap (0, 1)
1466 }
1467 if angles[sorted[2] as usize].0 > angles[sorted[3] as usize].0 {
1468 sorted.swap (2, 3)
1469 }
1470 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1471 sorted.swap (0, 2)
1472 }
1473 if angles[sorted[1] as usize].0 > angles[sorted[3] as usize].0 {
1474 sorted.swap (1, 3)
1475 }
1476 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1477 sorted.swap (1, 2)
1478 }
1479 }
1480 _ => unreachable!()
1481 }
1482 sorted
1483 }
1484 fn update_support <S : Real> (
1485 angles : &[(S, u32); 4],
1486 nangles : u32,
1487 sorted : &[u32; 4],
1488 hull : &Hull2 <S>,
1489 visited : &mut [bool],
1490 rect : &mut Rect <S>
1491 ) -> bool {
1492 let points = hull.points();
1493 let min_angle = angles[sorted[0] as usize];
1494 for k in 0..nangles as usize {
1495 let (angle, index) = angles[sorted[k] as usize];
1496 if angle == min_angle.0 {
1497 rect.indices[index as usize] += 1;
1498 #[expect(clippy::cast_possible_truncation)]
1499 if rect.indices[index as usize] == points.len() as u32 {
1500 rect.indices[index as usize] = 0;
1501 }
1502 }
1503 }
1504 let bottom = rect.indices[min_angle.1 as usize];
1505 if visited[bottom as usize] {
1506 return false
1507 }
1508 visited[bottom as usize] = true;
1509 let mut next_index = [u32::MAX; 4];
1510 for k in 0u32..4 {
1511 next_index[k as usize] = rect.indices[((min_angle.1 + k) % 4) as usize];
1512 }
1513 rect.indices = next_index;
1514 let j1 = rect.indices[0] as usize;
1515 let j0 = if j1 == 0 {
1516 points.len() - 1
1517 } else {
1518 j1 - 1
1519 };
1520 rect.edge = points[j1] - points[j0];
1521 rect.perp = vector2 (-rect.edge.y, rect.edge.x);
1522 let edge_len_squared = rect.edge.norm_squared();
1523 let diff = [
1524 points[rect.indices[1] as usize] - points[rect.indices[3] as usize],
1525 points[rect.indices[2] as usize] - points[rect.indices[0] as usize]
1526 ];
1527 rect.area = rect.edge.dot (diff[0]) * rect.perp.dot (diff[1]) / edge_len_squared;
1528 true
1529 }
1530 Some (obb)
1531 }
1532 #[inline]
1533 pub const fn center (&self) -> Point2 <S> {
1534 self.center
1535 }
1536 #[inline]
1537 pub const fn half_extent_x (&self) -> Positive <S> {
1538 self.half_extent_x
1539 }
1540 #[inline]
1541 pub const fn half_extent_y (&self) -> Positive <S> {
1542 self.half_extent_y
1543 }
1544 #[inline]
1545 pub const fn orientation (&self) -> AngleWrapped <S> {
1546 self.orientation
1547 }
1548 pub fn dimensions (&self) -> Vector2 <S> where S : Field {
1549 self.extents() * S::two()
1550 }
1551 pub fn extents (&self) -> Vector2 <S> {
1552 [*self.half_extent_x, *self.half_extent_y].into()
1553 }
1554 #[inline]
1556 pub fn width (&self) -> Positive <S> where S : Field {
1557 self.half_extent_x * Positive::unchecked (S::two())
1558 }
1559 #[inline]
1561 pub fn height (&self) -> Positive <S> where S : Field {
1562 self.half_extent_y * Positive::unchecked (S::two())
1563 }
1564 #[inline]
1565 pub fn area (&self) -> Positive <S> where S : Field {
1566 self.width() * self.height()
1567 }
1568 #[inline]
1569 pub fn corners (&self) -> [Point2 <S>; 4] where
1570 S : Real + num::real::Real + MaybeSerDes
1571 {
1572 let extents = self.extents();
1573 let mut points = [Point2::origin(); 4];
1574 for i in 0..2 {
1575 for j in 0..2 {
1576 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
1577 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
1578 points[i * 2 + j] = [extents.x * sign_x, extents.y * sign_y].into();
1579 }
1580 }
1581 let rotation = Rotation2::from_angle (self.orientation.angle());
1582 points.map (|point| rotation.rotate (point) + self.center.0)
1583 }
1584
1585 pub fn contains (&self, point : &Point2 <S>) -> bool where
1599 S : Real + num::real::Real + MaybeSerDes
1600 {
1601 let p = point - self.center().0;
1603 let p = Rotation2::from_angle (-self.orientation().angle()).rotate (p);
1605 p.0.x.abs() < *self.half_extent_x && p.0.y.abs() < *self.half_extent_y
1606 }
1607
1608 pub fn dilate (mut self, amount : S) -> Self where S : Field {
1625 self.half_extent_x = Positive::noisy (*self.half_extent_x + amount);
1626 self.half_extent_y = Positive::noisy (*self.half_extent_y + amount);
1627 self
1628 }
1629}
1630impl <S : Real> From <Aabb2 <S>> for Obb2 <S> {
1631 fn from (aabb : Aabb2 <S>) -> Self {
1644 use num::Zero;
1645 let center = aabb.center();
1646 let half_extent_x = Positive::noisy (*aabb.width() / S::two());
1647 let half_extent_y = Positive::noisy (*aabb.height() / S::two());
1648 let orientation = AngleWrapped::zero();
1649 Obb2::new (center, half_extent_x, half_extent_y, orientation)
1650 }
1651}
1652
1653impl_numcast_fields!(Obb3, center, half_extent_x, half_extent_y, half_extent_z,
1654 orientation);
1655impl <S : Ring> Obb3 <S> {
1656 #[inline]
1658 pub const fn new (
1659 center : Point3 <S>,
1660 half_extent_x : Positive <S>,
1661 half_extent_y : Positive <S>,
1662 half_extent_z : Positive <S>,
1663 orientation : Angles3 <S>
1664 ) -> Self {
1665 Obb3 { center, half_extent_x, half_extent_y, half_extent_z, orientation }
1666 }
1667 pub fn containing_points (_points : &[Point3 <S>]) -> Option <Self> where S : Real {
1669 unimplemented!("TODO")
1670 }
1671 pub fn containing_points_with_orientation (
1673 _points : &[Point3 <S>], _orientation : Angles3 <S>
1674 ) {
1675 unimplemented!("TODO")
1676 }
1677 pub fn containing (_hull : &Hull3 <S>) -> Option <Self> where S : Real {
1679 unimplemented!("TODO")
1680 }
1681 #[inline]
1682 pub const fn center (&self) -> Point3 <S> {
1683 self.center
1684 }
1685 #[inline]
1686 pub const fn half_extent_x (&self) -> Positive <S> {
1687 self.half_extent_x
1688 }
1689 #[inline]
1690 pub const fn half_extent_y (&self) -> Positive <S> {
1691 self.half_extent_y
1692 }
1693 #[inline]
1694 pub const fn half_extent_z (&self) -> Positive <S> {
1695 self.half_extent_z
1696 }
1697 #[inline]
1698 pub const fn orientation (&self) -> Angles3 <S> {
1699 self.orientation
1700 }
1701 pub fn dimensions (&self) -> Vector3 <S> where S : Field {
1702 self.extents() * S::two()
1703 }
1704 pub fn extents (&self) -> Vector3 <S> where S : Field {
1705 [*self.half_extent_x, *self.half_extent_y, *self.half_extent_z].into()
1706 }
1707 #[inline]
1709 pub fn width (&self) -> Positive <S> where S : Field {
1710 self.half_extent_x * Positive::unchecked (S::two())
1711 }
1712 #[inline]
1714 pub fn depth (&self) -> Positive <S> where S : Field {
1715 self.half_extent_y * Positive::unchecked (S::two())
1716 }
1717 #[inline]
1719 pub fn height (&self) -> Positive <S> where S : Field {
1720 self.half_extent_z * Positive::unchecked (S::two())
1721 }
1722 #[inline]
1723 pub fn volume (&self) -> Positive <S> where S : Field {
1724 self.width() * self.depth() * self.height()
1725 }
1726 #[inline]
1727 pub fn corners (&self) -> [Point3 <S>; 8] where
1728 S : Real + num::real::Real + MaybeSerDes
1729 {
1730 let extents = self.extents();
1731 let mut points = [Point3::origin(); 8];
1732 for i in 0..2 {
1733 for j in 0..2 {
1734 for k in 0..2 {
1735 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
1736 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
1737 let sign_z = if k % 2 == 0 { -S::one() } else { S::one() };
1738 points[i * 4 + j * 2 + k] =
1739 [extents.x * sign_x, extents.y * sign_y, extents.z * sign_z].into();
1740 }
1741 }
1742 }
1743 let rotation = Rotation3::from (self.orientation);
1744 points.map (|point| rotation.rotate (point) + self.center.0)
1745 }
1746
1747 pub fn contains (&self, _point : &Point2 <S>) -> bool where
1749 S : Real + num::real::Real + MaybeSerDes
1750 {
1751 unimplemented!("TODO")
1752 }
1753
1754 pub fn dilate (mut self, amount : S) -> Self where S : Field {
1771 self.half_extent_x = Positive::noisy (*self.half_extent_x + amount);
1772 self.half_extent_y = Positive::noisy (*self.half_extent_y + amount);
1773 self.half_extent_z = Positive::noisy (*self.half_extent_z + amount);
1774 self
1775 }
1776}
1777impl <S : Real> From <Aabb3 <S>> for Obb3 <S> {
1778 fn from (aabb : Aabb3 <S>) -> Self {
1791 let center = aabb.center();
1792 let [half_extent_x, half_extent_y, half_extent_z] =
1793 aabb.extents().into_array().map (Positive::noisy);
1794 let orientation = Angles3::zero();
1795 Obb3::new (center, half_extent_x, half_extent_y, half_extent_z, orientation)
1796 }
1797}
1798
1799impl_numcast_fields!(Capsule3, center, half_height, radius);
1800impl <S : Ring> Capsule3 <S> {
1801 pub fn aabb3 (&self) -> Aabb3 <S> where S : Real + std::fmt::Debug {
1802 use shape::Aabb;
1803 let shape_aabb = shape::Capsule {
1804 radius: self.radius,
1805 half_height: self.half_height
1806 }.aabb();
1807 let center_vec = self.center.0;
1808 let min = *shape_aabb.min() + center_vec;
1809 let max = *shape_aabb.max() + center_vec;
1810 Aabb3::with_minmax (min, max)
1811 }
1812 pub fn decompose (&self) -> (Sphere3 <S>, Option <Cylinder3 <S>>, Sphere3 <S>) {
1814 let cylinder = if *self.half_height > S::zero() {
1815 Some (Cylinder3 {
1816 center: self.center,
1817 radius: self.radius,
1818 half_height: Positive::unchecked (*self.half_height)
1819 })
1820 } else {
1821 None
1822 };
1823 let upper_sphere = Sphere3 {
1824 center: self.center + (Vector3::unit_z() * *self.half_height),
1825 radius: self.radius
1826 };
1827 let lower_sphere = Sphere3 {
1828 center: self.center - (Vector3::unit_z() * *self.half_height),
1829 radius: self.radius
1830 };
1831 (upper_sphere, cylinder, lower_sphere)
1832 }
1833}
1834
1835impl_numcast_fields!(Cylinder3, center, half_height, radius);
1838impl <S : Ring> Cylinder3 <S> {
1839 pub fn aabb3 (&self) -> Aabb3 <S> where S : Real + std::fmt::Debug {
1840 use shape::Aabb;
1841 let shape_aabb = shape::Cylinder::unchecked (*self.radius, *self.half_height).aabb();
1842 let center_vec = self.center.0;
1843 let min = *shape_aabb.min() + center_vec;
1844 let max = *shape_aabb.max() + center_vec;
1845 Aabb3::with_minmax (min, max)
1846 }
1847}
1848
1849impl_numcast_fields!(Line2, base, direction);
1852impl <S : Real> Line2 <S> {
1853 #[inline]
1855 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
1856 Line2 { base, direction }
1857 }
1858 #[inline]
1859 pub fn point (&self, t : S) -> Point2 <S> {
1860 self.base + *self.direction * t
1861 }
1862 #[inline]
1863 #[expect(clippy::type_complexity)]
1864 pub fn intersect_aabb (&self, aabb : &Aabb2 <S>)
1865 -> Option <((S, Point2 <S>), (S, Point2 <S>))>
1866 where S : std::fmt::Debug {
1867 intersect::continuous_line2_aabb2 (self, aabb)
1868 }
1869 #[inline]
1870 #[expect(clippy::type_complexity)]
1871 pub fn intersect_sphere (&self, sphere : &Sphere2 <S>)
1872 -> Option <((S, Point2 <S>), (S, Point2 <S>))>
1873 {
1874 intersect::continuous_line2_sphere2 (self, sphere)
1875 }
1876}
1877impl <S : Real> Default for Line2 <S> {
1878 fn default() -> Self {
1879 Line2 {
1880 base: Point2::origin(),
1881 direction: Unit2::axis_y()
1882 }
1883 }
1884}
1885
1886impl_numcast_fields!(Line3, base, direction);
1889impl <S : Real> Line3 <S> {
1890 #[inline]
1892 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
1893 Line3 { base, direction }
1894 }
1895 #[inline]
1896 pub fn point (&self, t : S) -> Point3 <S> {
1897 self.base + *self.direction * t
1898 }
1899 #[inline]
1900 pub fn intersect_plane (&self, plane : &Plane3 <S>) -> Option <(S, Point3 <S>)> where
1901 S : approx::RelativeEq
1902 {
1903 intersect::continuous_line3_plane3 (self, plane)
1904 }
1905 #[inline]
1906 #[expect(clippy::type_complexity)]
1907 pub fn intersect_aabb (&self, aabb : &Aabb3 <S>)
1908 -> Option <((S, Point3 <S>), (S, Point3 <S>))>
1909 where S : num::Float + approx::RelativeEq <Epsilon=S> + std::fmt::Debug {
1910 intersect::continuous_line3_aabb3 (self, aabb)
1911 }
1912 #[inline]
1913 #[expect(clippy::type_complexity)]
1914 pub fn intersect_sphere (&self, sphere : &Sphere3 <S>)
1915 -> Option <((S, Point3 <S>), (S, Point3 <S>))>
1916 {
1917 intersect::continuous_line3_sphere3 (self, sphere)
1918 }
1919}
1920impl <S : Real> Default for Line3 <S> {
1921 fn default() -> Self {
1922 Line3 {
1923 base: Point3::origin(),
1924 direction: Unit3::axis_z()
1925 }
1926 }
1927}
1928
1929impl_numcast_fields!(Plane3, base, normal);
1932impl <S : Real> Plane3 <S> {
1933 #[inline]
1935 pub const fn new (base : Point3 <S>, normal : Unit3 <S>) -> Self {
1936 Plane3 { base, normal }
1937 }
1938}
1939impl <S : Real> Default for Plane3 <S> {
1940 fn default() -> Self {
1941 Plane3 {
1942 base: Point3::origin(),
1943 normal: Unit3::axis_z()
1944 }
1945 }
1946}
1947
1948impl_numcast_fields!(Sphere2, center, radius);
1951impl <S : Ring> Sphere2 <S> {
1952 #[inline]
1954 pub fn unit() -> Self where S : Field {
1955 Sphere2 {
1956 center: Point2::origin(),
1957 radius: num::One::one()
1958 }
1959 }
1960 #[inline]
1962 pub fn intersects (&self, other : &Self) -> bool {
1963 intersect::discrete_sphere2_sphere2 (self, other)
1964 }
1965}
1966
1967impl_numcast_fields!(Sphere3, center, radius);
1970impl <S : Ring> Sphere3 <S> {
1971 #[inline]
1973 pub fn unit() -> Self where S : Field {
1974 Sphere3 {
1975 center: Point3::origin(),
1976 radius: num::One::one()
1977 }
1978 }
1979 #[inline]
1981 pub fn intersects (&self, other : &Self) -> bool {
1982 intersect::discrete_sphere3_sphere3 (self, other)
1983 }
1984}
1985
1986#[cfg(test)]
1993mod tests {
1994 use approx::{assert_relative_eq, assert_ulps_eq};
1995 use super::*;
1996
1997 #[test]
1998 fn project_2d_point_on_line() {
1999 use super::project_2d_point_on_line;
2000 use Unit2;
2001 let point : Point2 <f64> = [2.0, 2.0].into();
2002 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_x());
2003 assert_eq!(project_2d_point_on_line (&point, &line), [2.0, 0.0].into());
2004 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_y());
2005 assert_eq!(project_2d_point_on_line (&point, &line), [0.0, 2.0].into());
2006 let point : Point2 <f64> = [0.0, 1.0].into();
2007 let line = Line2::<f64>::new (
2008 [0.0, -1.0].into(), Unit2::normalize ([1.0, 1.0].into()));
2009 assert_relative_eq!(
2010 project_2d_point_on_line (&point, &line), [1.0, 0.0].into());
2011 let point : Point2 <f64> = [1.0, 3.0].into();
2013 let line_a = Line2::<f64>::new (
2014 [0.0, -1.0].into(), Unit2::normalize ([2.0, 1.0].into()));
2015 let line_b = Line2::<f64>::new (
2016 [2.0, 0.0].into(), Unit2::normalize ([2.0, 1.0].into()));
2017 assert_relative_eq!(
2018 project_2d_point_on_line (&point, &line_a),
2019 project_2d_point_on_line (&point, &line_b));
2020 let line_a = Line2::<f64>::new (
2021 [0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
2022 let line_b = Line2::<f64>::new (
2023 [-2.0, -2.0].into(), Unit2::normalize ([1.0, 1.0].into()));
2024 assert_ulps_eq!(
2025 project_2d_point_on_line (&point, &line_a),
2026 project_2d_point_on_line (&point, &line_b)
2027 );
2028 assert_relative_eq!(
2029 project_2d_point_on_line (&point, &line_a),
2030 [2.0, 2.0].into());
2031 }
2032
2033 #[test]
2034 fn project_3d_point_on_line() {
2035 use Unit3;
2036 use super::project_3d_point_on_line;
2037 let point : Point3 <f64> = [2.0, 2.0, 0.0].into();
2039 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
2040 assert_eq!(project_3d_point_on_line (&point, &line), [2.0, 0.0, 0.0].into());
2041 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
2042 assert_eq!(project_3d_point_on_line (&point, &line), [0.0, 2.0, 0.0].into());
2043 let point : Point3 <f64> = [0.0, 1.0, 0.0].into();
2044 let line = Line3::<f64>::new (
2045 [0.0, -1.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
2046 assert_relative_eq!(
2047 project_3d_point_on_line (&point, &line), [1.0, 0.0, 0.0].into());
2048 let point : Point3 <f64> = [1.0, 3.0, 0.0].into();
2050 let line_a = Line3::<f64>::new (
2051 [0.0, -1.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
2052 let line_b = Line3::<f64>::new (
2053 [2.0, 0.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
2054 assert_relative_eq!(
2055 project_3d_point_on_line (&point, &line_a),
2056 project_3d_point_on_line (&point, &line_b));
2057 let line_a = Line3::<f64>::new (
2058 [0.0, 0.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
2059 let line_b = Line3::<f64>::new (
2060 [2.0, 2.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
2061 assert_relative_eq!(
2062 project_3d_point_on_line (&point, &line_a),
2063 project_3d_point_on_line (&point, &line_b));
2064 assert_relative_eq!(
2065 project_3d_point_on_line (&point, &line_a),
2066 [2.0, 2.0, 0.0].into());
2067 let point : Point3 <f64> = [0.0, 0.0, 2.0].into();
2069 let line_a = Line3::<f64>::new (
2070 [-4.0, -4.0, -4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
2071 let line_b = Line3::<f64>::new (
2072 [4.0, 4.0, 4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
2073 assert_relative_eq!(
2074 project_3d_point_on_line (&point, &line_a),
2075 project_3d_point_on_line (&point, &line_b),
2076 epsilon = 0.000_000_000_000_01
2077 );
2078 assert_relative_eq!(
2079 project_3d_point_on_line (&point, &line_a),
2080 [2.0/3.0, 2.0/3.0, 2.0/3.0].into(),
2081 epsilon = 0.000_000_000_000_01
2082 );
2083 }
2084
2085 #[test]
2086 fn smallest_rect() {
2087 use super::smallest_rect;
2088 let points : Vec <Point2 <f32>> = [
2090 [-3.0, -3.0],
2091 [ 3.0, -3.0],
2092 [ 3.0, 3.0],
2093 [ 0.0, 6.0],
2094 [-3.0, 3.0]
2095 ].map (Point2::from).into_iter().collect();
2096 let hull = Hull2::from_points (points.as_slice()).unwrap();
2097 assert_eq!(hull.points(), points);
2098 let rect = smallest_rect (0, 1, &hull);
2099 assert_eq!(rect.area, 54.0);
2100 let points : Vec <Point2 <f32>> = [
2102 [-1.0, -4.0],
2103 [ 2.0, 2.0],
2104 [-4.0, -1.0]
2105 ].map (Point2::from).into_iter().collect();
2106 let hull = Hull2::from_points (points.as_slice()).unwrap();
2107 assert_eq!(hull.points(), points);
2108 let rect = smallest_rect (0, 1, &hull);
2109 assert_eq!(rect.area, 27.0);
2110 }
2111
2112 #[test]
2113 fn obb2() {
2114 let points : Vec <Point2 <f32>> = [
2115 [-1.0, -4.0],
2116 [ 2.0, 2.0],
2117 [-4.0, -1.0]
2118 ].map (Point2::from).into_iter().collect();
2119 let obb = Obb2::containing_points (&points).unwrap();
2120 let corners = obb.corners();
2121 assert_eq!(obb.center, [-0.25, -0.25].into());
2122 approx::assert_relative_eq!(point2 (-4.0, -1.0), corners[0], max_relative = 0.00001);
2123 approx::assert_relative_eq!(point2 ( 0.5, 3.5), corners[1], max_relative = 0.00001);
2124 approx::assert_relative_eq!(point2 (-1.0, -4.0), corners[2], max_relative = 0.00001);
2125 approx::assert_relative_eq!(point2 ( 3.5, 0.5), corners[3], max_relative = 0.00001);
2126 }
2127}