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 pub fn extrude (self, axis : SignedAxis2, amount : Positive <S>) -> Self {
1154 let (min, max) = match axis {
1155 SignedAxis2::PosX => (
1156 self.min + Vector2::zero().with_x (*self.width()),
1157 self.max + Vector2::zero().with_x (*amount) ),
1158 SignedAxis2::NegX => (
1159 self.min - Vector2::zero().with_x (*amount),
1160 self.max - Vector2::zero().with_x (*self.height()) ),
1161 SignedAxis2::PosY => (
1162 self.min + Vector2::zero().with_y (*self.height()),
1163 self.max + Vector2::zero().with_y (*amount) ),
1164 SignedAxis2::NegY => (
1165 self.min - Vector2::zero().with_y (*amount),
1166 self.max - Vector2::zero().with_y (*self.height()) )
1167 };
1168 Aabb2 { min, max }
1169 }
1170
1171 pub fn extend (&mut self, axis : SignedAxis2, amount : Positive <S>) {
1172 match axis {
1173 SignedAxis2::PosX => self.max.0.x += *amount,
1174 SignedAxis2::NegX => self.min.0.x -= *amount,
1175 SignedAxis2::PosY => self.max.0.y += *amount,
1176 SignedAxis2::NegY => self.min.0.y -= *amount
1177 }
1178 }
1179
1180 pub fn with_z (self, z : Interval <S>) -> Aabb3 <S> {
1181 Aabb3::with_minmax_unchecked (
1182 self.min.0.with_z (z.min()).into(),
1183 self.max.0.with_z (z.max()).into()
1184 )
1185 }
1186
1187 pub fn dilate (mut self, amount : S) -> Option <Self> {
1197 self.min -= Vector2::broadcast (amount);
1198 self.max += Vector2::broadcast (amount);
1199 if self.min == self.max || self.min != point2_min (self.min, self.max)
1200 || self.max != point2_max (self.min, self.max)
1201 {
1202 None
1203 } else {
1204 Some (self)
1205 }
1206 }
1207
1208 pub fn project (self, axis : Axis2) -> Interval <S> {
1212 let (min, max) = match axis {
1213 Axis2::X => (self.min.y(), self.max.y()),
1214 Axis2::Y => (self.min.x(), self.max.x())
1215 };
1216 Interval::with_minmax_unchecked (min, max)
1217 }
1218}
1219
1220impl <S> Primitive <S, Point2 <S>> for Aabb2 <S> where S : OrderedField {
1221 fn translate (&mut self, displacement : Vector2 <S>) {
1222 self.min += displacement;
1223 self.max += displacement;
1224 }
1225 fn scale (&mut self, scale : Positive <S>) {
1226 let center = self.center();
1227 self.translate (-center.0);
1228 self.min.0 *= *scale;
1229 self.max.0 *= *scale;
1230 self.translate (center.0)
1231 }
1232 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
1233 let out = if let Some (quadrant) = Quadrant::from_vec_strict (*direction) {
1234 self.corner (quadrant)
1235 } else if direction.x > S::zero() {
1236 debug_assert_eq!(direction.y, S::zero());
1237 point2 (self.max.x(), (self.min.y() + self.max.y()) / S::two())
1238 } else if direction.x < S::zero() {
1239 debug_assert_eq!(direction.y, S::zero());
1240 point2 (self.min.x(), (self.min.y() + self.max.y()) / S::two())
1241 } else {
1242 debug_assert_eq!(direction.x, S::zero());
1243 if direction.y > S::zero() {
1244 point2 ((self.min.x() + self.max.x()) / S::two(), self.max.y())
1245 } else {
1246 debug_assert!(direction.y < S::zero());
1247 point2 ((self.min.x() + self.max.x()) / S::two(), self.min.y())
1248 }
1249 };
1250 (out, out.0.dot (*direction))
1251 }
1252}
1253
1254impl <S> std::ops::Add <Vector2 <S>> for Aabb2 <S> where
1255 S : Field,
1256 Self : Primitive <S, Point2 <S>>
1257{
1258 type Output = Self;
1259 #[expect(clippy::renamed_function_params)]
1260 fn add (mut self, displacement : Vector2 <S>) -> Self {
1261 self.translate (displacement);
1262 self
1263 }
1264}
1265
1266impl <S> std::ops::Sub <Vector2 <S>> for Aabb2 <S> where
1267 S : Field,
1268 Self : Primitive <S, Point2 <S>>
1269{
1270 type Output = Self;
1271 #[expect(clippy::renamed_function_params)]
1272 fn sub (mut self, displacement : Vector2 <S>) -> Self {
1273 self.translate (-displacement);
1274 self
1275 }
1276}
1277
1278impl_numcast_fields!(Aabb3, min, max);
1279impl <S : OrderedRing> Aabb3 <S> {
1280 #[inline]
1282 pub fn with_minmax (min : Point3 <S>, max : Point3 <S>) -> Option <Self> {
1283 if min == max || min != point3_min (min, max) || max != point3_max (min, max) {
1284 None
1285 } else {
1286 Some (Aabb3 { min, max })
1287 }
1288 }
1289 #[inline]
1291 pub fn from_points (a : Point3 <S>, b : Point3 <S>) -> Option <Self> {
1292 if a == b {
1293 None
1294 } else {
1295 let min = point3_min (a, b);
1296 let max = point3_max (a, b);
1297 Some (Aabb3 { min, max })
1298 }
1299 }
1300 #[inline]
1320 pub fn with_minmax_unchecked (min : Point3 <S>, max : Point3 <S>) -> Self {
1321 debug_assert_ne!(min, max);
1322 debug_assert_eq!(min, point3_min (min, max));
1323 debug_assert_eq!(max, point3_max (min, max));
1324 Aabb3 { min, max }
1325 }
1326 #[inline]
1337 pub fn from_points_unchecked (a : Point3 <S>, b : Point3 <S>) -> Self {
1338 debug_assert_ne!(a, b);
1339 let min = point3_min (a, b);
1340 let max = point3_max (a, b);
1341 Aabb3 { min, max }
1342 }
1343 #[inline]
1357 pub fn containing (points : &[Point3 <S>]) -> Option <Self> {
1358 debug_assert!(points.len() >= 2);
1359 let mut min = point3_min (points[0], points[1]);
1360 let mut max = point3_max (points[0], points[1]);
1361 for point in points.iter().skip (2) {
1362 min = point3_min (min, *point);
1363 max = point3_max (max, *point);
1364 }
1365 Aabb3::with_minmax (min, max)
1366 }
1367 #[inline]
1369 pub fn union (a : Aabb3 <S>, b : Aabb3 <S>) -> Self {
1370 Aabb3::with_minmax_unchecked (
1371 point3_min (a.min(), b.min()), point3_max (a.max(), b.max()))
1372 }
1373 #[inline]
1374 pub const fn min (self) -> Point3 <S> {
1375 self.min
1376 }
1377 #[inline]
1378 pub const fn max (self) -> Point3 <S> {
1379 self.max
1380 }
1381 #[inline]
1382 pub fn center (self) -> Point3 <S> {
1383 Point3 ((self.min.0 + self.max.0) / S::two())
1384 }
1385 #[inline]
1386 pub fn dimensions (self) -> Vector3 <NonNegative <S>> {
1387 (self.max.0 - self.min.0).map (NonNegative::unchecked)
1388 }
1389 #[inline]
1390 pub fn extents (self) -> Vector3 <NonNegative <S>> where S : Field {
1391 self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1392 }
1393 #[inline]
1395 pub fn width (self) -> NonNegative <S> {
1396 NonNegative::unchecked (self.max.x() - self.min.x())
1397 }
1398 #[inline]
1400 pub fn depth (self) -> NonNegative <S> {
1401 NonNegative::unchecked (self.max.y() - self.min.y())
1402 }
1403 #[inline]
1405 pub fn height (self) -> NonNegative <S> {
1406 NonNegative::unchecked (self.max.z() - self.min.z())
1407 }
1408 #[inline]
1409 pub fn volume (self) -> NonNegative <S> {
1410 self.width() * self.depth() * self.height()
1411 }
1412 #[inline]
1413 pub fn contains (self, point : Point3 <S>) -> bool {
1414 self.min.x() < point.x() && point.x() < self.max.x() &&
1415 self.min.y() < point.y() && point.y() < self.max.y() &&
1416 self.min.z() < point.z() && point.z() < self.max.z()
1417 }
1418 pub fn clamp (self, point : Point3 <S>) -> Point3 <S> {
1430 [ point.x().clamp (self.min.x(), self.max.x()),
1431 point.y().clamp (self.min.y(), self.max.y()),
1432 point.z().clamp (self.min.z(), self.max.z())
1433 ].into()
1434 }
1435 #[inline]
1463 pub fn rand_point <R> (self, rng : &mut R) -> Point3 <S> where
1464 S : rand::distr::uniform::SampleUniform,
1465 R : rand::RngExt
1466 {
1467 let x_range = self.min.x()..self.max.x();
1468 let y_range = self.min.y()..self.max.y();
1469 let z_range = self.min.z()..self.max.z();
1470 let x = if x_range.is_empty() {
1471 self.min.x()
1472 } else {
1473 rng.random_range (x_range)
1474 };
1475 let y = if y_range.is_empty() {
1476 self.min.y()
1477 } else {
1478 rng.random_range (y_range)
1479 };
1480 let z = if z_range.is_empty() {
1481 self.min.z()
1482 } else {
1483 rng.random_range (z_range)
1484 };
1485 [x, y, z].into()
1486 }
1487 #[inline]
1488 pub fn intersects (self, other : Aabb3 <S>) -> bool {
1489 intersect::discrete_aabb3_aabb3 (self, other)
1490 }
1491 #[inline]
1492 pub fn intersection (self, other : Aabb3 <S>) -> Option <Aabb3 <S>> {
1493 intersect::continuous_aabb3_aabb3 (self, other)
1494 }
1495
1496 pub fn corner (self, octant : Octant) -> Point3 <S> {
1497 match octant {
1498 Octant::PosPosPos => self.max,
1499 Octant::NegPosPos => [self.min.x(), self.max.y(), self.max.z()].into(),
1500 Octant::PosNegPos => [self.max.x(), self.min.y(), self.max.z()].into(),
1501 Octant::NegNegPos => [self.min.x(), self.min.y(), self.max.z()].into(),
1502 Octant::PosPosNeg => [self.max.x(), self.max.y(), self.min.z()].into(),
1503 Octant::NegPosNeg => [self.min.x(), self.max.y(), self.min.z()].into(),
1504 Octant::PosNegNeg => [self.max.x(), self.min.y(), self.min.z()].into(),
1505 Octant::NegNegNeg => self.min
1506 }
1507 }
1508
1509 pub fn corners (self) -> [Point3 <S>; 8] {
1510 [ self.min,
1511 [self.min.x(), self.max.y(), self.max.z()].into(),
1512 [self.max.x(), self.min.y(), self.max.z()].into(),
1513 [self.min.x(), self.min.y(), self.max.z()].into(),
1514 [self.max.x(), self.max.y(), self.min.z()].into(),
1515 [self.min.x(), self.max.y(), self.min.z()].into(),
1516 [self.max.x(), self.min.y(), self.min.z()].into(),
1517 self.max
1518 ]
1519 }
1520
1521 pub fn face (self, direction : SignedAxis3) -> Self {
1522 let (min, max) = match direction {
1523 SignedAxis3::PosX => (
1524 self.corner (Octant::PosNegNeg),
1525 self.corner (Octant::PosPosPos) ),
1526 SignedAxis3::NegX => (
1527 self.corner (Octant::NegNegNeg),
1528 self.corner (Octant::NegPosPos) ),
1529 SignedAxis3::PosY => (
1530 self.corner (Octant::NegPosNeg),
1531 self.corner (Octant::PosPosPos) ),
1532 SignedAxis3::NegY => (
1533 self.corner (Octant::NegNegNeg),
1534 self.corner (Octant::PosNegPos) ),
1535 SignedAxis3::PosZ => (
1536 self.corner (Octant::NegNegPos),
1537 self.corner (Octant::PosPosPos) ),
1538 SignedAxis3::NegZ => (
1539 self.corner (Octant::NegNegNeg),
1540 self.corner (Octant::PosPosNeg) )
1541 };
1542 Aabb3::with_minmax_unchecked (min, max)
1543 }
1544
1545 pub fn extrude (self, axis : SignedAxis3, amount : Positive <S>) -> Self {
1546 let (min, max) = match axis {
1547 SignedAxis3::PosX => (
1548 self.min + Vector3::zero().with_x (*self.width()),
1549 self.max + Vector3::zero().with_x (*amount)
1550 ),
1551 SignedAxis3::NegX => (
1552 self.min - Vector3::zero().with_x (*amount),
1553 self.max - Vector3::zero().with_x (*self.width())
1554 ),
1555 SignedAxis3::PosY => (
1556 self.min + Vector3::zero().with_y (*self.depth()),
1557 self.max + Vector3::zero().with_y (*amount)
1558 ),
1559 SignedAxis3::NegY => (
1560 self.min - Vector3::zero().with_y (*amount),
1561 self.max - Vector3::zero().with_y (*self.depth())
1562 ),
1563 SignedAxis3::PosZ => (
1564 self.min + Vector3::zero().with_z (*self.height()),
1565 self.max + Vector3::zero().with_z (*amount)
1566 ),
1567 SignedAxis3::NegZ => (
1568 self.min - Vector3::zero().with_z (*amount),
1569 self.max - Vector3::zero().with_z (*self.height())
1570 )
1571 };
1572 Aabb3 { min, max }
1573 }
1574
1575 pub fn extend (&mut self, axis : SignedAxis3, amount : Positive <S>) {
1576 match axis {
1577 SignedAxis3::PosX => self.max.0.x += *amount,
1578 SignedAxis3::NegX => self.min.0.x -= *amount,
1579 SignedAxis3::PosY => self.max.0.y += *amount,
1580 SignedAxis3::NegY => self.min.0.y -= *amount,
1581 SignedAxis3::PosZ => self.max.0.z += *amount,
1582 SignedAxis3::NegZ => self.min.0.z -= *amount
1583 }
1584 }
1585
1586 pub fn dilate (mut self, amount : S) -> Option <Self> {
1596 self.min -= Vector3::broadcast (amount);
1597 self.max += Vector3::broadcast (amount);
1598 if self.min == self.max || self.min != point3_min (self.min, self.max)
1599 || self.max != point3_max (self.min, self.max)
1600 {
1601 None
1602 } else {
1603 Some (self)
1604 }
1605 }
1606
1607 pub fn project (self, axis : Axis3) -> Aabb2 <S> {
1611 let (min, max) = match axis {
1612 Axis3::X => ([self.min.y(), self.min.z()], [self.max.y(), self.max.z()]),
1613 Axis3::Y => ([self.min.x(), self.min.z()], [self.max.x(), self.max.z()]),
1614 Axis3::Z => ([self.min.x(), self.min.y()], [self.max.x(), self.max.y()]),
1615 };
1616 Aabb2::with_minmax_unchecked (min.into(), max.into())
1617 }
1618}
1619
1620impl <S> Primitive <S, Point3 <S>> for Aabb3 <S> where S : OrderedField {
1621 fn translate (&mut self, displacement : Vector3 <S>) {
1622 self.min += displacement;
1623 self.max += displacement;
1624 }
1625 fn scale (&mut self, scale : Positive <S>) {
1626 let center = self.center();
1627 self.translate (-center.0);
1628 self.min.0 *= *scale;
1629 self.max.0 *= *scale;
1630 self.translate (center.0);
1631 }
1632 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
1633 let out = if let Some (octant) = Octant::from_vec_strict (*direction) {
1634 self.corner (octant)
1635 } else {
1636 use Sign::*;
1637 let center = self.center();
1638 match direction.map (S::sign).into_array() {
1639 [Positive, Zero, Zero] => center.0.with_x (self.max.x()).into(),
1641 [Negative, Zero, Zero] => center.0.with_x (self.min.x()).into(),
1642 [Zero, Positive, Zero] => center.0.with_y (self.max.y()).into(),
1643 [Zero, Negative, Zero] => center.0.with_y (self.min.y()).into(),
1644 [Zero, Zero, Positive] => center.0.with_z (self.max.z()).into(),
1645 [Zero, Zero, Negative] => center.0.with_z (self.min.z()).into(),
1646 [Positive, Positive, Zero] =>
1648 center.0.with_x (self.max.x()).with_y (self.max.y()).into(),
1649 [Positive, Negative, Zero] =>
1650 center.0.with_x (self.max.x()).with_y (self.min.y()).into(),
1651 [Negative, Positive, Zero] =>
1652 center.0.with_x (self.min.x()).with_y (self.max.y()).into(),
1653 [Negative, Negative, Zero] =>
1654 center.0.with_x (self.min.x()).with_y (self.min.y()).into(),
1655
1656 [Positive, Zero, Positive] =>
1657 center.0.with_x (self.max.x()).with_z (self.max.z()).into(),
1658 [Positive, Zero, Negative] =>
1659 center.0.with_x (self.max.x()).with_z (self.min.z()).into(),
1660 [Negative, Zero, Positive] =>
1661 center.0.with_x (self.min.x()).with_z (self.max.z()).into(),
1662 [Negative, Zero, Negative] =>
1663 center.0.with_x (self.min.x()).with_z (self.min.z()).into(),
1664
1665 [Zero, Positive, Positive] =>
1666 center.0.with_y (self.max.y()).with_z (self.max.z()).into(),
1667 [Zero, Positive, Negative] =>
1668 center.0.with_y (self.max.y()).with_z (self.min.z()).into(),
1669 [Zero, Negative, Positive] =>
1670 center.0.with_y (self.min.y()).with_z (self.max.z()).into(),
1671 [Zero, Negative, Negative] =>
1672 center.0.with_y (self.min.y()).with_z (self.min.z()).into(),
1673 [_, _, _] => unreachable!()
1674 }
1675 };
1676 (out, out.0.dot (*direction))
1677 }
1678}
1679
1680impl <S> std::ops::Add <Vector3 <S>> for Aabb3 <S> where
1681 S : Field,
1682 Self : Primitive <S, Point3 <S>>
1683{
1684 type Output = Self;
1685 #[expect(clippy::renamed_function_params)]
1686 fn add (mut self, displacement : Vector3 <S>) -> Self {
1687 self.translate (displacement);
1688 self
1689 }
1690}
1691
1692impl <S> std::ops::Sub <Vector3 <S>> for Aabb3 <S> where
1693 S : Field,
1694 Self : Primitive <S, Point3 <S>>
1695{
1696 type Output = Self;
1697 #[expect(clippy::renamed_function_params)]
1698 fn sub (mut self, displacement : Vector3 <S>) -> Self {
1699 self.translate (-displacement);
1700 self
1701 }
1702}
1703
1704impl_numcast_fields!(Obb2, center, extents, orientation);
1705impl <S : OrderedRing> Obb2 <S> {
1706 #[inline]
1708 pub const fn new (
1709 center : Point2 <S>,
1710 extents : Vector2 <Positive <S>>,
1711 orientation : AngleWrapped <S>
1712 ) -> Self {
1713 Obb2 { center, extents, orientation }
1714 }
1715 pub fn containing_points_with_orientation (
1719 points : &[Point2 <S>], orientation : AngleWrapped <S>
1720 ) -> Option <Self> where
1721 S : Real + num::real::Real + MaybeSerDes
1722 {
1723 let [x, y] = Rotation2::from_angle (orientation.angle()).cols
1724 .map (NonZero2::unchecked).into_array();
1725 let min_dot_x = -support2 (points, -x).1;
1726 let max_dot_x = support2 (points, x).1;
1727 let min_dot_y = -support2 (points, -y).1;
1728 let max_dot_y = support2 (points, y).1;
1729 let center = Point2 (
1730 (*x * min_dot_x + *x * max_dot_x) * S::half() +
1731 (*y * min_dot_y + *y * max_dot_y) * S::half());
1732 let extents = vector2 (
1733 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
1734 Positive::new (S::half() * (max_dot_y - min_dot_y))?);
1735 Some (Obb2 { center, extents, orientation })
1736 }
1737 pub fn containing_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
1755 let hull = Hull2::from_points (points)?;
1756 Self::containing (&hull)
1757 }
1758 pub fn containing (hull : &Hull2 <S>) -> Option <Self> where S : Real {
1777 let points = hull.points();
1778 if points.len() < 3 {
1779 return None
1780 }
1781 let mut visited = vec![false; points.len()];
1782 #[expect(clippy::cast_possible_truncation)]
1783 let mut min_rect = smallest_rect (points.len() as u32 - 1, 0, hull);
1784 visited[min_rect.indices[0] as usize] = true;
1785 let mut rect = min_rect.clone();
1787 for _ in 0..points.len() {
1788 let mut angles = [(S::zero(), u32::MAX); 4];
1789 let mut nangles = u32::MAX;
1790 if !compute_angles (hull, &rect, &mut angles, &mut nangles) {
1791 break
1792 }
1793 let sorted = sort_angles (&angles, nangles);
1794 if !update_support (&angles, nangles, &sorted, hull, &mut visited, &mut rect) {
1795 break
1796 }
1797 if rect.area < min_rect.area {
1798 min_rect = rect.clone();
1799 }
1800 }
1801 let sum = [
1803 points[min_rect.indices[1] as usize].0 + points[min_rect.indices[3] as usize].0,
1804 points[min_rect.indices[2] as usize].0 + points[min_rect.indices[0] as usize].0
1805 ];
1806 let diff = [
1807 points[min_rect.indices[1] as usize].0 - points[min_rect.indices[3] as usize].0,
1808 points[min_rect.indices[2] as usize].0 - points[min_rect.indices[0] as usize].0
1809 ];
1810 let obb = {
1811 let center = Point2::from ((min_rect.edge * min_rect.edge.dot (sum[0]) +
1812 min_rect.perp * min_rect.perp.dot (sum[1]))
1813 * S::half() / min_rect.edge_len_squared);
1814 let extents = {
1815 let extent_x = Positive::unchecked (S::sqrt (
1816 (S::half() * min_rect.edge.dot (diff[0])).squared()
1817 / min_rect.edge_len_squared));
1818 let extent_y = Positive::unchecked (S::sqrt (
1819 (S::half() * min_rect.perp.dot (diff[1])).squared()
1820 / min_rect.edge_len_squared));
1821 vector2 (extent_x, extent_y)
1822 };
1823 let orientation =
1824 AngleWrapped::wrap (Rad (min_rect.edge.y.atan2 (min_rect.edge.x)));
1825 Obb2 { center, extents, orientation }
1826 };
1827 fn compute_angles <S : Real> (
1828 hull : &Hull2 <S>,
1829 rect : &Rect <S>,
1830 angles : &mut [(S, u32); 4],
1831 nangles : &mut u32
1832 ) -> bool {
1833 *nangles = 0;
1834 let points = hull.points();
1835 let mut k0 = 3;
1836 let mut k1 = 0;
1837 while k1 < 4 {
1838 if rect.indices[k0] != rect.indices[k1] {
1839 let d = {
1840 let mut d = if k0 & 1 != 0 {
1841 rect.perp
1842 } else {
1843 rect.edge
1844 };
1845 if k0 & 2 != 0 {
1846 d = -d;
1847 }
1848 d
1849 };
1850 let j0 = rect.indices[k0];
1851 let mut j1 = j0 + 1;
1852 #[expect(clippy::cast_possible_truncation)]
1853 if j1 == points.len() as u32 {
1854 j1 = 0;
1855 }
1856 let e = points[j1 as usize] - points[j0 as usize];
1857 let dp = d.dot ([e.y, -e.x].into());
1858 let e_lensq = e.magnitude_squared();
1859 let sin_theta_squared = (dp * dp) / e_lensq;
1860 angles[*nangles as usize] =
1861 #[expect(clippy::cast_possible_truncation)]
1862 (sin_theta_squared, k0 as u32);
1863 *nangles += 1;
1864 }
1865 k0 = k1;
1866 k1 += 1;
1867 }
1868 *nangles > 0
1869 }
1870 fn sort_angles <S : PartialOrd> (angles : &[(S, u32); 4], nangles : u32)
1871 -> [u32; 4]
1872 {
1873 let mut sorted = [0u32, 1, 2, 3];
1874 match nangles {
1875 0 | 1 => {}
1876 2 => if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1877 sorted.swap (0, 1)
1878 }
1879 3 => {
1880 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1881 sorted.swap (0, 1)
1882 }
1883 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1884 sorted.swap (0, 2)
1885 }
1886 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1887 sorted.swap (1, 2)
1888 }
1889 }
1890 4 => {
1891 if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1892 sorted.swap (0, 1)
1893 }
1894 if angles[sorted[2] as usize].0 > angles[sorted[3] as usize].0 {
1895 sorted.swap (2, 3)
1896 }
1897 if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1898 sorted.swap (0, 2)
1899 }
1900 if angles[sorted[1] as usize].0 > angles[sorted[3] as usize].0 {
1901 sorted.swap (1, 3)
1902 }
1903 if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1904 sorted.swap (1, 2)
1905 }
1906 }
1907 _ => unreachable!()
1908 }
1909 sorted
1910 }
1911 fn update_support <S : Real> (
1912 angles : &[(S, u32); 4],
1913 nangles : u32,
1914 sorted : &[u32; 4],
1915 hull : &Hull2 <S>,
1916 visited : &mut [bool],
1917 rect : &mut Rect <S>
1918 ) -> bool {
1919 let points = hull.points();
1920 let min_angle = angles[sorted[0] as usize];
1921 for k in 0..nangles as usize {
1922 let (angle, index) = angles[sorted[k] as usize];
1923 if angle == min_angle.0 {
1924 rect.indices[index as usize] += 1;
1925 #[expect(clippy::cast_possible_truncation)]
1926 if rect.indices[index as usize] == points.len() as u32 {
1927 rect.indices[index as usize] = 0;
1928 }
1929 }
1930 }
1931 let bottom = rect.indices[min_angle.1 as usize];
1932 if visited[bottom as usize] {
1933 return false
1934 }
1935 visited[bottom as usize] = true;
1936 let mut next_index = [u32::MAX; 4];
1937 for k in 0u32..4 {
1938 next_index[k as usize] = rect.indices[((min_angle.1 + k) % 4) as usize];
1939 }
1940 rect.indices = next_index;
1941 let j1 = rect.indices[0] as usize;
1942 let j0 = if j1 == 0 {
1943 points.len() - 1
1944 } else {
1945 j1 - 1
1946 };
1947 rect.edge = points[j1] - points[j0];
1948 rect.perp = vector2 (-rect.edge.y, rect.edge.x);
1949 let edge_len_squared = rect.edge.magnitude_squared();
1950 let diff = [
1951 points[rect.indices[1] as usize] - points[rect.indices[3] as usize],
1952 points[rect.indices[2] as usize] - points[rect.indices[0] as usize]
1953 ];
1954 rect.area = rect.edge.dot (diff[0]) * rect.perp.dot (diff[1]) / edge_len_squared;
1955 true
1956 }
1957 Some (obb)
1958 }
1959 #[inline]
1960 pub fn dimensions (self) -> Vector2 <Positive <S>> {
1961 self.extents * Positive::unchecked (S::two())
1962 }
1963 #[inline]
1965 pub fn width (self) -> Positive <S> {
1966 self.extents.x * Positive::unchecked (S::two())
1967 }
1968 #[inline]
1970 pub fn height (self) -> Positive <S> {
1971 self.extents.y * Positive::unchecked (S::two())
1972 }
1973 #[inline]
1974 pub fn area (self) -> Positive <S> {
1975 self.width() * self.height()
1976 }
1977 #[inline]
1978 pub fn corners (self) -> [Point2 <S>; 4] where
1979 S : Real + num::real::Real + MaybeSerDes
1980 {
1981 let mut points = [Point2::origin(); 4];
1982 for i in 0..2 {
1983 for j in 0..2 {
1984 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
1985 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
1986 points[i * 2 + j] = [*self.extents.x * sign_x, *self.extents.y * sign_y].into();
1987 }
1988 }
1989 let rotation = Rotation2::from_angle (self.orientation.angle());
1990 points.map (|point| rotation.rotate (point) + self.center.0)
1991 }
1992
1993 pub fn aabb (self) -> Aabb2 <S> where S : Real + num::real::Real + MaybeSerDes {
1994 Aabb2::containing (&self.corners()).unwrap()
1995 }
1996
1997 pub fn contains (self, point : Point2 <S>) -> bool where
2010 S : Real + num::real::Real + MaybeSerDes
2011 {
2012 let p = point - self.center.0;
2014 let p = Rotation2::from_angle (-self.orientation.angle()).rotate (p);
2016 p.x().abs() < *self.extents.x && p.y().abs() < *self.extents.y
2017 }
2018
2019 pub fn dilate (mut self, amount : S) -> Self {
2049 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2050 self
2051 }
2052}
2053impl <S : Real> From <Aabb2 <S>> for Obb2 <S> {
2054 fn from (aabb : Aabb2 <S>) -> Self {
2067 use num::Zero;
2068 let center = aabb.center();
2069 let extents = vector2 (
2070 Positive::noisy (*aabb.width() / S::two()),
2071 Positive::noisy (*aabb.height() / S::two()));
2072 let orientation = AngleWrapped::zero();
2073 Obb2::new (center, extents, orientation)
2074 }
2075}
2076impl <S> Primitive <S, Point2 <S>> for Obb2 <S> where
2077 S : Real + num::real::Real + MaybeSerDes
2078{
2079 fn translate (&mut self, displacement : Vector2 <S>) {
2080 self.center += displacement;
2081 }
2082 fn scale (&mut self, scale : Positive <S>) {
2083 self.extents *= scale;
2084 }
2085 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2086 use num::Inv;
2087 let rotation = Rotation2::from_angle (self.orientation.angle());
2088 let aabb = Aabb2::with_minmax_unchecked (
2089 self.center - self.extents.map (|e| *e),
2090 self.center + self.extents.map (|e| *e));
2091 let aabb_direction = rotation.inv() * direction;
2092 let (aabb_support, _) = aabb.support (aabb_direction);
2093 let out = rotation.rotate_around (aabb_support, self.center);
2094 (out, out.0.dot (*direction))
2095 }
2096}
2097
2098impl_numcast_fields!(Obb3, center, extents, orientation);
2099impl <S : OrderedRing> Obb3 <S> {
2100 #[inline]
2102 pub const fn new (
2103 center : Point3 <S>,
2104 extents : Vector3 <Positive <S>>,
2105 orientation : Angles3 <S>
2106 ) -> Self {
2107 Obb3 { center, extents, orientation }
2108 }
2109 pub fn containing_points_with_orientation (
2113 points : &[Point3 <S>], orientation : Angles3 <S>
2114 ) -> Option <Self> where
2115 S : Real + num::real::Real + MaybeSerDes
2116 {
2117 let [x, y, z] = Rotation3::from (orientation).cols.map (NonZero3::unchecked)
2118 .into_array();
2119 let min_dot_x = -support3 (points, -x).1;
2120 let max_dot_x = support3 (points, x).1;
2121 let min_dot_y = -support3 (points, -y).1;
2122 let max_dot_y = support3 (points, y).1;
2123 let min_dot_z = -support3 (points, -z).1;
2124 let max_dot_z = support3 (points, z).1;
2125 let center = Point3 (
2126 (*x * min_dot_x + *x * max_dot_x) * S::half() +
2127 (*y * min_dot_y + *y * max_dot_y) * S::half() +
2128 (*z * min_dot_z + *z * max_dot_z) * S::half());
2129 let extents = vector3 (
2130 Positive::new (S::half() * (max_dot_x - min_dot_x))?,
2131 Positive::new (S::half() * (max_dot_y - min_dot_y))?,
2132 Positive::new (S::half() * (max_dot_z - min_dot_z))?);
2133 Some (Obb3 { center, extents, orientation })
2134 }
2135 pub fn containing_points_approx (points : &[Point3 <S>]) -> Option <Self> where
2152 S : Real + num::real::Real + approx::RelativeEq <Epsilon=S> + MaybeSerDes
2153 {
2154 if points.len() < 4 {
2155 return None
2156 }
2157 let (hull, mesh) = Hull3::from_points_with_mesh (points)?;
2158 Self::containing_approx (&hull, &mesh)
2159 }
2160 pub fn containing_approx (hull : &Hull3 <S>, mesh : &VertexEdgeTriangleMesh)
2179 -> Option <Self>
2180 where S : Real + num::NumCast + approx::RelativeEq <Epsilon=S> + MaybeSerDes {
2181 use num::Inv;
2184 if hull.points().len() < 4 {
2185 return None
2186 }
2187 let mut min_obb : Obb3 <S> = Aabb3::containing (hull.points()).unwrap().into();
2188 let mut min_vol = min_obb.volume();
2189 for triangle in mesh.triangles().values() {
2190 let [a, b, c] = triangle.vertices().map (|i| hull.points()[i as usize]);
2191 let mut u = b - a;
2192 let mut v = c - a;
2193 let mut w = u.cross (v);
2194 v = w.cross (u);
2195 u = *u.normalize();
2196 v = *v.normalize();
2197 w = *w.normalize();
2198 let m = Matrix3::from_row_arrays (std::array::from_fn (|i| [u[i], v[i], w[i]]));
2199 let m_rot = Rotation3::new_approx (m).unwrap();
2200 let rot_inv = m_rot.inv();
2201 let center = a;
2202 let rotated = hull.points().iter().copied()
2203 .map (|p| rot_inv.rotate_around (p, center))
2204 .collect::<Vec <_>>();
2205 let aabb = Aabb3::containing (rotated.as_slice()).unwrap();
2206 let aabb_volume = aabb.volume();
2207 if approx::abs_diff_eq!(*aabb_volume, S::zero()) {
2208 return None
2210 }
2211 let volume = Positive::new (*aabb_volume).unwrap();
2212 if volume < min_vol {
2213 min_vol = volume;
2214 min_obb = aabb.into();
2215 min_obb.orientation = m_rot.into();
2216 min_obb.center = m_rot.rotate_around (min_obb.center, center);
2217 }
2218 }
2219 Some (min_obb)
2220 }
2221 pub fn containing_approx_pca (hull : &Hull3 <S>) -> Option <Self> where
2240 S : Real + num::real::Real + num::NumCast + approx::RelativeEq <Epsilon=S>
2241 + MaybeSerDes
2242 {
2243 if hull.points().len() < 4 {
2244 return None
2245 }
2246 let [p1, p2, p3] = &hull.points()[..3] else { unreachable!() };
2250 let mut coplanar = true;
2251 for p in &hull.points()[3..] {
2252 if !coplanar_3d (*p1, *p2, *p3, *p) {
2253 coplanar = false;
2254 break
2255 }
2256 }
2257 if coplanar {
2258 return None
2259 }
2260 let orientation = data::pca_3 (hull.points()).into();
2262 Some (Self::containing_points_with_orientation (hull.points(), orientation).unwrap())
2263 }
2264 #[inline]
2265 pub fn dimensions (self) -> Vector3 <Positive <S>> {
2266 self.extents * Positive::unchecked (S::two())
2267 }
2268 #[inline]
2270 pub fn width (self) -> Positive <S> {
2271 self.extents.x * Positive::unchecked (S::two())
2272 }
2273 #[inline]
2275 pub fn depth (self) -> Positive <S> {
2276 self.extents.y * Positive::unchecked (S::two())
2277 }
2278 #[inline]
2280 pub fn height (self) -> Positive <S> {
2281 self.extents.z * Positive::unchecked (S::two())
2282 }
2283 #[inline]
2284 pub fn volume (self) -> Positive <S> {
2285 self.width() * self.depth() * self.height()
2286 }
2287 #[inline]
2288 pub fn corners (self) -> [Point3 <S>; 8] where
2289 S : Real + num::real::Real + MaybeSerDes
2290 {
2291 let mut points = [Point3::origin(); 8];
2292 for i in 0..2 {
2293 for j in 0..2 {
2294 for k in 0..2 {
2295 let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2296 let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2297 let sign_z = if k % 2 == 0 { -S::one() } else { S::one() };
2298 points[i * 4 + j * 2 + k] = [
2299 *self.extents.x * sign_x,
2300 *self.extents.y * sign_y,
2301 *self.extents.z * sign_z
2302 ].into();
2303 }
2304 }
2305 }
2306 let rotation = Rotation3::from (self.orientation);
2307 points.map (|point| rotation.rotate (point) + self.center.0)
2308 }
2309
2310 pub fn aabb (self) -> Aabb3 <S> where S : Real + num::real::Real + MaybeSerDes {
2311 Aabb3::containing (&self.corners()).unwrap()
2312 }
2313
2314 pub fn contains (self, point : Point3 <S>) -> bool where
2316 S : Real + num::real::Real + MaybeSerDes
2317 {
2318 use num::Inv;
2319 let p = point - self.center.0;
2321 let p = Rotation3::from (self.orientation).inv().rotate (p);
2323 p.x().abs() < *self.extents.x &&
2324 p.y().abs() < *self.extents.y &&
2325 p.z().abs() < *self.extents.z
2326 }
2327
2328 pub fn dilate (mut self, amount : S) -> Self {
2343 self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2344 self
2345 }
2346}
2347impl <S : Real> From <Aabb3 <S>> for Obb3 <S> {
2348 fn from (aabb : Aabb3 <S>) -> Self {
2361 let center = aabb.center();
2362 let extents = aabb.extents().map (|d| Positive::noisy (*d));
2363 let orientation = Angles3::zero();
2364 Obb3::new (center, extents, orientation)
2365 }
2366}
2367impl <S> Primitive <S, Point3 <S>> for Obb3 <S> where
2368 S : Real + num::real::Real + MaybeSerDes
2369{
2370 fn translate (&mut self, displacement : Vector3 <S>) {
2371 self.center += displacement;
2372 }
2373 fn scale (&mut self, scale : Positive <S>) {
2374 self.extents *= scale;
2375 }
2376 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2377 use num::Inv;
2378 let rotation = Rotation3::from (self.orientation);
2379 let aabb = Aabb3::with_minmax_unchecked (
2380 self.center - self.extents.map (|e| *e),
2381 self.center + self.extents.map (|e| *e));
2382 let aabb_direction = rotation.inv() * direction;
2383 let (aabb_support, _) = aabb.support (aabb_direction);
2384 let out = rotation.rotate_around (aabb_support, self.center);
2385 (out, out.0.dot (*direction))
2386 }
2387}
2388
2389impl_numcast_fields!(Capsule3, center, half_height, radius);
2390impl <S : OrderedRing> Capsule3 <S> {
2391 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2392 use shape::Aabb;
2393 let shape_aabb = shape::Capsule {
2394 radius: self.radius,
2395 half_height: self.half_height
2396 }.aabb();
2397 let center_vec = self.center.0;
2398 let min = shape_aabb.min() + center_vec;
2399 let max = shape_aabb.max() + center_vec;
2400 Aabb3::with_minmax_unchecked (min, max)
2401 }
2402 pub fn decompose (self) -> (Sphere3 <S>, Option <Cylinder3 <S>>, Sphere3 <S>) {
2404 let cylinder = if *self.half_height > S::zero() {
2405 Some (Cylinder3 {
2406 center: self.center,
2407 radius: self.radius,
2408 half_height: Positive::unchecked (*self.half_height)
2409 })
2410 } else {
2411 None
2412 };
2413 let upper_sphere = Sphere3 {
2414 center: self.center + (Vector3::unit_z() * *self.half_height),
2415 radius: self.radius
2416 };
2417 let lower_sphere = Sphere3 {
2418 center: self.center - (Vector3::unit_z() * *self.half_height),
2419 radius: self.radius
2420 };
2421 (upper_sphere, cylinder, lower_sphere)
2422 }
2423}
2424impl <S> Primitive <S, Point3 <S>> for Capsule3 <S> where
2425 S : Real + num::real::Real + MaybeSerDes
2426{
2427 fn translate (&mut self, displacement : Vector3 <S>) {
2428 self.center += displacement;
2429 }
2430 fn scale (&mut self, scale : Positive <S>) {
2431 self.half_height *= scale;
2432 self.radius *= scale;
2433 }
2434 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2435 let dir_unit = direction.normalized();
2436 let out = self.center + if direction.z > S::zero() {
2437 Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2438 } else if direction.z < S::zero() {
2439 -Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2440 } else {
2441 dir_unit * *self.radius
2442 };
2443 (out, out.0.dot (*direction))
2444 }
2445}
2446
2447impl_numcast_fields!(Cylinder3, center, half_height, radius);
2448impl <S : OrderedRing> Cylinder3 <S> {
2449 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2450 use shape::Aabb;
2451 let shape_aabb = shape::Cylinder::unchecked (*self.radius, *self.half_height).aabb();
2452 let center_vec = self.center.0;
2453 let min = shape_aabb.min() + center_vec;
2454 let max = shape_aabb.max() + center_vec;
2455 Aabb3::with_minmax_unchecked (min, max)
2456 }
2457}
2458impl <S> Primitive <S, Point3 <S>> for Cylinder3 <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 out = self.center + if *direction == Vector3::unit_z() {
2470 Vector3::unit_z() * *self.half_height
2471 } else if *direction == -Vector3::unit_z() {
2472 -Vector3::unit_z() * *self.half_height
2473 } else if direction.z == S::zero() {
2474 direction.normalized() * *self.radius
2475 } else if direction.z > S::zero() {
2476 Vector3::unit_z() * *self.half_height
2477 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2478 } else {
2479 debug_assert!(direction.z < S::zero());
2480 -Vector3::unit_z() * *self.half_height
2481 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2482 };
2483 (out, out.0.dot (*direction))
2484 }
2485}
2486
2487impl_numcast_fields!(Cone3, center, half_height, radius);
2488impl <S : OrderedRing> Cone3 <S> {
2489 pub fn height (self) -> Positive <S> {
2490 self.half_height * Positive::unchecked (S::two())
2491 }
2492 pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2493 use shape::Aabb;
2494 let shape_aabb = shape::Cone {
2495 radius: self.radius,
2496 half_height: self.half_height
2497 }.aabb();
2498 let center_vec = self.center.0;
2499 let min = shape_aabb.min() + center_vec;
2500 let max = shape_aabb.max() + center_vec;
2501 Aabb3::with_minmax_unchecked (min, max)
2502 }
2503}
2504impl <S> Primitive <S, Point3 <S>> for Cone3 <S> where
2505 S : Real + num::real::Real + approx::RelativeEq + MaybeSerDes
2506{
2507 fn translate (&mut self, displacement : Vector3 <S>) {
2508 self.center += displacement;
2509 }
2510 fn scale (&mut self, scale : Positive <S>) {
2511 self.half_height *= scale;
2512 self.radius *= scale;
2513 }
2514 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2515 let top = || Vector3::unit_z() * *self.half_height;
2516 let bottom = || -top();
2517 let edge = || bottom()
2518 + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius;
2519 let out = self.center + if *direction == Vector3::unit_z() {
2520 top()
2521 } else if *direction == -Vector3::unit_z() {
2522 bottom()
2523 } else if direction.z <= S::zero() {
2524 edge()
2525 } else {
2526 let angle_dir =
2528 Trig::atan2 (direction.z, Sqrt::sqrt (S::one() - direction.z.squared()));
2529 debug_assert!(angle_dir > S::zero());
2530 let angle_cone = Trig::atan2 (-*self.height(), *self.radius);
2531 debug_assert!(angle_cone < S::zero());
2532 let pi_2 = S::pi() / S::two();
2533 let angle_total = angle_dir - angle_cone;
2534 if approx::relative_eq!(angle_total, pi_2) {
2535 (top() + edge()) * S::half()
2536 } else if angle_total > pi_2 {
2537 top()
2538 } else {
2539 debug_assert!(angle_total < pi_2);
2540 edge()
2541 }
2542 };
2543 (out, out.0.dot (*direction))
2544 }
2545}
2546
2547impl_numcast_fields!(Line2, base, direction);
2548impl <S : OrderedRing> Line2 <S> {
2549 #[inline]
2551 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2552 Line2 { base, direction }
2553 }
2554 #[inline]
2555 pub fn point (self, t : S) -> Point2 <S> {
2556 self.base + *self.direction * t
2557 }
2558 #[inline]
2559 pub fn affine_line (self) -> frame::Line2 <S> {
2560 frame::Line2 {
2561 origin: self.base,
2562 basis: self.direction.into()
2563 }
2564 }
2565 #[inline]
2566 pub fn nearest_point (self, point : Point2 <S>) -> Line2Point <S> {
2567 project_2d_point_on_line (point, self)
2568 }
2569 #[inline]
2570 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2571 -> Option <(Line2Point <S>, Line2Point <S>)>
2572 {
2573 intersect::continuous_line2_aabb2 (self, aabb)
2574 }
2575 #[inline]
2576 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2577 -> Option <(Line2Point <S>, Line2Point <S>)>
2578 where S : Field + Sqrt {
2579 intersect::continuous_line2_sphere2 (self, sphere)
2580 }
2581}
2582impl <S : Real> Default for Line2 <S> {
2583 fn default() -> Self {
2584 Line2 {
2585 base: Point2::origin(),
2586 direction: Unit2::axis_y()
2587 }
2588 }
2589}
2590impl <S> Primitive <S, Point2 <S>> for Line2 <S> where
2591 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2592{
2593 fn translate (&mut self, displacement : Vector2 <S>) {
2594 self.base += displacement;
2595 }
2596 #[expect(unused_variables)]
2597 fn scale (&mut self, scale : Positive <S>) {
2598 }
2600 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2601 let dot = self.direction.dot (*direction);
2602 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2603 self.base
2604 } else if dot > S::zero() {
2605 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2606 S::zero()
2607 } else {
2608 x * S::infinity()
2609 }))
2610 } else {
2611 debug_assert!(dot < S::zero());
2612 Point2 ((-self.direction).sigvec().map (|x| if x == S::zero() {
2613 S::zero()
2614 } else {
2615 x * S::infinity()
2616 }))
2617 };
2618 (out, out.0.dot (*direction))
2619 }
2620}
2621impl <S> From <Ray2 <S>> for Line2 <S> {
2622 fn from (Ray2 { base, direction } : Ray2 <S>) -> Line2 <S> {
2623 Line2 { base, direction }
2624 }
2625}
2626
2627impl_numcast_fields!(Ray2, base, direction);
2628impl <S : OrderedRing> Ray2 <S> {
2629 #[inline]
2631 pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2632 Ray2 { base, direction }
2633 }
2634 #[inline]
2635 pub fn point (self, t : S) -> Point2 <S> {
2636 self.base + *self.direction * t
2637 }
2638 #[inline]
2639 pub fn affine_line (self) -> frame::Line2 <S> {
2640 frame::Line2 {
2641 origin: self.base,
2642 basis: self.direction.into()
2643 }
2644 }
2645 pub fn nearest_point (self, point : Point2 <S>) -> Ray2Point <S> {
2646 use num::Zero;
2647 let (t, point) = Line2::from (self).nearest_point (point);
2648 if t < S::zero() {
2649 (NonNegative::zero(), self.base)
2650 } else {
2651 (NonNegative::unchecked (t), point)
2652 }
2653 }
2654 #[inline]
2655 pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2656 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2657 {
2658 intersect::continuous_ray2_aabb2 (self, aabb)
2659 }
2660 #[inline]
2661 pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2662 -> Option <(Ray2Point <S>, Ray2Point <S>)>
2663 {
2664 intersect::continuous_ray2_sphere2 (self, sphere)
2665 }
2666}
2667impl <S : Real> Default for Ray2 <S> {
2668 fn default() -> Self {
2669 Ray2 {
2670 base: Point2::origin(),
2671 direction: Unit2::axis_y()
2672 }
2673 }
2674}
2675impl <S> Primitive <S, Point2 <S>> for Ray2 <S> where
2676 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2677{
2678 fn translate (&mut self, displacement : Vector2 <S>) {
2679 self.base += displacement;
2680 }
2681 #[expect(unused_variables)]
2682 fn scale (&mut self, scale : Positive <S>) {
2683 }
2685 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2686 let dot = self.direction.dot (*direction);
2687 let out = if dot < S::default_epsilon() {
2688 self.base
2689 } else {
2690 Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2691 S::zero()
2692 } else {
2693 x * S::infinity()
2694 }))
2695 };
2696 (out, out.0.dot (*direction))
2697 }
2698}
2699impl <S> From <Line2 <S>> for Ray2 <S> {
2700 fn from (Line2 { base, direction } : Line2 <S>) -> Ray2 <S> {
2701 Ray2 { base, direction }
2702 }
2703}
2704
2705impl_numcast_fields!(Line3, base, direction);
2706impl <S : OrderedRing> Line3 <S> {
2707 #[inline]
2709 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2710 Line3 { base, direction }
2711 }
2712 #[inline]
2713 pub fn point (self, t : S) -> Point3 <S> {
2714 self.base + *self.direction * t
2715 }
2716 #[inline]
2717 pub fn affine_line (self) -> frame::Line3 <S> {
2718 frame::Line3 {
2719 origin: self.base,
2720 basis: self.direction.into()
2721 }
2722 }
2723 #[inline]
2724 pub fn nearest_point (self, point : Point3 <S>) -> Line3Point <S> {
2725 project_3d_point_on_line (point, self)
2726 }
2727 #[inline]
2728 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Line3Point <S>> where
2729 S : Field + approx::RelativeEq
2730 {
2731 intersect::continuous_line3_plane3 (self, plane)
2732 }
2733 #[inline]
2734 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2735 -> Option <(Line3Point <S>, Line3Point <S>)>
2736 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2737 intersect::continuous_line3_aabb3 (self, aabb)
2738 }
2739 #[inline]
2740 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2741 -> Option <(Line3Point <S>, Line3Point <S>)>
2742 where S : Field + Sqrt {
2743 intersect::continuous_line3_sphere3 (self, sphere)
2744 }
2745 #[inline]
2746 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2747 -> Option <Line3Point <S>>
2748 where S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2749 intersect::continuous_line3_triangle3 (self, triangle)
2750 }
2751}
2752impl <S : Real> Default for Line3 <S> {
2753 fn default() -> Self {
2754 Line3 {
2755 base: Point3::origin(),
2756 direction: Unit3::axis_z()
2757 }
2758 }
2759}
2760impl <S> Primitive <S, Point3 <S>> for Line3 <S> where
2761 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2762{
2763 fn translate (&mut self, displacement : Vector3 <S>) {
2764 self.base += displacement;
2765 }
2766 #[expect(unused_variables)]
2767 fn scale (&mut self, scale : Positive <S>) {
2768 }
2770 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2771 let dot = self.direction.dot (*direction);
2772 let out = if approx::abs_diff_eq!(dot, S::zero()) {
2773 self.base
2774 } else if dot > S::zero() {
2775 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2776 S::zero()
2777 } else {
2778 x * S::infinity()
2779 }))
2780 } else {
2781 debug_assert!(dot < S::zero());
2782 Point3 (-self.direction.sigvec().map (|x| if x == S::zero() {
2783 S::zero()
2784 } else {
2785 x * S::infinity()
2786 }))
2787 };
2788 (out, out.0.dot (*direction))
2789 }
2790}
2791impl <S> From <Ray3 <S>> for Line3 <S> {
2792 fn from (Ray3 { base, direction } : Ray3 <S>) -> Line3 <S> {
2793 Line3 { base, direction }
2794 }
2795}
2796
2797impl_numcast_fields!(Ray3, base, direction);
2798impl <S : OrderedRing> Ray3 <S> {
2799 #[inline]
2801 pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2802 Ray3 { base, direction }
2803 }
2804 #[inline]
2805 pub fn point (self, t : S) -> Point3 <S> {
2806 self.base + *self.direction * t
2807 }
2808 #[inline]
2809 pub fn affine_line (self) -> frame::Line3 <S> {
2810 frame::Line3 {
2811 origin: self.base,
2812 basis: self.direction.into()
2813 }
2814 }
2815 pub fn nearest_point (self, point : Point3 <S>) -> Ray3Point <S> {
2816 use num::Zero;
2817 let (t, point) = Line3::from (self).nearest_point (point);
2818 if t < S::zero() {
2819 (NonNegative::zero(), self.base)
2820 } else {
2821 (NonNegative::unchecked (t), point)
2822 }
2823 }
2824 #[inline]
2825 pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Ray3Point <S>> where
2826 S : approx::RelativeEq
2827 {
2828 intersect::continuous_ray3_plane3 (self, plane)
2829 }
2830 #[inline]
2831 pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2832 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2833 where S : num::Float + approx::RelativeEq <Epsilon=S> {
2834 intersect::continuous_ray3_aabb3 (self, aabb)
2835 }
2836 #[inline]
2837 pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2838 -> Option <(Ray3Point <S>, Ray3Point <S>)>
2839 {
2840 intersect::continuous_ray3_sphere3 (self, sphere)
2841 }
2842 #[inline]
2843 pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2844 -> Option <Ray3Point <S>>
2845 where S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2846 intersect::continuous_ray3_triangle3 (self, triangle)
2847 }
2848}
2849impl <S : Real> Default for Ray3 <S> {
2850 fn default() -> Self {
2851 Ray3 {
2852 base: Point3::origin(),
2853 direction: Unit3::axis_z()
2854 }
2855 }
2856}
2857impl <S> Primitive <S, Point3 <S>> for Ray3 <S> where
2858 S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2859{
2860 fn translate (&mut self, displacement : Vector3 <S>) {
2861 self.base += displacement;
2862 }
2863 #[expect(unused_variables)]
2864 fn scale (&mut self, scale : Positive <S>) {
2865 }
2867 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2868 let dot = self.direction.dot (*direction);
2869 let out = if dot < S::default_epsilon() {
2870 self.base
2871 } else {
2872 Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2873 S::zero()
2874 } else {
2875 x * S::infinity()
2876 }))
2877 };
2878 (out, out.0.dot (*direction))
2879 }
2880}
2881impl <S> From <Line3 <S>> for Ray3 <S> {
2882 fn from (Line3 { base, direction } : Line3 <S>) -> Ray3 <S> {
2883 Ray3 { base, direction }
2884 }
2885}
2886
2887impl_numcast_fields!(Plane3, base, normal);
2888impl <S> Plane3 <S> {
2889 #[inline]
2891 pub const fn new (base : Point3 <S>, normal : Unit3 <S>) -> Self {
2892 Plane3 { base, normal }
2893 }
2894}
2895impl <S : Real> Default for Plane3 <S> {
2896 fn default() -> Self {
2897 Plane3 {
2898 base: Point3::origin(),
2899 normal: Unit3::axis_z()
2900 }
2901 }
2902}
2903impl <S> Primitive <S, Point3 <S>> for Plane3 <S> where
2904 S : Real + num::float::FloatCore + approx::RelativeEq
2905{
2906 fn translate (&mut self, displacement : Vector3 <S>) {
2907 self.base += displacement;
2908 }
2909 #[expect(unused_variables)]
2910 fn scale (&mut self, scale : Positive <S>) {
2911 }
2913 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2914 let dot = self.normal.dot (*direction);
2915 let out = if approx::relative_eq!(dot, S::one()) {
2916 self.base
2917 } else {
2918 let projected =
2919 project_3d_point_on_plane (Point3 (self.base.0 + *direction), *self).0;
2920 Point3 (projected.sigvec().map (|x| if x == S::zero() {
2921 S::zero()
2922 } else {
2923 x * S::infinity()
2924 }))
2925 };
2926 (out, out.0.dot (*direction))
2927 }
2928}
2929
2930impl_numcast_fields!(Sphere2, center, radius);
2931impl <S : OrderedRing> Sphere2 <S> {
2932 #[inline]
2934 pub fn unit() -> Self {
2935 Sphere2 {
2936 center: Point2::origin(),
2937 radius: num::One::one()
2938 }
2939 }
2940 #[inline]
2942 pub fn intersects (self, other : Self) -> bool {
2943 intersect::discrete_sphere2_sphere2 (self, other)
2944 }
2945}
2946impl <S> Primitive <S, Point2 <S>> for Sphere2 <S> where
2947 S : Real + num::real::Real + MaybeSerDes
2948{
2949 fn translate (&mut self, displacement : Vector2 <S>) {
2950 self.center += displacement;
2951 }
2952 fn scale (&mut self, scale : Positive <S>) {
2953 self.radius *= scale;
2954 }
2955 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2956 let out = self.center + direction.normalized() * *self.radius;
2957 (out, out.0.dot (*direction))
2958 }
2959}
2960
2961impl_numcast_fields!(Sphere3, center, radius);
2962impl <S : OrderedRing> Sphere3 <S> {
2963 #[inline]
2965 pub fn unit() -> Self where S : Field {
2966 Sphere3 {
2967 center: Point3::origin(),
2968 radius: num::One::one()
2969 }
2970 }
2971 #[inline]
2973 pub fn intersects (self, other : Self) -> bool {
2974 intersect::discrete_sphere3_sphere3 (self, other)
2975 }
2976}
2977impl <S> Primitive <S, Point3 <S>> for Sphere3 <S> where
2978 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes
2979{
2980 fn translate (&mut self, displacement : Vector3 <S>) {
2981 self.center += displacement;
2982 }
2983 fn scale (&mut self, scale : Positive <S>) {
2984 self.radius *= scale;
2985 }
2986 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2987 let out = self.center + direction.normalized() * *self.radius;
2988 (out, out.0.dot (*direction))
2989 }
2990}
2991
2992#[cfg(test)]
2997mod tests {
2998 use approx::{assert_relative_eq, assert_ulps_eq};
2999 use rand;
3000 use rand_distr;
3001 use rand_xorshift;
3002 use sorted_vec::partial::SortedSet;
3003 use super::*;
3004
3005 #[test]
3006 fn colinear_2() {
3007 use rand::{RngExt, SeedableRng};
3008 use rand_xorshift::XorShiftRng;
3009 use rand_distr::Distribution;
3010 macro test ($t:ty) {
3011 let mut rng = XorShiftRng::seed_from_u64 (0);
3012 let normal = rand_distr::StandardNormal;
3013 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3014 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3015 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3016 for _ in 0..100000 {
3018 let dir = Unit2::<$t>::normalize ([randn (&mut rng), randn (&mut rng)].into());
3019 let base = point2 (randf (&mut rng), randf (&mut rng));
3020 let line = Line2::new (base, dir);
3021 let p1 = line.point (randf (&mut rng));
3022 let p2 = line.point (randf (&mut rng));
3023 let p3 = line.point (randf (&mut rng));
3024 assert!(colinear_2d (p1, p2, p3));
3025 }
3026 for _ in 0..100000 {
3028 let p1 = point2 (randf (&mut rng), randf (&mut rng));
3029 let v = p1.0;
3030 let len = 0.1 + randf (&mut rng).abs();
3031 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3032 let rotation = Rotation2::from_angle (angle);
3033 let p2 = p1 + rotation * (v.normalized() * len);
3034 let v = p2 - p1;
3035 let len = 0.1 + randf (&mut rng).abs();
3036 let mut angle = rng.random_range (5.0..175.0);
3037 if rng.random_bool (0.5) {
3038 angle = -angle;
3039 }
3040 let rotation = Rotation2::from_angle (Deg (angle).into());
3041 let p3 = p2 + rotation * v * len;
3042 assert!(!colinear_2d (p1, p2, p3));
3043 }
3044 }
3045 test!(f32);
3046 test!(f64);
3047 }
3048
3049 #[test]
3050 fn colinear_3() {
3051 use rand::{RngExt, SeedableRng};
3052 use rand_xorshift::XorShiftRng;
3053 use rand_distr::Distribution;
3054 macro test ($t:ty) {
3055 let mut rng = XorShiftRng::seed_from_u64 (0);
3056 let normal = rand_distr::StandardNormal;
3057 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3058 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3059 let randn = |rng : &mut XorShiftRng| normal.sample (rng);
3060 for _ in 0..50000 {
3062 let dir = Unit3::<$t>::normalize (
3063 [randn (&mut rng), randn (&mut rng), randn (&mut rng)].into());
3064 let base = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3065 let line = Line3::new (base, dir);
3066 let p1 = line.point (randf (&mut rng));
3067 let p2 = line.point (randf (&mut rng));
3068 let p3 = line.point (randf (&mut rng));
3069 assert!(colinear_3d (p1, p2, p3));
3070 }
3071 for _ in 0..50000 {
3073 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3074 let v = p1.0;
3075 let len = 0.1 + randf (&mut rng).abs();
3076 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3077 let angle = Deg (rng.random_range (-180.0..180.0)).into();
3078 let rotation = Rotation3::from_axis_angle (axis, angle);
3079 let p2 = p1 + rotation * (v.normalized() * len);
3080 let v = p2 - p1;
3081 let len = 0.1 + randf (&mut rng).abs();
3082 let axis = loop {
3083 let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3084 if axis.normalized().cross (v.normalized()).magnitude() > 0.1 {
3085 break axis
3086 }
3087 };
3088 let mut angle = rng.random_range (5.0..175.0);
3089 if rng.random_bool (0.5) {
3090 angle = -angle;
3091 }
3092 let rotation = Rotation3::from_axis_angle (axis, Deg (angle).into());
3093 let p3 = p2 + rotation * v * len;
3094 assert!(!colinear_3d (p1, p2, p3));
3095 }
3096 }
3097 test!(f32);
3098 test!(f64);
3099 }
3100
3101 #[test]
3102 fn coplanar_3() {
3103 use rand::SeedableRng;
3104 use rand_xorshift::XorShiftRng;
3105 use rand_distr::Distribution;
3106 macro test ($t:ty) {
3107 let mut rng = XorShiftRng::seed_from_u64 (0);
3108 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3109 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
3110 for _ in 0..50000 {
3112 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3113 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3114 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3115 let s = randf (&mut rng);
3116 let t = randf (&mut rng);
3117 let p4 = p1 + (p2 - p1) * s + (p3 - p1) * t;
3118 assert!(coplanar_3d::<$t> (p1, p2, p3, p4));
3119 }
3120 for _ in 0..50000 {
3122 let p1 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3123 let p2 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3124 let p3 = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3125 let s = randf (&mut rng);
3126 let t = randf (&mut rng);
3127 let e1 = p2 - p1;
3128 let e2 = p3 - p1;
3129 let n = e1.cross (e2).normalized();
3130 let rn = randf (&mut rng);
3131 let pnew = p1 + e1 * s + e2 * t;
3132 let n_len = (0.3 * (pnew - p1).magnitude()).mul_add (rn.signum(), rn);
3133 let p4 = pnew + n * n_len;
3135 assert!(!coplanar_3d::<$t> (p1, p2, p3, p4));
3136 }
3137 }
3138 test!(f32);
3139 test!(f64);
3140 }
3141
3142 #[test]
3143 fn triangle_area_squared() {
3144 assert_relative_eq!(
3145 81.0,
3146 *triangle_3d_area_squared (
3147 [-2.0, -1.0, 0.0].into(),
3148 [ 1.0, -1.0, 0.0].into(),
3149 [ 1.0, 5.0, 0.0].into())
3150 );
3151 assert_relative_eq!(
3152 20.25,
3153 *triangle_3d_area_squared (
3154 [-2.0, -1.0, 0.0].into(),
3155 [ 1.0, -1.0, 0.0].into(),
3156 [ 1.0, 2.0, 0.0].into())
3157 );
3158 assert_relative_eq!(
3159 20.25,
3160 *triangle_3d_area_squared (
3161 [ 1.0, -1.0, 0.0].into(),
3162 [-2.0, -1.0, 0.0].into(),
3163 [ 1.0, 2.0, 0.0].into())
3164 );
3165 assert_relative_eq!(
3166 20.25,
3167 *triangle_3d_area_squared (
3168 [ 1.0, -1.0, 0.0].into(),
3169 [ 1.0, 2.0, 0.0].into(),
3170 [-2.0, -1.0, 0.0].into())
3171 );
3172 }
3173
3174 #[test]
3175 fn project_2d_point_on_line() {
3176 use super::project_2d_point_on_line;
3177 use Unit2;
3178 let point : Point2 <f64> = [2.0, 2.0].into();
3179 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_x());
3180 assert_eq!(project_2d_point_on_line (point, line).1, [2.0, 0.0].into());
3181 let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_y());
3182 assert_eq!(project_2d_point_on_line (point, line).1, [0.0, 2.0].into());
3183 let point : Point2 <f64> = [0.0, 1.0].into();
3184 let line = Line2::<f64>::new (
3185 [0.0, -1.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3186 assert_relative_eq!(
3187 project_2d_point_on_line (point, line).1, [1.0, 0.0].into());
3188 let point : Point2 <f64> = [1.0, 3.0].into();
3190 let line_a = Line2::<f64>::new (
3191 [0.0, -1.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3192 let line_b = Line2::<f64>::new (
3193 [2.0, 0.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3194 assert_relative_eq!(
3195 project_2d_point_on_line (point, line_a).1,
3196 project_2d_point_on_line (point, line_b).1);
3197 let line_a = Line2::<f64>::new (
3198 [0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3199 let line_b = Line2::<f64>::new (
3200 [-2.0, -2.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3201 assert_ulps_eq!(
3202 project_2d_point_on_line (point, line_a).1,
3203 project_2d_point_on_line (point, line_b).1
3204 );
3205 assert_relative_eq!(
3206 project_2d_point_on_line (point, line_a).1,
3207 [2.0, 2.0].into());
3208 }
3209
3210 #[test]
3211 fn project_3d_point_on_line() {
3212 use Unit3;
3213 use super::project_3d_point_on_line;
3214 let point : Point3 <f64> = [2.0, 2.0, 0.0].into();
3216 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
3217 assert_eq!(project_3d_point_on_line (point, line).1, [2.0, 0.0, 0.0].into());
3218 let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
3219 assert_eq!(project_3d_point_on_line (point, line).1, [0.0, 2.0, 0.0].into());
3220 let point : Point3 <f64> = [0.0, 1.0, 0.0].into();
3221 let line = Line3::<f64>::new (
3222 [0.0, -1.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3223 assert_relative_eq!(
3224 project_3d_point_on_line (point, line).1, [1.0, 0.0, 0.0].into());
3225 let point : Point3 <f64> = [1.0, 3.0, 0.0].into();
3227 let line_a = Line3::<f64>::new (
3228 [0.0, -1.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3229 let line_b = Line3::<f64>::new (
3230 [2.0, 0.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3231 assert_relative_eq!(
3232 project_3d_point_on_line (point, line_a).1,
3233 project_3d_point_on_line (point, line_b).1);
3234 let line_a = Line3::<f64>::new (
3235 [0.0, 0.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3236 let line_b = Line3::<f64>::new (
3237 [2.0, 2.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3238 assert_relative_eq!(
3239 project_3d_point_on_line (point, line_a).1,
3240 project_3d_point_on_line (point, line_b).1);
3241 assert_relative_eq!(
3242 project_3d_point_on_line (point, line_a).1,
3243 [2.0, 2.0, 0.0].into());
3244 let point : Point3 <f64> = [0.0, 0.0, 2.0].into();
3246 let line_a = Line3::<f64>::new (
3247 [-4.0, -4.0, -4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3248 let line_b = Line3::<f64>::new (
3249 [4.0, 4.0, 4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3250 assert_relative_eq!(
3251 project_3d_point_on_line (point, line_a).1,
3252 project_3d_point_on_line (point, line_b).1,
3253 epsilon = 0.000_000_000_000_01
3254 );
3255 assert_relative_eq!(
3256 project_3d_point_on_line (point, line_a).1,
3257 [2.0/3.0, 2.0/3.0, 2.0/3.0].into(),
3258 epsilon = 0.000_000_000_000_01
3259 );
3260 }
3261
3262 #[test]
3263 fn smallest_rect() {
3264 use super::smallest_rect;
3265 let points : Vec <Point2 <f32>> = [
3267 [-3.0, -3.0],
3268 [ 3.0, -3.0],
3269 [ 3.0, 3.0],
3270 [ 0.0, 6.0],
3271 [-3.0, 3.0]
3272 ].map (Point2::from).into_iter().collect();
3273 let hull = Hull2::from_points (points.as_slice()).unwrap();
3274 assert_eq!(hull.points(), points);
3275 let rect = smallest_rect (0, 1, &hull);
3276 assert_eq!(rect.area, 54.0);
3277 let points : Vec <Point2 <f32>> = [
3279 [-1.0, -4.0],
3280 [ 2.0, 2.0],
3281 [-4.0, -1.0]
3282 ].map (Point2::from).into_iter().collect();
3283 let hull = Hull2::from_points (points.as_slice()).unwrap();
3284 assert_eq!(hull.points(), points);
3285 let rect = smallest_rect (0, 1, &hull);
3286 assert_eq!(rect.area, 27.0);
3287 }
3288
3289 #[test]
3290 fn capsule3() {
3291 use num::One;
3292 let capsule : Capsule3 <f32> = Capsule3 {
3293 center: Point3::origin(),
3294 radius: Positive::noisy (2.0),
3295 half_height: Positive::one()
3296 };
3297 let support = capsule.support (Unit3::axis_z().into()).0;
3298 assert_eq!(support, point3 (0.0, 0.0, 3.0));
3299 let support = capsule.support (Unit3::axis_x().into()).0;
3300 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3301 let support = capsule.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3302 assert_eq!(support,
3303 point3 (0.0, 0.0, 1.0) + *Unit3::normalize (vector3 (1.0, 0.0, 1.0)) * 2.0);
3304 }
3305
3306 #[test]
3307 fn cylinder3() {
3308 use num::One;
3309 let cylinder : Cylinder3 <f32> = Cylinder3 {
3310 center: Point3::origin(),
3311 radius: Positive::noisy (2.0),
3312 half_height: Positive::one()
3313 };
3314 let support = cylinder.support (Unit3::axis_z().into()).0;
3315 assert_eq!(support, point3 (0.0, 0.0, 1.0));
3316 let support = cylinder.support (Unit3::axis_x().into()).0;
3317 assert_eq!(support, point3 (2.0, 0.0, 0.0));
3318 let support = cylinder.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3319 assert_eq!(support, point3 (2.0, 0.0, 1.0));
3320 }
3321
3322 #[test]
3323 fn obb2() {
3324 let points : Vec <Point2 <f32>> = [
3325 [-1.0, -4.0],
3326 [ 2.0, 2.0],
3327 [-4.0, -1.0]
3328 ].map (Point2::from).into_iter().collect();
3329 let obb = Obb2::containing_points (&points).unwrap();
3330 let corners = obb.corners();
3331 assert_eq!(obb.center, [-0.25, -0.25].into());
3332 approx::assert_relative_eq!(point2 (-4.0, -1.0), corners[0], max_relative = 0.00001);
3333 approx::assert_relative_eq!(point2 ( 0.5, 3.5), corners[1], max_relative = 0.00001);
3334 approx::assert_relative_eq!(point2 (-1.0, -4.0), corners[2], max_relative = 0.00001);
3335 approx::assert_relative_eq!(point2 ( 3.5, 0.5), corners[3], max_relative = 0.00001);
3336 let points : Vec <Point2 <f32>> = [
3338 [-2.0, 0.0],
3339 [ 0.0, 2.0],
3340 [ 2.0, 0.0],
3341 [ 0.0, -2.0],
3342 ].map (Point2::from).into_iter().collect();
3343 let obb = Obb2::containing_points (&points).unwrap();
3344 let support = obb.support (Unit2::axis_y().into()).0;
3345 approx::assert_relative_eq!(support, point2 (0.0, 2.0), epsilon = 0.000_001);
3346 }
3347
3348 #[test]
3349 #[expect(clippy::suboptimal_flops)]
3350 fn obb3() {
3351 use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
3352 use approx::AbsDiffEq;
3353
3354 let points = [
3355 [ 1.0, -1.0, -1.0],
3356 [ 1.0, -1.0, 1.0],
3357 [ 1.0, 1.0, -1.0],
3358 [ 1.0, 1.0, 1.0],
3359 [-1.0, -1.0, -1.0],
3360 [-1.0, -1.0, 1.0],
3361 [-1.0, 1.0, -1.0],
3362 [-1.0, 1.0, 1.0]
3363 ].map (Point3::<f32>::from);
3364 let obb1 = Obb3::containing_points_with_orientation (&points, Angles3::zero())
3365 .unwrap();
3366 assert_eq!(
3367 SortedSet::from_unsorted (obb1.corners().to_vec()),
3368 SortedSet::from_unsorted (
3369 Aabb3::containing (&points).unwrap().corners().to_vec()));
3370 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3371 assert_eq!(obb1, obb2);
3372
3373 let rotation = Rotation3::from_angle_y (FRAC_PI_4.into());
3374 let points = points.map (|p| rotation.rotate (p));
3375 let obb1 = Obb3::containing_points_with_orientation (&points,
3376 Angles3::wrap (
3378 (3.0 * FRAC_PI_2).into(),
3379 (2.0 * PI - FRAC_PI_4).into(),
3380 FRAC_PI_2.into())
3381 ).unwrap();
3382 let obb2 = Obb3::containing_points_approx (&points).unwrap();
3383 approx::assert_abs_diff_eq!(obb1.center, obb2.center);
3384 assert_eq!(obb1.orientation, obb2.orientation);
3385 approx::assert_abs_diff_eq!(*obb1.extents.x, *obb2.extents.x,
3386 epsilon=4.0 * f32::default_epsilon());
3387 approx::assert_abs_diff_eq!(*obb1.extents.y, *obb2.extents.y,
3388 epsilon=4.0 * f32::default_epsilon());
3389 approx::assert_abs_diff_eq!(*obb1.extents.z, *obb2.extents.z,
3390 epsilon=2.0 * f32::default_epsilon());
3391 let corners = obb2.corners();
3393 approx::assert_abs_diff_eq!(corners[0], points[7],
3394 epsilon=8.0 * f32::default_epsilon());
3395 approx::assert_abs_diff_eq!(corners[1], points[5],
3396 epsilon=8.0 * f32::default_epsilon());
3397 approx::assert_abs_diff_eq!(corners[2], points[3],
3398 epsilon=8.0 * f32::default_epsilon());
3399 approx::assert_abs_diff_eq!(corners[3], points[1],
3400 epsilon=8.0 * f32::default_epsilon());
3401 approx::assert_abs_diff_eq!(corners[4], points[6],
3402 epsilon=8.0 * f32::default_epsilon());
3403 approx::assert_abs_diff_eq!(corners[5], points[4],
3404 epsilon=8.0 * f32::default_epsilon());
3405 approx::assert_abs_diff_eq!(corners[6], points[2],
3406 epsilon=8.0 * f32::default_epsilon());
3407 approx::assert_abs_diff_eq!(corners[7], points[0],
3408 epsilon=8.0 * f32::default_epsilon());
3409
3410 let points : Vec <Point3 <f32>> = [
3412 [-2.0, 0.0, -2.0],
3413 [ 0.0, 2.0, -2.0],
3414 [ 2.0, 0.0, -2.0],
3415 [ 0.0, -2.0, -2.0],
3416 [-2.0, 0.0, 2.0],
3417 [ 0.0, 2.0, 2.0],
3418 [ 2.0, 0.0, 2.0],
3419 [ 0.0, -2.0, 2.0]
3420 ].map (Point3::from).into_iter().collect();
3421 let obb = Obb3::containing_points_approx (&points).unwrap();
3422 let support = obb.support (Unit3::axis_y().into()).0;
3423 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 0.0), epsilon = 0.000_001);
3424 let support = obb.support (NonZero3::noisy (vector3 (0.0, 1.0, 1.0))).0;
3425 approx::assert_relative_eq!(support, point3 (0.0, 2.0, 2.0), epsilon = 0.000_001);
3426 }
3427
3428 #[test]
3429 fn support_aabb3() {
3430 let aabb = Aabb3::with_minmax (
3431 [-1.0, -2.0, -3.0].into(),
3432 [ 1.0, 2.0, 3.0].into()).unwrap();
3433 assert_eq!(
3434 [-1.0, -2.0, -3.0],
3435 aabb.support (NonZero3::noisy ([-1.0, -1.0, -1.0].into())).0.0.into_array());
3436 assert_eq!(
3437 [-1.0, -2.0, 3.0],
3438 aabb.support (NonZero3::noisy ([-1.0, -1.0, 1.0].into())).0.0.into_array());
3439 assert_eq!(
3440 [-1.0, 2.0, -3.0],
3441 aabb.support (NonZero3::noisy ([-1.0, 1.0, -1.0].into())).0.0.into_array());
3442 assert_eq!(
3443 [-1.0, 2.0, 3.0],
3444 aabb.support (NonZero3::noisy ([-1.0, 1.0, 1.0].into())).0.0.into_array());
3445 assert_eq!(
3446 [ 1.0, -2.0, -3.0],
3447 aabb.support (NonZero3::noisy ([ 1.0, -1.0, -1.0].into())).0.0.into_array());
3448 assert_eq!(
3449 [ 1.0, -2.0, 3.0],
3450 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 1.0].into())).0.0.into_array());
3451 assert_eq!(
3452 [ 1.0, 2.0, -3.0],
3453 aabb.support (NonZero3::noisy ([ 1.0, 1.0, -1.0].into())).0.0.into_array());
3454 assert_eq!(
3455 [ 1.0, 2.0, 3.0],
3456 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 1.0].into())).0.0.into_array());
3457
3458 assert_eq!(
3459 [-1.0, -2.0, 0.0],
3460 aabb.support (NonZero3::noisy ([-1.0, -1.0, 0.0].into())).0.0.into_array());
3461 assert_eq!(
3462 [-1.0, 2.0, 0.0],
3463 aabb.support (NonZero3::noisy ([-1.0, 1.0, 0.0].into())).0.0.into_array());
3464 assert_eq!(
3465 [ 1.0, -2.0, 0.0],
3466 aabb.support (NonZero3::noisy ([ 1.0, -1.0, 0.0].into())).0.0.into_array());
3467 assert_eq!(
3468 [ 1.0, 2.0, 0.0],
3469 aabb.support (NonZero3::noisy ([ 1.0, 1.0, 0.0].into())).0.0.into_array());
3470
3471 assert_eq!(
3472 [-1.0, 0.0, -3.0],
3473 aabb.support (NonZero3::noisy ([-1.0, 0.0, -1.0].into())).0.0.into_array());
3474 assert_eq!(
3475 [-1.0, 0.0, 3.0],
3476 aabb.support (NonZero3::noisy ([-1.0, 0.0, 1.0].into())).0.0.into_array());
3477 assert_eq!(
3478 [ 1.0, 0.0, -3.0],
3479 aabb.support (NonZero3::noisy ([ 1.0, 0.0, -1.0].into())).0.0.into_array());
3480 assert_eq!(
3481 [ 1.0, 0.0, 3.0],
3482 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 1.0].into())).0.0.into_array());
3483
3484 assert_eq!(
3485 [ 0.0, -2.0, -3.0],
3486 aabb.support (NonZero3::noisy ([ 0.0, -1.0, -1.0].into())).0.0.into_array());
3487 assert_eq!(
3488 [ 0.0, -2.0, 3.0],
3489 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 1.0].into())).0.0.into_array());
3490 assert_eq!(
3491 [ 0.0, 2.0, -3.0],
3492 aabb.support (NonZero3::noisy ([ 0.0, 1.0, -1.0].into())).0.0.into_array());
3493 assert_eq!(
3494 [ 0.0, 2.0, 3.0],
3495 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 1.0].into())).0.0.into_array());
3496
3497 assert_eq!(
3498 [-1.0, 0.0, 0.0],
3499 aabb.support (NonZero3::noisy ([-1.0, 0.0, 0.0].into())).0.0.into_array());
3500 assert_eq!(
3501 [ 1.0, 0.0, 0.0],
3502 aabb.support (NonZero3::noisy ([ 1.0, 0.0, 0.0].into())).0.0.into_array());
3503 assert_eq!(
3504 [ 0.0, -2.0, 0.0],
3505 aabb.support (NonZero3::noisy ([ 0.0, -1.0, 0.0].into())).0.0.into_array());
3506 assert_eq!(
3507 [ 0.0, 2.0, 0.0],
3508 aabb.support (NonZero3::noisy ([ 0.0, 1.0, 0.0].into())).0.0.into_array());
3509 assert_eq!(
3510 [ 0.0, 0.0, -3.0],
3511 aabb.support (NonZero3::noisy ([ 0.0, 0.0, -1.0].into())).0.0.into_array());
3512 assert_eq!(
3513 [ 0.0, 0.0, 3.0],
3514 aabb.support (NonZero3::noisy ([ 0.0, 0.0, 1.0].into())).0.0.into_array());
3515 }
3516}