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 : OrderedRing> Interval <S> {
699 #[inline]
700 pub fn from_points (a : S, b : S) -> Self {
701 let min = S::min (a, b);
702 let max = S::max (a, b);
703 Interval { min, max }
704 }
705 #[inline]
707 pub fn with_minmax (min : S, max : S) -> Option <Self> {
708 if min > max {
709 None
710 } else {
711 Some (Interval { min, max })
712 }
713 }
714 #[inline]
723 pub fn with_minmax_unchecked (min : S, max : S) -> Self {
724 debug_assert_eq!(min, S::min (min, max));
725 debug_assert_eq!(max, S::max (min, max));
726 Interval { min, max }
727 }
728 #[inline]
737 pub fn containing (points : &[S]) -> Option <Self> {
738 if points.len() < 2 {
739 None
740 } else {
741 debug_assert!(points.len() >= 2);
742 let mut min = S::min (points[0], points[1]);
743 let mut max = S::max (points[0], points[1]);
744 for point in points.iter().skip (2) {
745 min = S::min (min, *point);
746 max = S::max (max, *point);
747 }
748 Interval::with_minmax (min, max)
749 }
750 }
751 #[inline]
752 pub fn numcast <T> (self) -> Option <Interval <T>> where
753 S : num::ToPrimitive,
754 T : num::NumCast
755 {
756 Some (Interval {
757 min: T::from (self.min)?,
758 max: T::from (self.max)?
759 })
760 }
761 #[inline]
763 pub fn union (a : Interval <S>, b : Interval <S>) -> Self {
764 Interval::with_minmax_unchecked (
765 S::min (a.min(), b.min()), S::max (a.max(), b.max()))
766 }
767 #[inline]
768 pub const fn min (self) -> S {
769 self.min
770 }
771 #[inline]
772 pub const fn max (self) -> S {
773 self.max
774 }
775 #[inline]
776 pub fn length (self) -> NonNegative <S> {
777 self.width()
778 }
779 #[inline]
780 pub fn width (self) -> NonNegative <S> {
781 NonNegative::unchecked (self.max - self.min)
782 }
783 #[inline]
784 pub fn contains (self, point : S) -> bool {
785 self.min < point && point < self.max
786 }
787 #[inline]
797 pub fn clamp (self, point : S) -> S {
798 point.clamp (self.min, self.max)
799 }
800 #[inline]
825 pub fn rand_point <R> (self, rng : &mut R) -> S where
826 S : rand::distr::uniform::SampleUniform,
827 R : rand::RngExt
828 {
829 rng.random_range (self.min..self.max)
830 }
831 #[inline]
832 pub fn intersects (self, other : Interval <S>) -> bool {
833 intersect::discrete_interval (self, other)
834 }
835 #[inline]
836 pub fn intersection (self, other : Interval <S>) -> Option <Interval <S>> {
837 intersect::continuous_interval (self, other)
838 }
839
840 pub fn dilate (mut self, amount : S) -> Option <Self> {
850 self.min -= amount;
851 self.max += amount;
852 if self.min > self.max {
853 None
854 } else {
855 Some (self)
856 }
857 }
858}
859
860impl <S> Primitive <S, S> for Interval <S> where
861 S : OrderedField + AffineSpace <S, Translation=S>
862 + VectorSpace <S, NonZero=NonZero <S>>
863{
864 fn translate (&mut self, displacement : S) {
865 self.min += displacement;
866 self.max += displacement;
867 }
868 fn scale (&mut self, scale : Positive <S>) {
869 let old_width = *self.width();
870 let new_width = old_width * *scale;
871 let half_difference = (new_width - old_width) / S::two();
872 self.min -= half_difference;
873 self.max += half_difference;
874 }
875 fn support (&self, direction : NonZero <S>) -> (S, S) {
876 if *direction > S::zero() {
877 (self.max, self.max * *direction)
878 } else {
879 (self.min, self.min * *direction)
880 }
881 }
882}
883
884impl <S> std::ops::Add <S> for Interval <S> where
885 S : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
886 Self : Primitive <S, S>
887{
888 type Output = Self;
889 #[expect(clippy::renamed_function_params)]
890 fn add (mut self, displacement : S) -> Self {
891 self.translate (displacement);
892 self
893 }
894}
895
896impl <S> std::ops::Sub <S> for Interval <S> where
897 S : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
898 Self : Primitive <S, S>
899{
900 type Output = Self;
901 #[expect(clippy::renamed_function_params)]
902 fn sub (mut self, displacement : S) -> Self {
903 self.translate (-displacement);
904 self
905 }
906}
907
908impl_numcast_fields!(Aabb2, min, max);
909impl <S : OrderedRing> Aabb2 <S> {
910 #[inline]
912 pub fn with_minmax (min : Point2 <S>, max : Point2 <S>) -> Option <Self> {
913 if min == max || min != point2_min (min, max) || max != point2_max (min, max) {
914 None
915 } else {
916 Some (Aabb2 { min, max })
917 }
918 }
919 #[inline]
921 pub fn from_points (a : Point2 <S>, b : Point2 <S>) -> Option <Self> {
922 if a == b {
923 None
924 } else {
925 let min = point2_min (a, b);
926 let max = point2_max (a, b);
927 Some (Aabb2 { min, max })
928 }
929 }
930 #[inline]
950 pub fn with_minmax_unchecked (min : Point2 <S>, max : Point2 <S>) -> Self {
951 debug_assert_ne!(min, max);
952 debug_assert_eq!(min, point2_min (min, max));
953 debug_assert_eq!(max, point2_max (min, max));
954 Aabb2 { min, max }
955 }
956 #[inline]
967 pub fn from_points_unchecked (a : Point2 <S>, b : Point2 <S>) -> Self {
968 debug_assert_ne!(a, b);
969 let min = point2_min (a, b);
970 let max = point2_max (a, b);
971 Aabb2 { min, max }
972 }
973 #[inline]
987 pub fn containing (points : &[Point2 <S>]) -> Option <Self> {
988 if points.len() < 2 {
989 None
990 } else {
991 debug_assert!(points.len() >= 2);
992 let mut min = point2_min (points[0], points[1]);
993 let mut max = point2_max (points[0], points[1]);
994 for point in points.iter().skip (2) {
995 min = point2_min (min, *point);
996 max = point2_max (max, *point);
997 }
998 Aabb2::with_minmax (min, max)
999 }
1000 }
1001 #[inline]
1003 pub fn union (a : Aabb2 <S>, b : Aabb2 <S>) -> Self {
1004 Aabb2::with_minmax_unchecked (
1005 point2_min (a.min(), b.min()), point2_max (a.max(), b.max()))
1006 }
1007 #[inline]
1008 pub const fn min (self) -> Point2 <S> {
1009 self.min
1010 }
1011 #[inline]
1012 pub const fn max (self) -> Point2 <S> {
1013 self.max
1014 }
1015 #[inline]
1016 pub fn center (self) -> Point2 <S> {
1017 Point2 ((self.min.0 + self.max.0) / S::two())
1018 }
1019 #[inline]
1020 pub fn dimensions (self) -> Vector2 <NonNegative <S>> {
1021 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1022 }
1023 #[inline]
1024 pub fn extents (self) -> Vector2 <NonNegative <S>> where S : Field {
1025 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1026 }
1027 #[inline]
1029 pub fn width (self) -> NonNegative <S> {
1030 NonNegative::unchecked (self.max.x() - self.min.x())
1031 }
1032 #[inline]
1034 pub fn height (self) -> NonNegative <S> {
1035 NonNegative::unchecked (self.max.y() - self.min.y())
1036 }
1037 #[inline]
1038 pub fn area (self) -> NonNegative <S> {
1039 self.width() * self.height()
1040 }
1041 #[inline]
1042 pub fn contains (self, point : Point2 <S>) -> bool {
1043 self.min.x() < point.x() && point.x() < self.max.x() &&
1044 self.min.y() < point.y() && point.y() < self.max.y()
1045 }
1046 pub fn clamp (self, point : Point2 <S>) -> Point2 <S> {
1056 [ point.x().clamp (self.min.x(), self.max.x()),
1057 point.y().clamp (self.min.y(), self.max.y())
1058 ].into()
1059 }
1060 #[inline]
1088 pub fn rand_point <R> (self, rng : &mut R) -> Point2 <S> where
1089 S : rand::distr::uniform::SampleUniform,
1090 R : rand::RngExt
1091 {
1092 let x_range = self.min.x()..self.max.x();
1093 let y_range = self.min.y()..self.max.y();
1094 let x = if x_range.is_empty() {
1095 self.min.x()
1096 } else {
1097 rng.random_range (x_range)
1098 };
1099 let y = if y_range.is_empty() {
1100 self.min.y()
1101 } else {
1102 rng.random_range (y_range)
1103 };
1104 [x, y].into()
1105 }
1106 #[inline]
1107 pub fn intersects (self, other : Aabb2 <S>) -> bool {
1108 intersect::discrete_aabb2_aabb2 (self, other)
1109 }
1110 #[inline]
1111 pub fn intersection (self, other : Aabb2 <S>) -> Option <Aabb2 <S>> {
1112 intersect::continuous_aabb2_aabb2 (self, other)
1113 }
1114
1115 pub fn corner (self, quadrant : Quadrant) -> Point2 <S> {
1116 match quadrant {
1117 Quadrant::PosPos => self.max,
1119 Quadrant::PosNeg => [self.max.x(), self.min.y()].into(),
1121 Quadrant::NegPos => [self.min.x(), self.max.y()].into(),
1123 Quadrant::NegNeg => self.min
1125 }
1126 }
1127
1128 pub fn corners (self) -> [Point2 <S>; 4] {
1130 [ self.min, [self.min.x(), self.max.y()].into(),
1131 self.max, [self.max.x(), self.min.y()].into()
1132 ]
1133 }
1134
1135 pub fn edge (self, direction : SignedAxis2) -> Self {
1136 let (min, max) = match direction {
1137 SignedAxis2::PosX => (
1138 self.corner (Quadrant::PosNeg),
1139 self.corner (Quadrant::PosPos) ),
1140 SignedAxis2::NegX => (
1141 self.corner (Quadrant::NegNeg),
1142 self.corner (Quadrant::NegPos) ),
1143 SignedAxis2::PosY => (
1144 self.corner (Quadrant::NegPos),
1145 self.corner (Quadrant::PosPos) ),
1146 SignedAxis2::NegY => (
1147 self.corner (Quadrant::NegNeg),
1148 self.corner (Quadrant::PosNeg) )
1149 };
1150 Aabb2::with_minmax_unchecked (min, max)
1151 }
1152
1153 #[must_use]
1166 pub fn extrude (self, axis : SignedAxis2, amount : S) -> Option <Self> {
1167 let (p1, p2) = match axis {
1168 SignedAxis2::PosX => (
1169 self.min + Vector2::zero().with_x (*self.width()),
1170 self.max + Vector2::zero().with_x (amount) ),
1171 SignedAxis2::NegX => (
1172 self.min - Vector2::zero().with_x (amount),
1173 self.max - Vector2::zero().with_x (*self.height()) ),
1174 SignedAxis2::PosY => (
1175 self.min + Vector2::zero().with_y (*self.height()),
1176 self.max + Vector2::zero().with_y (amount) ),
1177 SignedAxis2::NegY => (
1178 self.min - Vector2::zero().with_y (amount),
1179 self.max - Vector2::zero().with_y (*self.height()) )
1180 };
1181 Self::from_points (p1, p2)
1182 }
1183
1184 #[must_use]
1186 pub fn extend (mut self, axis : SignedAxis2, amount : S) -> Option <Self> {
1187 match axis {
1188 SignedAxis2::PosX => self.max.0.x += amount,
1189 SignedAxis2::NegX => self.min.0.x -= amount,
1190 SignedAxis2::PosY => self.max.0.y += amount,
1191 SignedAxis2::NegY => self.min.0.y -= amount
1192 }
1193 Self::with_minmax (self.min, self.max)
1194 }
1195
1196 pub fn with_z (self, z : Interval <S>) -> Aabb3 <S> {
1197 Aabb3::with_minmax_unchecked (
1198 self.min.0.with_z (z.min()).into(),
1199 self.max.0.with_z (z.max()).into()
1200 )
1201 }
1202
1203 #[must_use]
1213 pub fn dilate (mut self, amount : S) -> Option <Self> {
1214 self.min -= Vector2::broadcast (amount);
1215 self.max += Vector2::broadcast (amount);
1216 if self.min == self.max || self.min != point2_min (self.min, self.max)
1217 || self.max != point2_max (self.min, self.max)
1218 {
1219 None
1220 } else {
1221 Some (self)
1222 }
1223 }
1224
1225 pub fn project (self, axis : Axis2) -> Interval <S> {
1229 let (min, max) = match axis {
1230 Axis2::X => (self.min.y(), self.max.y()),
1231 Axis2::Y => (self.min.x(), self.max.x())
1232 };
1233 Interval::with_minmax_unchecked (min, max)
1234 }
1235}
1236
1237impl <S> Primitive <S, Point2 <S>> for Aabb2 <S> where S : OrderedField {
1238 fn translate (&mut self, displacement : Vector2 <S>) {
1239 self.min += displacement;
1240 self.max += displacement;
1241 }
1242 fn scale (&mut self, scale : Positive <S>) {
1243 let center = self.center();
1244 self.translate (-center.0);
1245 self.min.0 *= *scale;
1246 self.max.0 *= *scale;
1247 self.translate (center.0)
1248 }
1249 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
1250 let out = if let Some (quadrant) = Quadrant::from_vec_strict (*direction) {
1251 self.corner (quadrant)
1252 } else if direction.x > S::zero() {
1253 debug_assert_eq!(direction.y, S::zero());
1254 point2 (self.max.x(), (self.min.y() + self.max.y()) / S::two())
1255 } else if direction.x < S::zero() {
1256 debug_assert_eq!(direction.y, S::zero());
1257 point2 (self.min.x(), (self.min.y() + self.max.y()) / S::two())
1258 } else {
1259 debug_assert_eq!(direction.x, S::zero());
1260 if direction.y > S::zero() {
1261 point2 ((self.min.x() + self.max.x()) / S::two(), self.max.y())
1262 } else {
1263 debug_assert!(direction.y < S::zero());
1264 point2 ((self.min.x() + self.max.x()) / S::two(), self.min.y())
1265 }
1266 };
1267 (out, out.0.dot (*direction))
1268 }
1269}
1270
1271impl <S> std::ops::Add <Vector2 <S>> for Aabb2 <S> where
1272 S : Field,
1273 Self : Primitive <S, Point2 <S>>
1274{
1275 type Output = Self;
1276 #[expect(clippy::renamed_function_params)]
1277 fn add (mut self, displacement : Vector2 <S>) -> Self {
1278 self.translate (displacement);
1279 self
1280 }
1281}
1282
1283impl <S> std::ops::Sub <Vector2 <S>> for Aabb2 <S> where
1284 S : Field,
1285 Self : Primitive <S, Point2 <S>>
1286{
1287 type Output = Self;
1288 #[expect(clippy::renamed_function_params)]
1289 fn sub (mut self, displacement : Vector2 <S>) -> Self {
1290 self.translate (-displacement);
1291 self
1292 }
1293}
1294
1295impl_numcast_fields!(Aabb3, min, max);
1296impl <S : OrderedRing> Aabb3 <S> {
1297 #[inline]
1299 pub fn with_minmax (min : Point3 <S>, max : Point3 <S>) -> Option <Self> {
1300 if min == max || min != point3_min (min, max) || max != point3_max (min, max) {
1301 None
1302 } else {
1303 Some (Aabb3 { min, max })
1304 }
1305 }
1306 #[inline]
1308 pub fn from_points (a : Point3 <S>, b : Point3 <S>) -> Option <Self> {
1309 if a == b {
1310 None
1311 } else {
1312 let min = point3_min (a, b);
1313 let max = point3_max (a, b);
1314 Some (Aabb3 { min, max })
1315 }
1316 }
1317 #[inline]
1337 pub fn with_minmax_unchecked (min : Point3 <S>, max : Point3 <S>) -> Self {
1338 debug_assert_ne!(min, max);
1339 debug_assert_eq!(min, point3_min (min, max));
1340 debug_assert_eq!(max, point3_max (min, max));
1341 Aabb3 { min, max }
1342 }
1343 #[inline]
1354 pub fn from_points_unchecked (a : Point3 <S>, b : Point3 <S>) -> Self {
1355 debug_assert_ne!(a, b);
1356 let min = point3_min (a, b);
1357 let max = point3_max (a, b);
1358 Aabb3 { min, max }
1359 }
1360 #[inline]
1374 pub fn containing (points : &[Point3 <S>]) -> Option <Self> {
1375 debug_assert!(points.len() >= 2);
1376 let mut min = point3_min (points[0], points[1]);
1377 let mut max = point3_max (points[0], points[1]);
1378 for point in points.iter().skip (2) {
1379 min = point3_min (min, *point);
1380 max = point3_max (max, *point);
1381 }
1382 Aabb3::with_minmax (min, max)
1383 }
1384 #[inline]
1386 pub fn union (a : Aabb3 <S>, b : Aabb3 <S>) -> Self {
1387 Aabb3::with_minmax_unchecked (
1388 point3_min (a.min(), b.min()), point3_max (a.max(), b.max()))
1389 }
1390 #[inline]
1391 pub const fn min (self) -> Point3 <S> {
1392 self.min
1393 }
1394 #[inline]
1395 pub const fn max (self) -> Point3 <S> {
1396 self.max
1397 }
1398 #[inline]
1399 pub fn center (self) -> Point3 <S> {
1400 Point3 ((self.min.0 + self.max.0) / S::two())
1401 }
1402 #[inline]
1403 pub fn dimensions (self) -> Vector3 <NonNegative <S>> {
1404 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1405 }
1406 #[inline]
1407 pub fn extents (self) -> Vector3 <NonNegative <S>> where S : Field {
1408 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1409 }
1410 #[inline]
1412 pub fn width (self) -> NonNegative <S> {
1413 NonNegative::unchecked (self.max.x() - self.min.x())
1414 }
1415 #[inline]
1417 pub fn depth (self) -> NonNegative <S> {
1418 NonNegative::unchecked (self.max.y() - self.min.y())
1419 }
1420 #[inline]
1422 pub fn height (self) -> NonNegative <S> {
1423 NonNegative::unchecked (self.max.z() - self.min.z())
1424 }
1425 #[inline]
1426 pub fn volume (self) -> NonNegative <S> {
1427 self.width() * self.depth() * self.height()
1428 }
1429 #[inline]
1430 pub fn contains (self, point : Point3 <S>) -> bool {
1431 self.min.x() < point.x() && point.x() < self.max.x() &&
1432 self.min.y() < point.y() && point.y() < self.max.y() &&
1433 self.min.z() < point.z() && point.z() < self.max.z()
1434 }
1435 pub fn clamp (self, point : Point3 <S>) -> Point3 <S> {
1447 [ point.x().clamp (self.min.x(), self.max.x()),
1448 point.y().clamp (self.min.y(), self.max.y()),
1449 point.z().clamp (self.min.z(), self.max.z())
1450 ].into()
1451 }
1452 #[inline]
1480 pub fn rand_point <R> (self, rng : &mut R) -> Point3 <S> where
1481 S : rand::distr::uniform::SampleUniform,
1482 R : rand::RngExt
1483 {
1484 let x_range = self.min.x()..self.max.x();
1485 let y_range = self.min.y()..self.max.y();
1486 let z_range = self.min.z()..self.max.z();
1487 let x = if x_range.is_empty() {
1488 self.min.x()
1489 } else {
1490 rng.random_range (x_range)
1491 };
1492 let y = if y_range.is_empty() {
1493 self.min.y()
1494 } else {
1495 rng.random_range (y_range)
1496 };
1497 let z = if z_range.is_empty() {
1498 self.min.z()
1499 } else {
1500 rng.random_range (z_range)
1501 };
1502 [x, y, z].into()
1503 }
1504 #[inline]
1505 pub fn intersects (self, other : Aabb3 <S>) -> bool {
1506 intersect::discrete_aabb3_aabb3 (self, other)
1507 }
1508 #[inline]
1509 pub fn intersection (self, other : Aabb3 <S>) -> Option <Aabb3 <S>> {
1510 intersect::continuous_aabb3_aabb3 (self, other)
1511 }
1512
1513 pub fn corner (self, octant : Octant) -> Point3 <S> {
1514 match octant {
1515 Octant::PosPosPos => self.max,
1516 Octant::NegPosPos => [self.min.x(), self.max.y(), self.max.z()].into(),
1517 Octant::PosNegPos => [self.max.x(), self.min.y(), self.max.z()].into(),
1518 Octant::NegNegPos => [self.min.x(), self.min.y(), self.max.z()].into(),
1519 Octant::PosPosNeg => [self.max.x(), self.max.y(), self.min.z()].into(),
1520 Octant::NegPosNeg => [self.min.x(), self.max.y(), self.min.z()].into(),
1521 Octant::PosNegNeg => [self.max.x(), self.min.y(), self.min.z()].into(),
1522 Octant::NegNegNeg => self.min
1523 }
1524 }
1525
1526 pub fn corners (self) -> [Point3 <S>; 8] {
1527 [ self.min,
1528 [self.min.x(), self.max.y(), self.max.z()].into(),
1529 [self.max.x(), self.min.y(), self.max.z()].into(),
1530 [self.min.x(), self.min.y(), self.max.z()].into(),
1531 [self.max.x(), self.max.y(), self.min.z()].into(),
1532 [self.min.x(), self.max.y(), self.min.z()].into(),
1533 [self.max.x(), self.min.y(), self.min.z()].into(),
1534 self.max
1535 ]
1536 }
1537
1538 pub fn face (self, direction : SignedAxis3) -> Self {
1539 let (min, max) = match direction {
1540 SignedAxis3::PosX => (
1541 self.corner (Octant::PosNegNeg),
1542 self.corner (Octant::PosPosPos) ),
1543 SignedAxis3::NegX => (
1544 self.corner (Octant::NegNegNeg),
1545 self.corner (Octant::NegPosPos) ),
1546 SignedAxis3::PosY => (
1547 self.corner (Octant::NegPosNeg),
1548 self.corner (Octant::PosPosPos) ),
1549 SignedAxis3::NegY => (
1550 self.corner (Octant::NegNegNeg),
1551 self.corner (Octant::PosNegPos) ),
1552 SignedAxis3::PosZ => (
1553 self.corner (Octant::NegNegPos),
1554 self.corner (Octant::PosPosPos) ),
1555 SignedAxis3::NegZ => (
1556 self.corner (Octant::NegNegNeg),
1557 self.corner (Octant::PosPosNeg) )
1558 };
1559 Aabb3::with_minmax_unchecked (min, max)
1560 }
1561
1562 #[must_use]
1575 pub fn extrude (self, axis : SignedAxis3, amount : S) -> Option <Self> {
1576 let (p1, p2) = match axis {
1577 SignedAxis3::PosX => (
1578 self.min + Vector3::zero().with_x (*self.width()),
1579 self.max + Vector3::zero().with_x (amount)
1580 ),
1581 SignedAxis3::NegX => (
1582 self.min - Vector3::zero().with_x (amount),
1583 self.max - Vector3::zero().with_x (*self.width())
1584 ),
1585 SignedAxis3::PosY => (
1586 self.min + Vector3::zero().with_y (*self.depth()),
1587 self.max + Vector3::zero().with_y (amount)
1588 ),
1589 SignedAxis3::NegY => (
1590 self.min - Vector3::zero().with_y (amount),
1591 self.max - Vector3::zero().with_y (*self.depth())
1592 ),
1593 SignedAxis3::PosZ => (
1594 self.min + Vector3::zero().with_z (*self.height()),
1595 self.max + Vector3::zero().with_z (amount)
1596 ),
1597 SignedAxis3::NegZ => (
1598 self.min - Vector3::zero().with_z (amount),
1599 self.max - Vector3::zero().with_z (*self.height())
1600 )
1601 };
1602 Aabb3::from_points (p1, p2)
1603 }
1604
1605 #[must_use]
1607 pub fn extend (mut self, axis : SignedAxis3, amount : S) -> Option <Self> {
1608 match axis {
1609 SignedAxis3::PosX => self.max.0.x += amount,
1610 SignedAxis3::NegX => self.min.0.x -= amount,
1611 SignedAxis3::PosY => self.max.0.y += amount,
1612 SignedAxis3::NegY => self.min.0.y -= amount,
1613 SignedAxis3::PosZ => self.max.0.z += amount,
1614 SignedAxis3::NegZ => self.min.0.z -= amount
1615 }
1616 Self::with_minmax (self.min, self.max)
1617 }
1618
1619 #[must_use]
1629 pub fn dilate (mut self, amount : S) -> Option <Self> {
1630 self.min -= Vector3::broadcast (amount);
1631 self.max += Vector3::broadcast (amount);
1632 if self.min == self.max || self.min != point3_min (self.min, self.max)
1633 || self.max != point3_max (self.min, self.max)
1634 {
1635 None
1636 } else {
1637 Some (self)
1638 }
1639 }
1640
1641 pub fn project (self, axis : Axis3) -> Aabb2 <S> {
1645 let (min, max) = match axis {
1646 Axis3::X => ([self.min.y(), self.min.z()], [self.max.y(), self.max.z()]),
1647 Axis3::Y => ([self.min.x(), self.min.z()], [self.max.x(), self.max.z()]),
1648 Axis3::Z => ([self.min.x(), self.min.y()], [self.max.x(), self.max.y()]),
1649 };
1650 Aabb2::with_minmax_unchecked (min.into(), max.into())
1651 }
1652}
1653
1654impl <S> Primitive <S, Point3 <S>> for Aabb3 <S> where S : OrderedField {
1655 fn translate (&mut self, displacement : Vector3 <S>) {
1656 self.min += displacement;
1657 self.max += displacement;
1658 }
1659 fn scale (&mut self, scale : Positive <S>) {
1660 let center = self.center();
1661 self.translate (-center.0);
1662 self.min.0 *= *scale;
1663 self.max.0 *= *scale;
1664 self.translate (center.0);
1665 }
1666 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
1667 let out = if let Some (octant) = Octant::from_vec_strict (*direction) {
1668 self.corner (octant)
1669 } else {
1670 use Sign::*;
1671 let center = self.center();
1672 match direction.map (S::sign).into_array() {
1673 [Positive, Zero, Zero] => center.0.with_x (self.max.x()).into(),
1675 [Negative, Zero, Zero] => center.0.with_x (self.min.x()).into(),
1676 [Zero, Positive, Zero] => center.0.with_y (self.max.y()).into(),
1677 [Zero, Negative, Zero] => center.0.with_y (self.min.y()).into(),
1678 [Zero, Zero, Positive] => center.0.with_z (self.max.z()).into(),
1679 [Zero, Zero, Negative] => center.0.with_z (self.min.z()).into(),
1680 [Positive, Positive, Zero] =>
1682 center.0.with_x (self.max.x()).with_y (self.max.y()).into(),
1683 [Positive, Negative, Zero] =>
1684 center.0.with_x (self.max.x()).with_y (self.min.y()).into(),
1685 [Negative, Positive, Zero] =>
1686 center.0.with_x (self.min.x()).with_y (self.max.y()).into(),
1687 [Negative, Negative, Zero] =>
1688 center.0.with_x (self.min.x()).with_y (self.min.y()).into(),
1689
1690 [Positive, Zero, Positive] =>
1691 center.0.with_x (self.max.x()).with_z (self.max.z()).into(),
1692 [Positive, Zero, Negative] =>
1693 center.0.with_x (self.max.x()).with_z (self.min.z()).into(),
1694 [Negative, Zero, Positive] =>
1695 center.0.with_x (self.min.x()).with_z (self.max.z()).into(),
1696 [Negative, Zero, Negative] =>
1697 center.0.with_x (self.min.x()).with_z (self.min.z()).into(),
1698
1699 [Zero, Positive, Positive] =>
1700 center.0.with_y (self.max.y()).with_z (self.max.z()).into(),
1701 [Zero, Positive, Negative] =>
1702 center.0.with_y (self.max.y()).with_z (self.min.z()).into(),
1703 [Zero, Negative, Positive] =>
1704 center.0.with_y (self.min.y()).with_z (self.max.z()).into(),
1705 [Zero, Negative, Negative] =>
1706 center.0.with_y (self.min.y()).with_z (self.min.z()).into(),
1707 [_, _, _] => unreachable!()
1708 }
1709 };
1710 (out, out.0.dot (*direction))
1711 }
1712}
1713
1714impl <S> std::ops::Add <Vector3 <S>> for Aabb3 <S> where
1715 S : Field,
1716 Self : Primitive <S, Point3 <S>>
1717{
1718 type Output = Self;
1719 #[expect(clippy::renamed_function_params)]
1720 fn add (mut self, displacement : Vector3 <S>) -> Self {
1721 self.translate (displacement);
1722 self
1723 }
1724}
1725
1726impl <S> std::ops::Sub <Vector3 <S>> for Aabb3 <S> where
1727 S : Field,
1728 Self : Primitive <S, Point3 <S>>
1729{
1730 type Output = Self;
1731 #[expect(clippy::renamed_function_params)]
1732 fn sub (mut self, displacement : Vector3 <S>) -> Self {
1733 self.translate (-displacement);
1734 self
1735 }
1736}
1737
1738impl_numcast_fields!(Obb2, center, extents, orientation);
1739impl <S : OrderedRing> Obb2 <S> {
1740 #[inline]
1742 pub const fn new (
1743 center : Point2 <S>,
1744 extents : Vector2 <Positive <S>>,
1745 orientation : AngleWrapped <S>
1746 ) -> Self {
1747 Obb2 { center, extents, orientation }
1748 }
1749 pub fn containing_points_with_orientation (
1753 points : &[Point2 <S>], orientation : AngleWrapped <S>
1754 ) -> Option <Self> where
1755 S : Real + num::real::Real + MaybeSerDes
1756 {
1757 let [x, y] = Rotation2::from_angle (orientation.angle()).cols
1758 .map (NonZero2::unchecked).into_array();
1759 let min_dot_x = -support2 (points, -x).1;
1760 let max_dot_x = support2 (points, x).1;
1761 let min_dot_y = -support2 (points, -y).1;
1762 let max_dot_y = support2 (points, y).1;
1763 let center = Point2 (
1764 (*x * min_dot_x + *x * max_dot_x) * S::half() +
1765 (*y * min_dot_y + *y * max_dot_y) * S::half());
1766 let extents = vector2 (
1767 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
1768 Positive::new (S::half() * (max_dot_y - min_dot_y))?);
1769 Some (Obb2 { center, extents, orientation })
1770 }
1771 pub fn containing_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
1789 let hull = Hull2::from_points (points)?;
1790 Self::containing (&hull)
1791 }
1792 pub fn containing (hull : &Hull2 <S>) -> Option <Self> where S : Real {
1811 let points = hull.points();
1812 if points.len() < 3 {
1813 return None
1814 }
1815 let mut visited = vec![false; points.len()];
1816 #[expect(clippy::cast_possible_truncation)]
1817 let mut min_rect = smallest_rect (points.len() as u32 - 1, 0, hull);
1818 visited[min_rect.indices[0] as usize] = true;
1819 let mut rect = min_rect.clone();
1821 for _ in 0..points.len() {
1822 let mut angles = [(S::zero(), u32::MAX); 4];
1823 let mut nangles = u32::MAX;
1824 if !compute_angles (hull, &rect, &mut angles, &mut nangles) {
1825 break
1826 }
1827 let sorted = sort_angles (&angles, nangles);
1828 if !update_support (&angles, nangles, &sorted, hull, &mut visited, &mut rect) {
1829 break
1830 }
1831 if rect.area < min_rect.area {
1832 min_rect = rect.clone();
1833 }
1834 }
1835 let sum = [
1837 points[min_rect.indices[1] as usize].0 + points[min_rect.indices[3] as usize].0,
1838 points[min_rect.indices[2] as usize].0 + points[min_rect.indices[0] as usize].0
1839 ];
1840 let diff = [
1841 points[min_rect.indices[1] as usize].0 - points[min_rect.indices[3] as usize].0,
1842 points[min_rect.indices[2] as usize].0 - points[min_rect.indices[0] as usize].0
1843 ];
1844 let obb = {
1845 let center = Point2::from ((min_rect.edge * min_rect.edge.dot (sum[0]) +
1846 min_rect.perp * min_rect.perp.dot (sum[1]))
1847 * S::half() / min_rect.edge_len_squared);
1848 let extents = {
1849 let extent_x = Positive::unchecked (S::sqrt (
1850 (S::half() * min_rect.edge.dot (diff[0])).squared()
1851 / min_rect.edge_len_squared));
1852 let extent_y = Positive::unchecked (S::sqrt (
1853 (S::half() * min_rect.perp.dot (diff[1])).squared()
1854 / min_rect.edge_len_squared));
1855 vector2 (extent_x, extent_y)
1856 };
1857 let orientation =
1858 AngleWrapped::wrap (Rad (min_rect.edge.y.atan2 (min_rect.edge.x)));
1859 Obb2 { center, extents, orientation }
1860 };
1861 fn compute_angles <S : Real> (
1862 hull : &Hull2 <S>,
1863 rect : &Rect <S>,
1864 angles : &mut [(S, u32); 4],
1865 nangles : &mut u32
1866 ) -> bool {
1867 *nangles = 0;
1868 let points = hull.points();
1869 let mut k0 = 3;
1870 let mut k1 = 0;
1871 while k1 < 4 {
1872 if rect.indices[k0] != rect.indices[k1] {
1873 let d = {
1874 let mut d = if k0 & 1 != 0 {
1875 rect.perp
1876 } else {
1877 rect.edge
1878 };
1879 if k0 & 2 != 0 {
1880 d = -d;
1881 }
1882 d
1883 };
1884 let j0 = rect.indices[k0];
1885 let mut j1 = j0 + 1;
1886 #[expect(clippy::cast_possible_truncation)]
1887 if j1 == points.len() as u32 {
1888 j1 = 0;
1889 }
1890 let e = points[j1 as usize] - points[j0 as usize];
1891 let dp = d.dot ([e.y, -e.x].into());
1892 let e_lensq = e.magnitude_squared();
1893 let sin_theta_squared = (dp * dp) / e_lensq;
1894 angles[*nangles as usize] =
1895 #[expect(clippy::cast_possible_truncation)]
1896 (sin_theta_squared, k0 as u32);
1897 *nangles += 1;
1898 }
1899 k0 = k1;
1900 k1 += 1;
1901 }
1902 *nangles > 0
1903 }
1904 fn sort_angles <S : PartialOrd> (angles : &[(S, u32); 4], nangles : u32)
1905 -> [u32; 4]
1906 {
1907 let mut sorted = [0u32, 1, 2, 3];
1908 match nangles {
1909 0 | 1 => {}
1910 2 => if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1911 sorted.swap (0, 1)
1912 }
1913 3 => {
1914 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1915 sorted.swap (0, 1)
1916 }
1917 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1918 sorted.swap (0, 2)
1919 }
1920 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1921 sorted.swap (1, 2)
1922 }
1923 }
1924 4 => {
1925 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1926 sorted.swap (0, 1)
1927 }
1928 if angles[sorted[2] as usize].0 > angles[sorted[3] as usize].0 {
1929 sorted.swap (2, 3)
1930 }
1931 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1932 sorted.swap (0, 2)
1933 }
1934 if angles[sorted[1] as usize].0 > angles[sorted[3] as usize].0 {
1935 sorted.swap (1, 3)
1936 }
1937 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1938 sorted.swap (1, 2)
1939 }
1940 }
1941 _ => unreachable!()
1942 }
1943 sorted
1944 }
1945 fn update_support <S : Real> (
1946 angles : &[(S, u32); 4],
1947 nangles : u32,
1948 sorted : &[u32; 4],
1949 hull : &Hull2 <S>,
1950 visited : &mut [bool],
1951 rect : &mut Rect <S>
1952 ) -> bool {
1953 let points = hull.points();
1954 let min_angle = angles[sorted[0] as usize];
1955 for k in 0..nangles as usize {
1956 let (angle, index) = angles[sorted[k] as usize];
1957 if angle == min_angle.0 {
1958 rect.indices[index as usize] += 1;
1959 #[expect(clippy::cast_possible_truncation)]
1960 if rect.indices[index as usize] == points.len() as u32 {
1961 rect.indices[index as usize] = 0;
1962 }
1963 }
1964 }
1965 let bottom = rect.indices[min_angle.1 as usize];
1966 if visited[bottom as usize] {
1967 return false
1968 }
1969 visited[bottom as usize] = true;
1970 let mut next_index = [u32::MAX; 4];
1971 for k in 0u32..4 {
1972 next_index[k as usize] = rect.indices[((min_angle.1 + k) % 4) as usize];
1973 }
1974 rect.indices = next_index;
1975 let j1 = rect.indices[0] as usize;
1976 let j0 = if j1 == 0 {
1977 points.len() - 1
1978 } else {
1979 j1 - 1
1980 };
1981 rect.edge = points[j1] - points[j0];
1982 rect.perp = vector2 (-rect.edge.y, rect.edge.x);
1983 let edge_len_squared = rect.edge.magnitude_squared();
1984 let diff = [
1985 points[rect.indices[1] as usize] - points[rect.indices[3] as usize],
1986 points[rect.indices[2] as usize] - points[rect.indices[0] as usize]
1987 ];
1988 rect.area = rect.edge.dot (diff[0]) * rect.perp.dot (diff[1]) / edge_len_squared;
1989 true
1990 }
1991 Some (obb)
1992 }
1993 #[inline]
1994 pub fn dimensions (self) -> Vector2 <Positive <S>> {
1995 self.extents * Positive::unchecked (S::two())
1996 }
1997 #[inline]
1999 pub fn width (self) -> Positive <S> {
2000 self.extents.x * Positive::unchecked (S::two())
2001 }
2002 #[inline]
2004 pub fn height (self) -> Positive <S> {
2005 self.extents.y * Positive::unchecked (S::two())
2006 }
2007 #[inline]
2008 pub fn area (self) -> Positive <S> {
2009 self.width() * self.height()
2010 }
2011 #[inline]
2012 pub fn corners (self) -> [Point2 <S>; 4] where
2013 S : Real + num::real::Real + MaybeSerDes
2014 {
2015 let mut points = [Point2::origin(); 4];
2016 for i in 0..2 {
2017 for j in 0..2 {
2018 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2019 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2020 points[i * 2 + j] = [*self.extents.x * sign_x, *self.extents.y * sign_y].into();
2021 }
2022 }
2023 let rotation = Rotation2::from_angle (self.orientation.angle());
2024 points.map (|point| rotation.rotate (point) + self.center.0)
2025 }
2026
2027 pub fn aabb (self) -> Aabb2 <S> where S : Real + num::real::Real + MaybeSerDes {
2028 Aabb2::containing (&self.corners()).unwrap()
2029 }
2030
2031 pub fn contains (self, point : Point2 <S>) -> bool where
2044 S : Real + num::real::Real + MaybeSerDes
2045 {
2046 let p = point - self.center.0;
2048 let p = Rotation2::from_angle (-self.orientation.angle()).rotate (p);
2050 p.x().abs() < *self.extents.x && p.y().abs() < *self.extents.y
2051 }
2052
2053 pub fn dilate (mut self, amount : S) -> Self {
2083 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2084 self
2085 }
2086}
2087impl <S : Real> From <Aabb2 <S>> for Obb2 <S> {
2088 fn from (aabb : Aabb2 <S>) -> Self {
2101 use num::Zero;
2102 let center = aabb.center();
2103 let extents = vector2 (
2104 Positive::noisy (*aabb.width() / S::two()),
2105 Positive::noisy (*aabb.height() / S::two()));
2106 let orientation = AngleWrapped::zero();
2107 Obb2::new (center, extents, orientation)
2108 }
2109}
2110impl <S> Primitive <S, Point2 <S>> for Obb2 <S> where
2111 S : Real + num::real::Real + MaybeSerDes
2112{
2113 fn translate (&mut self, displacement : Vector2 <S>) {
2114 self.center += displacement;
2115 }
2116 fn scale (&mut self, scale : Positive <S>) {
2117 self.extents *= scale;
2118 }
2119 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2120 use num::Inv;
2121 let rotation = Rotation2::from_angle (self.orientation.angle());
2122 let aabb = Aabb2::with_minmax_unchecked (
2123 self.center - self.extents.map (|e| *e),
2124 self.center + self.extents.map (|e| *e));
2125 let aabb_direction = rotation.inv() * direction;
2126 let (aabb_support, _) = aabb.support (aabb_direction);
2127 let out = rotation.rotate_around (aabb_support, self.center);
2128 (out, out.0.dot (*direction))
2129 }
2130}
2131
2132impl_numcast_fields!(Obb3, center, extents, orientation);
2133impl <S : OrderedRing> Obb3 <S> {
2134 #[inline]
2136 pub const fn new (
2137 center : Point3 <S>,
2138 extents : Vector3 <Positive <S>>,
2139 orientation : Angles3 <S>
2140 ) -> Self {
2141 Obb3 { center, extents, orientation }
2142 }
2143 pub fn containing_points_with_orientation (
2147 points : &[Point3 <S>], orientation : Angles3 <S>
2148 ) -> Option <Self> where
2149 S : Real + num::real::Real + MaybeSerDes
2150 {
2151 let [x, y, z] = Rotation3::from (orientation).cols.map (NonZero3::unchecked)
2152 .into_array();
2153 let min_dot_x = -support3 (points, -x).1;
2154 let max_dot_x = support3 (points, x).1;
2155 let min_dot_y = -support3 (points, -y).1;
2156 let max_dot_y = support3 (points, y).1;
2157 let min_dot_z = -support3 (points, -z).1;
2158 let max_dot_z = support3 (points, z).1;
2159 let center = Point3 (
2160 (*x * min_dot_x + *x * max_dot_x) * S::half() +
2161 (*y * min_dot_y + *y * max_dot_y) * S::half() +
2162 (*z * min_dot_z + *z * max_dot_z) * S::half());
2163 let extents = vector3 (
2164 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
2165 Positive::new (S::half() * (max_dot_y - min_dot_y))?,
2166 Positive::new (S::half() * (max_dot_z - min_dot_z))?);
2167 Some (Obb3 { center, extents, orientation })
2168 }
2169 pub fn containing_points_approx (points : &[Point3 <S>]) -> Option <Self> where
2186 S : Real + num::real::Real + approx::RelativeEq <Epsilon=S> + MaybeSerDes
2187 {
2188 if points.len() < 4 {
2189 return None
2190 }
2191 let (hull, mesh) = Hull3::from_points_with_mesh (points)?;
2192 Self::containing_approx (&hull, &mesh)
2193 }
2194 pub fn containing_approx (hull : &Hull3 <S>, mesh : &VertexEdgeTriangleMesh)
2213 -> Option <Self>
2214 where S : Real + num::NumCast + approx::RelativeEq <Epsilon=S> + MaybeSerDes {
2215 use num::Inv;
2218 if hull.points().len() < 4 {
2219 return None
2220 }
2221 let mut min_obb : Obb3 <S> = Aabb3::containing (hull.points()).unwrap().into();
2222 let mut min_vol = min_obb.volume();
2223 for triangle in mesh.triangles().values() {
2224 let [a, b, c] = triangle.vertices().map (|i| hull.points()[i as usize]);
2225 let mut u = b - a;
2226 let mut v = c - a;
2227 let mut w = u.cross (v);
2228 v = w.cross (u);
2229 u = *u.normalize();
2230 v = *v.normalize();
2231 w = *w.normalize();
2232 let m = Matrix3::from_row_arrays (std::array::from_fn (|i| [u[i], v[i], w[i]]));
2233 let m_rot = Rotation3::new_approx (m).unwrap();
2234 let rot_inv = m_rot.inv();
2235 let center = a;
2236 let rotated = hull.points().iter().copied()
2237 .map (|p| rot_inv.rotate_around (p, center))
2238 .collect::<Vec <_>>();
2239 let aabb = Aabb3::containing (rotated.as_slice()).unwrap();
2240 let aabb_volume = aabb.volume();
2241 if approx::abs_diff_eq!(*aabb_volume, S::zero()) {
2242 return None
2244 }
2245 let volume = Positive::new (*aabb_volume).unwrap();
2246 if volume < min_vol {
2247 min_vol = volume;
2248 min_obb = aabb.into();
2249 min_obb.orientation = m_rot.into();
2250 min_obb.center = m_rot.rotate_around (min_obb.center, center);
2251 }
2252 }
2253 Some (min_obb)
2254 }
2255 pub fn containing_approx_pca (hull : &Hull3 <S>) -> Option <Self> where
2274 S : Real + num::real::Real + num::NumCast + approx::RelativeEq <Epsilon=S>
2275 + MaybeSerDes
2276 {
2277 if hull.points().len() < 4 {
2278 return None
2279 }
2280 let [p1, p2, p3] = &hull.points()[..3] else { unreachable!() };
2284 let mut coplanar = true;
2285 for p in &hull.points()[3..] {
2286 if !coplanar_3d (*p1, *p2, *p3, *p) {
2287 coplanar = false;
2288 break
2289 }
2290 }
2291 if coplanar {
2292 return None
2293 }
2294 let orientation = data::pca_3 (hull.points()).into();
2296 Some (Self::containing_points_with_orientation (hull.points(), orientation).unwrap())
2297 }
2298 #[inline]
2299 pub fn dimensions (self) -> Vector3 <Positive <S>> {
2300 self.extents * Positive::unchecked (S::two())
2301 }
2302 #[inline]
2304 pub fn width (self) -> Positive <S> {
2305 self.extents.x * Positive::unchecked (S::two())
2306 }
2307 #[inline]
2309 pub fn depth (self) -> Positive <S> {
2310 self.extents.y * Positive::unchecked (S::two())
2311 }
2312 #[inline]
2314 pub fn height (self) -> Positive <S> {
2315 self.extents.z * Positive::unchecked (S::two())
2316 }
2317 #[inline]
2318 pub fn volume (self) -> Positive <S> {
2319 self.width() * self.depth() * self.height()
2320 }
2321 #[inline]
2322 pub fn corners (self) -> [Point3 <S>; 8] where
2323 S : Real + num::real::Real + MaybeSerDes
2324 {
2325 let mut points = [Point3::origin(); 8];
2326 for i in 0..2 {
2327 for j in 0..2 {
2328 for k in 0..2 {
2329 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2330 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2331 let sign_z = if k % 2 == 0 { -S::one() } else { S::one() };
2332 points[i * 4 + j * 2 + k] = [
2333 *self.extents.x * sign_x,
2334 *self.extents.y * sign_y,
2335 *self.extents.z * sign_z
2336 ].into();
2337 }
2338 }
2339 }
2340 let rotation = Rotation3::from (self.orientation);
2341 points.map (|point| rotation.rotate (point) + self.center.0)
2342 }
2343
2344 pub fn aabb (self) -> Aabb3 <S> where S : Real + num::real::Real + MaybeSerDes {
2345 Aabb3::containing (&self.corners()).unwrap()
2346 }
2347
2348 pub fn contains (self, point : Point3 <S>) -> bool where
2350 S : Real + num::real::Real + MaybeSerDes
2351 {
2352 use num::Inv;
2353 let p = point - self.center.0;
2355 let p = Rotation3::from (self.orientation).inv().rotate (p);
2357 p.x().abs() < *self.extents.x &&
2358 p.y().abs() < *self.extents.y &&
2359 p.z().abs() < *self.extents.z
2360 }
2361
2362 pub fn dilate (mut self, amount : S) -> Self {
2377 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2378 self
2379 }
2380}
2381impl <S : Real> From <Aabb3 <S>> for Obb3 <S> {
2382 fn from (aabb : Aabb3 <S>) -> Self {
2395 let center = aabb.center();
2396 let extents = aabb.extents().map (|d| Positive::noisy (*d));
2397 let orientation = Angles3::zero();
2398 Obb3::new (center, extents, orientation)
2399 }
2400}
2401impl <S> Primitive <S, Point3 <S>> for Obb3 <S> where
2402 S : Real + num::real::Real + MaybeSerDes
2403{
2404 fn translate (&mut self, displacement : Vector3 <S>) {
2405 self.center += displacement;
2406 }
2407 fn scale (&mut self, scale : Positive <S>) {
2408 self.extents *= scale;
2409 }
2410 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2411 use num::Inv;
2412 let rotation = Rotation3::from (self.orientation);
2413 let aabb = Aabb3::with_minmax_unchecked (
2414 self.center - self.extents.map (|e| *e),
2415 self.center + self.extents.map (|e| *e));
2416 let aabb_direction = rotation.inv() * direction;
2417 let (aabb_support, _) = aabb.support (aabb_direction);
2418 let out = rotation.rotate_around (aabb_support, self.center);
2419 (out, out.0.dot (*direction))
2420 }
2421}
2422
2423impl_numcast_fields!(Capsule3, center, half_height, radius);
2424impl <S : OrderedRing> Capsule3 <S> {
2425 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2426 use shape::Aabb;
2427 let shape_aabb = shape::Capsule {
2428 radius: self.radius,
2429 half_height: self.half_height
2430 }.aabb();
2431 let center_vec = self.center.0;
2432 let min = shape_aabb.min() + center_vec;
2433 let max = shape_aabb.max() + center_vec;
2434 Aabb3::with_minmax_unchecked (min, max)
2435 }
2436 pub fn decompose (self) -> (Sphere3 <S>, Option <Cylinder3 <S>>, Sphere3 <S>) {
2438 let cylinder = if *self.half_height > S::zero() {
2439 Some (Cylinder3 {
2440 center: self.center,
2441 radius: self.radius,
2442 half_height: Positive::unchecked (*self.half_height)
2443 })
2444 } else {
2445 None
2446 };
2447 let upper_sphere = Sphere3 {
2448 center: self.center + (Vector3::unit_z() * *self.half_height),
2449 radius: self.radius
2450 };
2451 let lower_sphere = Sphere3 {
2452 center: self.center - (Vector3::unit_z() * *self.half_height),
2453 radius: self.radius
2454 };
2455 (upper_sphere, cylinder, lower_sphere)
2456 }
2457}
2458impl <S> Primitive <S, Point3 <S>> for Capsule3 <S> where
2459 S : Real + num::real::Real + MaybeSerDes
2460{
2461 fn translate (&mut self, displacement : Vector3 <S>) {
2462 self.center += displacement;
2463 }
2464 fn scale (&mut self, scale : Positive <S>) {
2465 self.half_height *= scale;
2466 self.radius *= scale;
2467 }
2468 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2469 let dir_unit = direction.normalized();
2470 let out = self.center + if direction.z > S::zero() {
2471 Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2472 } else if direction.z < S::zero() {
2473 -Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2474 } else {
2475 dir_unit * *self.radius
2476 };
2477 (out, out.0.dot (*direction))
2478 }
2479}
2480
2481impl_numcast_fields!(Cylinder3, center, half_height, radius);
2482impl <S : OrderedRing> Cylinder3 <S> {
2483 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2484 use shape::Aabb;
2485 let shape_aabb = shape::Cylinder::unchecked (*self.radius, *self.half_height).aabb();
2486 let center_vec = self.center.0;
2487 let min = shape_aabb.min() + center_vec;
2488 let max = shape_aabb.max() + center_vec;
2489 Aabb3::with_minmax_unchecked (min, max)
2490 }
2491}
2492impl <S> Primitive <S, Point3 <S>> for Cylinder3 <S> where
2493 S : Real + num::real::Real + MaybeSerDes
2494{
2495 fn translate (&mut self, displacement : Vector3 <S>) {
2496 self.center += displacement;
2497 }
2498 fn scale (&mut self, scale : Positive <S>) {
2499 self.half_height *= scale;
2500 self.radius *= scale;
2501 }
2502 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2503 let out = self.center + if *direction == Vector3::unit_z() {
2504 Vector3::unit_z() * *self.half_height
2505 } else if *direction == -Vector3::unit_z() {
2506 -Vector3::unit_z() * *self.half_height
2507 } else if direction.z == S::zero() {
2508 direction.normalized() * *self.radius
2509 } else if direction.z > S::zero() {
2510 Vector3::unit_z() * *self.half_height
2511 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2512 } else {
2513 debug_assert!(direction.z < S::zero());
2514 -Vector3::unit_z() * *self.half_height
2515 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2516 };
2517 (out, out.0.dot (*direction))
2518 }
2519}
2520
2521impl_numcast_fields!(Cone3, center, half_height, radius);
2522impl <S : OrderedRing> Cone3 <S> {
2523 pub fn height (self) -> Positive <S> {
2524 self.half_height * Positive::unchecked (S::two())
2525 }
2526 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2527 use shape::Aabb;
2528 let shape_aabb = shape::Cone {
2529 radius: self.radius,
2530 half_height: self.half_height
2531 }.aabb();
2532 let center_vec = self.center.0;
2533 let min = shape_aabb.min() + center_vec;
2534 let max = shape_aabb.max() + center_vec;
2535 Aabb3::with_minmax_unchecked (min, max)
2536 }
2537}
2538impl <S> Primitive <S, Point3 <S>> for Cone3 <S> where
2539 S : Real + num::real::Real + approx::RelativeEq + MaybeSerDes
2540{
2541 fn translate (&mut self, displacement : Vector3 <S>) {
2542 self.center += displacement;
2543 }
2544 fn scale (&mut self, scale : Positive <S>) {
2545 self.half_height *= scale;
2546 self.radius *= scale;
2547 }
2548 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2549 let top = || Vector3::unit_z() * *self.half_height;
2550 let bottom = || -top();
2551 let edge = || bottom()
2552 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius;
2553 let out = self.center + if *direction == Vector3::unit_z() {
2554 top()
2555 } else if *direction == -Vector3::unit_z() {
2556 bottom()
2557 } else if direction.z <= S::zero() {
2558 edge()
2559 } else {
2560 let angle_dir =
2562 Trig::atan2 (direction.z, Sqrt::sqrt (S::one() - direction.z.squared()));
2563 debug_assert!(angle_dir > S::zero());
2564 let angle_cone = Trig::atan2 (-*self.height(), *self.radius);
2565 debug_assert!(angle_cone < S::zero());
2566 let pi_2 = S::pi() / S::two();
2567 let angle_total = angle_dir - angle_cone;
2568 if approx::relative_eq!(angle_total, pi_2) {
2569 (top() + edge()) * S::half()
2570 } else if angle_total > pi_2 {
2571 top()
2572 } else {
2573 debug_assert!(angle_total < pi_2);
2574 edge()
2575 }
2576 };
2577 (out, out.0.dot (*direction))
2578 }
2579}
2580
2581impl_numcast_fields!(Line2, base, direction);
2582impl <S : OrderedRing> Line2 <S> {
2583 #[inline]
2585 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2586 Line2 { base, direction }
2587 }
2588 #[inline]
2589 pub fn point (self, t : S) -> Point2 <S> {
2590 self.base + *self.direction * t
2591 }
2592 #[inline]
2593 pub fn affine_line (self) -> frame::Line2 <S> {
2594 frame::Line2 {
2595 origin: self.base,
2596 basis: self.direction.into()
2597 }
2598 }
2599 #[inline]
2600 pub fn nearest_point (self, point : Point2 <S>) -> Line2Point <S> {
2601 project_2d_point_on_line (point, self)
2602 }
2603 #[inline]
2604 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2605 -> Option <(Line2Point <S>, Line2Point <S>)>
2606 {
2607 intersect::continuous_line2_aabb2 (self, aabb)
2608 }
2609 #[inline]
2610 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2611 -> Option <(Line2Point <S>, Line2Point <S>)>
2612 where S : Field + Sqrt {
2613 intersect::continuous_line2_sphere2 (self, sphere)
2614 }
2615}
2616impl <S : Real> Default for Line2 <S> {
2617 fn default() -> Self {
2618 Line2 {
2619 base: Point2::origin(),
2620 direction: Unit2::axis_y()
2621 }
2622 }
2623}
2624impl <S> Primitive <S, Point2 <S>> for Line2 <S> where
2625 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2626{
2627 fn translate (&mut self, displacement : Vector2 <S>) {
2628 self.base += displacement;
2629 }
2630 #[expect(unused_variables)]
2631 fn scale (&mut self, scale : Positive <S>) {
2632 }
2634 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2635 let dot = self.direction.dot (*direction);
2636 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2637 self.base
2638 } else if dot > S::zero() {
2639 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2640 S::zero()
2641 } else {
2642 x * S::infinity()
2643 }))
2644 } else {
2645 debug_assert!(dot < S::zero());
2646 Point2 ((-self.direction).sigvec().map (|x| if x == S::zero() {
2647 S::zero()
2648 } else {
2649 x * S::infinity()
2650 }))
2651 };
2652 (out, out.0.dot (*direction))
2653 }
2654}
2655impl <S> From <Ray2 <S>> for Line2 <S> {
2656 fn from (Ray2 { base, direction } : Ray2 <S>) -> Line2 <S> {
2657 Line2 { base, direction }
2658 }
2659}
2660
2661impl_numcast_fields!(Ray2, base, direction);
2662impl <S : OrderedRing> Ray2 <S> {
2663 #[inline]
2665 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2666 Ray2 { base, direction }
2667 }
2668 #[inline]
2669 pub fn point (self, t : S) -> Point2 <S> {
2670 self.base + *self.direction * t
2671 }
2672 #[inline]
2673 pub fn affine_line (self) -> frame::Line2 <S> {
2674 frame::Line2 {
2675 origin: self.base,
2676 basis: self.direction.into()
2677 }
2678 }
2679 pub fn nearest_point (self, point : Point2 <S>) -> Ray2Point <S> {
2680 use num::Zero;
2681 let (t, point) = Line2::from (self).nearest_point (point);
2682 if t < S::zero() {
2683 (NonNegative::zero(), self.base)
2684 } else {
2685 (NonNegative::unchecked (t), point)
2686 }
2687 }
2688 #[inline]
2689 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2690 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2691 {
2692 intersect::continuous_ray2_aabb2 (self, aabb)
2693 }
2694 #[inline]
2695 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2696 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2697 {
2698 intersect::continuous_ray2_sphere2 (self, sphere)
2699 }
2700}
2701impl <S : Real> Default for Ray2 <S> {
2702 fn default() -> Self {
2703 Ray2 {
2704 base: Point2::origin(),
2705 direction: Unit2::axis_y()
2706 }
2707 }
2708}
2709impl <S> Primitive <S, Point2 <S>> for Ray2 <S> where
2710 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2711{
2712 fn translate (&mut self, displacement : Vector2 <S>) {
2713 self.base += displacement;
2714 }
2715 #[expect(unused_variables)]
2716 fn scale (&mut self, scale : Positive <S>) {
2717 }
2719 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2720 let dot = self.direction.dot (*direction);
2721 let out = if dot < S::default_epsilon() {
2722 self.base
2723 } else {
2724 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2725 S::zero()
2726 } else {
2727 x * S::infinity()
2728 }))
2729 };
2730 (out, out.0.dot (*direction))
2731 }
2732}
2733impl <S> From <Line2 <S>> for Ray2 <S> {
2734 fn from (Line2 { base, direction } : Line2 <S>) -> Ray2 <S> {
2735 Ray2 { base, direction }
2736 }
2737}
2738
2739impl_numcast_fields!(Line3, base, direction);
2740impl <S : OrderedRing> Line3 <S> {
2741 #[inline]
2743 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2744 Line3 { base, direction }
2745 }
2746 #[inline]
2747 pub fn point (self, t : S) -> Point3 <S> {
2748 self.base + *self.direction * t
2749 }
2750 #[inline]
2751 pub fn affine_line (self) -> frame::Line3 <S> {
2752 frame::Line3 {
2753 origin: self.base,
2754 basis: self.direction.into()
2755 }
2756 }
2757 #[inline]
2758 pub fn nearest_point (self, point : Point3 <S>) -> Line3Point <S> {
2759 project_3d_point_on_line (point, self)
2760 }
2761 #[inline]
2762 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Line3Point <S>> where
2763 S : Field + approx::RelativeEq
2764 {
2765 intersect::continuous_line3_plane3 (self, plane)
2766 }
2767 #[inline]
2768 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2769 -> Option <(Line3Point <S>, Line3Point <S>)>
2770 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2771 intersect::continuous_line3_aabb3 (self, aabb)
2772 }
2773 #[inline]
2774 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2775 -> Option <(Line3Point <S>, Line3Point <S>)>
2776 where S : Field + Sqrt {
2777 intersect::continuous_line3_sphere3 (self, sphere)
2778 }
2779 #[inline]
2780 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2781 -> Option <Line3Point <S>>
2782 where S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2783 intersect::continuous_line3_triangle3 (self, triangle)
2784 }
2785}
2786impl <S : Real> Default for Line3 <S> {
2787 fn default() -> Self {
2788 Line3 {
2789 base: Point3::origin(),
2790 direction: Unit3::axis_z()
2791 }
2792 }
2793}
2794impl <S> Primitive <S, Point3 <S>> for Line3 <S> where
2795 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2796{
2797 fn translate (&mut self, displacement : Vector3 <S>) {
2798 self.base += displacement;
2799 }
2800 #[expect(unused_variables)]
2801 fn scale (&mut self, scale : Positive <S>) {
2802 }
2804 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2805 let dot = self.direction.dot (*direction);
2806 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2807 self.base
2808 } else if dot > S::zero() {
2809 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2810 S::zero()
2811 } else {
2812 x * S::infinity()
2813 }))
2814 } else {
2815 debug_assert!(dot < S::zero());
2816 Point3 (-self.direction.sigvec().map (|x| if x == S::zero() {
2817 S::zero()
2818 } else {
2819 x * S::infinity()
2820 }))
2821 };
2822 (out, out.0.dot (*direction))
2823 }
2824}
2825impl <S> From <Ray3 <S>> for Line3 <S> {
2826 fn from (Ray3 { base, direction } : Ray3 <S>) -> Line3 <S> {
2827 Line3 { base, direction }
2828 }
2829}
2830
2831impl_numcast_fields!(Ray3, base, direction);
2832impl <S : OrderedRing> Ray3 <S> {
2833 #[inline]
2835 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2836 Ray3 { base, direction }
2837 }
2838 #[inline]
2839 pub fn point (self, t : S) -> Point3 <S> {
2840 self.base + *self.direction * t
2841 }
2842 #[inline]
2843 pub fn affine_line (self) -> frame::Line3 <S> {
2844 frame::Line3 {
2845 origin: self.base,
2846 basis: self.direction.into()
2847 }
2848 }
2849 pub fn nearest_point (self, point : Point3 <S>) -> Ray3Point <S> {
2850 use num::Zero;
2851 let (t, point) = Line3::from (self).nearest_point (point);
2852 if t < S::zero() {
2853 (NonNegative::zero(), self.base)
2854 } else {
2855 (NonNegative::unchecked (t), point)
2856 }
2857 }
2858 #[inline]
2859 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Ray3Point <S>> where
2860 S : approx::RelativeEq
2861 {
2862 intersect::continuous_ray3_plane3 (self, plane)
2863 }
2864 #[inline]
2865 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2866 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2867 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2868 intersect::continuous_ray3_aabb3 (self, aabb)
2869 }
2870 #[inline]
2871 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2872 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2873 {
2874 intersect::continuous_ray3_sphere3 (self, sphere)
2875 }
2876 #[inline]
2877 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2878 -> Option <Ray3Point <S>>
2879 where S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2880 intersect::continuous_ray3_triangle3 (self, triangle)
2881 }
2882}
2883impl <S : Real> Default for Ray3 <S> {
2884 fn default() -> Self {
2885 Ray3 {
2886 base: Point3::origin(),
2887 direction: Unit3::axis_z()
2888 }
2889 }
2890}
2891impl <S> Primitive <S, Point3 <S>> for Ray3 <S> where
2892 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2893{
2894 fn translate (&mut self, displacement : Vector3 <S>) {
2895 self.base += displacement;
2896 }
2897 #[expect(unused_variables)]
2898 fn scale (&mut self, scale : Positive <S>) {
2899 }
2901 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2902 let dot = self.direction.dot (*direction);
2903 let out = if dot < S::default_epsilon() {
2904 self.base
2905 } else {
2906 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2907 S::zero()
2908 } else {
2909 x * S::infinity()
2910 }))
2911 };
2912 (out, out.0.dot (*direction))
2913 }
2914}
2915impl <S> From <Line3 <S>> for Ray3 <S> {
2916 fn from (Line3 { base, direction } : Line3 <S>) -> Ray3 <S> {
2917 Ray3 { base, direction }
2918 }
2919}
2920
2921impl_numcast_fields!(Plane3, base, normal);
2922impl <S> Plane3 <S> {
2923 #[inline]
2925 pub const fn new (base : Point3 <S>, normal : Unit3 <S>) -> Self {
2926 Plane3 { base, normal }
2927 }
2928}
2929impl <S : Real> Default for Plane3 <S> {
2930 fn default() -> Self {
2931 Plane3 {
2932 base: Point3::origin(),
2933 normal: Unit3::axis_z()
2934 }
2935 }
2936}
2937impl <S> Primitive <S, Point3 <S>> for Plane3 <S> where
2938 S : Real + num::float::FloatCore + approx::RelativeEq
2939{
2940 fn translate (&mut self, displacement : Vector3 <S>) {
2941 self.base += displacement;
2942 }
2943 #[expect(unused_variables)]
2944 fn scale (&mut self, scale : Positive <S>) {
2945 }
2947 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2948 let dot = self.normal.dot (*direction);
2949 let out = if approx::relative_eq!(dot, S::one()) {
2950 self.base
2951 } else {
2952 let projected =
2953 project_3d_point_on_plane (Point3 (self.base.0 + *direction), *self).0;
2954 Point3 (projected.sigvec().map (|x| if x == S::zero() {
2955 S::zero()
2956 } else {
2957 x * S::infinity()
2958 }))
2959 };
2960 (out, out.0.dot (*direction))
2961 }
2962}
2963
2964impl_numcast_fields!(Sphere2, center, radius);
2965impl <S : OrderedRing> Sphere2 <S> {
2966 #[inline]
2968 pub fn unit() -> Self {
2969 Sphere2 {
2970 center: Point2::origin(),
2971 radius: num::One::one()
2972 }
2973 }
2974 #[inline]
2976 pub fn intersects (self, other : Self) -> bool {
2977 intersect::discrete_sphere2_sphere2 (self, other)
2978 }
2979}
2980impl <S> Primitive <S, Point2 <S>> for Sphere2 <S> where
2981 S : Real + num::real::Real + MaybeSerDes
2982{
2983 fn translate (&mut self, displacement : Vector2 <S>) {
2984 self.center += displacement;
2985 }
2986 fn scale (&mut self, scale : Positive <S>) {
2987 self.radius *= scale;
2988 }
2989 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2990 let out = self.center + direction.normalized() * *self.radius;
2991 (out, out.0.dot (*direction))
2992 }
2993}
2994
2995impl_numcast_fields!(Sphere3, center, radius);
2996impl <S : OrderedRing> Sphere3 <S> {
2997 #[inline]
2999 pub fn unit() -> Self where S : Field {
3000 Sphere3 {
3001 center: Point3::origin(),
3002 radius: num::One::one()
3003 }
3004 }
3005 #[inline]
3007 pub fn intersects (self, other : Self) -> bool {
3008 intersect::discrete_sphere3_sphere3 (self, other)
3009 }
3010}
3011impl <S> Primitive <S, Point3 <S>> for Sphere3 <S> where
3012 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes
3013{
3014 fn translate (&mut self, displacement : Vector3 <S>) {
3015 self.center += displacement;
3016 }
3017 fn scale (&mut self, scale : Positive <S>) {
3018 self.radius *= scale;
3019 }
3020 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
3021 let out = self.center + direction.normalized() * *self.radius;
3022 (out, out.0.dot (*direction))
3023 }
3024}
3025
3026#[cfg(test)]
3031mod tests {
3032 use approx::{assert_relative_eq, assert_ulps_eq};
3033 use rand;
3034 use rand_distr;
3035 use rand_xorshift;
3036 use sorted_vec::partial::SortedSet;
3037 use super::*;
3038
3039 #[test]
3040 fn colinear_2() {
3041 use rand::{RngExt, SeedableRng};
3042 use rand_xorshift::XorShiftRng;
3043 use rand_distr::Distribution;
3044 macro test ($t:ty) {
3045 let mut rng = XorShiftRng::seed_from_u64 (0);
3046 let normal = rand_distr::StandardNormal;
3047 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3048 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3049 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3050 for _ in 0..100000 {
3052 let dir = Unit2::<$t>::normalize ([randn (&mut rng), randn (&mut rng)].into());
3053 let base = point2 (randf (&mut rng), randf (&mut rng));
3054 let line = Line2::new (base, dir);
3055 let p1 = line.point (randf (&mut rng));
3056 let p2 = line.point (randf (&mut rng));
3057 let p3 = line.point (randf (&mut rng));
3058 assert!(colinear_2d (p1, p2, p3));
3059 }
3060 for _ in 0..100000 {
3062 let p1 = point2 (randf (&mut rng), randf (&mut rng));
3063 let v = p1.0;
3064 let len = 0.1 + randf (&mut rng).abs();
3065 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3066 let rotation = Rotation2::from_angle (angle);
3067 let p2 = p1 + rotation * (v.normalized() * len);
3068 let v = p2 - p1;
3069 let len = 0.1 + randf (&mut rng).abs();
3070 let mut angle = rng.random_range (5.0..175.0);
3071 if rng.random_bool (0.5) {
3072 angle = -angle;
3073 }
3074 let rotation = Rotation2::from_angle (Deg (angle).into());
3075 let p3 = p2 + rotation * v * len;
3076 assert!(!colinear_2d (p1, p2, p3));
3077 }
3078 }
3079 test!(f32);
3080 test!(f64);
3081 }
3082
3083 #[test]
3084 fn colinear_3() {
3085 use rand::{RngExt, SeedableRng};
3086 use rand_xorshift::XorShiftRng;
3087 use rand_distr::Distribution;
3088 macro test ($t:ty) {
3089 let mut rng = XorShiftRng::seed_from_u64 (0);
3090 let normal = rand_distr::StandardNormal;
3091 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3092 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3093 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3094 for _ in 0..50000 {
3096 let dir = Unit3::<$t>::normalize (
3097 [randn (&mut rng), randn (&mut rng), randn (&mut rng)].into());
3098 let base = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3099 let line = Line3::new (base, dir);
3100 let p1 = line.point (randf (&mut rng));
3101 let p2 = line.point (randf (&mut rng));
3102 let p3 = line.point (randf (&mut rng));
3103 assert!(colinear_3d (p1, p2, p3));
3104 }
3105 for _ in 0..50000 {
3107 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3108 let v = p1.0;
3109 let len = 0.1 + randf (&mut rng).abs();
3110 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3111 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3112 let rotation = Rotation3::from_axis_angle (axis, angle);
3113 let p2 = p1 + rotation * (v.normalized() * len);
3114 let v = p2 - p1;
3115 let len = 0.1 + randf (&mut rng).abs();
3116 let axis = loop {
3117 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3118 if axis.normalized().cross (v.normalized()).magnitude() > 0.1 {
3119 break axis
3120 }
3121 };
3122 let mut angle = rng.random_range (5.0..175.0);
3123 if rng.random_bool (0.5) {
3124 angle = -angle;
3125 }
3126 let rotation = Rotation3::from_axis_angle (axis, Deg (angle).into());
3127 let p3 = p2 + rotation * v * len;
3128 assert!(!colinear_3d (p1, p2, p3));
3129 }
3130 }
3131 test!(f32);
3132 test!(f64);
3133 }
3134
3135 #[test]
3136 fn coplanar_3() {
3137 use rand::SeedableRng;
3138 use rand_xorshift::XorShiftRng;
3139 use rand_distr::Distribution;
3140 macro test ($t:ty) {
3141 let mut rng = XorShiftRng::seed_from_u64 (0);
3142 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3143 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3144 for _ in 0..50000 {
3146 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3147 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3148 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3149 let s = randf (&mut rng);
3150 let t = randf (&mut rng);
3151 let p4 = p1 + (p2 - p1) * s + (p3 - p1) * t;
3152 assert!(coplanar_3d::<$t> (p1, p2, p3, p4));
3153 }
3154 for _ in 0..50000 {
3156 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3157 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3158 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3159 let s = randf (&mut rng);
3160 let t = randf (&mut rng);
3161 let e1 = p2 - p1;
3162 let e2 = p3 - p1;
3163 let n = e1.cross (e2).normalized();
3164 let rn = randf (&mut rng);
3165 let pnew = p1 + e1 * s + e2 * t;
3166 let n_len = (0.3 * (pnew - p1).magnitude()).mul_add (rn.signum(), rn);
3167 let p4 = pnew + n * n_len;
3169 assert!(!coplanar_3d::<$t> (p1, p2, p3, p4));
3170 }
3171 }
3172 test!(f32);
3173 test!(f64);
3174 }
3175
3176 #[test]
3177 fn triangle_area_squared() {
3178 assert_relative_eq!(
3179 81.0,
3180 *triangle_3d_area_squared (
3181 [-2.0, -1.0, 0.0].into(),
3182 [ 1.0, -1.0, 0.0].into(),
3183 [ 1.0, 5.0, 0.0].into())
3184 );
3185 assert_relative_eq!(
3186 20.25,
3187 *triangle_3d_area_squared (
3188 [-2.0, -1.0, 0.0].into(),
3189 [ 1.0, -1.0, 0.0].into(),
3190 [ 1.0, 2.0, 0.0].into())
3191 );
3192 assert_relative_eq!(
3193 20.25,
3194 *triangle_3d_area_squared (
3195 [ 1.0, -1.0, 0.0].into(),
3196 [-2.0, -1.0, 0.0].into(),
3197 [ 1.0, 2.0, 0.0].into())
3198 );
3199 assert_relative_eq!(
3200 20.25,
3201 *triangle_3d_area_squared (
3202 [ 1.0, -1.0, 0.0].into(),
3203 [ 1.0, 2.0, 0.0].into(),
3204 [-2.0, -1.0, 0.0].into())
3205 );
3206 }
3207
3208 #[test]
3209 fn project_2d_point_on_line() {
3210 use super::project_2d_point_on_line;
3211 use Unit2;
3212 let point : Point2 <f64> = [2.0, 2.0].into();
3213 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_x());
3214 assert_eq!(project_2d_point_on_line (point, line).1, [2.0, 0.0].into());
3215 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_y());
3216 assert_eq!(project_2d_point_on_line (point, line).1, [0.0, 2.0].into());
3217 let point : Point2 <f64> = [0.0, 1.0].into();
3218 let line = Line2::<f64>::new (
3219 [0.0, -1.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3220 assert_relative_eq!(
3221 project_2d_point_on_line (point, line).1, [1.0, 0.0].into());
3222 let point : Point2 <f64> = [1.0, 3.0].into();
3224 let line_a = Line2::<f64>::new (
3225 [0.0, -1.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3226 let line_b = Line2::<f64>::new (
3227 [2.0, 0.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3228 assert_relative_eq!(
3229 project_2d_point_on_line (point, line_a).1,
3230 project_2d_point_on_line (point, line_b).1);
3231 let line_a = Line2::<f64>::new (
3232 [0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3233 let line_b = Line2::<f64>::new (
3234 [-2.0, -2.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3235 assert_ulps_eq!(
3236 project_2d_point_on_line (point, line_a).1,
3237 project_2d_point_on_line (point, line_b).1
3238 );
3239 assert_relative_eq!(
3240 project_2d_point_on_line (point, line_a).1,
3241 [2.0, 2.0].into());
3242 }
3243
3244 #[test]
3245 fn project_3d_point_on_line() {
3246 use Unit3;
3247 use super::project_3d_point_on_line;
3248 let point : Point3 <f64> = [2.0, 2.0, 0.0].into();
3250 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
3251 assert_eq!(project_3d_point_on_line (point, line).1, [2.0, 0.0, 0.0].into());
3252 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
3253 assert_eq!(project_3d_point_on_line (point, line).1, [0.0, 2.0, 0.0].into());
3254 let point : Point3 <f64> = [0.0, 1.0, 0.0].into();
3255 let line = Line3::<f64>::new (
3256 [0.0, -1.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3257 assert_relative_eq!(
3258 project_3d_point_on_line (point, line).1, [1.0, 0.0, 0.0].into());
3259 let point : Point3 <f64> = [1.0, 3.0, 0.0].into();
3261 let line_a = Line3::<f64>::new (
3262 [0.0, -1.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3263 let line_b = Line3::<f64>::new (
3264 [2.0, 0.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3265 assert_relative_eq!(
3266 project_3d_point_on_line (point, line_a).1,
3267 project_3d_point_on_line (point, line_b).1);
3268 let line_a = Line3::<f64>::new (
3269 [0.0, 0.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3270 let line_b = Line3::<f64>::new (
3271 [2.0, 2.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3272 assert_relative_eq!(
3273 project_3d_point_on_line (point, line_a).1,
3274 project_3d_point_on_line (point, line_b).1);
3275 assert_relative_eq!(
3276 project_3d_point_on_line (point, line_a).1,
3277 [2.0, 2.0, 0.0].into());
3278 let point : Point3 <f64> = [0.0, 0.0, 2.0].into();
3280 let line_a = Line3::<f64>::new (
3281 [-4.0, -4.0, -4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3282 let line_b = Line3::<f64>::new (
3283 [4.0, 4.0, 4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3284 assert_relative_eq!(
3285 project_3d_point_on_line (point, line_a).1,
3286 project_3d_point_on_line (point, line_b).1,
3287 epsilon = 0.000_000_000_000_01
3288 );
3289 assert_relative_eq!(
3290 project_3d_point_on_line (point, line_a).1,
3291 [2.0/3.0, 2.0/3.0, 2.0/3.0].into(),
3292 epsilon = 0.000_000_000_000_01
3293 );
3294 }
3295
3296 #[test]
3297 fn smallest_rect() {
3298 use super::smallest_rect;
3299 let points : Vec <Point2 <f32>> = [
3301 [-3.0, -3.0],
3302 [ 3.0, -3.0],
3303 [ 3.0, 3.0],
3304 [ 0.0, 6.0],
3305 [-3.0, 3.0]
3306 ].map (Point2::from).into_iter().collect();
3307 let hull = Hull2::from_points (points.as_slice()).unwrap();
3308 assert_eq!(hull.points(), points);
3309 let rect = smallest_rect (0, 1, &hull);
3310 assert_eq!(rect.area, 54.0);
3311 let points : Vec <Point2 <f32>> = [
3313 [-1.0, -4.0],
3314 [ 2.0, 2.0],
3315 [-4.0, -1.0]
3316 ].map (Point2::from).into_iter().collect();
3317 let hull = Hull2::from_points (points.as_slice()).unwrap();
3318 assert_eq!(hull.points(), points);
3319 let rect = smallest_rect (0, 1, &hull);
3320 assert_eq!(rect.area, 27.0);
3321 }
3322
3323 #[test]
3324 fn capsule3() {
3325 use num::One;
3326 let capsule : Capsule3 <f32> = Capsule3 {
3327 center: Point3::origin(),
3328 radius: Positive::noisy (2.0),
3329 half_height: Positive::one()
3330 };
3331 let support = capsule.support (Unit3::axis_z().into()).0;
3332 assert_eq!(support, point3 (0.0, 0.0, 3.0));
3333 let support = capsule.support (Unit3::axis_x().into()).0;
3334 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3335 let support = capsule.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3336 assert_eq!(support,
3337 point3 (0.0, 0.0, 1.0) + *Unit3::normalize (vector3 (1.0, 0.0, 1.0)) * 2.0);
3338 }
3339
3340 #[test]
3341 fn cylinder3() {
3342 use num::One;
3343 let cylinder : Cylinder3 <f32> = Cylinder3 {
3344 center: Point3::origin(),
3345 radius: Positive::noisy (2.0),
3346 half_height: Positive::one()
3347 };
3348 let support = cylinder.support (Unit3::axis_z().into()).0;
3349 assert_eq!(support, point3 (0.0, 0.0, 1.0));
3350 let support = cylinder.support (Unit3::axis_x().into()).0;
3351 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3352 let support = cylinder.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3353 assert_eq!(support, point3 (2.0, 0.0, 1.0));
3354 }
3355
3356 #[test]
3357 fn obb2() {
3358 let points : Vec <Point2 <f32>> = [
3359 [-1.0, -4.0],
3360 [ 2.0, 2.0],
3361 [-4.0, -1.0]
3362 ].map (Point2::from).into_iter().collect();
3363 let obb = Obb2::containing_points (&points).unwrap();
3364 let corners = obb.corners();
3365 assert_eq!(obb.center, [-0.25, -0.25].into());
3366 approx::assert_relative_eq!(point2 (-4.0, -1.0), corners[0], max_relative = 0.00001);
3367 approx::assert_relative_eq!(point2 ( 0.5, 3.5), corners[1], max_relative = 0.00001);
3368 approx::assert_relative_eq!(point2 (-1.0, -4.0), corners[2], max_relative = 0.00001);
3369 approx::assert_relative_eq!(point2 ( 3.5, 0.5), corners[3], max_relative = 0.00001);
3370 let points : Vec <Point2 <f32>> = [
3372 [-2.0, 0.0],
3373 [ 0.0, 2.0],
3374 [ 2.0, 0.0],
3375 [ 0.0, -2.0],
3376 ].map (Point2::from).into_iter().collect();
3377 let obb = Obb2::containing_points (&points).unwrap();
3378 let support = obb.support (Unit2::axis_y().into()).0;
3379 approx::assert_relative_eq!(support, point2 (0.0, 2.0), epsilon = 0.000_001);
3380 }
3381
3382 #[test]
3383 #[expect(clippy::suboptimal_flops)]
3384 fn obb3() {
3385 use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
3386 use approx::AbsDiffEq;
3387
3388 let points = [
3389 [ 1.0, -1.0, -1.0],
3390 [ 1.0, -1.0, 1.0],
3391 [ 1.0, 1.0, -1.0],
3392 [ 1.0, 1.0, 1.0],
3393 [-1.0, -1.0, -1.0],
3394 [-1.0, -1.0, 1.0],
3395 [-1.0, 1.0, -1.0],
3396 [-1.0, 1.0, 1.0]
3397 ].map (Point3::<f32>::from);
3398 let obb1 = Obb3::containing_points_with_orientation (&points, Angles3::zero())
3399 .unwrap();
3400 assert_eq!(
3401 SortedSet::from_unsorted (obb1.corners().to_vec()),
3402 SortedSet::from_unsorted (
3403 Aabb3::containing (&points).unwrap().corners().to_vec()));
3404 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3405 assert_eq!(obb1, obb2);
3406
3407 let rotation = Rotation3::from_angle_y (FRAC_PI_4.into());
3408 let points = points.map (|p| rotation.rotate (p));
3409 let obb1 = Obb3::containing_points_with_orientation (&points,
3410 Angles3::wrap (
3412 (3.0 * FRAC_PI_2).into(),
3413 (2.0 * PI - FRAC_PI_4).into(),
3414 FRAC_PI_2.into())
3415 ).unwrap();
3416 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3417 approx::assert_abs_diff_eq!(obb1.center, obb2.center);
3418 assert_eq!(obb1.orientation, obb2.orientation);
3419 approx::assert_abs_diff_eq!(*obb1.extents.x, *obb2.extents.x,
3420 epsilon=4.0 * f32::default_epsilon());
3421 approx::assert_abs_diff_eq!(*obb1.extents.y, *obb2.extents.y,
3422 epsilon=4.0 * f32::default_epsilon());
3423 approx::assert_abs_diff_eq!(*obb1.extents.z, *obb2.extents.z,
3424 epsilon=2.0 * f32::default_epsilon());
3425 let corners = obb2.corners();
3427 approx::assert_abs_diff_eq!(corners[0], points[7],
3428 epsilon=8.0 * f32::default_epsilon());
3429 approx::assert_abs_diff_eq!(corners[1], points[5],
3430 epsilon=8.0 * f32::default_epsilon());
3431 approx::assert_abs_diff_eq!(corners[2], points[3],
3432 epsilon=8.0 * f32::default_epsilon());
3433 approx::assert_abs_diff_eq!(corners[3], points[1],
3434 epsilon=8.0 * f32::default_epsilon());
3435 approx::assert_abs_diff_eq!(corners[4], points[6],
3436 epsilon=8.0 * f32::default_epsilon());
3437 approx::assert_abs_diff_eq!(corners[5], points[4],
3438 epsilon=8.0 * f32::default_epsilon());
3439 approx::assert_abs_diff_eq!(corners[6], points[2],
3440 epsilon=8.0 * f32::default_epsilon());
3441 approx::assert_abs_diff_eq!(corners[7], points[0],
3442 epsilon=8.0 * f32::default_epsilon());
3443
3444 let points : Vec <Point3 <f32>> = [
3446 [-2.0, 0.0, -2.0],
3447 [ 0.0, 2.0, -2.0],
3448 [ 2.0, 0.0, -2.0],
3449 [ 0.0, -2.0, -2.0],
3450 [-2.0, 0.0, 2.0],
3451 [ 0.0, 2.0, 2.0],
3452 [ 2.0, 0.0, 2.0],
3453 [ 0.0, -2.0, 2.0]
3454 ].map (Point3::from).into_iter().collect();
3455 let obb = Obb3::containing_points_approx (&points).unwrap();
3456 let support = obb.support (Unit3::axis_y().into()).0;
3457 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 0.0), epsilon = 0.000_001);
3458 let support = obb.support (NonZero3::noisy (vector3 (0.0, 1.0, 1.0))).0;
3459 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 2.0), epsilon = 0.000_001);
3460 }
3461
3462 #[test]
3463 fn support_aabb3() {
3464 let aabb = Aabb3::with_minmax (
3465 [-1.0, -2.0, -3.0].into(),
3466 [ 1.0, 2.0, 3.0].into()).unwrap();
3467 assert_eq!(
3468 [-1.0, -2.0, -3.0],
3469 aabb.support (NonZero3::noisy ([-1.0, -1.0, -1.0].into())).0.0.into_array());
3470 assert_eq!(
3471 [-1.0, -2.0, 3.0],
3472 aabb.support (NonZero3::noisy ([-1.0, -1.0, 1.0].into())).0.0.into_array());
3473 assert_eq!(
3474 [-1.0, 2.0, -3.0],
3475 aabb.support (NonZero3::noisy ([-1.0, 1.0, -1.0].into())).0.0.into_array());
3476 assert_eq!(
3477 [-1.0, 2.0, 3.0],
3478 aabb.support (NonZero3::noisy ([-1.0, 1.0, 1.0].into())).0.0.into_array());
3479 assert_eq!(
3480 [ 1.0, -2.0, -3.0],
3481 aabb.support (NonZero3::noisy ([ 1.0, -1.0, -1.0].into())).0.0.into_array());
3482 assert_eq!(
3483 [ 1.0, -2.0, 3.0],
3484 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 1.0].into())).0.0.into_array());
3485 assert_eq!(
3486 [ 1.0, 2.0, -3.0],
3487 aabb.support (NonZero3::noisy ([ 1.0, 1.0, -1.0].into())).0.0.into_array());
3488 assert_eq!(
3489 [ 1.0, 2.0, 3.0],
3490 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 1.0].into())).0.0.into_array());
3491
3492 assert_eq!(
3493 [-1.0, -2.0, 0.0],
3494 aabb.support (NonZero3::noisy ([-1.0, -1.0, 0.0].into())).0.0.into_array());
3495 assert_eq!(
3496 [-1.0, 2.0, 0.0],
3497 aabb.support (NonZero3::noisy ([-1.0, 1.0, 0.0].into())).0.0.into_array());
3498 assert_eq!(
3499 [ 1.0, -2.0, 0.0],
3500 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 0.0].into())).0.0.into_array());
3501 assert_eq!(
3502 [ 1.0, 2.0, 0.0],
3503 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 0.0].into())).0.0.into_array());
3504
3505 assert_eq!(
3506 [-1.0, 0.0, -3.0],
3507 aabb.support (NonZero3::noisy ([-1.0, 0.0, -1.0].into())).0.0.into_array());
3508 assert_eq!(
3509 [-1.0, 0.0, 3.0],
3510 aabb.support (NonZero3::noisy ([-1.0, 0.0, 1.0].into())).0.0.into_array());
3511 assert_eq!(
3512 [ 1.0, 0.0, -3.0],
3513 aabb.support (NonZero3::noisy ([ 1.0, 0.0, -1.0].into())).0.0.into_array());
3514 assert_eq!(
3515 [ 1.0, 0.0, 3.0],
3516 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 1.0].into())).0.0.into_array());
3517
3518 assert_eq!(
3519 [ 0.0, -2.0, -3.0],
3520 aabb.support (NonZero3::noisy ([ 0.0, -1.0, -1.0].into())).0.0.into_array());
3521 assert_eq!(
3522 [ 0.0, -2.0, 3.0],
3523 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 1.0].into())).0.0.into_array());
3524 assert_eq!(
3525 [ 0.0, 2.0, -3.0],
3526 aabb.support (NonZero3::noisy ([ 0.0, 1.0, -1.0].into())).0.0.into_array());
3527 assert_eq!(
3528 [ 0.0, 2.0, 3.0],
3529 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 1.0].into())).0.0.into_array());
3530
3531 assert_eq!(
3532 [-1.0, 0.0, 0.0],
3533 aabb.support (NonZero3::noisy ([-1.0, 0.0, 0.0].into())).0.0.into_array());
3534 assert_eq!(
3535 [ 1.0, 0.0, 0.0],
3536 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 0.0].into())).0.0.into_array());
3537 assert_eq!(
3538 [ 0.0, -2.0, 0.0],
3539 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 0.0].into())).0.0.into_array());
3540 assert_eq!(
3541 [ 0.0, 2.0, 0.0],
3542 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 0.0].into())).0.0.into_array());
3543 assert_eq!(
3544 [ 0.0, 0.0, -3.0],
3545 aabb.support (NonZero3::noisy ([ 0.0, 0.0, -1.0].into())).0.0.into_array());
3546 assert_eq!(
3547 [ 0.0, 0.0, 3.0],
3548 aabb.support (NonZero3::noisy ([ 0.0, 0.0, 1.0].into())).0.0.into_array());
3549 }
3550}