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
18pub use self::hull::*;
19pub use self::simplex::{simplex2, simplex3, Simplex2, Simplex3};
20pub use simplex2::{
21 Segment as Segment2,
22 Triangle as Triangle2,
23 SegmentPoint as Segment2Point,
24 TrianglePoint as Triangle2Point
25};
26pub use simplex3::{
27 Segment as Segment3,
28 Triangle as Triangle3,
29 Tetrahedron as Tetrahedron3,
30 SegmentPoint as Segment3Point,
31 TrianglePoint as Triangle3Point,
32 TetrahedronPoint as Tetrahedron3Point
33};
34
35pub type Line2Point <S> = (S, Point2 <S>);
37pub type Line3Point <S> = (S, Point3 <S>);
39pub type Ray2Point <S> = (NonNegative <S>, Point2 <S>);
41pub type Ray3Point <S> = (NonNegative <S>, Point3 <S>);
43
44pub trait Primitive <S, P> : std::fmt::Debug where
46 S : Field,
47 P : AffineSpace <S>
48{
49 fn translate (&mut self, displacement : P::Translation);
50 fn scale (&mut self, scale : Positive <S>);
51 fn support (&self, direction : <P::Translation as VectorSpace <S>>::NonZero)
53 -> (P, S);
54}
55
56#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
62#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub struct Interval <S> {
64 min : S,
65 max : S
66}
67
68#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
88#[derive(Clone, Copy, Debug, Eq, PartialEq)]
89pub struct Aabb2 <S> {
90 min : Point2 <S>,
91 max : Point2 <S>
92}
93
94#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
120#[derive(Clone, Copy, Debug, Eq, PartialEq)]
121pub struct Aabb3 <S> {
122 min : Point3 <S>,
123 max : Point3 <S>
124}
125
126#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129pub struct Obb2 <S> {
130 pub center : Point2 <S>,
131 pub extents : Vector2 <Positive <S>>,
132 pub orientation : AngleWrapped <S>
133}
134
135#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
137#[derive(Clone, Copy, Debug, Eq, PartialEq)]
138pub struct Obb3 <S> {
139 pub center : Point3 <S>,
140 pub extents : Vector3 <Positive <S>>,
141 pub orientation : Angles3 <S>
142}
143
144#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
146#[derive(Clone, Copy, Debug, Eq, PartialEq)]
147pub struct Cylinder3 <S> {
148 pub center : Point3 <S>,
149 pub half_height : Positive <S>,
150 pub radius : Positive <S>
151}
152
153#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
155#[derive(Clone, Copy, Debug, Eq, PartialEq)]
156pub struct Capsule3 <S> {
157 pub center : Point3 <S>,
158 pub half_height : Positive <S>,
159 pub radius : Positive <S>
160}
161
162#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
164#[derive(Clone, Copy, Debug, Eq, PartialEq)]
165pub struct Cone3 <S> {
166 pub center : Point3 <S>,
167 pub half_height : Positive <S>,
168 pub radius : Positive <S>
169}
170
171#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
174#[derive(Clone, Copy, Debug, Eq, PartialEq)]
175pub struct Line2 <S> {
176 pub base : Point2 <S>,
177 pub direction : Unit2 <S>
178}
179
180#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
183#[derive(Clone, Copy, Debug, Eq, PartialEq)]
184pub struct Ray2 <S> {
185 pub base : Point2 <S>,
186 pub direction : Unit2 <S>
187}
188
189#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
196pub struct Line3 <S> {
197 pub base : Point3 <S>,
198 pub direction : Unit3 <S>
199}
200
201#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
204#[derive(Clone, Copy, Debug, Eq, PartialEq)]
205pub struct Ray3 <S> {
206 pub base : Point3 <S>,
207 pub direction : Unit3 <S>
208}
209
210#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
212#[derive(Clone, Copy, Debug, Eq, PartialEq)]
213pub struct Plane3 <S> {
214 pub base : Point3 <S>,
215 pub normal : Unit3 <S>
216}
217
218#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
220#[derive(Clone, Copy, Debug, Eq, PartialEq)]
221pub struct Sphere2 <S> {
222 pub center : Point2 <S>,
223 pub radius : Positive <S>
224}
225
226#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
228#[derive(Clone, Copy, Debug, Eq, PartialEq)]
229pub struct Sphere3 <S> {
230 pub center : Point3 <S>,
231 pub radius : Positive <S>
232}
233
234#[derive(Clone, Debug)]
237struct Rect <S> {
238 pub edge : Vector2 <S>,
239 pub perp : Vector2 <S>,
240 pub indices : [u32; 4],
242 pub area : S,
243 pub edge_len_squared : S
244}
245
246pub fn colinear_2d <S> (a : Point2 <S>, b : Point2 <S>, c : Point2 <S>) -> bool where
266 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
267{
268 use num::Zero;
269 let max_point_max_norm = a.0.norm_max().max (b.0.norm_max()).max (c.0.norm_max());
270 let min_edge_max_norm = (b - a).norm_max().min ((c - a).norm_max());
271 let ratio = if min_edge_max_norm == NonNegative::zero() {
272 NonNegative::zero()
273 } else {
274 max_point_max_norm / min_edge_max_norm
275 };
276 let epsilon = S::default_epsilon() * S::eight() * (S::one() + *ratio);
277 colinear_2d_epsilon (a, b, c, epsilon)
278}
279
280pub fn colinear_2d_epsilon <S> (
281 a : Point2 <S>, b : Point2 <S>, c : Point2 <S>, epsilon : S
282) -> bool where S : OrderedField {
283 if a == b || a == c || b == c {
285 return true
286 }
287 let e1 = b - a;
288 let e2 = c - a;
289 let e1_max = e1.norm_max();
291 let e2_max = e2.norm_max();
292 if *e1_max == S::zero() || *e2_max == S::zero() {
293 return true
294 }
295 let u = e1 / *e1_max;
296 let v = e2 / *e2_max;
297 let u_mag_sq = u.magnitude_squared();
298 let v_mag_sq = v.magnitude_squared();
299 let cross = u.exterior_product (v);
300 cross * cross <= epsilon * epsilon * u_mag_sq * v_mag_sq
301}
302
303pub fn colinear_3d <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>) -> bool where
314 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
315{
316 use num::Zero;
317 let max_epsilon = S::two().powi (-3);
318 let max_point_max_norm = a.0.norm_max().max (b.0.norm_max()).max (c.0.norm_max());
319 let min_edge_max_norm = (b - a).norm_max().min ((c - a).norm_max());
320 let ratio = if min_edge_max_norm == NonNegative::zero() {
321 NonNegative::zero()
322 } else {
323 max_point_max_norm / min_edge_max_norm
324 };
325 let epsilon = (S::default_epsilon() * S::eight() * (S::one() + *ratio))
326 .min (max_epsilon);
327 colinear_3d_epsilon (a, b, c, epsilon)
328}
329
330pub fn colinear_3d_epsilon <S> (
331 a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, epsilon : S
332) -> bool where
333 S : OrderedField
334{
335 if a == b || a == c || b == c {
337 return true
338 }
339 let e1 = b - a;
340 let e2 = c - a;
341 let e1_max = e1.norm_max();
343 let e2_max = e2.norm_max();
344 if *e1_max == S::zero() || *e2_max == S::zero() {
345 return true
346 }
347 let u = e1 / *e1_max;
348 let v = e2 / *e2_max;
349 let u_mag_sq = u.magnitude_squared();
350 let v_mag_sq = v.magnitude_squared();
351 let cross_sq = u.cross (v).magnitude_squared();
352 cross_sq <= epsilon * epsilon * u_mag_sq * v_mag_sq
353}
354
355pub fn coplanar_3d <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>)
367 -> bool
368where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
369 use num::Zero;
370 let max_epsilon = S::two().powi (-2);
371 let max_point_max_norm =
372 a.0.norm_max().max (b.0.norm_max()).max (c.0.norm_max()).max (d.0.norm_max());
373 let min_edge_max_norm =
374 (b - a).norm_max().min ((c - a).norm_max()).min ((d - a).norm_max());
375 let ratio = if min_edge_max_norm == NonNegative::zero() {
376 NonNegative::zero()
377 } else {
378 max_point_max_norm / min_edge_max_norm
379 };
380 let epsilon = (S::default_epsilon() * S::eight() * (S::one() + *ratio))
381 .min (max_epsilon);
382 coplanar_3d_epsilon (a, b, c, d, epsilon)
383}
384
385pub fn coplanar_3d_epsilon <S> (
386 a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>, epsilon : S
387) -> bool where
388 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
389{
390 let ab = b - a;
391 let ac = c - a;
392 let ad = d - a;
393 let ab_max_norm = ab.norm_max();
394 let ac_max_norm = ac.norm_max();
395 let ad_max_norm = ad.norm_max();
396 if *ab_max_norm == S::zero() || *ac_max_norm == S::zero()
397 || *ad_max_norm == S::zero()
398 {
399 return true
401 }
402 let u = ab / *ab_max_norm;
403 let v = ac / *ac_max_norm;
404 let w = ad / *ad_max_norm;
405 let n = u.cross (v);
406 let n_sq = n.magnitude_squared();
407 if n_sq == S::zero() {
408 return true
410 }
411 let tp = n.dot (w);
412 let w_sq = w.magnitude_squared();
413 tp * tp <= epsilon * epsilon * n_sq * w_sq
414}
415
416pub fn plane_side_3d <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>)
421 -> S
422where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
423 use num::Zero;
424 let max_epsilon = S::two().powi (-2);
425 let max_point_max_norm =
426 a.0.norm_max().max (b.0.norm_max()).max (c.0.norm_max()).max (d.0.norm_max());
427 let min_edge_max_norm =
428 (b - a).norm_max().min ((c - a).norm_max()).min ((d - a).norm_max());
429 let ratio = if min_edge_max_norm == NonNegative::zero() {
430 NonNegative::zero()
431 } else {
432 max_point_max_norm / min_edge_max_norm
433 };
434 let epsilon = (S::default_epsilon() * S::eight() * (S::one() + *ratio))
435 .min (max_epsilon);
436 plane_side_3d_epsilon (a, b, c, d, epsilon)
437}
438
439pub fn plane_side_3d_epsilon <S> (
440 a : Point3 <S>, b : Point3 <S>, c : Point3 <S>, d : Point3 <S>, epsilon : S
441) -> S where
442 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
443{
444 let ab = b - a;
445 let ac = c - a;
446 let ad = d - a;
447 let ab_max_norm = ab.norm_max();
448 let ac_max_norm = ac.norm_max();
449 let ad_max_norm = ad.norm_max();
450 if *ab_max_norm == S::zero() || *ac_max_norm == S::zero()
451 || *ad_max_norm == S::zero()
452 {
453 return S::zero()
455 }
456 let u = ab / *ab_max_norm;
457 let v = ac / *ac_max_norm;
458 let w = ad / *ad_max_norm;
459 let n = u.cross (v);
460 let n_sq = n.magnitude_squared();
461 if n_sq == S::zero() {
462 return S::zero()
464 }
465 let tp = n.dot (w);
466 let w_sq = w.magnitude_squared();
467 if tp * tp <= epsilon * epsilon * n_sq * w_sq {
468 S::zero()
469 } else if tp > S::zero() {
470 S::one()
471 } else {
472 -S::one()
473 }
474}
475
476#[inline]
478pub fn determinant_3d <S : Ring> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>) -> S {
479 Matrix3::from_col_arrays ([a.0.into_array(), b.0.into_array(), c.0.into_array()])
480 .determinant()
481}
482
483pub fn orthonormal_basis <S> (e1 : Unit3 <S>) -> [Unit3 <S>; 3] where S : Real {
485 use num::Zero;
486 let u = Unit3::axis_x();
487 let e2 = {
488 let mut e2 = (*e1).cross (*u);
489 e2.z += if e2.is_zero() {
490 S::one()
491 } else {
492 S::zero()
493 };
494 Unit3::new (e2).unwrap()
495 };
496 let e3 = Unit3::new ((*e1).cross (*e2)).unwrap();
497 [e1, e2, e3]
498}
499
500#[inline]
502pub fn point2_min <S : PartialOrd> (a : Point2 <S>, b : Point2 <S>) -> Point2 <S> {
503 Vector2::partial_min (a.0, b.0).into()
504}
505
506#[inline]
508pub fn point2_max <S : PartialOrd> (a : Point2 <S>, b : Point2 <S>) -> Point2 <S> {
509 Vector2::partial_max (a.0, b.0).into()
510}
511
512#[inline]
514pub fn point3_min <S : PartialOrd> (a : Point3 <S>, b : Point3 <S>) -> Point3 <S> {
515 Vector3::partial_min (a.0, b.0).into()
516}
517
518#[inline]
520pub fn point3_max <S : PartialOrd> (a : Point3 <S>, b : Point3 <S>) -> Point3 <S> {
521 Vector3::partial_max (a.0, b.0).into()
522}
523
524#[inline]
527pub fn project_2d_point_on_line <S : OrderedRing> (point : Point2 <S>, line : Line2 <S>)
528 -> Line2Point <S>
529{
530 let dot_dir = line.direction.dot (point - line.base);
531 (dot_dir, line.point (dot_dir))
532}
533
534#[inline]
537pub fn project_3d_point_on_line <S : OrderedRing> (point : Point3 <S>, line : Line3 <S>)
538 -> Line3Point <S>
539{
540 let dot_dir = line.direction.dot (point - line.base);
541 (dot_dir, line.point (dot_dir))
542}
543
544#[inline]
547pub fn project_3d_point_on_plane <S : Real> (point : Point3 <S>, plane : Plane3 <S>)
548 -> Point3 <S>
549{
550 let v = point - plane.base;
551 let distance = (*plane.normal).dot (v);
552 point - *plane.normal * distance
553}
554
555#[inline]
558pub fn support2 <S> (points : &[Point2 <S>], direction : NonZero2 <S>) -> (Point2 <S>, S)
559 where S : Ring + PartialOrd
560{
561 debug_assert!(!points.is_empty());
562 points.iter()
563 .map (|point| (*point, direction.dot (point.0)))
564 .max_by (|(_, dot_a), (_, dot_b)| dot_a.partial_cmp (dot_b).unwrap()).unwrap()
565}
566
567#[inline]
570pub fn support3 <S> (points : &[Point3 <S>], direction : NonZero3 <S>) -> (Point3 <S>, S)
571 where S : Ring + PartialOrd
572{
573 debug_assert!(!points.is_empty());
574 points.iter()
575 .map (|point| (*point, direction.dot (point.0)))
576 .max_by (|(_, dot_a), (_, dot_b)| dot_a.partial_cmp (dot_b).unwrap()).unwrap()
577}
578
579pub fn triangle_3d_area_squared <S> (a : Point3 <S>, b : Point3 <S>, c : Point3 <S>)
609 -> NonNegative <S>
610where
611 S : OrderedField + Sqrt + approx::AbsDiffEq <Epsilon = S>
612{
613 use num::Zero;
614 if a == b || a == c || b == c {
615 return NonNegative::zero()
616 }
617 let ab_mag = *(b-a).norm();
619 let ac_mag = *(c-a).norm();
620 let bc_mag = *(c-b).norm();
621 let min_ab_ac = S::min (ab_mag, ac_mag);
623 let max_ab_ac = S::max (ab_mag, ac_mag);
624 let (min, mid, max) = if bc_mag <= min_ab_ac {
625 (bc_mag, min_ab_ac, max_ab_ac)
626 } else if bc_mag >= max_ab_ac {
627 (min_ab_ac, max_ab_ac, bc_mag)
628 } else {
629 (min_ab_ac, bc_mag, max_ab_ac)
630 };
631 let frac = S::two().powi (-4);
633 let mut area_squared = frac
634 * (max + (mid + min))
635 * (min - (max - mid))
636 * (min + (max - mid))
637 * (max + (mid - min));
638 if area_squared < S::zero() {
642 if cfg!(debug_assertions) {
643 let lengths_squared = (b - a).magnitude_squared() + (c - b).magnitude_squared()
644 + (a - c).magnitude_squared();
645 approx::assert_abs_diff_eq!(area_squared, S::zero(),
646 epsilon = S::default_epsilon() * S::two().powi (44) * lengths_squared)
647 }
648 area_squared = S::zero();
649 }
650 NonNegative::unchecked (area_squared)
651}
652
653fn smallest_rect <S : Real> (i0 : u32, i1 : u32, hull : &Hull2 <S>) -> Rect <S> {
657 let points = hull.points();
661 let edge = points[i1 as usize] - points[i0 as usize];
662 let perp = vector2 (-edge.y, edge.x);
663 let edge_len_squared = edge.magnitude_squared();
664 let origin = points[i1 as usize];
665 let mut support = [Point2::<S>::origin(); 4];
666 let mut rect_index = [i1, i1, i1, i1];
667 #[expect(clippy::cast_possible_truncation)]
668 for (i, point) in points.iter().enumerate() {
669 let diff = point - origin;
670 let v = point2 (edge.dot (diff), perp.dot (diff));
671 if v.x() > support[1].x() || v.x() == support[1].x() && v.y() > support[1].y() {
672 rect_index[1] = i as u32;
673 support[1] = v;
674 }
675 if v.y() > support[2].y() || v.y() == support[2].y() && v.x() < support[2].x() {
676 rect_index[2] = i as u32;
677 support[2] = v;
678 }
679 if v.x() < support[3].x() || v.x() == support[3].x() && v.y() < support[3].y() {
680 rect_index[3] = i as u32;
681 support[3] = v;
682 }
683 }
684 let scaled_width = support[1].x() - support[3].x();
685 let scaled_height = support[2].y();
686 let area = scaled_width * scaled_height / edge_len_squared;
687
688 Rect {
689 edge, perp, area, edge_len_squared,
690 indices: [rect_index[0], rect_index[1], rect_index[2], rect_index[3]]
691 }
692}
693
694impl <S> Interval <S> where S : Copy {
699 #[inline]
700 pub const fn min (self) -> S {
701 self.min
702 }
703 #[inline]
704 pub const fn max (self) -> S {
705 self.max
706 }
707}
708impl <S> Interval <S> where
709 S : MinMax + Copy + PartialOrd + std::fmt::Debug
710{
711 #[inline]
712 pub fn from_points (a : S, b : S) -> Self {
713 let min = S::min (a, b);
714 let max = S::max (a, b);
715 Interval { min, max }
716 }
717 #[inline]
719 pub fn with_minmax (min : S, max : S) -> Option <Self> {
720 if min > max {
721 None
722 } else {
723 Some (Interval { min, max })
724 }
725 }
726 #[inline]
735 pub fn with_minmax_unchecked (min : S, max : S) -> Self {
736 debug_assert_eq!(min, S::min (min, max));
737 debug_assert_eq!(max, S::max (min, max));
738 Interval { min, max }
739 }
740 #[inline]
749 pub fn containing (points : &[S]) -> Option <Self> {
750 if points.len() < 2 {
751 None
752 } else {
753 debug_assert!(points.len() >= 2);
754 let mut min = S::min (points[0], points[1]);
755 let mut max = S::max (points[0], points[1]);
756 for point in points.iter().skip (2) {
757 min = S::min (min, *point);
758 max = S::max (max, *point);
759 }
760 Interval::with_minmax (min, max)
761 }
762 }
763 #[inline]
764 pub fn numcast <T> (self) -> Option <Interval <T>> where
765 S : num::ToPrimitive,
766 T : num::NumCast
767 {
768 Some (Interval {
769 min: T::from (self.min)?,
770 max: T::from (self.max)?
771 })
772 }
773 #[inline]
775 pub fn union (a : Interval <S>, b : Interval <S>) -> Self {
776 Interval::with_minmax_unchecked (
777 S::min (a.min(), b.min()), S::max (a.max(), b.max()))
778 }
779 #[inline]
780 pub fn length (self) -> NonNegative <S> where
781 S : num::Zero + std::ops::Sub <Output=S>
782 {
783 self.width()
784 }
785 #[inline]
786 pub fn width (self) -> NonNegative <S> where
787 S : num::Zero + std::ops::Sub <Output=S>
788 {
789 NonNegative::unchecked (self.max - self.min)
790 }
791 #[inline]
792 pub fn contains (self, point : S) -> bool {
793 self.min < point && point < self.max
794 }
795 #[inline]
805 pub fn clamp (self, point : S) -> S {
806 point.clamp (self.min, self.max)
807 }
808 #[inline]
833 pub fn rand_point <R> (self, rng : &mut R) -> S where
834 S : rand::distr::uniform::SampleUniform,
835 R : rand::RngExt
836 {
837 rng.random_range (self.min..self.max)
838 }
839 #[inline]
840 pub fn intersects (self, other : Interval <S>) -> bool {
841 intersect::discrete_interval (self, other)
842 }
843 #[inline]
844 pub fn intersection (self, other : Interval <S>) -> Option <Interval <S>> {
845 intersect::continuous_interval (self, other)
846 }
847
848 pub fn dilate (mut self, amount : S) -> Option <Self> where
858 S : std::ops::AddAssign + std::ops::SubAssign
859 {
860 self.min -= amount;
861 self.max += amount;
862 if self.min > self.max {
863 None
864 } else {
865 Some (self)
866 }
867 }
868}
869
870impl <S> Primitive <S, S> for Interval <S> where
871 S : OrderedField + AffineSpace <S, Translation=S>
872 + VectorSpace <S, NonZero=NonZero <S>>
873{
874 fn translate (&mut self, displacement : S) {
875 self.min += displacement;
876 self.max += displacement;
877 }
878 fn scale (&mut self, scale : Positive <S>) {
879 let old_width = *self.width();
880 let new_width = old_width * *scale;
881 let half_difference = (new_width - old_width) / S::two();
882 self.min -= half_difference;
883 self.max += half_difference;
884 }
885 fn support (&self, direction : NonZero <S>) -> (S, S) {
886 if *direction > S::zero() {
887 (self.max, self.max * *direction)
888 } else {
889 (self.min, self.min * *direction)
890 }
891 }
892}
893
894impl <S> std::ops::Add <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 add (mut self, displacement : S) -> Self {
901 self.translate (displacement);
902 self
903 }
904}
905
906impl <S> std::ops::Sub <S> for Interval <S> where
907 S : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
908 Self : Primitive <S, S>
909{
910 type Output = Self;
911 #[expect(clippy::renamed_function_params)]
912 fn sub (mut self, displacement : S) -> Self {
913 self.translate (-displacement);
914 self
915 }
916}
917
918impl_numcast_fields!(Aabb2, min, max);
919impl <S> Aabb2 <S> where S : Copy {
920 #[inline]
921 pub const fn min (self) -> Point2 <S> {
922 self.min
923 }
924 #[inline]
925 pub const fn max (self) -> Point2 <S> {
926 self.max
927 }
928}
929impl <S> Aabb2 <S> where
930 S : Copy + PartialOrd + std::fmt::Debug
931{
932 #[inline]
934 pub fn with_minmax (min : Point2 <S>, max : Point2 <S>) -> Option <Self> {
935 if min == max || min != point2_min (min, max) || max != point2_max (min, max) {
936 None
937 } else {
938 Some (Aabb2 { min, max })
939 }
940 }
941 #[inline]
943 pub fn from_points (a : Point2 <S>, b : Point2 <S>) -> Option <Self> {
944 if a == b {
945 None
946 } else {
947 let min = point2_min (a, b);
948 let max = point2_max (a, b);
949 Some (Aabb2 { min, max })
950 }
951 }
952 #[inline]
972 pub fn with_minmax_unchecked (min : Point2 <S>, max : Point2 <S>) -> Self {
973 debug_assert_ne!(min, max);
974 debug_assert_eq!(min, point2_min (min, max));
975 debug_assert_eq!(max, point2_max (min, max));
976 Aabb2 { min, max }
977 }
978 #[inline]
989 pub fn from_points_unchecked (a : Point2 <S>, b : Point2 <S>) -> Self {
990 debug_assert_ne!(a, b);
991 let min = point2_min (a, b);
992 let max = point2_max (a, b);
993 Aabb2 { min, max }
994 }
995 #[inline]
1009 pub fn containing (points : &[Point2 <S>]) -> Option <Self> {
1010 if points.len() < 2 {
1011 None
1012 } else {
1013 debug_assert!(points.len() >= 2);
1014 let mut min = point2_min (points[0], points[1]);
1015 let mut max = point2_max (points[0], points[1]);
1016 for point in points.iter().skip (2) {
1017 min = point2_min (min, *point);
1018 max = point2_max (max, *point);
1019 }
1020 Aabb2::with_minmax (min, max)
1021 }
1022 }
1023 #[inline]
1025 pub fn union (a : Aabb2 <S>, b : Aabb2 <S>) -> Self {
1026 Aabb2::with_minmax_unchecked (
1027 point2_min (a.min(), b.min()), point2_max (a.max(), b.max()))
1028 }
1029 #[inline]
1030 pub fn center (self) -> Point2 <S> where S : Ring + std::ops::Div <Output=S> {
1031 Point2 ((self.min.0 + self.max.0) / S::two())
1032 }
1033 #[inline]
1034 pub fn dimensions (self) -> Vector2 <NonNegative <S>> where
1035 S : num::Zero + std::ops::Sub <Output=S>
1036 {
1037 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1038 }
1039 #[inline]
1040 pub fn extents (self) -> Vector2 <NonNegative <S>> where S : Field {
1041 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1042 }
1043 #[inline]
1045 pub fn width (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1046 NonNegative::unchecked (self.max.x() - self.min.x())
1047 }
1048 #[inline]
1050 pub fn height (self) -> NonNegative <S> where
1051 S : num::Zero + std::ops::Sub <Output=S>
1052 {
1053 NonNegative::unchecked (self.max.y() - self.min.y())
1054 }
1055 #[inline]
1056 pub fn area (self) -> NonNegative <S> where
1057 S : num::Zero + std::ops::Sub <Output=S> + std::ops::Mul <Output=S>
1058 {
1059 self.width() * self.height()
1060 }
1061 #[inline]
1062 pub fn contains (self, point : Point2 <S>) -> bool {
1063 self.min.x() < point.x() && point.x() < self.max.x() &&
1064 self.min.y() < point.y() && point.y() < self.max.y()
1065 }
1066 pub fn clamp (self, point : Point2 <S>) -> Point2 <S> where S : MinMax {
1076 [ point.x().clamp (self.min.x(), self.max.x()),
1077 point.y().clamp (self.min.y(), self.max.y())
1078 ].into()
1079 }
1080 #[inline]
1108 pub fn rand_point <R> (self, rng : &mut R) -> Point2 <S> where
1109 S : rand::distr::uniform::SampleUniform,
1110 R : rand::RngExt
1111 {
1112 let x_range = self.min.x()..self.max.x();
1113 let y_range = self.min.y()..self.max.y();
1114 let x = if x_range.is_empty() {
1115 self.min.x()
1116 } else {
1117 rng.random_range (x_range)
1118 };
1119 let y = if y_range.is_empty() {
1120 self.min.y()
1121 } else {
1122 rng.random_range (y_range)
1123 };
1124 [x, y].into()
1125 }
1126 #[inline]
1127 pub fn intersects (self, other : Aabb2 <S>) -> bool {
1128 intersect::discrete_aabb2_aabb2 (self, other)
1129 }
1130 #[inline]
1131 pub fn intersection (self, other : Aabb2 <S>) -> Option <Aabb2 <S>> {
1132 intersect::continuous_aabb2_aabb2 (self, other)
1133 }
1134
1135 pub fn corner (self, quadrant : Quadrant) -> Point2 <S> {
1136 match quadrant {
1137 Quadrant::PosPos => self.max,
1139 Quadrant::PosNeg => [self.max.x(), self.min.y()].into(),
1141 Quadrant::NegPos => [self.min.x(), self.max.y()].into(),
1143 Quadrant::NegNeg => self.min
1145 }
1146 }
1147
1148 pub fn corners (self) -> [Point2 <S>; 4] {
1150 [ self.min, [self.min.x(), self.max.y()].into(),
1151 self.max, [self.max.x(), self.min.y()].into()
1152 ]
1153 }
1154
1155 pub fn edge (self, direction : SignedAxis2) -> Self {
1156 let (min, max) = match direction {
1157 SignedAxis2::PosX => (
1158 self.corner (Quadrant::PosNeg),
1159 self.corner (Quadrant::PosPos) ),
1160 SignedAxis2::NegX => (
1161 self.corner (Quadrant::NegNeg),
1162 self.corner (Quadrant::NegPos) ),
1163 SignedAxis2::PosY => (
1164 self.corner (Quadrant::NegPos),
1165 self.corner (Quadrant::PosPos) ),
1166 SignedAxis2::NegY => (
1167 self.corner (Quadrant::NegNeg),
1168 self.corner (Quadrant::PosNeg) )
1169 };
1170 Aabb2::with_minmax_unchecked (min, max)
1171 }
1172
1173 #[must_use]
1186 pub fn extrude (self, axis : SignedAxis2, amount : S) -> Option <Self> where S : Ring {
1187 let (p1, p2) = match axis {
1188 SignedAxis2::PosX => (
1189 self.min + Vector2::zero().with_x (*self.width()),
1190 self.max + Vector2::zero().with_x (amount) ),
1191 SignedAxis2::NegX => (
1192 self.min - Vector2::zero().with_x (amount),
1193 self.max - Vector2::zero().with_x (*self.height()) ),
1194 SignedAxis2::PosY => (
1195 self.min + Vector2::zero().with_y (*self.height()),
1196 self.max + Vector2::zero().with_y (amount) ),
1197 SignedAxis2::NegY => (
1198 self.min - Vector2::zero().with_y (amount),
1199 self.max - Vector2::zero().with_y (*self.height()) )
1200 };
1201 Self::from_points (p1, p2)
1202 }
1203
1204 #[must_use]
1206 pub fn extend (mut self, axis : SignedAxis2, amount : S) -> Option <Self>
1207 where S : std::ops::AddAssign + std::ops::SubAssign
1208 {
1209 match axis {
1210 SignedAxis2::PosX => self.max.0.x += amount,
1211 SignedAxis2::NegX => self.min.0.x -= amount,
1212 SignedAxis2::PosY => self.max.0.y += amount,
1213 SignedAxis2::NegY => self.min.0.y -= amount
1214 }
1215 Self::with_minmax (self.min, self.max)
1216 }
1217
1218 pub fn with_z (self, z : Interval <S>) -> Aabb3 <S> {
1219 Aabb3::with_minmax_unchecked (
1220 self.min.0.with_z (z.min()).into(),
1221 self.max.0.with_z (z.max()).into()
1222 )
1223 }
1224
1225 #[must_use]
1235 pub fn dilate (mut self, amount : S) -> Option <Self> where S : Ring {
1236 self.min -= Vector2::broadcast (amount);
1237 self.max += Vector2::broadcast (amount);
1238 if self.min == self.max || self.min != point2_min (self.min, self.max)
1239 || self.max != point2_max (self.min, self.max)
1240 {
1241 None
1242 } else {
1243 Some (self)
1244 }
1245 }
1246
1247 pub fn project (self, axis : Axis2) -> Interval <S> where S : MinMax {
1251 let (min, max) = match axis {
1252 Axis2::X => (self.min.y(), self.max.y()),
1253 Axis2::Y => (self.min.x(), self.max.x())
1254 };
1255 Interval::with_minmax_unchecked (min, max)
1256 }
1257}
1258
1259impl <S> Primitive <S, Point2 <S>> for Aabb2 <S> where S : OrderedField {
1260 fn translate (&mut self, displacement : Vector2 <S>) {
1261 self.min += displacement;
1262 self.max += displacement;
1263 }
1264 fn scale (&mut self, scale : Positive <S>) {
1265 let center = self.center();
1266 self.translate (-center.0);
1267 self.min.0 *= *scale;
1268 self.max.0 *= *scale;
1269 self.translate (center.0)
1270 }
1271 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
1272 let out = if let Some (quadrant) = Quadrant::from_vec_strict (*direction) {
1273 self.corner (quadrant)
1274 } else if direction.x > S::zero() {
1275 debug_assert_eq!(direction.y, S::zero());
1276 point2 (self.max.x(), (self.min.y() + self.max.y()) / S::two())
1277 } else if direction.x < S::zero() {
1278 debug_assert_eq!(direction.y, S::zero());
1279 point2 (self.min.x(), (self.min.y() + self.max.y()) / S::two())
1280 } else {
1281 debug_assert_eq!(direction.x, S::zero());
1282 if direction.y > S::zero() {
1283 point2 ((self.min.x() + self.max.x()) / S::two(), self.max.y())
1284 } else {
1285 debug_assert!(direction.y < S::zero());
1286 point2 ((self.min.x() + self.max.x()) / S::two(), self.min.y())
1287 }
1288 };
1289 (out, out.0.dot (*direction))
1290 }
1291}
1292
1293impl <S> std::ops::Add <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 add (mut self, displacement : Vector2 <S>) -> Self {
1300 self.translate (displacement);
1301 self
1302 }
1303}
1304
1305impl <S> std::ops::Sub <Vector2 <S>> for Aabb2 <S> where
1306 S : Field,
1307 Self : Primitive <S, Point2 <S>>
1308{
1309 type Output = Self;
1310 #[expect(clippy::renamed_function_params)]
1311 fn sub (mut self, displacement : Vector2 <S>) -> Self {
1312 self.translate (-displacement);
1313 self
1314 }
1315}
1316
1317impl_numcast_fields!(Aabb3, min, max);
1318impl <S> Aabb3 <S> where S : Copy {
1319 #[inline]
1320 pub const fn min (self) -> Point3 <S> {
1321 self.min
1322 }
1323 #[inline]
1324 pub const fn max (self) -> Point3 <S> {
1325 self.max
1326 }
1327}
1328impl <S> Aabb3 <S> where S : Copy + PartialOrd + std::fmt::Debug {
1329 #[inline]
1331 pub fn with_minmax (min : Point3 <S>, max : Point3 <S>) -> Option <Self> {
1332 if min == max || min != point3_min (min, max) || max != point3_max (min, max) {
1333 None
1334 } else {
1335 Some (Aabb3 { min, max })
1336 }
1337 }
1338 #[inline]
1340 pub fn from_points (a : Point3 <S>, b : Point3 <S>) -> Option <Self> {
1341 if a == b {
1342 None
1343 } else {
1344 let min = point3_min (a, b);
1345 let max = point3_max (a, b);
1346 Some (Aabb3 { min, max })
1347 }
1348 }
1349 #[inline]
1369 pub fn with_minmax_unchecked (min : Point3 <S>, max : Point3 <S>) -> Self {
1370 debug_assert_ne!(min, max);
1371 debug_assert_eq!(min, point3_min (min, max));
1372 debug_assert_eq!(max, point3_max (min, max));
1373 Aabb3 { min, max }
1374 }
1375 #[inline]
1386 pub fn from_points_unchecked (a : Point3 <S>, b : Point3 <S>) -> Self {
1387 debug_assert_ne!(a, b);
1388 let min = point3_min (a, b);
1389 let max = point3_max (a, b);
1390 Aabb3 { min, max }
1391 }
1392 #[inline]
1406 pub fn containing (points : &[Point3 <S>]) -> Option <Self> {
1407 debug_assert!(points.len() >= 2);
1408 let mut min = point3_min (points[0], points[1]);
1409 let mut max = point3_max (points[0], points[1]);
1410 for point in points.iter().skip (2) {
1411 min = point3_min (min, *point);
1412 max = point3_max (max, *point);
1413 }
1414 Aabb3::with_minmax (min, max)
1415 }
1416 #[inline]
1418 pub fn union (a : Aabb3 <S>, b : Aabb3 <S>) -> Self {
1419 Aabb3::with_minmax_unchecked (
1420 point3_min (a.min(), b.min()), point3_max (a.max(), b.max()))
1421 }
1422 #[inline]
1423 pub fn center (self) -> Point3 <S> where S : Ring + std::ops::Div <Output=S> {
1424 Point3 ((self.min.0 + self.max.0) / S::two())
1425 }
1426 #[inline]
1427 pub fn dimensions (self) -> Vector3 <NonNegative <S>> where
1428 S : num::Zero + std::ops::Sub <Output=S>
1429 {
1430 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1431 }
1432 #[inline]
1433 pub fn extents (self) -> Vector3 <NonNegative <S>> where S : Field {
1434 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1435 }
1436 #[inline]
1438 pub fn width (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1439 NonNegative::unchecked (self.max.x() - self.min.x())
1440 }
1441 #[inline]
1443 pub fn depth (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1444 NonNegative::unchecked (self.max.y() - self.min.y())
1445 }
1446 #[inline]
1448 pub fn height (self) -> NonNegative <S> where
1449 S : num::Zero + std::ops::Sub <Output=S>
1450 {
1451 NonNegative::unchecked (self.max.z() - self.min.z())
1452 }
1453 #[inline]
1454 pub fn volume (self) -> NonNegative <S> where
1455 S : num::Zero + std::ops::Sub <Output=S> + std::ops::Mul <Output=S>
1456 {
1457 self.width() * self.depth() * self.height()
1458 }
1459 #[inline]
1460 pub fn contains (self, point : Point3 <S>) -> bool {
1461 self.min.x() < point.x() && point.x() < self.max.x() &&
1462 self.min.y() < point.y() && point.y() < self.max.y() &&
1463 self.min.z() < point.z() && point.z() < self.max.z()
1464 }
1465 pub fn clamp (self, point : Point3 <S>) -> Point3 <S> where S : MinMax {
1477 [ point.x().clamp (self.min.x(), self.max.x()),
1478 point.y().clamp (self.min.y(), self.max.y()),
1479 point.z().clamp (self.min.z(), self.max.z())
1480 ].into()
1481 }
1482 #[inline]
1510 pub fn rand_point <R> (self, rng : &mut R) -> Point3 <S> where
1511 S : rand::distr::uniform::SampleUniform,
1512 R : rand::RngExt
1513 {
1514 let x_range = self.min.x()..self.max.x();
1515 let y_range = self.min.y()..self.max.y();
1516 let z_range = self.min.z()..self.max.z();
1517 let x = if x_range.is_empty() {
1518 self.min.x()
1519 } else {
1520 rng.random_range (x_range)
1521 };
1522 let y = if y_range.is_empty() {
1523 self.min.y()
1524 } else {
1525 rng.random_range (y_range)
1526 };
1527 let z = if z_range.is_empty() {
1528 self.min.z()
1529 } else {
1530 rng.random_range (z_range)
1531 };
1532 [x, y, z].into()
1533 }
1534 #[inline]
1535 pub fn intersects (self, other : Aabb3 <S>) -> bool {
1536 intersect::discrete_aabb3_aabb3 (self, other)
1537 }
1538 #[inline]
1539 pub fn intersection (self, other : Aabb3 <S>) -> Option <Aabb3 <S>> {
1540 intersect::continuous_aabb3_aabb3 (self, other)
1541 }
1542
1543 pub fn corner (self, octant : Octant) -> Point3 <S> {
1544 match octant {
1545 Octant::PosPosPos => self.max,
1546 Octant::NegPosPos => [self.min.x(), self.max.y(), self.max.z()].into(),
1547 Octant::PosNegPos => [self.max.x(), self.min.y(), self.max.z()].into(),
1548 Octant::NegNegPos => [self.min.x(), self.min.y(), self.max.z()].into(),
1549 Octant::PosPosNeg => [self.max.x(), self.max.y(), self.min.z()].into(),
1550 Octant::NegPosNeg => [self.min.x(), self.max.y(), self.min.z()].into(),
1551 Octant::PosNegNeg => [self.max.x(), self.min.y(), self.min.z()].into(),
1552 Octant::NegNegNeg => self.min
1553 }
1554 }
1555
1556 pub fn corners (self) -> [Point3 <S>; 8] {
1557 [ self.min,
1558 [self.min.x(), self.max.y(), self.max.z()].into(),
1559 [self.max.x(), self.min.y(), self.max.z()].into(),
1560 [self.min.x(), self.min.y(), self.max.z()].into(),
1561 [self.max.x(), self.max.y(), self.min.z()].into(),
1562 [self.min.x(), self.max.y(), self.min.z()].into(),
1563 [self.max.x(), self.min.y(), self.min.z()].into(),
1564 self.max
1565 ]
1566 }
1567
1568 pub fn face (self, direction : SignedAxis3) -> Self {
1569 let (min, max) = match direction {
1570 SignedAxis3::PosX => (
1571 self.corner (Octant::PosNegNeg),
1572 self.corner (Octant::PosPosPos) ),
1573 SignedAxis3::NegX => (
1574 self.corner (Octant::NegNegNeg),
1575 self.corner (Octant::NegPosPos) ),
1576 SignedAxis3::PosY => (
1577 self.corner (Octant::NegPosNeg),
1578 self.corner (Octant::PosPosPos) ),
1579 SignedAxis3::NegY => (
1580 self.corner (Octant::NegNegNeg),
1581 self.corner (Octant::PosNegPos) ),
1582 SignedAxis3::PosZ => (
1583 self.corner (Octant::NegNegPos),
1584 self.corner (Octant::PosPosPos) ),
1585 SignedAxis3::NegZ => (
1586 self.corner (Octant::NegNegNeg),
1587 self.corner (Octant::PosPosNeg) )
1588 };
1589 Aabb3::with_minmax_unchecked (min, max)
1590 }
1591
1592 #[must_use]
1605 pub fn extrude (self, axis : SignedAxis3, amount : S) -> Option <Self> where S : Ring {
1606 let (p1, p2) = match axis {
1607 SignedAxis3::PosX => (
1608 self.min + Vector3::zero().with_x (*self.width()),
1609 self.max + Vector3::zero().with_x (amount)
1610 ),
1611 SignedAxis3::NegX => (
1612 self.min - Vector3::zero().with_x (amount),
1613 self.max - Vector3::zero().with_x (*self.width())
1614 ),
1615 SignedAxis3::PosY => (
1616 self.min + Vector3::zero().with_y (*self.depth()),
1617 self.max + Vector3::zero().with_y (amount)
1618 ),
1619 SignedAxis3::NegY => (
1620 self.min - Vector3::zero().with_y (amount),
1621 self.max - Vector3::zero().with_y (*self.depth())
1622 ),
1623 SignedAxis3::PosZ => (
1624 self.min + Vector3::zero().with_z (*self.height()),
1625 self.max + Vector3::zero().with_z (amount)
1626 ),
1627 SignedAxis3::NegZ => (
1628 self.min - Vector3::zero().with_z (amount),
1629 self.max - Vector3::zero().with_z (*self.height())
1630 )
1631 };
1632 Aabb3::from_points (p1, p2)
1633 }
1634
1635 #[must_use]
1637 pub fn extend (mut self, axis : SignedAxis3, amount : S) -> Option <Self>
1638 where S : std::ops::AddAssign + std::ops::SubAssign
1639 {
1640 match axis {
1641 SignedAxis3::PosX => self.max.0.x += amount,
1642 SignedAxis3::NegX => self.min.0.x -= amount,
1643 SignedAxis3::PosY => self.max.0.y += amount,
1644 SignedAxis3::NegY => self.min.0.y -= amount,
1645 SignedAxis3::PosZ => self.max.0.z += amount,
1646 SignedAxis3::NegZ => self.min.0.z -= amount
1647 }
1648 Self::with_minmax (self.min, self.max)
1649 }
1650
1651 #[must_use]
1661 pub fn dilate (mut self, amount : S) -> Option <Self> where S : Ring {
1662 self.min -= Vector3::broadcast (amount);
1663 self.max += Vector3::broadcast (amount);
1664 if self.min == self.max || self.min != point3_min (self.min, self.max)
1665 || self.max != point3_max (self.min, self.max)
1666 {
1667 None
1668 } else {
1669 Some (self)
1670 }
1671 }
1672
1673 pub fn project (self, axis : Axis3) -> Aabb2 <S> {
1677 let (min, max) = match axis {
1678 Axis3::X => ([self.min.y(), self.min.z()], [self.max.y(), self.max.z()]),
1679 Axis3::Y => ([self.min.x(), self.min.z()], [self.max.x(), self.max.z()]),
1680 Axis3::Z => ([self.min.x(), self.min.y()], [self.max.x(), self.max.y()]),
1681 };
1682 Aabb2::with_minmax_unchecked (min.into(), max.into())
1683 }
1684}
1685
1686impl <S> Primitive <S, Point3 <S>> for Aabb3 <S> where S : OrderedField {
1687 fn translate (&mut self, displacement : Vector3 <S>) {
1688 self.min += displacement;
1689 self.max += displacement;
1690 }
1691 fn scale (&mut self, scale : Positive <S>) {
1692 let center = self.center();
1693 self.translate (-center.0);
1694 self.min.0 *= *scale;
1695 self.max.0 *= *scale;
1696 self.translate (center.0);
1697 }
1698 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
1699 let out = if let Some (octant) = Octant::from_vec_strict (*direction) {
1700 self.corner (octant)
1701 } else {
1702 use Sign::*;
1703 let center = self.center();
1704 match direction.map (S::sign).into_array() {
1705 [Positive, Zero, Zero] => center.0.with_x (self.max.x()).into(),
1707 [Negative, Zero, Zero] => center.0.with_x (self.min.x()).into(),
1708 [Zero, Positive, Zero] => center.0.with_y (self.max.y()).into(),
1709 [Zero, Negative, Zero] => center.0.with_y (self.min.y()).into(),
1710 [Zero, Zero, Positive] => center.0.with_z (self.max.z()).into(),
1711 [Zero, Zero, Negative] => center.0.with_z (self.min.z()).into(),
1712 [Positive, Positive, Zero] =>
1714 center.0.with_x (self.max.x()).with_y (self.max.y()).into(),
1715 [Positive, Negative, Zero] =>
1716 center.0.with_x (self.max.x()).with_y (self.min.y()).into(),
1717 [Negative, Positive, Zero] =>
1718 center.0.with_x (self.min.x()).with_y (self.max.y()).into(),
1719 [Negative, Negative, Zero] =>
1720 center.0.with_x (self.min.x()).with_y (self.min.y()).into(),
1721
1722 [Positive, Zero, Positive] =>
1723 center.0.with_x (self.max.x()).with_z (self.max.z()).into(),
1724 [Positive, Zero, Negative] =>
1725 center.0.with_x (self.max.x()).with_z (self.min.z()).into(),
1726 [Negative, Zero, Positive] =>
1727 center.0.with_x (self.min.x()).with_z (self.max.z()).into(),
1728 [Negative, Zero, Negative] =>
1729 center.0.with_x (self.min.x()).with_z (self.min.z()).into(),
1730
1731 [Zero, Positive, Positive] =>
1732 center.0.with_y (self.max.y()).with_z (self.max.z()).into(),
1733 [Zero, Positive, Negative] =>
1734 center.0.with_y (self.max.y()).with_z (self.min.z()).into(),
1735 [Zero, Negative, Positive] =>
1736 center.0.with_y (self.min.y()).with_z (self.max.z()).into(),
1737 [Zero, Negative, Negative] =>
1738 center.0.with_y (self.min.y()).with_z (self.min.z()).into(),
1739 [_, _, _] => unreachable!()
1740 }
1741 };
1742 (out, out.0.dot (*direction))
1743 }
1744}
1745
1746impl <S> std::ops::Add <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 add (mut self, displacement : Vector3 <S>) -> Self {
1753 self.translate (displacement);
1754 self
1755 }
1756}
1757
1758impl <S> std::ops::Sub <Vector3 <S>> for Aabb3 <S> where
1759 S : Field,
1760 Self : Primitive <S, Point3 <S>>
1761{
1762 type Output = Self;
1763 #[expect(clippy::renamed_function_params)]
1764 fn sub (mut self, displacement : Vector3 <S>) -> Self {
1765 self.translate (-displacement);
1766 self
1767 }
1768}
1769
1770impl_numcast_fields!(Obb2, center, extents, orientation);
1771impl <S : OrderedRing> Obb2 <S> {
1772 #[inline]
1774 pub const fn new (
1775 center : Point2 <S>,
1776 extents : Vector2 <Positive <S>>,
1777 orientation : AngleWrapped <S>
1778 ) -> Self {
1779 Obb2 { center, extents, orientation }
1780 }
1781 pub fn containing_points_with_orientation (
1785 points : &[Point2 <S>], orientation : AngleWrapped <S>
1786 ) -> Option <Self> where
1787 S : Real + num::real::Real + MaybeSerDes
1788 {
1789 let [x, y] = Rotation2::from_angle (orientation.angle()).cols
1790 .map (NonZero2::unchecked).into_array();
1791 let min_dot_x = -support2 (points, -x).1;
1792 let max_dot_x = support2 (points, x).1;
1793 let min_dot_y = -support2 (points, -y).1;
1794 let max_dot_y = support2 (points, y).1;
1795 let center = Point2 (
1796 (*x * min_dot_x + *x * max_dot_x) * S::half() +
1797 (*y * min_dot_y + *y * max_dot_y) * S::half());
1798 let extents = vector2 (
1799 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
1800 Positive::new (S::half() * (max_dot_y - min_dot_y))?);
1801 Some (Obb2 { center, extents, orientation })
1802 }
1803 pub fn containing_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
1821 let hull = Hull2::from_points (points)?;
1822 Self::containing (&hull)
1823 }
1824 pub fn containing (hull : &Hull2 <S>) -> Option <Self> where S : Real {
1843 let points = hull.points();
1844 if points.len() < 3 {
1845 return None
1846 }
1847 let mut visited = vec![false; points.len()];
1848 #[expect(clippy::cast_possible_truncation)]
1849 let mut min_rect = smallest_rect (points.len() as u32 - 1, 0, hull);
1850 visited[min_rect.indices[0] as usize] = true;
1851 let mut rect = min_rect.clone();
1853 for _ in 0..points.len() {
1854 let mut angles = [(S::zero(), u32::MAX); 4];
1855 let mut nangles = u32::MAX;
1856 if !compute_angles (hull, &rect, &mut angles, &mut nangles) {
1857 break
1858 }
1859 let sorted = sort_angles (&angles, nangles);
1860 if !update_support (&angles, nangles, &sorted, hull, &mut visited, &mut rect) {
1861 break
1862 }
1863 if rect.area < min_rect.area {
1864 min_rect = rect.clone();
1865 }
1866 }
1867 let sum = [
1869 points[min_rect.indices[1] as usize].0 + points[min_rect.indices[3] as usize].0,
1870 points[min_rect.indices[2] as usize].0 + points[min_rect.indices[0] as usize].0
1871 ];
1872 let diff = [
1873 points[min_rect.indices[1] as usize].0 - points[min_rect.indices[3] as usize].0,
1874 points[min_rect.indices[2] as usize].0 - points[min_rect.indices[0] as usize].0
1875 ];
1876 let obb = {
1877 let center = Point2::from ((min_rect.edge * min_rect.edge.dot (sum[0]) +
1878 min_rect.perp * min_rect.perp.dot (sum[1]))
1879 * S::half() / min_rect.edge_len_squared);
1880 let extents = {
1881 let extent_x = Positive::unchecked (S::sqrt (
1882 (S::half() * min_rect.edge.dot (diff[0])).squared()
1883 / min_rect.edge_len_squared));
1884 let extent_y = Positive::unchecked (S::sqrt (
1885 (S::half() * min_rect.perp.dot (diff[1])).squared()
1886 / min_rect.edge_len_squared));
1887 vector2 (extent_x, extent_y)
1888 };
1889 let orientation =
1890 AngleWrapped::wrap (Rad (min_rect.edge.y.atan2 (min_rect.edge.x)));
1891 Obb2 { center, extents, orientation }
1892 };
1893 fn compute_angles <S : Real> (
1894 hull : &Hull2 <S>,
1895 rect : &Rect <S>,
1896 angles : &mut [(S, u32); 4],
1897 nangles : &mut u32
1898 ) -> bool {
1899 *nangles = 0;
1900 let points = hull.points();
1901 let mut k0 = 3;
1902 let mut k1 = 0;
1903 while k1 < 4 {
1904 if rect.indices[k0] != rect.indices[k1] {
1905 let d = {
1906 let mut d = if k0 & 1 != 0 {
1907 rect.perp
1908 } else {
1909 rect.edge
1910 };
1911 if k0 & 2 != 0 {
1912 d = -d;
1913 }
1914 d
1915 };
1916 let j0 = rect.indices[k0];
1917 let mut j1 = j0 + 1;
1918 #[expect(clippy::cast_possible_truncation)]
1919 if j1 == points.len() as u32 {
1920 j1 = 0;
1921 }
1922 let e = points[j1 as usize] - points[j0 as usize];
1923 let dp = d.dot ([e.y, -e.x].into());
1924 let e_lensq = e.magnitude_squared();
1925 let sin_theta_squared = (dp * dp) / e_lensq;
1926 angles[*nangles as usize] =
1927 #[expect(clippy::cast_possible_truncation)]
1928 (sin_theta_squared, k0 as u32);
1929 *nangles += 1;
1930 }
1931 k0 = k1;
1932 k1 += 1;
1933 }
1934 *nangles > 0
1935 }
1936 fn sort_angles <S : PartialOrd> (angles : &[(S, u32); 4], nangles : u32)
1937 -> [u32; 4]
1938 {
1939 let mut sorted = [0u32, 1, 2, 3];
1940 match nangles {
1941 0 | 1 => {}
1942 2 => if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1943 sorted.swap (0, 1)
1944 }
1945 3 => {
1946 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1947 sorted.swap (0, 1)
1948 }
1949 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1950 sorted.swap (0, 2)
1951 }
1952 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1953 sorted.swap (1, 2)
1954 }
1955 }
1956 4 => {
1957 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1958 sorted.swap (0, 1)
1959 }
1960 if angles[sorted[2] as usize].0 > angles[sorted[3] as usize].0 {
1961 sorted.swap (2, 3)
1962 }
1963 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1964 sorted.swap (0, 2)
1965 }
1966 if angles[sorted[1] as usize].0 > angles[sorted[3] as usize].0 {
1967 sorted.swap (1, 3)
1968 }
1969 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1970 sorted.swap (1, 2)
1971 }
1972 }
1973 _ => unreachable!()
1974 }
1975 sorted
1976 }
1977 fn update_support <S : Real> (
1978 angles : &[(S, u32); 4],
1979 nangles : u32,
1980 sorted : &[u32; 4],
1981 hull : &Hull2 <S>,
1982 visited : &mut [bool],
1983 rect : &mut Rect <S>
1984 ) -> bool {
1985 let points = hull.points();
1986 let min_angle = angles[sorted[0] as usize];
1987 for k in 0..nangles as usize {
1988 let (angle, index) = angles[sorted[k] as usize];
1989 if angle == min_angle.0 {
1990 rect.indices[index as usize] += 1;
1991 #[expect(clippy::cast_possible_truncation)]
1992 if rect.indices[index as usize] == points.len() as u32 {
1993 rect.indices[index as usize] = 0;
1994 }
1995 }
1996 }
1997 let bottom = rect.indices[min_angle.1 as usize];
1998 if visited[bottom as usize] {
1999 return false
2000 }
2001 visited[bottom as usize] = true;
2002 let mut next_index = [u32::MAX; 4];
2003 for k in 0u32..4 {
2004 next_index[k as usize] = rect.indices[((min_angle.1 + k) % 4) as usize];
2005 }
2006 rect.indices = next_index;
2007 let j1 = rect.indices[0] as usize;
2008 let j0 = if j1 == 0 {
2009 points.len() - 1
2010 } else {
2011 j1 - 1
2012 };
2013 rect.edge = points[j1] - points[j0];
2014 rect.perp = vector2 (-rect.edge.y, rect.edge.x);
2015 let edge_len_squared = rect.edge.magnitude_squared();
2016 let diff = [
2017 points[rect.indices[1] as usize] - points[rect.indices[3] as usize],
2018 points[rect.indices[2] as usize] - points[rect.indices[0] as usize]
2019 ];
2020 rect.area = rect.edge.dot (diff[0]) * rect.perp.dot (diff[1]) / edge_len_squared;
2021 true
2022 }
2023 Some (obb)
2024 }
2025 #[inline]
2026 pub fn dimensions (self) -> Vector2 <Positive <S>> {
2027 self.extents * Positive::unchecked (S::two())
2028 }
2029 #[inline]
2031 pub fn width (self) -> Positive <S> {
2032 self.extents.x * Positive::unchecked (S::two())
2033 }
2034 #[inline]
2036 pub fn height (self) -> Positive <S> {
2037 self.extents.y * Positive::unchecked (S::two())
2038 }
2039 #[inline]
2040 pub fn area (self) -> Positive <S> {
2041 self.width() * self.height()
2042 }
2043 #[inline]
2044 pub fn corners (self) -> [Point2 <S>; 4] where
2045 S : Real + num::real::Real + MaybeSerDes
2046 {
2047 let mut points = [Point2::origin(); 4];
2048 for i in 0..2 {
2049 for j in 0..2 {
2050 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2051 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2052 points[i * 2 + j] = [*self.extents.x * sign_x, *self.extents.y * sign_y].into();
2053 }
2054 }
2055 let rotation = Rotation2::from_angle (self.orientation.angle());
2056 points.map (|point| rotation.rotate (point) + self.center.0)
2057 }
2058
2059 pub fn aabb (self) -> Aabb2 <S> where S : Real + num::real::Real + MaybeSerDes {
2060 Aabb2::containing (&self.corners()).unwrap()
2061 }
2062
2063 pub fn contains (self, point : Point2 <S>) -> bool where
2076 S : Real + num::real::Real + MaybeSerDes
2077 {
2078 let p = point - self.center.0;
2080 let p = Rotation2::from_angle (-self.orientation.angle()).rotate (p);
2082 p.x().abs() < *self.extents.x && p.y().abs() < *self.extents.y
2083 }
2084
2085 pub fn dilate (mut self, amount : S) -> Self {
2115 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2116 self
2117 }
2118}
2119impl <S : Real> From <Aabb2 <S>> for Obb2 <S> {
2120 fn from (aabb : Aabb2 <S>) -> Self {
2133 use num::Zero;
2134 let center = aabb.center();
2135 let extents = vector2 (
2136 Positive::noisy (*aabb.width() / S::two()),
2137 Positive::noisy (*aabb.height() / S::two()));
2138 let orientation = AngleWrapped::zero();
2139 Obb2::new (center, extents, orientation)
2140 }
2141}
2142impl <S> Primitive <S, Point2 <S>> for Obb2 <S> where
2143 S : Real + num::real::Real + MaybeSerDes
2144{
2145 fn translate (&mut self, displacement : Vector2 <S>) {
2146 self.center += displacement;
2147 }
2148 fn scale (&mut self, scale : Positive <S>) {
2149 self.extents *= scale;
2150 }
2151 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2152 use num::Inv;
2153 let rotation = Rotation2::from_angle (self.orientation.angle());
2154 let aabb = Aabb2::with_minmax_unchecked (
2155 self.center - self.extents.map (|e| *e),
2156 self.center + self.extents.map (|e| *e));
2157 let aabb_direction = rotation.inv() * direction;
2158 let (aabb_support, _) = aabb.support (aabb_direction);
2159 let out = rotation.rotate_around (aabb_support, self.center);
2160 (out, out.0.dot (*direction))
2161 }
2162}
2163
2164impl_numcast_fields!(Obb3, center, extents, orientation);
2165impl <S : OrderedRing> Obb3 <S> {
2166 #[inline]
2168 pub const fn new (
2169 center : Point3 <S>,
2170 extents : Vector3 <Positive <S>>,
2171 orientation : Angles3 <S>
2172 ) -> Self {
2173 Obb3 { center, extents, orientation }
2174 }
2175 pub fn containing_points_with_orientation (
2179 points : &[Point3 <S>], orientation : Angles3 <S>
2180 ) -> Option <Self> where
2181 S : Real + num::real::Real + MaybeSerDes
2182 {
2183 let [x, y, z] = Rotation3::from (orientation).cols.map (NonZero3::unchecked)
2184 .into_array();
2185 let min_dot_x = -support3 (points, -x).1;
2186 let max_dot_x = support3 (points, x).1;
2187 let min_dot_y = -support3 (points, -y).1;
2188 let max_dot_y = support3 (points, y).1;
2189 let min_dot_z = -support3 (points, -z).1;
2190 let max_dot_z = support3 (points, z).1;
2191 let center = Point3 (
2192 (*x * min_dot_x + *x * max_dot_x) * S::half() +
2193 (*y * min_dot_y + *y * max_dot_y) * S::half() +
2194 (*z * min_dot_z + *z * max_dot_z) * S::half());
2195 let extents = vector3 (
2196 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
2197 Positive::new (S::half() * (max_dot_y - min_dot_y))?,
2198 Positive::new (S::half() * (max_dot_z - min_dot_z))?);
2199 Some (Obb3 { center, extents, orientation })
2200 }
2201 pub fn containing_points_approx (points : &[Point3 <S>]) -> Option <Self> where
2218 S : Real + num::real::Real + approx::RelativeEq <Epsilon=S> + MaybeSerDes
2219 {
2220 if points.len() < 4 {
2221 return None
2222 }
2223 let (hull, mesh) = Hull3::from_points_with_mesh (points)?;
2224 Self::containing_approx (&hull, &mesh)
2225 }
2226 pub fn containing_approx (hull : &Hull3 <S>, mesh : &VertexEdgeTriangleMesh)
2245 -> Option <Self>
2246 where S : Real + num::NumCast + approx::RelativeEq <Epsilon=S> + MaybeSerDes {
2247 use num::Inv;
2250 if hull.points().len() < 4 {
2251 return None
2252 }
2253 let mut min_obb : Obb3 <S> = Aabb3::containing (hull.points()).unwrap().into();
2254 let mut min_vol = min_obb.volume();
2255 for triangle in mesh.triangles().values() {
2256 let [a, b, c] = triangle.vertices().map (|i| hull.points()[i as usize]);
2257 let mut u = b - a;
2258 let mut v = c - a;
2259 let mut w = u.cross (v);
2260 v = w.cross (u);
2261 u = *u.normalize();
2262 v = *v.normalize();
2263 w = *w.normalize();
2264 let m = Matrix3::from_row_arrays (std::array::from_fn (|i| [u[i], v[i], w[i]]));
2265 let m_rot = Rotation3::new_approx (m).unwrap();
2266 let rot_inv = m_rot.inv();
2267 let center = a;
2268 let rotated = hull.points().iter().copied()
2269 .map (|p| rot_inv.rotate_around (p, center))
2270 .collect::<Vec <_>>();
2271 let aabb = Aabb3::containing (rotated.as_slice()).unwrap();
2272 let aabb_volume = aabb.volume();
2273 if approx::abs_diff_eq!(*aabb_volume, S::zero()) {
2274 return None
2276 }
2277 let volume = Positive::new (*aabb_volume).unwrap();
2278 if volume < min_vol {
2279 min_vol = volume;
2280 min_obb = aabb.into();
2281 min_obb.orientation = m_rot.into();
2282 min_obb.center = m_rot.rotate_around (min_obb.center, center);
2283 }
2284 }
2285 Some (min_obb)
2286 }
2287 pub fn containing_approx_pca (hull : &Hull3 <S>) -> Option <Self> where
2306 S : Real + num::real::Real + num::NumCast + approx::RelativeEq <Epsilon=S>
2307 + MaybeSerDes
2308 {
2309 if hull.points().len() < 4 {
2310 return None
2311 }
2312 let [p1, p2, p3] = &hull.points()[..3] else { unreachable!() };
2316 let mut coplanar = true;
2317 for p in &hull.points()[3..] {
2318 if !coplanar_3d (*p1, *p2, *p3, *p) {
2319 coplanar = false;
2320 break
2321 }
2322 }
2323 if coplanar {
2324 return None
2325 }
2326 let orientation = data::pca_3 (hull.points()).into();
2328 Some (Self::containing_points_with_orientation (hull.points(), orientation).unwrap())
2329 }
2330 #[inline]
2331 pub fn dimensions (self) -> Vector3 <Positive <S>> {
2332 self.extents * Positive::unchecked (S::two())
2333 }
2334 #[inline]
2336 pub fn width (self) -> Positive <S> {
2337 self.extents.x * Positive::unchecked (S::two())
2338 }
2339 #[inline]
2341 pub fn depth (self) -> Positive <S> {
2342 self.extents.y * Positive::unchecked (S::two())
2343 }
2344 #[inline]
2346 pub fn height (self) -> Positive <S> {
2347 self.extents.z * Positive::unchecked (S::two())
2348 }
2349 #[inline]
2350 pub fn volume (self) -> Positive <S> {
2351 self.width() * self.depth() * self.height()
2352 }
2353 #[inline]
2354 pub fn corners (self) -> [Point3 <S>; 8] where
2355 S : Real + num::real::Real + MaybeSerDes
2356 {
2357 let mut points = [Point3::origin(); 8];
2358 for i in 0..2 {
2359 for j in 0..2 {
2360 for k in 0..2 {
2361 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2362 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2363 let sign_z = if k % 2 == 0 { -S::one() } else { S::one() };
2364 points[i * 4 + j * 2 + k] = [
2365 *self.extents.x * sign_x,
2366 *self.extents.y * sign_y,
2367 *self.extents.z * sign_z
2368 ].into();
2369 }
2370 }
2371 }
2372 let rotation = Rotation3::from (self.orientation);
2373 points.map (|point| rotation.rotate (point) + self.center.0)
2374 }
2375
2376 pub fn aabb (self) -> Aabb3 <S> where S : Real + num::real::Real + MaybeSerDes {
2377 Aabb3::containing (&self.corners()).unwrap()
2378 }
2379
2380 pub fn contains (self, point : Point3 <S>) -> bool where
2382 S : Real + num::real::Real + MaybeSerDes
2383 {
2384 use num::Inv;
2385 let p = point - self.center.0;
2387 let p = Rotation3::from (self.orientation).inv().rotate (p);
2389 p.x().abs() < *self.extents.x &&
2390 p.y().abs() < *self.extents.y &&
2391 p.z().abs() < *self.extents.z
2392 }
2393
2394 pub fn dilate (mut self, amount : S) -> Self {
2409 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2410 self
2411 }
2412}
2413impl <S : Real> From <Aabb3 <S>> for Obb3 <S> {
2414 fn from (aabb : Aabb3 <S>) -> Self {
2427 let center = aabb.center();
2428 let extents = aabb.extents().map (|d| Positive::noisy (*d));
2429 let orientation = Angles3::zero();
2430 Obb3::new (center, extents, orientation)
2431 }
2432}
2433impl <S> Primitive <S, Point3 <S>> for Obb3 <S> where
2434 S : Real + num::real::Real + MaybeSerDes
2435{
2436 fn translate (&mut self, displacement : Vector3 <S>) {
2437 self.center += displacement;
2438 }
2439 fn scale (&mut self, scale : Positive <S>) {
2440 self.extents *= scale;
2441 }
2442 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2443 use num::Inv;
2444 let rotation = Rotation3::from (self.orientation);
2445 let aabb = Aabb3::with_minmax_unchecked (
2446 self.center - self.extents.map (|e| *e),
2447 self.center + self.extents.map (|e| *e));
2448 let aabb_direction = rotation.inv() * direction;
2449 let (aabb_support, _) = aabb.support (aabb_direction);
2450 let out = rotation.rotate_around (aabb_support, self.center);
2451 (out, out.0.dot (*direction))
2452 }
2453}
2454
2455impl_numcast_fields!(Capsule3, center, half_height, radius);
2456impl <S : OrderedRing> Capsule3 <S> {
2457 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2458 use shape::Aabb;
2459 let shape_aabb = shape::Capsule {
2460 radius: self.radius,
2461 half_height: self.half_height
2462 }.aabb();
2463 let center_vec = self.center.0;
2464 let min = shape_aabb.min() + center_vec;
2465 let max = shape_aabb.max() + center_vec;
2466 Aabb3::with_minmax_unchecked (min, max)
2467 }
2468 pub fn decompose (self) -> (Sphere3 <S>, Option <Cylinder3 <S>>, Sphere3 <S>) {
2470 let cylinder = if *self.half_height > S::zero() {
2471 Some (Cylinder3 {
2472 center: self.center,
2473 radius: self.radius,
2474 half_height: Positive::unchecked (*self.half_height)
2475 })
2476 } else {
2477 None
2478 };
2479 let upper_sphere = Sphere3 {
2480 center: self.center + (Vector3::unit_z() * *self.half_height),
2481 radius: self.radius
2482 };
2483 let lower_sphere = Sphere3 {
2484 center: self.center - (Vector3::unit_z() * *self.half_height),
2485 radius: self.radius
2486 };
2487 (upper_sphere, cylinder, lower_sphere)
2488 }
2489}
2490impl <S> Primitive <S, Point3 <S>> for Capsule3 <S> where
2491 S : Real + num::real::Real + MaybeSerDes
2492{
2493 fn translate (&mut self, displacement : Vector3 <S>) {
2494 self.center += displacement;
2495 }
2496 fn scale (&mut self, scale : Positive <S>) {
2497 self.half_height *= scale;
2498 self.radius *= scale;
2499 }
2500 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2501 let dir_unit = direction.normalized();
2502 let out = self.center + if direction.z > S::zero() {
2503 Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2504 } else if direction.z < S::zero() {
2505 -Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2506 } else {
2507 dir_unit * *self.radius
2508 };
2509 (out, out.0.dot (*direction))
2510 }
2511}
2512
2513impl_numcast_fields!(Cylinder3, center, half_height, radius);
2514impl <S : OrderedRing> Cylinder3 <S> {
2515 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2516 use shape::Aabb;
2517 let shape_aabb = shape::Cylinder::unchecked (*self.radius, *self.half_height).aabb();
2518 let center_vec = self.center.0;
2519 let min = shape_aabb.min() + center_vec;
2520 let max = shape_aabb.max() + center_vec;
2521 Aabb3::with_minmax_unchecked (min, max)
2522 }
2523}
2524impl <S> Primitive <S, Point3 <S>> for Cylinder3 <S> where
2525 S : Real + num::real::Real + MaybeSerDes
2526{
2527 fn translate (&mut self, displacement : Vector3 <S>) {
2528 self.center += displacement;
2529 }
2530 fn scale (&mut self, scale : Positive <S>) {
2531 self.half_height *= scale;
2532 self.radius *= scale;
2533 }
2534 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2535 let out = self.center + if *direction == Vector3::unit_z() {
2536 Vector3::unit_z() * *self.half_height
2537 } else if *direction == -Vector3::unit_z() {
2538 -Vector3::unit_z() * *self.half_height
2539 } else if direction.z == S::zero() {
2540 direction.normalized() * *self.radius
2541 } else if direction.z > S::zero() {
2542 Vector3::unit_z() * *self.half_height
2543 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2544 } else {
2545 debug_assert!(direction.z < S::zero());
2546 -Vector3::unit_z() * *self.half_height
2547 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2548 };
2549 (out, out.0.dot (*direction))
2550 }
2551}
2552
2553impl_numcast_fields!(Cone3, center, half_height, radius);
2554impl <S : OrderedRing> Cone3 <S> {
2555 pub fn height (self) -> Positive <S> {
2556 self.half_height * Positive::unchecked (S::two())
2557 }
2558 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2559 use shape::Aabb;
2560 let shape_aabb = shape::Cone {
2561 radius: self.radius,
2562 half_height: self.half_height
2563 }.aabb();
2564 let center_vec = self.center.0;
2565 let min = shape_aabb.min() + center_vec;
2566 let max = shape_aabb.max() + center_vec;
2567 Aabb3::with_minmax_unchecked (min, max)
2568 }
2569}
2570impl <S> Primitive <S, Point3 <S>> for Cone3 <S> where
2571 S : Real + num::real::Real + approx::RelativeEq + MaybeSerDes
2572{
2573 fn translate (&mut self, displacement : Vector3 <S>) {
2574 self.center += displacement;
2575 }
2576 fn scale (&mut self, scale : Positive <S>) {
2577 self.half_height *= scale;
2578 self.radius *= scale;
2579 }
2580 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2581 let top = || Vector3::unit_z() * *self.half_height;
2582 let bottom = || -top();
2583 let edge = || bottom()
2584 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius;
2585 let out = self.center + if *direction == Vector3::unit_z() {
2586 top()
2587 } else if *direction == -Vector3::unit_z() {
2588 bottom()
2589 } else if direction.z <= S::zero() {
2590 edge()
2591 } else {
2592 let angle_dir =
2594 Trig::atan2 (direction.z, Sqrt::sqrt (S::one() - direction.z.squared()));
2595 debug_assert!(angle_dir > S::zero());
2596 let angle_cone = Trig::atan2 (-*self.height(), *self.radius);
2597 debug_assert!(angle_cone < S::zero());
2598 let pi_2 = S::pi() / S::two();
2599 let angle_total = angle_dir - angle_cone;
2600 if approx::relative_eq!(angle_total, pi_2) {
2601 (top() + edge()) * S::half()
2602 } else if angle_total > pi_2 {
2603 top()
2604 } else {
2605 debug_assert!(angle_total < pi_2);
2606 edge()
2607 }
2608 };
2609 (out, out.0.dot (*direction))
2610 }
2611}
2612
2613impl_numcast_fields!(Line2, base, direction);
2614impl <S : OrderedRing> Line2 <S> {
2615 #[inline]
2617 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2618 Line2 { base, direction }
2619 }
2620 #[inline]
2621 pub fn point (self, t : S) -> Point2 <S> {
2622 self.base + *self.direction * t
2623 }
2624 #[inline]
2625 pub fn affine_line (self) -> frame::Line2 <S> {
2626 frame::Line2 {
2627 origin: self.base,
2628 basis: self.direction.into()
2629 }
2630 }
2631 #[inline]
2632 pub fn nearest_point (self, point : Point2 <S>) -> Line2Point <S> {
2633 project_2d_point_on_line (point, self)
2634 }
2635 #[inline]
2636 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2637 -> Option <(Line2Point <S>, Line2Point <S>)>
2638 {
2639 intersect::continuous_line2_aabb2 (self, aabb)
2640 }
2641 #[inline]
2642 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2643 -> Option <(Line2Point <S>, Line2Point <S>)>
2644 where S : Field + Sqrt {
2645 intersect::continuous_line2_sphere2 (self, sphere)
2646 }
2647}
2648impl <S : Real> Default for Line2 <S> {
2649 fn default() -> Self {
2650 Line2 {
2651 base: Point2::origin(),
2652 direction: Unit2::axis_y()
2653 }
2654 }
2655}
2656impl <S> Primitive <S, Point2 <S>> for Line2 <S> where
2657 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2658{
2659 fn translate (&mut self, displacement : Vector2 <S>) {
2660 self.base += displacement;
2661 }
2662 #[expect(unused_variables)]
2663 fn scale (&mut self, scale : Positive <S>) {
2664 }
2666 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2667 let dot = self.direction.dot (*direction);
2668 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2669 self.base
2670 } else if dot > S::zero() {
2671 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2672 S::zero()
2673 } else {
2674 x * S::infinity()
2675 }))
2676 } else {
2677 debug_assert!(dot < S::zero());
2678 Point2 ((-self.direction).sigvec().map (|x| if x == S::zero() {
2679 S::zero()
2680 } else {
2681 x * S::infinity()
2682 }))
2683 };
2684 (out, out.0.dot (*direction))
2685 }
2686}
2687impl <S> From <Ray2 <S>> for Line2 <S> {
2688 fn from (Ray2 { base, direction } : Ray2 <S>) -> Line2 <S> {
2689 Line2 { base, direction }
2690 }
2691}
2692
2693impl_numcast_fields!(Ray2, base, direction);
2694impl <S : OrderedRing> Ray2 <S> {
2695 #[inline]
2697 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2698 Ray2 { base, direction }
2699 }
2700 #[inline]
2701 pub fn point (self, t : S) -> Point2 <S> {
2702 self.base + *self.direction * t
2703 }
2704 #[inline]
2705 pub fn affine_line (self) -> frame::Line2 <S> {
2706 frame::Line2 {
2707 origin: self.base,
2708 basis: self.direction.into()
2709 }
2710 }
2711 pub fn nearest_point (self, point : Point2 <S>) -> Ray2Point <S> {
2712 use num::Zero;
2713 let (t, point) = Line2::from (self).nearest_point (point);
2714 if t < S::zero() {
2715 (NonNegative::zero(), self.base)
2716 } else {
2717 (NonNegative::unchecked (t), point)
2718 }
2719 }
2720 #[inline]
2721 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2722 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2723 {
2724 intersect::continuous_ray2_aabb2 (self, aabb)
2725 }
2726 #[inline]
2727 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2728 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2729 {
2730 intersect::continuous_ray2_sphere2 (self, sphere)
2731 }
2732}
2733impl <S : Real> Default for Ray2 <S> {
2734 fn default() -> Self {
2735 Ray2 {
2736 base: Point2::origin(),
2737 direction: Unit2::axis_y()
2738 }
2739 }
2740}
2741impl <S> Primitive <S, Point2 <S>> for Ray2 <S> where
2742 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2743{
2744 fn translate (&mut self, displacement : Vector2 <S>) {
2745 self.base += displacement;
2746 }
2747 #[expect(unused_variables)]
2748 fn scale (&mut self, scale : Positive <S>) {
2749 }
2751 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2752 let dot = self.direction.dot (*direction);
2753 let out = if dot < S::default_epsilon() {
2754 self.base
2755 } else {
2756 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2757 S::zero()
2758 } else {
2759 x * S::infinity()
2760 }))
2761 };
2762 (out, out.0.dot (*direction))
2763 }
2764}
2765impl <S> From <Line2 <S>> for Ray2 <S> {
2766 fn from (Line2 { base, direction } : Line2 <S>) -> Ray2 <S> {
2767 Ray2 { base, direction }
2768 }
2769}
2770
2771impl_numcast_fields!(Line3, base, direction);
2772impl <S : OrderedRing> Line3 <S> {
2773 #[inline]
2775 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2776 Line3 { base, direction }
2777 }
2778 #[inline]
2779 pub fn point (self, t : S) -> Point3 <S> {
2780 self.base + *self.direction * t
2781 }
2782 #[inline]
2783 pub fn affine_line (self) -> frame::Line3 <S> {
2784 frame::Line3 {
2785 origin: self.base,
2786 basis: self.direction.into()
2787 }
2788 }
2789 #[inline]
2790 pub fn nearest_point (self, point : Point3 <S>) -> Line3Point <S> {
2791 project_3d_point_on_line (point, self)
2792 }
2793 #[inline]
2794 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Line3Point <S>> where
2795 S : Field + approx::RelativeEq
2796 {
2797 intersect::continuous_line3_plane3 (self, plane)
2798 }
2799 #[inline]
2800 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2801 -> Option <(Line3Point <S>, Line3Point <S>)>
2802 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2803 intersect::continuous_line3_aabb3 (self, aabb)
2804 }
2805 #[inline]
2806 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2807 -> Option <(Line3Point <S>, Line3Point <S>)>
2808 where S : Field + Sqrt {
2809 intersect::continuous_line3_sphere3 (self, sphere)
2810 }
2811 #[inline]
2812 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2813 -> Option <Line3Point <S>>
2814 where S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2815 intersect::continuous_line3_triangle3 (self, triangle)
2816 }
2817}
2818impl <S : Real> Default for Line3 <S> {
2819 fn default() -> Self {
2820 Line3 {
2821 base: Point3::origin(),
2822 direction: Unit3::axis_z()
2823 }
2824 }
2825}
2826impl <S> Primitive <S, Point3 <S>> for Line3 <S> where
2827 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2828{
2829 fn translate (&mut self, displacement : Vector3 <S>) {
2830 self.base += displacement;
2831 }
2832 #[expect(unused_variables)]
2833 fn scale (&mut self, scale : Positive <S>) {
2834 }
2836 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2837 let dot = self.direction.dot (*direction);
2838 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2839 self.base
2840 } else if dot > S::zero() {
2841 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2842 S::zero()
2843 } else {
2844 x * S::infinity()
2845 }))
2846 } else {
2847 debug_assert!(dot < S::zero());
2848 Point3 (-self.direction.sigvec().map (|x| if x == S::zero() {
2849 S::zero()
2850 } else {
2851 x * S::infinity()
2852 }))
2853 };
2854 (out, out.0.dot (*direction))
2855 }
2856}
2857impl <S> From <Ray3 <S>> for Line3 <S> {
2858 fn from (Ray3 { base, direction } : Ray3 <S>) -> Line3 <S> {
2859 Line3 { base, direction }
2860 }
2861}
2862
2863impl_numcast_fields!(Ray3, base, direction);
2864impl <S : OrderedRing> Ray3 <S> {
2865 #[inline]
2867 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2868 Ray3 { base, direction }
2869 }
2870 #[inline]
2871 pub fn point (self, t : S) -> Point3 <S> {
2872 self.base + *self.direction * t
2873 }
2874 #[inline]
2875 pub fn affine_line (self) -> frame::Line3 <S> {
2876 frame::Line3 {
2877 origin: self.base,
2878 basis: self.direction.into()
2879 }
2880 }
2881 pub fn nearest_point (self, point : Point3 <S>) -> Ray3Point <S> {
2882 use num::Zero;
2883 let (t, point) = Line3::from (self).nearest_point (point);
2884 if t < S::zero() {
2885 (NonNegative::zero(), self.base)
2886 } else {
2887 (NonNegative::unchecked (t), point)
2888 }
2889 }
2890 #[inline]
2891 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Ray3Point <S>> where
2892 S : approx::RelativeEq
2893 {
2894 intersect::continuous_ray3_plane3 (self, plane)
2895 }
2896 #[inline]
2897 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2898 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2899 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2900 intersect::continuous_ray3_aabb3 (self, aabb)
2901 }
2902 #[inline]
2903 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2904 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2905 {
2906 intersect::continuous_ray3_sphere3 (self, sphere)
2907 }
2908 #[inline]
2909 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2910 -> Option <Ray3Point <S>>
2911 where S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2912 intersect::continuous_ray3_triangle3 (self, triangle)
2913 }
2914}
2915impl <S : Real> Default for Ray3 <S> {
2916 fn default() -> Self {
2917 Ray3 {
2918 base: Point3::origin(),
2919 direction: Unit3::axis_z()
2920 }
2921 }
2922}
2923impl <S> Primitive <S, Point3 <S>> for Ray3 <S> where
2924 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2925{
2926 fn translate (&mut self, displacement : Vector3 <S>) {
2927 self.base += displacement;
2928 }
2929 #[expect(unused_variables)]
2930 fn scale (&mut self, scale : Positive <S>) {
2931 }
2933 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2934 let dot = self.direction.dot (*direction);
2935 let out = if dot < S::default_epsilon() {
2936 self.base
2937 } else {
2938 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2939 S::zero()
2940 } else {
2941 x * S::infinity()
2942 }))
2943 };
2944 (out, out.0.dot (*direction))
2945 }
2946}
2947impl <S> From <Line3 <S>> for Ray3 <S> {
2948 fn from (Line3 { base, direction } : Line3 <S>) -> Ray3 <S> {
2949 Ray3 { base, direction }
2950 }
2951}
2952
2953impl_numcast_fields!(Plane3, base, normal);
2954impl <S> Plane3 <S> {
2955 #[inline]
2957 pub const fn new (base : Point3 <S>, normal : Unit3 <S>) -> Self {
2958 Plane3 { base, normal }
2959 }
2960}
2961impl <S : Real> Default for Plane3 <S> {
2962 fn default() -> Self {
2963 Plane3 {
2964 base: Point3::origin(),
2965 normal: Unit3::axis_z()
2966 }
2967 }
2968}
2969impl <S> Primitive <S, Point3 <S>> for Plane3 <S> where
2970 S : Real + num::float::FloatCore + approx::RelativeEq
2971{
2972 fn translate (&mut self, displacement : Vector3 <S>) {
2973 self.base += displacement;
2974 }
2975 #[expect(unused_variables)]
2976 fn scale (&mut self, scale : Positive <S>) {
2977 }
2979 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2980 let dot = self.normal.dot (*direction);
2981 let out = if approx::relative_eq!(dot, S::one()) {
2982 self.base
2983 } else {
2984 let projected =
2985 project_3d_point_on_plane (Point3 (self.base.0 + *direction), *self).0;
2986 Point3 (projected.sigvec().map (|x| if x == S::zero() {
2987 S::zero()
2988 } else {
2989 x * S::infinity()
2990 }))
2991 };
2992 (out, out.0.dot (*direction))
2993 }
2994}
2995
2996impl_numcast_fields!(Sphere2, center, radius);
2997impl <S : OrderedRing> Sphere2 <S> {
2998 #[inline]
3000 pub fn unit() -> Self {
3001 Sphere2 {
3002 center: Point2::origin(),
3003 radius: num::One::one()
3004 }
3005 }
3006 #[inline]
3008 pub fn intersects (self, other : Self) -> bool {
3009 intersect::discrete_sphere2_sphere2 (self, other)
3010 }
3011}
3012impl <S> Primitive <S, Point2 <S>> for Sphere2 <S> where
3013 S : Real + num::real::Real + MaybeSerDes
3014{
3015 fn translate (&mut self, displacement : Vector2 <S>) {
3016 self.center += displacement;
3017 }
3018 fn scale (&mut self, scale : Positive <S>) {
3019 self.radius *= scale;
3020 }
3021 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
3022 let out = self.center + direction.normalized() * *self.radius;
3023 (out, out.0.dot (*direction))
3024 }
3025}
3026
3027impl_numcast_fields!(Sphere3, center, radius);
3028impl <S : OrderedRing> Sphere3 <S> {
3029 #[inline]
3031 pub fn unit() -> Self where S : Field {
3032 Sphere3 {
3033 center: Point3::origin(),
3034 radius: num::One::one()
3035 }
3036 }
3037 #[inline]
3039 pub fn intersects (self, other : Self) -> bool {
3040 intersect::discrete_sphere3_sphere3 (self, other)
3041 }
3042}
3043impl <S> Primitive <S, Point3 <S>> for Sphere3 <S> where
3044 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes
3045{
3046 fn translate (&mut self, displacement : Vector3 <S>) {
3047 self.center += displacement;
3048 }
3049 fn scale (&mut self, scale : Positive <S>) {
3050 self.radius *= scale;
3051 }
3052 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
3053 let out = self.center + direction.normalized() * *self.radius;
3054 (out, out.0.dot (*direction))
3055 }
3056}
3057
3058#[cfg(test)]
3063mod tests {
3064 use approx::{assert_relative_eq, assert_ulps_eq};
3065 use rand;
3066 use rand_distr;
3067 use rand_xorshift;
3068 use sorted_vec::partial::SortedSet;
3069 use super::*;
3070
3071 #[test]
3072 fn colinear_2() {
3073 use rand::{RngExt, SeedableRng};
3074 use rand_xorshift::XorShiftRng;
3075 use rand_distr::Distribution;
3076 macro test ($t:ty) {
3077 let mut rng = XorShiftRng::seed_from_u64 (0);
3078 let normal = rand_distr::StandardNormal;
3079 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3080 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3081 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3082 for _ in 0..100000 {
3084 let dir = Unit2::<$t>::normalize ([randn (&mut rng), randn (&mut rng)].into());
3085 let base = point2 (randf (&mut rng), randf (&mut rng));
3086 let line = Line2::new (base, dir);
3087 let p1 = line.point (randf (&mut rng));
3088 let p2 = line.point (randf (&mut rng));
3089 let p3 = line.point (randf (&mut rng));
3090 assert!(colinear_2d (p1, p2, p3));
3091 }
3092 for _ in 0..100000 {
3094 let p1 = point2 (randf (&mut rng), randf (&mut rng));
3095 let v = p1.0;
3096 let len = 0.1 + randf (&mut rng).abs();
3097 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3098 let rotation = Rotation2::from_angle (angle);
3099 let p2 = p1 + rotation * (v.normalized() * len);
3100 let v = p2 - p1;
3101 let len = 0.1 + randf (&mut rng).abs();
3102 let mut angle = rng.random_range (5.0..175.0);
3103 if rng.random_bool (0.5) {
3104 angle = -angle;
3105 }
3106 let rotation = Rotation2::from_angle (Deg (angle).into());
3107 let p3 = p2 + rotation * v * len;
3108 assert!(!colinear_2d (p1, p2, p3));
3109 }
3110 }
3111 test!(f32);
3112 test!(f64);
3113 }
3114
3115 #[test]
3116 fn colinear_3() {
3117 use rand::{RngExt, SeedableRng};
3118 use rand_xorshift::XorShiftRng;
3119 use rand_distr::Distribution;
3120 macro test ($t:ty) {
3121 let mut rng = XorShiftRng::seed_from_u64 (0);
3122 let normal = rand_distr::StandardNormal;
3123 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3124 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3125 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3126 for _ in 0..50000 {
3128 let dir = Unit3::<$t>::normalize (
3129 [randn (&mut rng), randn (&mut rng), randn (&mut rng)].into());
3130 let base = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3131 let line = Line3::new (base, dir);
3132 let p1 = line.point (randf (&mut rng));
3133 let p2 = line.point (randf (&mut rng));
3134 let p3 = line.point (randf (&mut rng));
3135 assert!(colinear_3d (p1, p2, p3));
3136 }
3137 for _ in 0..50000 {
3139 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3140 let v = p1.0;
3141 let len = 0.1 + randf (&mut rng).abs();
3142 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3143 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3144 let rotation = Rotation3::from_axis_angle (axis, angle);
3145 let p2 = p1 + rotation * (v.normalized() * len);
3146 let v = p2 - p1;
3147 let len = 0.1 + randf (&mut rng).abs();
3148 let axis = loop {
3149 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3150 if axis.normalized().cross (v.normalized()).magnitude() > 0.1 {
3151 break axis
3152 }
3153 };
3154 let mut angle = rng.random_range (5.0..175.0);
3155 if rng.random_bool (0.5) {
3156 angle = -angle;
3157 }
3158 let rotation = Rotation3::from_axis_angle (axis, Deg (angle).into());
3159 let p3 = p2 + rotation * v * len;
3160 assert!(!colinear_3d (p1, p2, p3));
3161 }
3162 }
3163 test!(f32);
3164 test!(f64);
3165 }
3166
3167 #[test]
3168 fn coplanar_3() {
3169 use rand::SeedableRng;
3170 use rand_xorshift::XorShiftRng;
3171 use rand_distr::Distribution;
3172 macro test ($t:ty) {
3173 let mut rng = XorShiftRng::seed_from_u64 (0);
3174 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3175 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3176 for _ in 0..50000 {
3178 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3179 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3180 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3181 let s = randf (&mut rng);
3182 let t = randf (&mut rng);
3183 let p4 = p1 + (p2 - p1) * s + (p3 - p1) * t;
3184 assert!(coplanar_3d::<$t> (p1, p2, p3, p4));
3185 }
3186 for _ in 0..50000 {
3188 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3189 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3190 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3191 let s = randf (&mut rng);
3192 let t = randf (&mut rng);
3193 let e1 = p2 - p1;
3194 let e2 = p3 - p1;
3195 let n = e1.cross (e2).normalized();
3196 let rn = randf (&mut rng);
3197 let pnew = p1 + e1 * s + e2 * t;
3198 let n_len = (0.3 * (pnew - p1).magnitude()).mul_add (rn.signum(), rn);
3199 let p4 = pnew + n * n_len;
3201 assert!(!coplanar_3d::<$t> (p1, p2, p3, p4));
3202 }
3203 }
3204 test!(f32);
3205 test!(f64);
3206 }
3207
3208 #[test]
3209 fn triangle_area_squared() {
3210 assert_relative_eq!(
3211 81.0,
3212 *triangle_3d_area_squared (
3213 [-2.0, -1.0, 0.0].into(),
3214 [ 1.0, -1.0, 0.0].into(),
3215 [ 1.0, 5.0, 0.0].into())
3216 );
3217 assert_relative_eq!(
3218 20.25,
3219 *triangle_3d_area_squared (
3220 [-2.0, -1.0, 0.0].into(),
3221 [ 1.0, -1.0, 0.0].into(),
3222 [ 1.0, 2.0, 0.0].into())
3223 );
3224 assert_relative_eq!(
3225 20.25,
3226 *triangle_3d_area_squared (
3227 [ 1.0, -1.0, 0.0].into(),
3228 [-2.0, -1.0, 0.0].into(),
3229 [ 1.0, 2.0, 0.0].into())
3230 );
3231 assert_relative_eq!(
3232 20.25,
3233 *triangle_3d_area_squared (
3234 [ 1.0, -1.0, 0.0].into(),
3235 [ 1.0, 2.0, 0.0].into(),
3236 [-2.0, -1.0, 0.0].into())
3237 );
3238 }
3239
3240 #[test]
3241 fn project_2d_point_on_line() {
3242 use super::project_2d_point_on_line;
3243 use Unit2;
3244 let point : Point2 <f64> = [2.0, 2.0].into();
3245 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_x());
3246 assert_eq!(project_2d_point_on_line (point, line).1, [2.0, 0.0].into());
3247 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_y());
3248 assert_eq!(project_2d_point_on_line (point, line).1, [0.0, 2.0].into());
3249 let point : Point2 <f64> = [0.0, 1.0].into();
3250 let line = Line2::<f64>::new (
3251 [0.0, -1.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3252 assert_relative_eq!(
3253 project_2d_point_on_line (point, line).1, [1.0, 0.0].into());
3254 let point : Point2 <f64> = [1.0, 3.0].into();
3256 let line_a = Line2::<f64>::new (
3257 [0.0, -1.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3258 let line_b = Line2::<f64>::new (
3259 [2.0, 0.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3260 assert_relative_eq!(
3261 project_2d_point_on_line (point, line_a).1,
3262 project_2d_point_on_line (point, line_b).1);
3263 let line_a = Line2::<f64>::new (
3264 [0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3265 let line_b = Line2::<f64>::new (
3266 [-2.0, -2.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3267 assert_ulps_eq!(
3268 project_2d_point_on_line (point, line_a).1,
3269 project_2d_point_on_line (point, line_b).1
3270 );
3271 assert_relative_eq!(
3272 project_2d_point_on_line (point, line_a).1,
3273 [2.0, 2.0].into());
3274 }
3275
3276 #[test]
3277 fn project_3d_point_on_line() {
3278 use Unit3;
3279 use super::project_3d_point_on_line;
3280 let point : Point3 <f64> = [2.0, 2.0, 0.0].into();
3282 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
3283 assert_eq!(project_3d_point_on_line (point, line).1, [2.0, 0.0, 0.0].into());
3284 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
3285 assert_eq!(project_3d_point_on_line (point, line).1, [0.0, 2.0, 0.0].into());
3286 let point : Point3 <f64> = [0.0, 1.0, 0.0].into();
3287 let line = Line3::<f64>::new (
3288 [0.0, -1.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3289 assert_relative_eq!(
3290 project_3d_point_on_line (point, line).1, [1.0, 0.0, 0.0].into());
3291 let point : Point3 <f64> = [1.0, 3.0, 0.0].into();
3293 let line_a = Line3::<f64>::new (
3294 [0.0, -1.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3295 let line_b = Line3::<f64>::new (
3296 [2.0, 0.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3297 assert_relative_eq!(
3298 project_3d_point_on_line (point, line_a).1,
3299 project_3d_point_on_line (point, line_b).1);
3300 let line_a = Line3::<f64>::new (
3301 [0.0, 0.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3302 let line_b = Line3::<f64>::new (
3303 [2.0, 2.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3304 assert_relative_eq!(
3305 project_3d_point_on_line (point, line_a).1,
3306 project_3d_point_on_line (point, line_b).1);
3307 assert_relative_eq!(
3308 project_3d_point_on_line (point, line_a).1,
3309 [2.0, 2.0, 0.0].into());
3310 let point : Point3 <f64> = [0.0, 0.0, 2.0].into();
3312 let line_a = Line3::<f64>::new (
3313 [-4.0, -4.0, -4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3314 let line_b = Line3::<f64>::new (
3315 [4.0, 4.0, 4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3316 assert_relative_eq!(
3317 project_3d_point_on_line (point, line_a).1,
3318 project_3d_point_on_line (point, line_b).1,
3319 epsilon = 0.000_000_000_000_01
3320 );
3321 assert_relative_eq!(
3322 project_3d_point_on_line (point, line_a).1,
3323 [2.0/3.0, 2.0/3.0, 2.0/3.0].into(),
3324 epsilon = 0.000_000_000_000_01
3325 );
3326 }
3327
3328 #[test]
3329 fn smallest_rect() {
3330 use super::smallest_rect;
3331 let points : Vec <Point2 <f32>> = [
3333 [-3.0, -3.0],
3334 [ 3.0, -3.0],
3335 [ 3.0, 3.0],
3336 [ 0.0, 6.0],
3337 [-3.0, 3.0]
3338 ].map (Point2::from).into_iter().collect();
3339 let hull = Hull2::from_points (points.as_slice()).unwrap();
3340 assert_eq!(hull.points(), points);
3341 let rect = smallest_rect (0, 1, &hull);
3342 assert_eq!(rect.area, 54.0);
3343 let points : Vec <Point2 <f32>> = [
3345 [-1.0, -4.0],
3346 [ 2.0, 2.0],
3347 [-4.0, -1.0]
3348 ].map (Point2::from).into_iter().collect();
3349 let hull = Hull2::from_points (points.as_slice()).unwrap();
3350 assert_eq!(hull.points(), points);
3351 let rect = smallest_rect (0, 1, &hull);
3352 assert_eq!(rect.area, 27.0);
3353 }
3354
3355 #[test]
3356 fn capsule3() {
3357 use num::One;
3358 let capsule : Capsule3 <f32> = Capsule3 {
3359 center: Point3::origin(),
3360 radius: Positive::noisy (2.0),
3361 half_height: Positive::one()
3362 };
3363 let support = capsule.support (Unit3::axis_z().into()).0;
3364 assert_eq!(support, point3 (0.0, 0.0, 3.0));
3365 let support = capsule.support (Unit3::axis_x().into()).0;
3366 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3367 let support = capsule.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3368 assert_eq!(support,
3369 point3 (0.0, 0.0, 1.0) + *Unit3::normalize (vector3 (1.0, 0.0, 1.0)) * 2.0);
3370 }
3371
3372 #[test]
3373 fn cylinder3() {
3374 use num::One;
3375 let cylinder : Cylinder3 <f32> = Cylinder3 {
3376 center: Point3::origin(),
3377 radius: Positive::noisy (2.0),
3378 half_height: Positive::one()
3379 };
3380 let support = cylinder.support (Unit3::axis_z().into()).0;
3381 assert_eq!(support, point3 (0.0, 0.0, 1.0));
3382 let support = cylinder.support (Unit3::axis_x().into()).0;
3383 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3384 let support = cylinder.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3385 assert_eq!(support, point3 (2.0, 0.0, 1.0));
3386 }
3387
3388 #[test]
3389 fn obb2() {
3390 let points : Vec <Point2 <f32>> = [
3391 [-1.0, -4.0],
3392 [ 2.0, 2.0],
3393 [-4.0, -1.0]
3394 ].map (Point2::from).into_iter().collect();
3395 let obb = Obb2::containing_points (&points).unwrap();
3396 let corners = obb.corners();
3397 assert_eq!(obb.center, [-0.25, -0.25].into());
3398 approx::assert_relative_eq!(point2 (-4.0, -1.0), corners[0], max_relative = 0.00001);
3399 approx::assert_relative_eq!(point2 ( 0.5, 3.5), corners[1], max_relative = 0.00001);
3400 approx::assert_relative_eq!(point2 (-1.0, -4.0), corners[2], max_relative = 0.00001);
3401 approx::assert_relative_eq!(point2 ( 3.5, 0.5), corners[3], max_relative = 0.00001);
3402 let points : Vec <Point2 <f32>> = [
3404 [-2.0, 0.0],
3405 [ 0.0, 2.0],
3406 [ 2.0, 0.0],
3407 [ 0.0, -2.0],
3408 ].map (Point2::from).into_iter().collect();
3409 let obb = Obb2::containing_points (&points).unwrap();
3410 let support = obb.support (Unit2::axis_y().into()).0;
3411 approx::assert_relative_eq!(support, point2 (0.0, 2.0), epsilon = 0.000_001);
3412 }
3413
3414 #[test]
3415 #[expect(clippy::suboptimal_flops)]
3416 fn obb3() {
3417 use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
3418 use approx::AbsDiffEq;
3419
3420 let points = [
3421 [ 1.0, -1.0, -1.0],
3422 [ 1.0, -1.0, 1.0],
3423 [ 1.0, 1.0, -1.0],
3424 [ 1.0, 1.0, 1.0],
3425 [-1.0, -1.0, -1.0],
3426 [-1.0, -1.0, 1.0],
3427 [-1.0, 1.0, -1.0],
3428 [-1.0, 1.0, 1.0]
3429 ].map (Point3::<f32>::from);
3430 let obb1 = Obb3::containing_points_with_orientation (&points, Angles3::zero())
3431 .unwrap();
3432 assert_eq!(
3433 SortedSet::from_unsorted (obb1.corners().to_vec()),
3434 SortedSet::from_unsorted (
3435 Aabb3::containing (&points).unwrap().corners().to_vec()));
3436 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3437 assert_eq!(obb1, obb2);
3438
3439 let rotation = Rotation3::from_angle_y (FRAC_PI_4.into());
3440 let points = points.map (|p| rotation.rotate (p));
3441 let obb1 = Obb3::containing_points_with_orientation (&points,
3442 Angles3::wrap (
3444 (3.0 * FRAC_PI_2).into(),
3445 (2.0 * PI - FRAC_PI_4).into(),
3446 FRAC_PI_2.into())
3447 ).unwrap();
3448 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3449 approx::assert_abs_diff_eq!(obb1.center, obb2.center);
3450 assert_eq!(obb1.orientation, obb2.orientation);
3451 approx::assert_abs_diff_eq!(*obb1.extents.x, *obb2.extents.x,
3452 epsilon=4.0 * f32::default_epsilon());
3453 approx::assert_abs_diff_eq!(*obb1.extents.y, *obb2.extents.y,
3454 epsilon=4.0 * f32::default_epsilon());
3455 approx::assert_abs_diff_eq!(*obb1.extents.z, *obb2.extents.z,
3456 epsilon=2.0 * f32::default_epsilon());
3457 let corners = obb2.corners();
3459 approx::assert_abs_diff_eq!(corners[0], points[7],
3460 epsilon=8.0 * f32::default_epsilon());
3461 approx::assert_abs_diff_eq!(corners[1], points[5],
3462 epsilon=8.0 * f32::default_epsilon());
3463 approx::assert_abs_diff_eq!(corners[2], points[3],
3464 epsilon=8.0 * f32::default_epsilon());
3465 approx::assert_abs_diff_eq!(corners[3], points[1],
3466 epsilon=8.0 * f32::default_epsilon());
3467 approx::assert_abs_diff_eq!(corners[4], points[6],
3468 epsilon=8.0 * f32::default_epsilon());
3469 approx::assert_abs_diff_eq!(corners[5], points[4],
3470 epsilon=8.0 * f32::default_epsilon());
3471 approx::assert_abs_diff_eq!(corners[6], points[2],
3472 epsilon=8.0 * f32::default_epsilon());
3473 approx::assert_abs_diff_eq!(corners[7], points[0],
3474 epsilon=8.0 * f32::default_epsilon());
3475
3476 let points : Vec <Point3 <f32>> = [
3478 [-2.0, 0.0, -2.0],
3479 [ 0.0, 2.0, -2.0],
3480 [ 2.0, 0.0, -2.0],
3481 [ 0.0, -2.0, -2.0],
3482 [-2.0, 0.0, 2.0],
3483 [ 0.0, 2.0, 2.0],
3484 [ 2.0, 0.0, 2.0],
3485 [ 0.0, -2.0, 2.0]
3486 ].map (Point3::from).into_iter().collect();
3487 let obb = Obb3::containing_points_approx (&points).unwrap();
3488 let support = obb.support (Unit3::axis_y().into()).0;
3489 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 0.0), epsilon = 0.000_001);
3490 let support = obb.support (NonZero3::noisy (vector3 (0.0, 1.0, 1.0))).0;
3491 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 2.0), epsilon = 0.000_001);
3492 }
3493
3494 #[test]
3495 fn support_aabb3() {
3496 let aabb = Aabb3::with_minmax (
3497 [-1.0, -2.0, -3.0].into(),
3498 [ 1.0, 2.0, 3.0].into()).unwrap();
3499 assert_eq!(
3500 [-1.0, -2.0, -3.0],
3501 aabb.support (NonZero3::noisy ([-1.0, -1.0, -1.0].into())).0.0.into_array());
3502 assert_eq!(
3503 [-1.0, -2.0, 3.0],
3504 aabb.support (NonZero3::noisy ([-1.0, -1.0, 1.0].into())).0.0.into_array());
3505 assert_eq!(
3506 [-1.0, 2.0, -3.0],
3507 aabb.support (NonZero3::noisy ([-1.0, 1.0, -1.0].into())).0.0.into_array());
3508 assert_eq!(
3509 [-1.0, 2.0, 3.0],
3510 aabb.support (NonZero3::noisy ([-1.0, 1.0, 1.0].into())).0.0.into_array());
3511 assert_eq!(
3512 [ 1.0, -2.0, -3.0],
3513 aabb.support (NonZero3::noisy ([ 1.0, -1.0, -1.0].into())).0.0.into_array());
3514 assert_eq!(
3515 [ 1.0, -2.0, 3.0],
3516 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 1.0].into())).0.0.into_array());
3517 assert_eq!(
3518 [ 1.0, 2.0, -3.0],
3519 aabb.support (NonZero3::noisy ([ 1.0, 1.0, -1.0].into())).0.0.into_array());
3520 assert_eq!(
3521 [ 1.0, 2.0, 3.0],
3522 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 1.0].into())).0.0.into_array());
3523
3524 assert_eq!(
3525 [-1.0, -2.0, 0.0],
3526 aabb.support (NonZero3::noisy ([-1.0, -1.0, 0.0].into())).0.0.into_array());
3527 assert_eq!(
3528 [-1.0, 2.0, 0.0],
3529 aabb.support (NonZero3::noisy ([-1.0, 1.0, 0.0].into())).0.0.into_array());
3530 assert_eq!(
3531 [ 1.0, -2.0, 0.0],
3532 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 0.0].into())).0.0.into_array());
3533 assert_eq!(
3534 [ 1.0, 2.0, 0.0],
3535 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 0.0].into())).0.0.into_array());
3536
3537 assert_eq!(
3538 [-1.0, 0.0, -3.0],
3539 aabb.support (NonZero3::noisy ([-1.0, 0.0, -1.0].into())).0.0.into_array());
3540 assert_eq!(
3541 [-1.0, 0.0, 3.0],
3542 aabb.support (NonZero3::noisy ([-1.0, 0.0, 1.0].into())).0.0.into_array());
3543 assert_eq!(
3544 [ 1.0, 0.0, -3.0],
3545 aabb.support (NonZero3::noisy ([ 1.0, 0.0, -1.0].into())).0.0.into_array());
3546 assert_eq!(
3547 [ 1.0, 0.0, 3.0],
3548 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 1.0].into())).0.0.into_array());
3549
3550 assert_eq!(
3551 [ 0.0, -2.0, -3.0],
3552 aabb.support (NonZero3::noisy ([ 0.0, -1.0, -1.0].into())).0.0.into_array());
3553 assert_eq!(
3554 [ 0.0, -2.0, 3.0],
3555 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 1.0].into())).0.0.into_array());
3556 assert_eq!(
3557 [ 0.0, 2.0, -3.0],
3558 aabb.support (NonZero3::noisy ([ 0.0, 1.0, -1.0].into())).0.0.into_array());
3559 assert_eq!(
3560 [ 0.0, 2.0, 3.0],
3561 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 1.0].into())).0.0.into_array());
3562
3563 assert_eq!(
3564 [-1.0, 0.0, 0.0],
3565 aabb.support (NonZero3::noisy ([-1.0, 0.0, 0.0].into())).0.0.into_array());
3566 assert_eq!(
3567 [ 1.0, 0.0, 0.0],
3568 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 0.0].into())).0.0.into_array());
3569 assert_eq!(
3570 [ 0.0, -2.0, 0.0],
3571 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 0.0].into())).0.0.into_array());
3572 assert_eq!(
3573 [ 0.0, 2.0, 0.0],
3574 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 0.0].into())).0.0.into_array());
3575 assert_eq!(
3576 [ 0.0, 0.0, -3.0],
3577 aabb.support (NonZero3::noisy ([ 0.0, 0.0, -1.0].into())).0.0.into_array());
3578 assert_eq!(
3579 [ 0.0, 0.0, 3.0],
3580 aabb.support (NonZero3::noisy ([ 0.0, 0.0, 1.0].into())).0.0.into_array());
3581 }
3582}