Skip to main content

math_utils/geometry/primitive/
mod.rs

1//! Affine, convex, and combinatorial spaces
2
3use rand;
4
5use crate::*;
6use super::{intersect, shape};
7use super::mesh::VertexEdgeTriangleMesh;
8
9#[cfg(feature = "derive_serdes")]
10use serde::{Deserialize, Serialize};
11
12/// Primitives over signed integers
13pub 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
35/// A parameterized point on a 2D line
36pub type Line2Point <S> = (S, Point2 <S>);
37/// A parameterized point on a 3D line
38pub type Line3Point <S> = (S, Point3 <S>);
39/// A parameterized point on a 2D ray
40pub type Ray2Point <S> = (NonNegative <S>, Point2 <S>);
41/// A parameterized point on a 3D ray
42pub type Ray3Point <S> = (NonNegative <S>, Point3 <S>);
43
44/// Geometric primitives generic over dimension
45pub 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  /// Returns the point with largeest dot product in search direction
52  fn support   (&self, direction : <P::Translation as VectorSpace <S>>::NonZero)
53    -> (P, S);
54}
55
56////////////////////////////////////////////////////////////////////////////////////////
57//  structs                                                                           //
58////////////////////////////////////////////////////////////////////////////////////////
59
60/// 1D interval. Points are allowed to be equal.
61#[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/// 2D axis-aligned bounding box.
69///
70/// Min/max can be co-linear:
71/// ```
72/// # use math_utils::geometry::Aabb2;
73/// let x = Aabb2::with_minmax (
74///   [0.0, 0.0].into(),
75///   [0.0, 1.0].into()
76/// ).unwrap();
77/// ```
78/// But not identical:
79/// ```should_panic
80/// # use math_utils::geometry::Aabb2;
81/// // panic!
82/// let x = Aabb2::with_minmax (
83///   [0.0, 0.0].into(),
84///   [0.0, 0.0].into()
85/// ).unwrap();
86/// ```
87#[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/// 3D axis-aligned bounding box.
95///
96/// See also `shape::Aabb` trait for primitive and shape types that can compute a 3D
97/// AABB.
98///
99/// Min/max can be co-planar or co-linear:
100/// ```
101/// # use math_utils::geometry::Aabb3;
102/// let x = Aabb3::with_minmax (
103///   [0.0, 0.0, 0.0].into(),
104///   [0.0, 1.0, 1.0].into()
105/// ).unwrap();
106/// let y = Aabb3::with_minmax (
107///   [0.0, 0.0, 0.0].into(),
108///   [0.0, 1.0, 0.0].into()
109/// ).unwrap();
110/// ```
111/// But not identical:
112/// ```should_panic
113/// # use math_utils::geometry::Aabb3;
114/// let x = Aabb3::with_minmax (
115///   [0.0, 0.0, 0.0].into(),
116///   [0.0, 0.0, 0.0].into()
117/// ).unwrap();
118/// ```
119#[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/// 2D oriented bounding box
127#[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/// 3D oriented bounding box
136#[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/// 3D Z-axis-aligned cylinder
145#[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/// 3D Z-axis-aligned capsule
154#[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/// 3D Z-axis-aligned cone
163#[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/// An infinitely extended line in 2D space defined by a base point and normalized
172/// direction
173#[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/// An infinitely extended ray in 2D space defined by a base point and normalized
181/// direction
182#[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/// An infinitely extended line in 3D space defined by a base point and normalized
190/// direction
191// TODO: currently most algorithms for lines are written to work with a line defined by
192// a line segment (no need to normalize); can we create another line type to improve
193// type checking?
194#[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/// An infinitely extended ray in 3D space defined by a base point and normalized
202/// direction
203#[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/// A plane in 3D space defined by a base point and (unit) normal vector
211#[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/// Sphere in 2D space (a circle)
219#[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/// Sphere in 3D space
227#[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/// Describes points of a 2D convex hull that support a rectangle and the area of the
235/// rectangle
236#[derive(Clone, Debug)]
237struct Rect <S> {
238  pub edge    : Vector2 <S>,
239  pub perp    : Vector2 <S>,
240  /// bottom, right, top, left
241  pub indices : [u32; 4],
242  pub area    : S,
243  pub edge_len_squared : S
244}
245
246////////////////////////////////////////////////////////////////////////////////////////
247//  functions                                                                         //
248////////////////////////////////////////////////////////////////////////////////////////
249
250/// Returns true when three points lie on the same line in 2D space.
251///
252/// ```
253/// # use math_utils::geometry::colinear_2d;
254/// assert!(colinear_2d (
255///   [-1.0, -1.0].into(),
256///   [ 0.0,  0.0].into(),
257///   [ 1.0,  1.0].into())
258/// );
259/// assert!(!colinear_2d (
260///   [-1.0, -1.0].into(),
261///   [ 0.0,  1.0].into(),
262///   [ 1.0, -1.0].into())
263/// );
264/// ```
265pub 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  // early exit: a pair of points are equal
284  if a == b || a == c || b == c {
285    return true
286  }
287  let e1 = b - a;
288  let e2 = c - a;
289  // pre-scale by max norm: components will lie in [-1, 1]
290  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
303/// Returns true when three points lie on the same line in 3D space.
304///
305/// ```
306/// # use math_utils::geometry::colinear_3d;
307/// assert!(colinear_3d (
308///   [-1.0, -1.0, -1.0].into(),
309///   [ 0.0,  0.0,  0.0].into(),
310///   [ 1.0,  1.0,  1.0].into())
311/// );
312/// ```
313pub 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  // early exit: a pair of points are equal
336  if a == b || a == c || b == c {
337    return true
338  }
339  let e1 = b - a;
340  let e2 = c - a;
341  // pre-scale by max norm: components will lie in [-1, 1]
342  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
355/// Returns true when four points lie on the same plane in 3D space.
356///
357/// ```
358/// # use math_utils::geometry::coplanar_3d;
359/// assert!(coplanar_3d (
360///   [-1.0, -1.0, -1.0].into(),
361///   [ 1.0,  1.0,  1.0].into(),
362///   [-1.0,  1.0,  0.0].into(),
363///   [ 1.0, -1.0,  0.0].into()
364/// ));
365/// ```
366pub 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    // early exit: a pair of points are equal
400    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    // a, b, c are colinear
409    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
416/// Return a signum indicating which side of the triangle ABC that point D lies on,
417/// where +1 indicates that D lies on the side that the normal $(B - A) \times (C - A)$
418/// points to, -1 if D lies on the opposite side, and 0 if D lies on the plane ABC,
419/// within tolerance
420pub 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    // a pair of points are equal => degenerate
454    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    // a, b, c are colinear
463    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/// Computes the determinant of a matrix formed by the three points as columns
477#[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
483/// Create an orthonormal basis given the first basis component
484pub 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/// Coordinate-wise min
501#[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/// Coordinate-wise max
507#[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/// Coordinate-wise min
513#[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/// Coordinate-wise max
519#[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/// Given a 2D point and a 2D line, returns the nearest point on the line to the given
525/// point
526#[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/// Given a 3D point and a 3D line, returns the nearest point on the line to the given
535/// point
536#[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/// Given a 3D point and a 3D plane, returns the nearest point on the plane to the given
545/// point
546#[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/// Returns point furthest along given direction together with dot product with the
556/// direction vector
557#[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/// Returns point furthest along given direction together with dot product with the
568/// direction vector
569#[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
579/// Square area of three points in 3D space.
580///
581/// Uses a numerically stable Heron's formula:
582/// <https://en.wikipedia.org/wiki/Heron%27s_formula#Numerical_stability>
583///
584/// ```
585/// # use math_utils::approx::assert_relative_eq;
586/// # use math_utils::geometry::triangle_3d_area_squared;
587/// assert_relative_eq!(
588///   3.0/4.0,
589///   *triangle_3d_area_squared (
590///     [-1.0,  0.0,  0.0].into(),
591///     [ 0.0,  0.0,  1.0].into(),
592///     [ 0.0,  1.0,  0.0].into())
593/// );
594/// ```
595///
596/// If the area squared is zero then the points are colinear:
597///
598/// ```
599/// # use math_utils::geometry::triangle_3d_area_squared;
600/// assert_eq!(
601///   0.0,
602///   *triangle_3d_area_squared (
603///     [-1.0, -1.0, -1.0].into(),
604///     [ 0.0,  0.0,  0.0].into(),
605///     [ 1.0,  1.0,  1.0].into())
606/// );
607/// ```
608pub 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  // compute the length of each side
618  let ab_mag = *(b-a).norm();
619  let ac_mag = *(c-a).norm();
620  let bc_mag = *(c-b).norm();
621  // order as min <= mid <= max
622  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  // 1.0/16.0
632  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  // numerical accuracy may result in a negative value; from tests this is always a
639  // relatively small value, in pathological cases (e.g. colinear points at several
640  // thousand units) can result in a value on the order of -0.00001
641  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
653// private
654
655/// Smallest rectangle for given edge indices
656fn smallest_rect <S : Real> (i0 : u32, i1 : u32, hull : &Hull2 <S>) -> Rect <S> {
657  // NOTE: the following algorithm assumes that the points in the hull are
658  // counter-clockwise sorted and that the hull does not contain any colinear points.
659  // The construction of the hull should ensure these conditions.
660  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
694////////////////////////////////////////////////////////////////////////////////
695//  impls                                                                     //
696////////////////////////////////////////////////////////////////////////////////
697
698impl <S> Interval <S> where S : Copy {
699  #[inline]
700  pub const fn min (self) -> S {
701    self.min
702  }
703  #[inline]
704  pub const fn max (self) -> S {
705    self.max
706  }
707}
708impl <S> Interval <S> where
709  S : MinMax + Copy + PartialOrd + std::fmt::Debug
710{
711  #[inline]
712  pub fn from_points (a : S, b : S) -> Self {
713    let min = S::min (a, b);
714    let max = S::max (a, b);
715    Interval { min, max }
716  }
717  /// Returns `None` if points are not min/max
718  #[inline]
719  pub fn with_minmax (min : S, max : S) -> Option <Self> {
720    if min > max {
721      None
722    } else {
723      Some (Interval { min, max })
724    }
725  }
726  /// Construct a new AABB with given min and max points.
727  ///
728  /// Debug panic if points are not min/max:
729  ///
730  /// ```should_panic
731  /// # use math_utils::geometry::*;
732  /// let b = Interval::with_minmax_unchecked (1.0, 0.0);  // panic!
733  /// ```
734  #[inline]
735  pub fn with_minmax_unchecked (min : S, max : S) -> Self {
736    debug_assert_eq!(min, S::min (min, max));
737    debug_assert_eq!(max, S::max (min, max));
738    Interval { min, max }
739  }
740  /// Construct the minimum AABB containing the given set of points.
741  ///
742  /// Returns `None` fewer than 2 points are given or all points are identical:
743  ///
744  /// ```
745  /// # use math_utils::geometry::*;
746  /// assert!(Interval::<f32>::containing (&[]).is_none());
747  /// ```
748  #[inline]
749  pub fn containing (points : &[S]) -> Option <Self> {
750    if points.len() < 2 {
751      None
752    } else {
753      debug_assert!(points.len() >= 2);
754      let mut min = S::min (points[0], points[1]);
755      let mut max = S::max (points[0], points[1]);
756      for point in points.iter().skip (2) {
757        min = S::min (min, *point);
758        max = S::max (max, *point);
759      }
760      Interval::with_minmax (min, max)
761    }
762  }
763  #[inline]
764  pub fn numcast <T> (self) -> Option <Interval <T>> where
765    S : num::ToPrimitive,
766    T : num::NumCast
767  {
768    Some (Interval {
769      min: T::from (self.min)?,
770      max: T::from (self.max)?
771    })
772  }
773  /// Create a new AABB that is the union of the two input AABBs
774  #[inline]
775  pub fn union (a : Interval <S>, b : Interval <S>) -> Self {
776    Interval::with_minmax_unchecked (
777      S::min (a.min(), b.min()), S::max (a.max(), b.max()))
778  }
779  #[inline]
780  pub fn length (self) -> NonNegative <S> where
781    S : num::Zero + std::ops::Sub <Output=S>
782  {
783    self.width()
784  }
785  #[inline]
786  pub fn width (self) -> NonNegative <S> where
787    S : num::Zero + std::ops::Sub <Output=S>
788  {
789    NonNegative::unchecked (self.max - self.min)
790  }
791  #[inline]
792  pub fn contains (self, point : S) -> bool {
793    self.min < point && point < self.max
794  }
795  /// Clamp a given point to the AABB interval.
796  ///
797  /// ```
798  /// # use math_utils::geometry::*;
799  /// let b = Interval::with_minmax_unchecked (-1.0, 1.0);
800  /// assert_eq!(b.clamp (-2.0), -1.0);
801  /// assert_eq!(b.clamp ( 2.0),  1.0);
802  /// assert_eq!(b.clamp ( 0.0),  0.0);
803  /// ```
804  #[inline]
805  pub fn clamp (self, point : S) -> S {
806    point.clamp (self.min, self.max)
807  }
808  /// Generate a random point contained in the AABB
809  ///
810  /// ```
811  /// # use rand;
812  /// # use rand_xorshift;
813  /// # use math_utils::geometry::*;
814  /// # fn main () {
815  /// use rand_xorshift;
816  /// use rand::SeedableRng;
817  /// // random sequence will be the same each time this is run
818  /// let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
819  /// let aabb = Interval::<f32>::with_minmax_unchecked (-10.0, 10.0);
820  /// let point = aabb.rand_point (&mut rng);
821  /// assert!(aabb.contains (point));
822  /// let point = aabb.rand_point (&mut rng);
823  /// assert!(aabb.contains (point));
824  /// let point = aabb.rand_point (&mut rng);
825  /// assert!(aabb.contains (point));
826  /// let point = aabb.rand_point (&mut rng);
827  /// assert!(aabb.contains (point));
828  /// let point = aabb.rand_point (&mut rng);
829  /// assert!(aabb.contains (point));
830  /// # }
831  /// ```
832  #[inline]
833  pub fn rand_point <R> (self, rng : &mut R) -> S where
834    S : rand::distr::uniform::SampleUniform,
835    R : rand::RngExt
836  {
837    rng.random_range (self.min..self.max)
838  }
839  #[inline]
840  pub fn intersects (self, other : Interval <S>) -> bool {
841    intersect::discrete_interval (self, other)
842  }
843  #[inline]
844  pub fn intersection (self, other : Interval <S>) -> Option <Interval <S>> {
845    intersect::continuous_interval (self, other)
846  }
847
848  /// Increase or decrease each endpoint by the given amount.
849  ///
850  /// Returns `None` if the interval width is less than twice the given amount:
851  ///
852  /// ```
853  /// # use math_utils::geometry::*;
854  /// let x = Interval::<f32>::with_minmax_unchecked (-1.0, 1.0);
855  /// assert!(x.dilate (-2.0).is_none());
856  /// ```
857  pub fn dilate (mut self, amount : S) -> Option <Self> where
858    S : std::ops::AddAssign + std::ops::SubAssign
859  {
860    self.min -= amount;
861    self.max += amount;
862    if self.min > self.max {
863      None
864    } else {
865      Some (self)
866    }
867  }
868}
869
870impl <S> Primitive <S, S> for Interval <S> where
871  S : OrderedField + AffineSpace <S, Translation=S>
872    + VectorSpace <S, NonZero=NonZero <S>>
873{
874  fn translate (&mut self, displacement : S) {
875    self.min += displacement;
876    self.max += displacement;
877  }
878  fn scale (&mut self, scale : Positive <S>) {
879    let old_width = *self.width();
880    let new_width = old_width * *scale;
881    let half_difference = (new_width - old_width) / S::two();
882    self.min -= half_difference;
883    self.max += half_difference;
884  }
885  fn support (&self, direction : NonZero <S>) -> (S, S) {
886    if *direction > S::zero() {
887      (self.max, self.max * *direction)
888    } else {
889      (self.min, self.min * *direction)
890    }
891  }
892}
893
894impl <S> std::ops::Add <S> for Interval <S> where
895  S    : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
896  Self : Primitive <S, S>
897{
898  type Output = Self;
899  #[expect(clippy::renamed_function_params)]
900  fn add (mut self, displacement : S) -> Self {
901    self.translate (displacement);
902    self
903  }
904}
905
906impl <S> std::ops::Sub <S> for Interval <S> where
907  S    : Field + AffineSpace <S, Translation=S> + VectorSpace <S>,
908  Self : Primitive <S, S>
909{
910  type Output = Self;
911  #[expect(clippy::renamed_function_params)]
912  fn sub (mut self, displacement : S) -> Self {
913    self.translate (-displacement);
914    self
915  }
916}
917
918impl_numcast_fields!(Aabb2, min, max);
919impl <S> Aabb2 <S> where S : Copy {
920  #[inline]
921  pub const fn min (self) -> Point2 <S> {
922    self.min
923  }
924  #[inline]
925  pub const fn max (self) -> Point2 <S> {
926    self.max
927  }
928}
929impl <S> Aabb2 <S> where
930  S : Copy + PartialOrd + std::fmt::Debug
931{
932  /// Returns `None` if points are not min/max or points are identical
933  #[inline]
934  pub fn with_minmax (min : Point2 <S>, max : Point2 <S>) -> Option <Self> {
935    if min == max || min != point2_min (min, max) || max != point2_max (min, max) {
936      None
937    } else {
938      Some (Aabb2 { min, max })
939    }
940  }
941  /// Returns `None` if points are identical
942  #[inline]
943  pub fn from_points (a : Point2 <S>, b : Point2 <S>) -> Option <Self> {
944    if a == b {
945      None
946    } else {
947      let min = point2_min (a, b);
948      let max = point2_max (a, b);
949      Some (Aabb2 { min, max })
950    }
951  }
952  /// Construct a new AABB with given min and max points.
953  ///
954  /// Debug panic if points are not min/max:
955  ///
956  /// ```should_panic
957  /// # use math_utils::geometry::*;
958  /// let b = Aabb2::with_minmax_unchecked (
959  ///   [1.0, 1.0].into(),
960  ///   [0.0, 0.0].into());  // panic!
961  /// ```
962  ///
963  /// Debug panic if points are identical:
964  ///
965  /// ```should_panic
966  /// # use math_utils::geometry::*;
967  /// let b = Aabb2::with_minmax_unchecked (
968  ///   [0.0, 0.0].into(),
969  ///   [0.0, 0.0].into());  // panic!
970  /// ```
971  #[inline]
972  pub fn with_minmax_unchecked (min : Point2 <S>, max : Point2 <S>) -> Self {
973    debug_assert_ne!(min, max);
974    debug_assert_eq!(min, point2_min (min, max));
975    debug_assert_eq!(max, point2_max (min, max));
976    Aabb2 { min, max }
977  }
978  /// Construct a new AABB using the two given points to determine min/max.
979  ///
980  /// Debug panic if points are identical:
981  ///
982  /// ```should_panic
983  /// # use math_utils::geometry::*;
984  /// let b = Aabb2::from_points_unchecked (
985  ///   [0.0, 0.0].into(),
986  ///   [0.0, 0.0].into());  // panic!
987  /// ```
988  #[inline]
989  pub fn from_points_unchecked (a : Point2 <S>, b : Point2 <S>) -> Self {
990    debug_assert_ne!(a, b);
991    let min = point2_min (a, b);
992    let max = point2_max (a, b);
993    Aabb2 { min, max }
994  }
995  /// Construct the minimum AABB containing the given set of points.
996  ///
997  /// Returns `None` if fewer than 2 points are given or all points are identical:
998  ///
999  /// ```
1000  /// # use math_utils::geometry::*;
1001  /// assert!(Aabb2::<f32>::containing (&[]).is_none());
1002  /// ```
1003  ///
1004  /// ```
1005  /// # use math_utils::geometry::*;
1006  /// assert!(Aabb2::containing (&[[0.0, 0.0].into()]).is_none());
1007  /// ```
1008  #[inline]
1009  pub fn containing (points : &[Point2 <S>]) -> Option <Self> {
1010    if points.len() < 2 {
1011      None
1012    } else {
1013      debug_assert!(points.len() >= 2);
1014      let mut min = point2_min (points[0], points[1]);
1015      let mut max = point2_max (points[0], points[1]);
1016      for point in points.iter().skip (2) {
1017        min = point2_min (min, *point);
1018        max = point2_max (max, *point);
1019      }
1020      Aabb2::with_minmax (min, max)
1021    }
1022  }
1023  /// Create a new AABB that is the union of the two input AABBs
1024  #[inline]
1025  pub fn union (a : Aabb2 <S>, b : Aabb2 <S>) -> Self {
1026    Aabb2::with_minmax_unchecked (
1027      point2_min (a.min(), b.min()), point2_max (a.max(), b.max()))
1028  }
1029  #[inline]
1030  pub fn center (self) -> Point2 <S> where S : Ring + std::ops::Div <Output=S> {
1031    Point2 ((self.min.0 + self.max.0) / S::two())
1032  }
1033  #[inline]
1034  pub fn dimensions (self) -> Vector2 <NonNegative <S>> where
1035    S : num::Zero + std::ops::Sub <Output=S>
1036  {
1037    (self.max.0 - self.min.0).map (NonNegative::unchecked)
1038  }
1039  #[inline]
1040  pub fn extents (self) -> Vector2 <NonNegative <S>> where S : Field {
1041    self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1042  }
1043  /// X dimension
1044  #[inline]
1045  pub fn width (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1046    NonNegative::unchecked (self.max.x() - self.min.x())
1047  }
1048  /// Y dimension
1049  #[inline]
1050  pub fn height (self) -> NonNegative <S> where
1051    S : num::Zero + std::ops::Sub <Output=S>
1052  {
1053    NonNegative::unchecked (self.max.y() - self.min.y())
1054  }
1055  #[inline]
1056  pub fn area (self) -> NonNegative <S> where
1057    S : num::Zero + std::ops::Sub <Output=S> + std::ops::Mul <Output=S>
1058  {
1059    self.width() * self.height()
1060  }
1061  #[inline]
1062  pub fn contains (self, point : Point2 <S>) -> bool {
1063    self.min.x() < point.x() && point.x() < self.max.x() &&
1064    self.min.y() < point.y() && point.y() < self.max.y()
1065  }
1066  /// Clamp a given point to the AABB.
1067  ///
1068  /// ```
1069  /// # use math_utils::geometry::*;
1070  /// let b = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into()).unwrap();
1071  /// assert_eq!(b.clamp ([-2.0, 0.0].into()), [-1.0, 0.0].into());
1072  /// assert_eq!(b.clamp ([ 2.0, 2.0].into()), [ 1.0, 1.0].into());
1073  /// assert_eq!(b.clamp ([-0.5, 0.5].into()), [-0.5, 0.5].into());
1074  /// ```
1075  pub fn clamp (self, point : Point2 <S>) -> Point2 <S> where S : MinMax {
1076    [ point.x().clamp (self.min.x(), self.max.x()),
1077      point.y().clamp (self.min.y(), self.max.y())
1078    ].into()
1079  }
1080  /// Generate a random point contained in the AABB
1081  ///
1082  /// ```
1083  /// # use rand;
1084  /// # use rand_xorshift;
1085  /// # use math_utils::geometry::*;
1086  /// # fn main () {
1087  /// use rand_xorshift;
1088  /// use rand::SeedableRng;
1089  /// // random sequence will be the same each time this is run
1090  /// let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
1091  /// let aabb = Aabb2::<f32>::with_minmax (
1092  ///   [-10.0, -10.0].into(),
1093  ///   [ 10.0,  10.0].into()
1094  /// ).unwrap();
1095  /// let point = aabb.rand_point (&mut rng);
1096  /// assert!(aabb.contains (point));
1097  /// let point = aabb.rand_point (&mut rng);
1098  /// assert!(aabb.contains (point));
1099  /// let point = aabb.rand_point (&mut rng);
1100  /// assert!(aabb.contains (point));
1101  /// let point = aabb.rand_point (&mut rng);
1102  /// assert!(aabb.contains (point));
1103  /// let point = aabb.rand_point (&mut rng);
1104  /// assert!(aabb.contains (point));
1105  /// # }
1106  /// ```
1107  #[inline]
1108  pub fn rand_point <R> (self, rng : &mut R) -> Point2 <S> where
1109    S : rand::distr::uniform::SampleUniform,
1110    R : rand::RngExt
1111  {
1112    let x_range = self.min.x()..self.max.x();
1113    let y_range = self.min.y()..self.max.y();
1114    let x = if x_range.is_empty() {
1115      self.min.x()
1116    } else {
1117      rng.random_range (x_range)
1118    };
1119    let y = if y_range.is_empty() {
1120      self.min.y()
1121    } else {
1122      rng.random_range (y_range)
1123    };
1124    [x, y].into()
1125  }
1126  #[inline]
1127  pub fn intersects (self, other : Aabb2 <S>) -> bool {
1128    intersect::discrete_aabb2_aabb2 (self, other)
1129  }
1130  #[inline]
1131  pub fn intersection (self, other : Aabb2 <S>) -> Option <Aabb2 <S>> {
1132    intersect::continuous_aabb2_aabb2 (self, other)
1133  }
1134
1135  pub fn corner (self, quadrant : Quadrant) -> Point2 <S> {
1136    match quadrant {
1137      // upper-right
1138      Quadrant::PosPos => self.max,
1139      // lower-right
1140      Quadrant::PosNeg => [self.max.x(), self.min.y()].into(),
1141      // upper-left
1142      Quadrant::NegPos => [self.min.x(), self.max.y()].into(),
1143      // lower-left
1144      Quadrant::NegNeg => self.min
1145    }
1146  }
1147
1148  /// Corner points clockwise from min
1149  pub fn corners (self) -> [Point2 <S>; 4] {
1150    [ self.min, [self.min.x(), self.max.y()].into(),
1151      self.max, [self.max.x(), self.min.y()].into()
1152    ]
1153  }
1154
1155  pub fn edge (self, direction : SignedAxis2) -> Self {
1156    let (min, max) = match direction {
1157      SignedAxis2::PosX => (
1158        self.corner (Quadrant::PosNeg),
1159        self.corner (Quadrant::PosPos) ),
1160      SignedAxis2::NegX => (
1161        self.corner (Quadrant::NegNeg),
1162        self.corner (Quadrant::NegPos) ),
1163      SignedAxis2::PosY => (
1164        self.corner (Quadrant::NegPos),
1165        self.corner (Quadrant::PosPos) ),
1166      SignedAxis2::NegY => (
1167        self.corner (Quadrant::NegNeg),
1168        self.corner (Quadrant::PosNeg) )
1169    };
1170    Aabb2::with_minmax_unchecked (min, max)
1171  }
1172
1173  /// Extrude (or inset) a new Aabb from a face:
1174  ///
1175  /// ```
1176  /// # use math_utils::*;
1177  /// # use math_utils::geometry::*;
1178  /// let x = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into())
1179  ///   .unwrap();
1180  /// assert_eq!(x.extrude (SignedAxis2::PosY, 0.5),
1181  ///   Aabb2::with_minmax ([-1.0, 1.0].into(), [1.0, 1.5].into()));
1182  /// assert_eq!(x.extrude (SignedAxis2::PosY, -0.5),
1183  ///   Aabb2::with_minmax ([-1.0, 0.5].into(), [1.0, 1.0].into()));
1184  /// ```
1185  #[must_use]
1186  pub fn extrude (self, axis : SignedAxis2, amount : S) -> Option <Self> where S : Ring {
1187    let (p1, p2) = match axis {
1188      SignedAxis2::PosX => (
1189        self.min + Vector2::zero().with_x (*self.width()),
1190        self.max + Vector2::zero().with_x (amount) ),
1191      SignedAxis2::NegX => (
1192        self.min - Vector2::zero().with_x (amount),
1193        self.max - Vector2::zero().with_x (*self.height()) ),
1194      SignedAxis2::PosY => (
1195        self.min + Vector2::zero().with_y (*self.height()),
1196        self.max + Vector2::zero().with_y (amount) ),
1197      SignedAxis2::NegY => (
1198        self.min - Vector2::zero().with_y (amount),
1199        self.max - Vector2::zero().with_y (*self.height()) )
1200    };
1201    Self::from_points (p1, p2)
1202  }
1203
1204  /// Extend (or contract) one of the extents
1205  #[must_use]
1206  pub fn extend (mut self, axis : SignedAxis2, amount : S) -> Option <Self>
1207    where S : std::ops::AddAssign + std::ops::SubAssign
1208  {
1209    match axis {
1210      SignedAxis2::PosX => self.max.0.x += amount,
1211      SignedAxis2::NegX => self.min.0.x -= amount,
1212      SignedAxis2::PosY => self.max.0.y += amount,
1213      SignedAxis2::NegY => self.min.0.y -= amount
1214    }
1215    Self::with_minmax (self.min, self.max)
1216  }
1217
1218  pub fn with_z (self, z : Interval <S>) -> Aabb3 <S> {
1219    Aabb3::with_minmax_unchecked (
1220      self.min.0.with_z (z.min()).into(),
1221      self.max.0.with_z (z.max()).into()
1222    )
1223  }
1224
1225  /// Increase or decrease each extent by the given amount.
1226  ///
1227  /// Returns `None` if any extent would become negative or if all dimensions become
1228  /// zero:
1229  /// ```
1230  /// # use math_utils::geometry::*;
1231  /// let x = Aabb2::with_minmax ([0.0, 0.0].into(), [1.0, 2.0].into()).unwrap();
1232  /// assert!(x.dilate (-1.0).is_none());
1233  /// ```
1234  #[must_use]
1235  pub fn dilate (mut self, amount : S) -> Option <Self> where S : Ring {
1236    self.min -= Vector2::broadcast (amount);
1237    self.max += Vector2::broadcast (amount);
1238    if self.min == self.max || self.min != point2_min (self.min, self.max)
1239      || self.max != point2_max (self.min, self.max)
1240    {
1241      None
1242    } else {
1243      Some (self)
1244    }
1245  }
1246
1247  /// Project *along* the given axis.
1248  ///
1249  /// For example, projecting *along* the X-axis projects *onto* the Y-axis.
1250  pub fn project (self, axis : Axis2) -> Interval <S> where S : MinMax {
1251    let (min, max) = match axis {
1252      Axis2::X => (self.min.y(), self.max.y()),
1253      Axis2::Y => (self.min.x(), self.max.x())
1254    };
1255    Interval::with_minmax_unchecked (min, max)
1256  }
1257}
1258
1259impl <S> Primitive <S, Point2 <S>> for Aabb2 <S> where S : OrderedField {
1260  fn translate (&mut self, displacement : Vector2 <S>) {
1261    self.min += displacement;
1262    self.max += displacement;
1263  }
1264  fn scale (&mut self, scale : Positive <S>) {
1265    let center = self.center();
1266    self.translate (-center.0);
1267    self.min.0 *= *scale;
1268    self.max.0 *= *scale;
1269    self.translate (center.0)
1270  }
1271  fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
1272    let out = if let Some (quadrant) = Quadrant::from_vec_strict (*direction) {
1273      self.corner (quadrant)
1274    } else if direction.x > S::zero() {
1275      debug_assert_eq!(direction.y, S::zero());
1276      point2 (self.max.x(), (self.min.y() + self.max.y()) / S::two())
1277    } else if direction.x < S::zero() {
1278      debug_assert_eq!(direction.y, S::zero());
1279      point2 (self.min.x(), (self.min.y() + self.max.y()) / S::two())
1280    } else {
1281      debug_assert_eq!(direction.x, S::zero());
1282      if direction.y > S::zero() {
1283        point2 ((self.min.x() + self.max.x()) / S::two(), self.max.y())
1284      } else {
1285        debug_assert!(direction.y < S::zero());
1286        point2 ((self.min.x() + self.max.x()) / S::two(), self.min.y())
1287      }
1288    };
1289    (out, out.0.dot (*direction))
1290  }
1291}
1292
1293impl <S> std::ops::Add <Vector2 <S>> for Aabb2 <S> where
1294  S    : Field,
1295  Self : Primitive <S, Point2 <S>>
1296{
1297  type Output = Self;
1298  #[expect(clippy::renamed_function_params)]
1299  fn add (mut self, displacement : Vector2 <S>) -> Self {
1300    self.translate (displacement);
1301    self
1302  }
1303}
1304
1305impl <S> std::ops::Sub <Vector2 <S>> for Aabb2 <S> where
1306  S    : Field,
1307  Self : Primitive <S, Point2 <S>>
1308{
1309  type Output = Self;
1310  #[expect(clippy::renamed_function_params)]
1311  fn sub (mut self, displacement : Vector2 <S>) -> Self {
1312    self.translate (-displacement);
1313    self
1314  }
1315}
1316
1317impl_numcast_fields!(Aabb3, min, max);
1318impl <S> Aabb3 <S> where S : Copy {
1319  #[inline]
1320  pub const fn min (self) -> Point3 <S> {
1321    self.min
1322  }
1323  #[inline]
1324  pub const fn max (self) -> Point3 <S> {
1325    self.max
1326  }
1327}
1328impl <S> Aabb3 <S> where S : Copy + PartialOrd + std::fmt::Debug {
1329  /// Returns `None` if points are not min/max or points are identical
1330  #[inline]
1331  pub fn with_minmax (min : Point3 <S>, max : Point3 <S>) -> Option <Self> {
1332    if min == max || min != point3_min (min, max) || max != point3_max (min, max) {
1333      None
1334    } else {
1335      Some (Aabb3 { min, max })
1336    }
1337  }
1338  /// Returns `None` if points are identical
1339  #[inline]
1340  pub fn from_points (a : Point3 <S>, b : Point3 <S>) -> Option <Self> {
1341    if a == b {
1342      None
1343    } else {
1344      let min = point3_min (a, b);
1345      let max = point3_max (a, b);
1346      Some (Aabb3 { min, max })
1347    }
1348  }
1349  /// Construct a new AABB with given min and max points.
1350  ///
1351  /// Debug panic if points are not min/max:
1352  ///
1353  /// ```should_panic
1354  /// # use math_utils::geometry::*;
1355  /// let b = Aabb3::with_minmax_unchecked (
1356  ///   [1.0, 1.0, 1.0].into(),
1357  ///   [0.0, 0.0, 0.0].into());  // panic!
1358  /// ```
1359  ///
1360  /// Debug panic if points are identical:
1361  ///
1362  /// ```should_panic
1363  /// # use math_utils::geometry::*;
1364  /// let b = Aabb3::with_minmax_unchecked (
1365  ///   [0.0, 0.0, 0.0].into(),
1366  ///   [0.0, 0.0, 0.0].into());  // panic!
1367  /// ```
1368  #[inline]
1369  pub fn with_minmax_unchecked (min : Point3 <S>, max : Point3 <S>) -> Self {
1370    debug_assert_ne!(min, max);
1371    debug_assert_eq!(min, point3_min (min, max));
1372    debug_assert_eq!(max, point3_max (min, max));
1373    Aabb3 { min, max }
1374  }
1375  /// Construct a new AABB using the two given points to determine min/max.
1376  ///
1377  /// Panic if points are identical:
1378  ///
1379  /// ```should_panic
1380  /// # use math_utils::geometry::*;
1381  /// let b = Aabb3::from_points_unchecked (
1382  ///   [0.0, 0.0, 0.0].into(),
1383  ///   [0.0, 0.0, 0.0].into());  // panic!
1384  /// ```
1385  #[inline]
1386  pub fn from_points_unchecked (a : Point3 <S>, b : Point3 <S>) -> Self {
1387    debug_assert_ne!(a, b);
1388    let min = point3_min (a, b);
1389    let max = point3_max (a, b);
1390    Aabb3 { min, max }
1391  }
1392  /// Construct the minimum AABB containing the given set of points.
1393  ///
1394  /// Returns `None` if fewer than 2 points are given or if all points are identical:
1395  ///
1396  /// ```should_panic
1397  /// # use math_utils::geometry::*;
1398  /// assert!(Aabb3::<f32>::containing (&[]).is_none());
1399  /// ```
1400  ///
1401  /// ```should_panic
1402  /// # use math_utils::geometry::*;
1403  /// assert!(Aabb3::containing (&[[0.0, 0.0, 0.0].into()]).is_none());
1404  /// ```
1405  #[inline]
1406  pub fn containing (points : &[Point3 <S>]) -> Option <Self> {
1407    debug_assert!(points.len() >= 2);
1408    let mut min = point3_min (points[0], points[1]);
1409    let mut max = point3_max (points[0], points[1]);
1410    for point in points.iter().skip (2) {
1411      min = point3_min (min, *point);
1412      max = point3_max (max, *point);
1413    }
1414    Aabb3::with_minmax (min, max)
1415  }
1416  /// Create a new AABB that is the union of the two input AABBs
1417  #[inline]
1418  pub fn union (a : Aabb3 <S>, b : Aabb3 <S>) -> Self {
1419    Aabb3::with_minmax_unchecked (
1420      point3_min (a.min(), b.min()), point3_max (a.max(), b.max()))
1421  }
1422  #[inline]
1423  pub fn center (self) -> Point3 <S> where S : Ring + std::ops::Div <Output=S> {
1424    Point3 ((self.min.0 + self.max.0) / S::two())
1425  }
1426  #[inline]
1427  pub fn dimensions (self) -> Vector3 <NonNegative <S>> where
1428    S : num::Zero + std::ops::Sub <Output=S>
1429  {
1430    (self.max.0 - self.min.0).map (NonNegative::unchecked)
1431  }
1432  #[inline]
1433  pub fn extents (self) -> Vector3 <NonNegative <S>> where S : Field {
1434    self.dimensions().map (|d| d * NonNegative::unchecked (S::half()))
1435  }
1436  /// X dimension
1437  #[inline]
1438  pub fn width (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1439    NonNegative::unchecked (self.max.x() - self.min.x())
1440  }
1441  /// Y dimension
1442  #[inline]
1443  pub fn depth (self) -> NonNegative <S> where S : num::Zero + std::ops::Sub <Output=S> {
1444    NonNegative::unchecked (self.max.y() - self.min.y())
1445  }
1446  /// Z dimension
1447  #[inline]
1448  pub fn height (self) -> NonNegative <S> where
1449    S : num::Zero + std::ops::Sub <Output=S>
1450  {
1451    NonNegative::unchecked (self.max.z() - self.min.z())
1452  }
1453  #[inline]
1454  pub fn volume (self) -> NonNegative <S> where
1455    S : num::Zero + std::ops::Sub <Output=S> + std::ops::Mul <Output=S>
1456  {
1457    self.width() * self.depth() * self.height()
1458  }
1459  #[inline]
1460  pub fn contains (self, point : Point3 <S>) -> bool {
1461    self.min.x() < point.x() && point.x() < self.max.x() &&
1462    self.min.y() < point.y() && point.y() < self.max.y() &&
1463    self.min.z() < point.z() && point.z() < self.max.z()
1464  }
1465  /// Clamp a given point to the AABB.
1466  ///
1467  /// ```
1468  /// # use math_utils::geometry::*;
1469  /// let b = Aabb3::with_minmax ([-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into())
1470  ///   .unwrap();
1471  /// assert_eq!(b.clamp ([-2.0, 0.0, 0.0].into()), [-1.0, 0.0, 0.0].into());
1472  /// assert_eq!(b.clamp ([ 2.0, 2.0, 0.0].into()), [ 1.0, 1.0, 0.0].into());
1473  /// assert_eq!(b.clamp ([-1.0, 2.0, 3.0].into()), [-1.0, 1.0, 1.0].into());
1474  /// assert_eq!(b.clamp ([-0.5, 0.5, 0.0].into()), [-0.5, 0.5, 0.0].into());
1475  /// ```
1476  pub fn clamp (self, point : Point3 <S>) -> Point3 <S> where S : MinMax {
1477    [ point.x().clamp (self.min.x(), self.max.x()),
1478      point.y().clamp (self.min.y(), self.max.y()),
1479      point.z().clamp (self.min.z(), self.max.z())
1480    ].into()
1481  }
1482  /// Generate a random point contained in the AABB
1483  ///
1484  /// ```
1485  /// # use rand;
1486  /// # use rand_xorshift;
1487  /// # use math_utils::geometry::*;
1488  /// # fn main () {
1489  /// use rand_xorshift;
1490  /// use rand::SeedableRng;
1491  /// // random sequence will be the same each time this is run
1492  /// let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
1493  /// let aabb = Aabb3::<f32>::with_minmax (
1494  ///   [-10.0, -10.0, -10.0].into(),
1495  ///   [ 10.0,  10.0,  10.0].into()
1496  /// ).unwrap();
1497  /// let point = aabb.rand_point (&mut rng);
1498  /// assert!(aabb.contains (point));
1499  /// let point = aabb.rand_point (&mut rng);
1500  /// assert!(aabb.contains (point));
1501  /// let point = aabb.rand_point (&mut rng);
1502  /// assert!(aabb.contains (point));
1503  /// let point = aabb.rand_point (&mut rng);
1504  /// assert!(aabb.contains (point));
1505  /// let point = aabb.rand_point (&mut rng);
1506  /// assert!(aabb.contains (point));
1507  /// # }
1508  /// ```
1509  #[inline]
1510  pub fn rand_point <R> (self, rng : &mut R) -> Point3 <S> where
1511    S : rand::distr::uniform::SampleUniform,
1512    R : rand::RngExt
1513  {
1514    let x_range = self.min.x()..self.max.x();
1515    let y_range = self.min.y()..self.max.y();
1516    let z_range = self.min.z()..self.max.z();
1517    let x = if x_range.is_empty() {
1518      self.min.x()
1519    } else {
1520      rng.random_range (x_range)
1521    };
1522    let y = if y_range.is_empty() {
1523      self.min.y()
1524    } else {
1525      rng.random_range (y_range)
1526    };
1527    let z = if z_range.is_empty() {
1528      self.min.z()
1529    } else {
1530      rng.random_range (z_range)
1531    };
1532    [x, y, z].into()
1533  }
1534  #[inline]
1535  pub fn intersects (self, other : Aabb3 <S>) -> bool {
1536    intersect::discrete_aabb3_aabb3 (self, other)
1537  }
1538  #[inline]
1539  pub fn intersection (self, other : Aabb3 <S>) -> Option <Aabb3 <S>> {
1540    intersect::continuous_aabb3_aabb3 (self, other)
1541  }
1542
1543  pub fn corner (self, octant : Octant) -> Point3 <S> {
1544    match octant {
1545      Octant::PosPosPos => self.max,
1546      Octant::NegPosPos => [self.min.x(), self.max.y(), self.max.z()].into(),
1547      Octant::PosNegPos => [self.max.x(), self.min.y(), self.max.z()].into(),
1548      Octant::NegNegPos => [self.min.x(), self.min.y(), self.max.z()].into(),
1549      Octant::PosPosNeg => [self.max.x(), self.max.y(), self.min.z()].into(),
1550      Octant::NegPosNeg => [self.min.x(), self.max.y(), self.min.z()].into(),
1551      Octant::PosNegNeg => [self.max.x(), self.min.y(), self.min.z()].into(),
1552      Octant::NegNegNeg => self.min
1553    }
1554  }
1555
1556  pub fn corners (self) -> [Point3 <S>; 8] {
1557    [ self.min,
1558      [self.min.x(), self.max.y(), self.max.z()].into(),
1559      [self.max.x(), self.min.y(), self.max.z()].into(),
1560      [self.min.x(), self.min.y(), self.max.z()].into(),
1561      [self.max.x(), self.max.y(), self.min.z()].into(),
1562      [self.min.x(), self.max.y(), self.min.z()].into(),
1563      [self.max.x(), self.min.y(), self.min.z()].into(),
1564      self.max
1565    ]
1566  }
1567
1568  pub fn face (self, direction : SignedAxis3) -> Self {
1569    let (min, max) = match direction {
1570      SignedAxis3::PosX => (
1571        self.corner (Octant::PosNegNeg),
1572        self.corner (Octant::PosPosPos) ),
1573      SignedAxis3::NegX => (
1574        self.corner (Octant::NegNegNeg),
1575        self.corner (Octant::NegPosPos) ),
1576      SignedAxis3::PosY => (
1577        self.corner (Octant::NegPosNeg),
1578        self.corner (Octant::PosPosPos) ),
1579      SignedAxis3::NegY => (
1580        self.corner (Octant::NegNegNeg),
1581        self.corner (Octant::PosNegPos) ),
1582      SignedAxis3::PosZ => (
1583        self.corner (Octant::NegNegPos),
1584        self.corner (Octant::PosPosPos) ),
1585      SignedAxis3::NegZ => (
1586        self.corner (Octant::NegNegNeg),
1587        self.corner (Octant::PosPosNeg) )
1588    };
1589    Aabb3::with_minmax_unchecked (min, max)
1590  }
1591
1592  /// Extrude (or inset) a new Aabb from a face:
1593  ///
1594  /// ```
1595  /// # use math_utils::*;
1596  /// # use math_utils::geometry::*;
1597  /// let x = Aabb3::with_minmax ([-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into())
1598  ///   .unwrap();
1599  /// assert_eq!(x.extrude (SignedAxis3::PosZ, 0.5),
1600  ///   Aabb3::with_minmax ([-1.0, -1.0, 1.0].into(), [1.0, 1.0, 1.5].into()));
1601  /// assert_eq!(x.extrude (SignedAxis3::PosZ, -0.5),
1602  ///   Aabb3::with_minmax ([-1.0, -1.0, 0.5].into(), [1.0, 1.0, 1.0].into()));
1603  /// ```
1604  #[must_use]
1605  pub fn extrude (self, axis : SignedAxis3, amount : S) -> Option <Self> where S : Ring {
1606    let (p1, p2) = match axis {
1607      SignedAxis3::PosX => (
1608        self.min + Vector3::zero().with_x (*self.width()),
1609        self.max + Vector3::zero().with_x (amount)
1610      ),
1611      SignedAxis3::NegX => (
1612        self.min - Vector3::zero().with_x (amount),
1613        self.max - Vector3::zero().with_x (*self.width())
1614      ),
1615      SignedAxis3::PosY => (
1616        self.min + Vector3::zero().with_y (*self.depth()),
1617        self.max + Vector3::zero().with_y (amount)
1618      ),
1619      SignedAxis3::NegY => (
1620        self.min - Vector3::zero().with_y (amount),
1621        self.max - Vector3::zero().with_y (*self.depth())
1622      ),
1623      SignedAxis3::PosZ => (
1624        self.min + Vector3::zero().with_z (*self.height()),
1625        self.max + Vector3::zero().with_z (amount)
1626      ),
1627      SignedAxis3::NegZ => (
1628        self.min - Vector3::zero().with_z (amount),
1629        self.max - Vector3::zero().with_z (*self.height())
1630      )
1631    };
1632    Aabb3::from_points (p1, p2)
1633  }
1634
1635  /// Extend (or contract) one of the extents
1636  #[must_use]
1637  pub fn extend (mut self, axis : SignedAxis3, amount : S) -> Option <Self>
1638    where S : std::ops::AddAssign + std::ops::SubAssign
1639  {
1640    match axis {
1641      SignedAxis3::PosX => self.max.0.x += amount,
1642      SignedAxis3::NegX => self.min.0.x -= amount,
1643      SignedAxis3::PosY => self.max.0.y += amount,
1644      SignedAxis3::NegY => self.min.0.y -= amount,
1645      SignedAxis3::PosZ => self.max.0.z += amount,
1646      SignedAxis3::NegZ => self.min.0.z -= amount
1647    }
1648    Self::with_minmax (self.min, self.max)
1649  }
1650
1651  /// Increase or decrease each extent by the given amount.
1652  ///
1653  /// Returns `None` if any extent would become negative:
1654  /// ```
1655  /// # use math_utils::geometry::*;
1656  /// let x = Aabb3::with_minmax ([0.0, 0.0, 0.0].into(), [1.0, 2.0, 3.0].into())
1657  ///   .unwrap();
1658  /// assert!(x.dilate (-1.0).is_none());
1659  /// ```
1660  #[must_use]
1661  pub fn dilate (mut self, amount : S) -> Option <Self> where S : Ring {
1662    self.min -= Vector3::broadcast (amount);
1663    self.max += Vector3::broadcast (amount);
1664    if self.min == self.max || self.min != point3_min (self.min, self.max)
1665      || self.max != point3_max (self.min, self.max)
1666    {
1667      None
1668    } else {
1669      Some (self)
1670    }
1671  }
1672
1673  /// Project *along* the given axis.
1674  ///
1675  /// For example, projecting *along* the X-axis projects *onto* the YZ-plane.
1676  pub fn project (self, axis : Axis3) -> Aabb2 <S> {
1677    let (min, max) = match axis {
1678      Axis3::X => ([self.min.y(), self.min.z()], [self.max.y(), self.max.z()]),
1679      Axis3::Y => ([self.min.x(), self.min.z()], [self.max.x(), self.max.z()]),
1680      Axis3::Z => ([self.min.x(), self.min.y()], [self.max.x(), self.max.y()]),
1681    };
1682    Aabb2::with_minmax_unchecked (min.into(), max.into())
1683  }
1684}
1685
1686impl <S> Primitive <S, Point3 <S>> for Aabb3 <S> where S : OrderedField {
1687  fn translate (&mut self, displacement : Vector3 <S>) {
1688    self.min += displacement;
1689    self.max += displacement;
1690  }
1691  fn scale (&mut self, scale : Positive <S>) {
1692    let center = self.center();
1693    self.translate (-center.0);
1694    self.min.0 *= *scale;
1695    self.max.0 *= *scale;
1696    self.translate (center.0);
1697  }
1698  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
1699    let out = if let Some (octant) = Octant::from_vec_strict (*direction) {
1700      self.corner (octant)
1701    } else {
1702      use Sign::*;
1703      let center = self.center();
1704      match direction.map (S::sign).into_array() {
1705        // faces
1706        [Positive, Zero, Zero] => center.0.with_x (self.max.x()).into(),
1707        [Negative, Zero, Zero] => center.0.with_x (self.min.x()).into(),
1708        [Zero, Positive, Zero] => center.0.with_y (self.max.y()).into(),
1709        [Zero, Negative, Zero] => center.0.with_y (self.min.y()).into(),
1710        [Zero, Zero, Positive] => center.0.with_z (self.max.z()).into(),
1711        [Zero, Zero, Negative] => center.0.with_z (self.min.z()).into(),
1712        // edges
1713        [Positive, Positive, Zero] =>
1714          center.0.with_x (self.max.x()).with_y (self.max.y()).into(),
1715        [Positive, Negative, Zero] =>
1716          center.0.with_x (self.max.x()).with_y (self.min.y()).into(),
1717        [Negative, Positive, Zero] =>
1718          center.0.with_x (self.min.x()).with_y (self.max.y()).into(),
1719        [Negative, Negative, Zero] =>
1720          center.0.with_x (self.min.x()).with_y (self.min.y()).into(),
1721
1722        [Positive, Zero, Positive] =>
1723          center.0.with_x (self.max.x()).with_z (self.max.z()).into(),
1724        [Positive, Zero, Negative] =>
1725          center.0.with_x (self.max.x()).with_z (self.min.z()).into(),
1726        [Negative, Zero, Positive] =>
1727          center.0.with_x (self.min.x()).with_z (self.max.z()).into(),
1728        [Negative, Zero, Negative] =>
1729          center.0.with_x (self.min.x()).with_z (self.min.z()).into(),
1730
1731        [Zero, Positive, Positive] =>
1732          center.0.with_y (self.max.y()).with_z (self.max.z()).into(),
1733        [Zero, Positive, Negative] =>
1734          center.0.with_y (self.max.y()).with_z (self.min.z()).into(),
1735        [Zero, Negative, Positive] =>
1736          center.0.with_y (self.min.y()).with_z (self.max.z()).into(),
1737        [Zero, Negative, Negative] =>
1738          center.0.with_y (self.min.y()).with_z (self.min.z()).into(),
1739        [_, _, _] => unreachable!()
1740      }
1741    };
1742    (out, out.0.dot (*direction))
1743  }
1744}
1745
1746impl <S> std::ops::Add <Vector3 <S>> for Aabb3 <S> where
1747  S    : Field,
1748  Self : Primitive <S, Point3 <S>>
1749{
1750  type Output = Self;
1751  #[expect(clippy::renamed_function_params)]
1752  fn add (mut self, displacement : Vector3 <S>) -> Self {
1753    self.translate (displacement);
1754    self
1755  }
1756}
1757
1758impl <S> std::ops::Sub <Vector3 <S>> for Aabb3 <S> where
1759  S    : Field,
1760  Self : Primitive <S, Point3 <S>>
1761{
1762  type Output = Self;
1763  #[expect(clippy::renamed_function_params)]
1764  fn sub (mut self, displacement : Vector3 <S>) -> Self {
1765    self.translate (-displacement);
1766    self
1767  }
1768}
1769
1770impl_numcast_fields!(Obb2, center, extents, orientation);
1771impl <S : OrderedRing> Obb2 <S> {
1772  /// Construct a new OBB
1773  #[inline]
1774  pub const fn new (
1775    center      : Point2 <S>,
1776    extents     : Vector2 <Positive <S>>,
1777    orientation : AngleWrapped <S>
1778  ) -> Self {
1779    Obb2 { center, extents, orientation }
1780  }
1781  /// Compute the OBB of a given orientation.
1782  ///
1783  /// Returns None if the points span an empty set.
1784  pub fn containing_points_with_orientation (
1785    points : &[Point2 <S>], orientation : AngleWrapped <S>
1786  ) -> Option <Self> where
1787    S : Real + num::real::Real + MaybeSerDes
1788  {
1789    let [x, y] = Rotation2::from_angle (orientation.angle()).cols
1790      .map (NonZero2::unchecked).into_array();
1791    let min_dot_x = -support2 (points, -x).1;
1792    let max_dot_x =  support2 (points,  x).1;
1793    let min_dot_y = -support2 (points, -y).1;
1794    let max_dot_y =  support2 (points,  y).1;
1795    let center = Point2 (
1796      (*x * min_dot_x + *x * max_dot_x) * S::half() +
1797      (*y * min_dot_y + *y * max_dot_y) * S::half());
1798    let extents = vector2 (
1799      Positive::new (S::half() * (max_dot_x - min_dot_x))?,
1800      Positive::new (S::half() * (max_dot_y - min_dot_y))?);
1801    Some (Obb2 { center, extents, orientation })
1802  }
1803  /// Construct the minimum OBB containing the convex hull of a given set of points
1804  /// using rotating calipers algorithm. To construct the OBB containing a convex hull
1805  /// directoy use the `Obb2::containing` method.
1806  ///
1807  /// Returns None if fewer than 3 points are given or if points span an empty set:
1808  ///
1809  /// ```
1810  /// # use math_utils::*;
1811  /// # use math_utils::geometry::*;
1812  /// assert!(Obb2::containing_points (
1813  ///   &[[0.0, 1.0], [1.0, 0.0]].map (Point2::from)
1814  /// ).is_none());
1815  ///
1816  /// assert!(Obb2::containing_points (
1817  ///   &[[1.0, 1.0], [0.0, 0.0], [-1.0, -1.0]].map (Point2::from)
1818  /// ).is_none());
1819  /// ```
1820  pub fn containing_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
1821    let hull = Hull2::from_points (points)?;
1822    Self::containing (&hull)
1823  }
1824  /// Construct the minimum OBB containing the given convex hull of points using
1825  /// rotating calipers algorithm.
1826  ///
1827  /// Returns None if fewer than 3 points are given or if points span an empty set:
1828  ///
1829  /// ```
1830  /// # use math_utils::*;
1831  /// # use math_utils::geometry::*;
1832  /// let hull = Hull2::from_points (
1833  ///   &[[0.0, 1.0], [1.0, 0.0]].map (Point2::from)).unwrap();
1834  /// assert!(Obb2::containing (&hull).is_none());
1835  ///
1836  /// let hull = Hull2::from_points (
1837  ///   &[[1.0, 1.0], [0.0, 0.0], [-1.0, -1.0]].map (Point2::from)).unwrap();
1838  /// assert!(Obb2::containing (&hull).is_none());
1839  /// ```
1840  // code follows GeometricTools:
1841  // <https://github.com/davideberly/GeometricTools/blob/f78dd0b65bc3a0a4723586de6991dd2c339b08ad/GTE/Mathematics/MinimumAreaBox2.h>
1842  pub fn containing (hull : &Hull2 <S>) -> Option <Self> where S : Real {
1843    let points = hull.points();
1844    if points.len() < 3 {
1845      return None
1846    }
1847    let mut visited  = vec![false; points.len()];
1848    #[expect(clippy::cast_possible_truncation)]
1849    let mut min_rect = smallest_rect (points.len() as u32 - 1, 0, hull);
1850    visited[min_rect.indices[0] as usize] = true;
1851    // rotating calipers
1852    let mut rect = min_rect.clone();
1853    for _ in 0..points.len() {
1854      let mut angles  = [(S::zero(), u32::MAX); 4];
1855      let mut nangles = u32::MAX;
1856      if !compute_angles (hull, &rect, &mut angles, &mut nangles) {
1857        break
1858      }
1859      let sorted = sort_angles (&angles, nangles);
1860      if !update_support (&angles, nangles, &sorted, hull, &mut visited, &mut rect) {
1861        break
1862      }
1863      if rect.area < min_rect.area {
1864        min_rect = rect.clone();
1865      }
1866    }
1867    // construct obb from min rect
1868    let sum = [
1869      points[min_rect.indices[1] as usize].0 + points[min_rect.indices[3] as usize].0,
1870      points[min_rect.indices[2] as usize].0 + points[min_rect.indices[0] as usize].0
1871    ];
1872    let diff = [
1873      points[min_rect.indices[1] as usize].0 - points[min_rect.indices[3] as usize].0,
1874      points[min_rect.indices[2] as usize].0 - points[min_rect.indices[0] as usize].0
1875    ];
1876    let obb = {
1877      let center = Point2::from ((min_rect.edge * min_rect.edge.dot (sum[0]) +
1878        min_rect.perp * min_rect.perp.dot (sum[1]))
1879        * S::half() / min_rect.edge_len_squared);
1880      let extents = {
1881        let extent_x = Positive::unchecked (S::sqrt (
1882          (S::half() * min_rect.edge.dot (diff[0])).squared()
1883          / min_rect.edge_len_squared));
1884        let extent_y = Positive::unchecked (S::sqrt (
1885          (S::half() * min_rect.perp.dot (diff[1])).squared()
1886          / min_rect.edge_len_squared));
1887        vector2 (extent_x, extent_y)
1888      };
1889      let orientation =
1890        AngleWrapped::wrap (Rad (min_rect.edge.y.atan2 (min_rect.edge.x)));
1891      Obb2 { center, extents, orientation }
1892    };
1893    fn compute_angles <S : Real> (
1894      hull    : &Hull2 <S>,
1895      rect    : &Rect <S>,
1896      angles  : &mut [(S, u32); 4],
1897      nangles : &mut u32
1898    ) -> bool {
1899      *nangles = 0;
1900      let points = hull.points();
1901      let mut k0 = 3;
1902      let mut k1 = 0;
1903      while k1 < 4 {
1904        if rect.indices[k0] != rect.indices[k1] {
1905          let d = {
1906            let mut d = if k0 & 1 != 0 {
1907              rect.perp
1908            } else {
1909              rect.edge
1910            };
1911            if k0 & 2 != 0 {
1912              d = -d;
1913            }
1914            d
1915          };
1916          let j0 = rect.indices[k0];
1917          let mut j1 = j0 + 1;
1918          #[expect(clippy::cast_possible_truncation)]
1919          if j1 == points.len() as u32 {
1920            j1 = 0;
1921          }
1922          let e = points[j1 as usize] - points[j0 as usize];
1923          let dp = d.dot ([e.y, -e.x].into());
1924          let e_lensq = e.magnitude_squared();
1925          let sin_theta_squared = (dp * dp) / e_lensq;
1926          angles[*nangles as usize] =
1927            #[expect(clippy::cast_possible_truncation)]
1928            (sin_theta_squared, k0 as u32);
1929          *nangles += 1;
1930        }
1931        k0 = k1;
1932        k1 += 1;
1933      }
1934      *nangles > 0
1935    }
1936    fn sort_angles <S : PartialOrd> (angles : &[(S, u32); 4], nangles : u32)
1937      -> [u32; 4]
1938    {
1939      let mut sorted = [0u32, 1, 2, 3];
1940      match nangles {
1941        0 | 1 => {}
1942        2 => if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1943          sorted.swap (0, 1)
1944        }
1945        3 => {
1946          if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1947            sorted.swap (0, 1)
1948          }
1949          if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1950            sorted.swap (0, 2)
1951          }
1952          if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1953            sorted.swap (1, 2)
1954          }
1955        }
1956        4 => {
1957          if angles[sorted[0] as usize].0 > angles[sorted[1] as usize].0 {
1958            sorted.swap (0, 1)
1959          }
1960          if angles[sorted[2] as usize].0 > angles[sorted[3] as usize].0 {
1961            sorted.swap (2, 3)
1962          }
1963          if angles[sorted[0] as usize].0 > angles[sorted[2] as usize].0 {
1964            sorted.swap (0, 2)
1965          }
1966          if angles[sorted[1] as usize].0 > angles[sorted[3] as usize].0 {
1967            sorted.swap (1, 3)
1968          }
1969          if angles[sorted[1] as usize].0 > angles[sorted[2] as usize].0 {
1970            sorted.swap (1, 2)
1971          }
1972        }
1973        _ => unreachable!()
1974      }
1975      sorted
1976    }
1977    fn update_support <S : Real> (
1978      angles  : &[(S, u32); 4],
1979      nangles : u32,
1980      sorted  : &[u32; 4],
1981      hull    : &Hull2 <S>,
1982      visited : &mut [bool],
1983      rect    : &mut Rect <S>
1984    ) -> bool {
1985      let points = hull.points();
1986      let min_angle = angles[sorted[0] as usize];
1987      for k in 0..nangles as usize {
1988        let (angle, index) = angles[sorted[k] as usize];
1989        if angle == min_angle.0 {
1990          rect.indices[index as usize] += 1;
1991          #[expect(clippy::cast_possible_truncation)]
1992          if rect.indices[index as usize] == points.len() as u32 {
1993            rect.indices[index as usize] = 0;
1994          }
1995        }
1996      }
1997      let bottom = rect.indices[min_angle.1 as usize];
1998      if visited[bottom as usize] {
1999        return false
2000      }
2001      visited[bottom as usize] = true;
2002      let mut next_index = [u32::MAX; 4];
2003      for k in 0u32..4 {
2004        next_index[k as usize] = rect.indices[((min_angle.1 + k) % 4) as usize];
2005      }
2006      rect.indices = next_index;
2007      let j1 = rect.indices[0] as usize;
2008      let j0 = if j1 == 0 {
2009        points.len() - 1
2010      } else {
2011        j1 - 1
2012      };
2013      rect.edge = points[j1] - points[j0];
2014      rect.perp = vector2 (-rect.edge.y, rect.edge.x);
2015      let edge_len_squared = rect.edge.magnitude_squared();
2016      let diff = [
2017        points[rect.indices[1] as usize] - points[rect.indices[3] as usize],
2018        points[rect.indices[2] as usize] - points[rect.indices[0] as usize]
2019      ];
2020      rect.area = rect.edge.dot (diff[0]) * rect.perp.dot (diff[1]) / edge_len_squared;
2021      true
2022    }
2023    Some (obb)
2024  }
2025  #[inline]
2026  pub fn dimensions (self) -> Vector2 <Positive <S>> {
2027    self.extents * Positive::unchecked (S::two())
2028  }
2029  /// X dimension
2030  #[inline]
2031  pub fn width (self) -> Positive <S> {
2032    self.extents.x * Positive::unchecked (S::two())
2033  }
2034  /// Y dimension
2035  #[inline]
2036  pub fn height (self) -> Positive <S> {
2037    self.extents.y * Positive::unchecked (S::two())
2038  }
2039  #[inline]
2040  pub fn area (self) -> Positive <S> {
2041    self.width() * self.height()
2042  }
2043  #[inline]
2044  pub fn corners (self) -> [Point2 <S>; 4] where
2045    S : Real + num::real::Real + MaybeSerDes
2046  {
2047    let mut points = [Point2::origin(); 4];
2048    for i in 0..2 {
2049      for j in 0..2 {
2050        let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2051        let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2052        points[i * 2 + j] = [*self.extents.x * sign_x, *self.extents.y * sign_y].into();
2053      }
2054    }
2055    let rotation = Rotation2::from_angle (self.orientation.angle());
2056    points.map (|point| rotation.rotate (point) + self.center.0)
2057  }
2058
2059  pub fn aabb (self) -> Aabb2 <S> where S : Real + num::real::Real + MaybeSerDes {
2060    Aabb2::containing (&self.corners()).unwrap()
2061  }
2062
2063  /// Check if point is contained by the bounding box.
2064  ///
2065  /// ```
2066  /// # use math_utils::*;
2067  /// # use math_utils::geometry::*;
2068  /// let x = Obb2::new (
2069  ///   [3.0, 6.0].into(),
2070  ///   [2.0, 0.75].map (Positive::noisy).into(),
2071  ///   AngleWrapped::wrap (Rad (std::f32::consts::FRAC_PI_4)));
2072  /// assert!(x.contains ([2.0f32, 5.0].into()));
2073  /// assert!(!x.contains ([3.0f32, 2.0].into()));
2074  /// ```
2075  pub fn contains (self, point : Point2 <S>) -> bool where
2076    S : Real + num::real::Real + MaybeSerDes
2077  {
2078    // re-center
2079    let p = point - self.center.0;
2080    // reverse rotation
2081    let p = Rotation2::from_angle (-self.orientation.angle()).rotate (p);
2082    p.x().abs() < *self.extents.x && p.y().abs() < *self.extents.y
2083  }
2084
2085  /// Increase or decrease each extent by the given amount.
2086  ///
2087  /// ```
2088  /// # use math_utils::*;
2089  /// # use math_utils::geometry::*;
2090  /// # use math_utils::num::Zero;
2091  /// let x = Obb2::new (
2092  ///   Point2::origin(),
2093  ///   [0.5, 0.5].map (Positive::noisy).into(),
2094  ///   AngleWrapped::zero()
2095  /// );
2096  /// let y = x.dilate (1.0);
2097  /// assert_eq!(*y.dimensions().x, *x.dimensions().x + 2.0);
2098  /// assert_eq!(*y.dimensions().y, *x.dimensions().y + 2.0);
2099  /// ```
2100  ///
2101  /// Panics if any extent would become zero or negative:
2102  ///
2103  /// ```should_panic
2104  /// # use math_utils::*;
2105  /// # use math_utils::geometry::*;
2106  /// # use math_utils::num::Zero;
2107  /// let x = Obb2::new (
2108  ///   Point2::origin(),
2109  ///   [0.5, 0.5].map (Positive::noisy).into(),
2110  ///   AngleWrapped::zero()
2111  /// );
2112  /// let y = x.dilate (-1.0); // panic!
2113  /// ```
2114  pub fn dilate (mut self, amount : S) -> Self {
2115    self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2116    self
2117  }
2118}
2119impl <S : Real> From <Aabb2 <S>> for Obb2 <S> {
2120  /// Panics if AABB has a zero dimension:
2121  ///
2122  /// ```should_panic
2123  /// # use math_utils::*;
2124  /// # use math_utils::geometry::*;
2125  /// # use math_utils::num::Zero;
2126  /// let x = Aabb2::with_minmax (
2127  ///   [0.0, 0.0].into(),
2128  ///   [0.0, 1.0].into()
2129  /// ).unwrap();
2130  /// let y = Obb2::from (x); // panic!
2131  /// ```
2132  fn from (aabb : Aabb2 <S>) -> Self {
2133    use num::Zero;
2134    let center        = aabb.center();
2135    let extents = vector2 (
2136      Positive::noisy (*aabb.width()  / S::two()),
2137      Positive::noisy (*aabb.height() / S::two()));
2138    let orientation   = AngleWrapped::zero();
2139    Obb2::new (center, extents, orientation)
2140  }
2141}
2142impl <S> Primitive <S, Point2 <S>> for Obb2 <S> where
2143  S : Real + num::real::Real + MaybeSerDes
2144{
2145  fn translate (&mut self, displacement : Vector2 <S>) {
2146    self.center += displacement;
2147  }
2148  fn scale (&mut self, scale : Positive <S>) {
2149    self.extents *= scale;
2150  }
2151  fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2152    use num::Inv;
2153    let rotation = Rotation2::from_angle (self.orientation.angle());
2154    let aabb = Aabb2::with_minmax_unchecked (
2155      self.center - self.extents.map (|e| *e),
2156      self.center + self.extents.map (|e| *e));
2157    let aabb_direction = rotation.inv() * direction;
2158    let (aabb_support, _) = aabb.support (aabb_direction);
2159    let out = rotation.rotate_around (aabb_support, self.center);
2160    (out, out.0.dot (*direction))
2161  }
2162}
2163
2164impl_numcast_fields!(Obb3, center, extents, orientation);
2165impl <S : OrderedRing> Obb3 <S> {
2166  /// Construct a new OBB
2167  #[inline]
2168  pub const fn new (
2169    center      : Point3 <S>,
2170    extents     : Vector3 <Positive <S>>,
2171    orientation : Angles3 <S>
2172  ) -> Self {
2173    Obb3 { center, extents, orientation }
2174  }
2175  /// Compute the OBB of a given orientation.
2176  ///
2177  /// Returns None if the points span an empty set.
2178  pub fn containing_points_with_orientation (
2179    points : &[Point3 <S>], orientation : Angles3 <S>
2180  ) -> Option <Self> where
2181    S : Real + num::real::Real + MaybeSerDes
2182  {
2183    let [x, y, z] = Rotation3::from (orientation).cols.map (NonZero3::unchecked)
2184      .into_array();
2185    let min_dot_x = -support3 (points, -x).1;
2186    let max_dot_x =  support3 (points,  x).1;
2187    let min_dot_y = -support3 (points, -y).1;
2188    let max_dot_y =  support3 (points,  y).1;
2189    let min_dot_z = -support3 (points, -z).1;
2190    let max_dot_z =  support3 (points,  z).1;
2191    let center = Point3 (
2192      (*x * min_dot_x + *x * max_dot_x) * S::half() +
2193      (*y * min_dot_y + *y * max_dot_y) * S::half() +
2194      (*z * min_dot_z + *z * max_dot_z) * S::half());
2195    let extents = vector3 (
2196      Positive::new (S::half() * (max_dot_x - min_dot_x))?,
2197      Positive::new (S::half() * (max_dot_y - min_dot_y))?,
2198      Positive::new (S::half() * (max_dot_z - min_dot_z))?);
2199    Some (Obb3 { center, extents, orientation })
2200  }
2201  /// Construct an approximate minimum OBB containing the convex hull of a given set of
2202  /// points.
2203  ///
2204  /// Returns None if fewer than 4 points are given or if points span an empty set:
2205  /// ```
2206  /// # use math_utils::*;
2207  /// # use math_utils::geometry::*;
2208  /// assert!(Obb3::containing_points_approx (
2209  ///   &[[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]].map (Point3::from)
2210  /// ).is_none());
2211  ///
2212  /// assert!(Obb3::containing_points_approx (
2213  ///   &[[1.0, 0.0, 1.0], [0.0, 1.0, 0.0], [0.0, -1.0, 0.0], [-1.0,  0.0, -1.0]]
2214  ///     .map (Point3::from)
2215  /// ).is_none());
2216  /// ```
2217  pub fn containing_points_approx (points : &[Point3 <S>]) -> Option <Self> where
2218    S : Real + num::real::Real + approx::RelativeEq <Epsilon=S> + MaybeSerDes
2219  {
2220    if points.len() < 4 {
2221      return None
2222    }
2223    let (hull, mesh) = Hull3::from_points_with_mesh (points)?;
2224    Self::containing_approx (&hull, &mesh)
2225  }
2226  /// Construct an approximate minimum OBB containing the given convex hull and
2227  /// corresponding mesh.
2228  ///
2229  /// Returns None if fewer than 4 points are given or if points span an empty set:
2230  /// ```
2231  /// # use math_utils::*;
2232  /// # use math_utils::geometry::*;
2233  /// let (hull, mesh) = Hull3::from_points_with_mesh (
2234  ///   &[[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]].map (Point3::from)
2235  /// ).unwrap();
2236  /// assert!(Obb3::containing_approx (&hull, &mesh).is_none());
2237  ///
2238  /// let (hull, mesh) = Hull3::from_points_with_mesh (
2239  ///   &[[1.0, 0.0, 1.0], [0.0, 1.0, 0.0], [0.0, -1.0, 0.0], [-1.0,  0.0, -1.0]]
2240  ///     .map (Point3::from)
2241  /// ).unwrap();
2242  /// assert!(Obb3::containing_approx (&hull, &mesh).is_none());
2243  /// ```
2244  pub fn containing_approx (hull : &Hull3 <S>, mesh : &VertexEdgeTriangleMesh)
2245    -> Option <Self>
2246  where S : Real + num::NumCast + approx::RelativeEq <Epsilon=S> + MaybeSerDes {
2247    // algorithm from:
2248    // <https://github.com/isl-org/Open3D/blob/fb7088ceebef38d54c575b47935e568979b50954/cpp/open3d/t/geometry/kernel/MinimumOBB.cpp>
2249    use num::Inv;
2250    if hull.points().len() < 4 {
2251      return None
2252    }
2253    let mut min_obb : Obb3 <S> = Aabb3::containing (hull.points()).unwrap().into();
2254    let mut min_vol = min_obb.volume();
2255    for triangle in mesh.triangles().values() {
2256      let [a, b, c] = triangle.vertices().map (|i| hull.points()[i as usize]);
2257      let mut u = b - a;
2258      let mut v = c - a;
2259      let mut w = u.cross (v);
2260      v = w.cross (u);
2261      u = *u.normalize();
2262      v = *v.normalize();
2263      w = *w.normalize();
2264      let m = Matrix3::from_row_arrays (std::array::from_fn (|i| [u[i], v[i], w[i]]));
2265      let m_rot       = Rotation3::new_approx (m).unwrap();
2266      let rot_inv     = m_rot.inv();
2267      let center      = a;
2268      let rotated     = hull.points().iter().copied()
2269        .map (|p| rot_inv.rotate_around (p, center))
2270        .collect::<Vec <_>>();
2271      let aabb        = Aabb3::containing (rotated.as_slice()).unwrap();
2272      let aabb_volume = aabb.volume();
2273      if approx::abs_diff_eq!(*aabb_volume, S::zero()) {
2274        // hull contains no volume
2275        return None
2276      }
2277      let volume = Positive::new (*aabb_volume).unwrap();
2278      if volume < min_vol {
2279        min_vol = volume;
2280        min_obb = aabb.into();
2281        min_obb.orientation = m_rot.into();
2282        min_obb.center = m_rot.rotate_around (min_obb.center, center);
2283      }
2284    }
2285    Some (min_obb)
2286  }
2287  /// Construct an approximate minimum OBB containing the given convex hull of points
2288  /// oriented according to principal component analysis bases.
2289  ///
2290  /// Returns None if fewer than 4 points are given or if points span an empty set:
2291  /// ```
2292  /// # use math_utils::*;
2293  /// # use math_utils::geometry::*;
2294  /// let hull = Hull3::from_points (
2295  ///   &[[0.0, 1.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]].map (Point3::from)
2296  /// ).unwrap();
2297  /// assert!(Obb3::containing_approx_pca (&hull).is_none());
2298  ///
2299  /// let hull = Hull3::from_points (
2300  ///   &[[1.0, 0.0, 1.0], [0.0, 1.0, 0.0], [0.0, -1.0, 0.0], [-1.0,  0.0, -1.0]]
2301  ///     .map (Point3::from)
2302  /// ).unwrap();
2303  /// assert!(Obb3::containing_approx_pca (&hull).is_none());
2304  /// ```
2305  pub fn containing_approx_pca (hull : &Hull3 <S>) -> Option <Self> where
2306    S : Real + num::real::Real + num::NumCast + approx::RelativeEq <Epsilon=S>
2307      + MaybeSerDes
2308  {
2309    if hull.points().len() < 4 {
2310      return None
2311    }
2312    // coplanar check
2313    // TODO: should we store dimensionality information in the hull to avoid doing this
2314    // calculation ?
2315    let [p1, p2, p3] = &hull.points()[..3] else { unreachable!() };
2316    let mut coplanar = true;
2317    for p in &hull.points()[3..] {
2318      if !coplanar_3d (*p1, *p2, *p3, *p) {
2319        coplanar = false;
2320        break
2321      }
2322    }
2323    if coplanar {
2324      return None
2325    }
2326    // do PCA analysis of points
2327    let orientation = data::pca_3 (hull.points()).into();
2328    Some (Self::containing_points_with_orientation (hull.points(), orientation).unwrap())
2329  }
2330  #[inline]
2331  pub fn dimensions (self) -> Vector3 <Positive <S>> {
2332    self.extents * Positive::unchecked (S::two())
2333  }
2334  /// X dimension
2335  #[inline]
2336  pub fn width (self) -> Positive <S> {
2337    self.extents.x * Positive::unchecked (S::two())
2338  }
2339  /// Y dimension
2340  #[inline]
2341  pub fn depth (self) -> Positive <S> {
2342    self.extents.y * Positive::unchecked (S::two())
2343  }
2344  /// Z dimension
2345  #[inline]
2346  pub fn height (self) -> Positive <S> {
2347    self.extents.z * Positive::unchecked (S::two())
2348  }
2349  #[inline]
2350  pub fn volume (self) -> Positive <S> {
2351    self.width() * self.depth() * self.height()
2352  }
2353  #[inline]
2354  pub fn corners (self) -> [Point3 <S>; 8] where
2355    S : Real + num::real::Real + MaybeSerDes
2356  {
2357    let mut points = [Point3::origin(); 8];
2358    for i in 0..2 {
2359      for j in 0..2 {
2360        for k in 0..2 {
2361          let sign_x = if i % 2 == 0 { -S::one() } else { S::one() };
2362          let sign_y = if j % 2 == 0 { -S::one() } else { S::one() };
2363          let sign_z = if k % 2 == 0 { -S::one() } else { S::one() };
2364          points[i * 4 + j * 2 + k] = [
2365            *self.extents.x * sign_x,
2366            *self.extents.y * sign_y,
2367            *self.extents.z * sign_z
2368          ].into();
2369        }
2370      }
2371    }
2372    let rotation = Rotation3::from (self.orientation);
2373    points.map (|point| rotation.rotate (point) + self.center.0)
2374  }
2375
2376  pub fn aabb (self) -> Aabb3 <S> where S : Real + num::real::Real + MaybeSerDes {
2377    Aabb3::containing (&self.corners()).unwrap()
2378  }
2379
2380  /// Check if point is contained by the bounding box
2381  pub fn contains (self, point : Point3 <S>) -> bool where
2382    S : Real + num::real::Real + MaybeSerDes
2383  {
2384    use num::Inv;
2385    // re-center
2386    let p = point - self.center.0;
2387    // reverse rotation
2388    let p = Rotation3::from (self.orientation).inv().rotate (p);
2389    p.x().abs() < *self.extents.x &&
2390    p.y().abs() < *self.extents.y &&
2391    p.z().abs() < *self.extents.z
2392  }
2393
2394  /// Increase or decrease each extent by the given amount.
2395  ///
2396  /// Panics if any extent would become zero or negative:
2397  ///
2398  /// ```should_panic
2399  /// # use math_utils::*;
2400  /// # use math_utils::geometry::*;
2401  /// let x = Obb3::new (
2402  ///   Point3::origin(),
2403  ///   [0.5, 0.5, 0.5].map (Positive::noisy).into(),
2404  ///   Angles3::zero()
2405  /// );
2406  /// let y = x.dilate (-1.0); // panic!
2407  /// ```
2408  pub fn dilate (mut self, amount : S) -> Self {
2409    self.extents = self.extents.map (|e| e.map_noisy (|e| e + amount));
2410    self
2411  }
2412}
2413impl <S : Real> From <Aabb3 <S>> for Obb3 <S> {
2414  /// Panics if AABB has a zero dimension:
2415  ///
2416  /// ```should_panic
2417  /// # use math_utils::*;
2418  /// # use math_utils::geometry::*;
2419  /// # use math_utils::num::Zero;
2420  /// let x = Aabb3::with_minmax (
2421  ///   [0.0, 0.0, 0.0].into(),
2422  ///   [0.0, 1.0, 1.0].into()
2423  /// ).unwrap();
2424  /// let y = Obb3::from (x); // panic!
2425  /// ```
2426  fn from (aabb : Aabb3 <S>) -> Self {
2427    let center = aabb.center();
2428    let extents = aabb.extents().map (|d| Positive::noisy (*d));
2429    let orientation = Angles3::zero();
2430    Obb3::new (center, extents, orientation)
2431  }
2432}
2433impl <S> Primitive <S, Point3 <S>> for Obb3 <S> where
2434  S : Real + num::real::Real + MaybeSerDes
2435{
2436  fn translate (&mut self, displacement : Vector3 <S>) {
2437    self.center += displacement;
2438  }
2439  fn scale (&mut self, scale : Positive <S>) {
2440    self.extents *= scale;
2441  }
2442  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2443    use num::Inv;
2444    let rotation = Rotation3::from (self.orientation);
2445    let aabb = Aabb3::with_minmax_unchecked (
2446      self.center - self.extents.map (|e| *e),
2447      self.center + self.extents.map (|e| *e));
2448    let aabb_direction = rotation.inv() * direction;
2449    let (aabb_support, _) = aabb.support (aabb_direction);
2450    let out = rotation.rotate_around (aabb_support, self.center);
2451    (out, out.0.dot (*direction))
2452  }
2453}
2454
2455impl_numcast_fields!(Capsule3, center, half_height, radius);
2456impl <S : OrderedRing> Capsule3 <S> {
2457  pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2458    use shape::Aabb;
2459    let shape_aabb = shape::Capsule {
2460      radius:      self.radius,
2461      half_height: self.half_height
2462    }.aabb();
2463    let center_vec = self.center.0;
2464    let min        = shape_aabb.min() + center_vec;
2465    let max        = shape_aabb.max() + center_vec;
2466    Aabb3::with_minmax_unchecked (min, max)
2467  }
2468  /// Return the (upper sphere, cylinder, lower sphere) making up this capsule
2469  pub fn decompose (self) -> (Sphere3 <S>, Option <Cylinder3 <S>>, Sphere3 <S>) {
2470    let cylinder = if *self.half_height > S::zero() {
2471      Some (Cylinder3 {
2472        center:      self.center,
2473        radius:      self.radius,
2474        half_height: Positive::unchecked (*self.half_height)
2475      })
2476    } else {
2477      None
2478    };
2479    let upper_sphere = Sphere3 {
2480      center: self.center + (Vector3::unit_z() * *self.half_height),
2481      radius: self.radius
2482    };
2483    let lower_sphere = Sphere3 {
2484      center: self.center - (Vector3::unit_z() * *self.half_height),
2485      radius: self.radius
2486    };
2487    (upper_sphere, cylinder, lower_sphere)
2488  }
2489}
2490impl <S> Primitive <S, Point3 <S>> for Capsule3 <S> where
2491  S : Real + num::real::Real + MaybeSerDes
2492{
2493  fn translate (&mut self, displacement : Vector3 <S>) {
2494    self.center += displacement;
2495  }
2496  fn scale (&mut self, scale : Positive <S>) {
2497    self.half_height *= scale;
2498    self.radius *= scale;
2499  }
2500  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2501    let dir_unit = direction.normalized();
2502    let out = self.center + if direction.z > S::zero() {
2503      Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2504    } else if direction.z < S::zero() {
2505      -Vector3::unit_z() * *self.half_height + dir_unit * *self.radius
2506    } else {
2507      dir_unit * *self.radius
2508    };
2509    (out, out.0.dot (*direction))
2510  }
2511}
2512
2513impl_numcast_fields!(Cylinder3, center, half_height, radius);
2514impl <S : OrderedRing> Cylinder3 <S> {
2515  pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2516    use shape::Aabb;
2517    let shape_aabb = shape::Cylinder::unchecked (*self.radius, *self.half_height).aabb();
2518    let center_vec = self.center.0;
2519    let min        = shape_aabb.min() + center_vec;
2520    let max        = shape_aabb.max() + center_vec;
2521    Aabb3::with_minmax_unchecked (min, max)
2522  }
2523}
2524impl <S> Primitive <S, Point3 <S>> for Cylinder3 <S> where
2525  S : Real + num::real::Real + MaybeSerDes
2526{
2527  fn translate (&mut self, displacement : Vector3 <S>) {
2528    self.center += displacement;
2529  }
2530  fn scale (&mut self, scale : Positive <S>) {
2531    self.half_height *= scale;
2532    self.radius *= scale;
2533  }
2534  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2535    let out = self.center + if *direction == Vector3::unit_z() {
2536      Vector3::unit_z() * *self.half_height
2537    } else if *direction == -Vector3::unit_z() {
2538      -Vector3::unit_z() * *self.half_height
2539    } else if direction.z == S::zero() {
2540      direction.normalized() * *self.radius
2541    } else if direction.z > S::zero() {
2542      Vector3::unit_z() * *self.half_height
2543        + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2544    } else {
2545      debug_assert!(direction.z < S::zero());
2546      -Vector3::unit_z() * *self.half_height
2547        + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius
2548    };
2549    (out, out.0.dot (*direction))
2550  }
2551}
2552
2553impl_numcast_fields!(Cone3, center, half_height, radius);
2554impl <S : OrderedRing> Cone3 <S> {
2555  pub fn height (self) -> Positive <S> {
2556    self.half_height * Positive::unchecked (S::two())
2557  }
2558  pub fn aabb3 (self) -> Aabb3 <S> where S : Real {
2559    use shape::Aabb;
2560    let shape_aabb = shape::Cone {
2561      radius:      self.radius,
2562      half_height: self.half_height
2563    }.aabb();
2564    let center_vec = self.center.0;
2565    let min        = shape_aabb.min() + center_vec;
2566    let max        = shape_aabb.max() + center_vec;
2567    Aabb3::with_minmax_unchecked (min, max)
2568  }
2569}
2570impl <S> Primitive <S, Point3 <S>> for Cone3 <S> where
2571  S : Real + num::real::Real + approx::RelativeEq + MaybeSerDes
2572{
2573  fn translate (&mut self, displacement : Vector3 <S>) {
2574    self.center += displacement;
2575  }
2576  fn scale (&mut self, scale : Positive <S>) {
2577    self.half_height *= scale;
2578    self.radius *= scale;
2579  }
2580  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2581    let top = || Vector3::unit_z() * *self.half_height;
2582    let bottom = || -top();
2583    let edge = || bottom()
2584      + vector3 (direction.x, direction.y, S::zero()).normalized() * *self.radius;
2585    let out = self.center + if *direction == Vector3::unit_z() {
2586      top()
2587    } else if *direction == -Vector3::unit_z() {
2588      bottom()
2589    } else if direction.z <= S::zero() {
2590      edge()
2591    } else {
2592      // TODO: possibly cheaper with dot products ?
2593      let angle_dir =
2594        Trig::atan2 (direction.z, Sqrt::sqrt (S::one() - direction.z.squared()));
2595      debug_assert!(angle_dir > S::zero());
2596      let angle_cone = Trig::atan2 (-*self.height(), *self.radius);
2597      debug_assert!(angle_cone < S::zero());
2598      let pi_2 = S::pi() / S::two();
2599      let angle_total = angle_dir - angle_cone;
2600      if approx::relative_eq!(angle_total, pi_2) {
2601        (top() + edge()) * S::half()
2602      } else if angle_total > pi_2 {
2603        top()
2604      } else {
2605        debug_assert!(angle_total < pi_2);
2606        edge()
2607      }
2608    };
2609    (out, out.0.dot (*direction))
2610  }
2611}
2612
2613impl_numcast_fields!(Line2, base, direction);
2614impl <S : OrderedRing> Line2 <S> {
2615  /// Construct a new 2D line
2616  #[inline]
2617  pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2618    Line2 { base, direction }
2619  }
2620  #[inline]
2621  pub fn point (self, t : S) -> Point2 <S> {
2622    self.base + *self.direction * t
2623  }
2624  #[inline]
2625  pub fn affine_line (self) -> frame::Line2 <S> {
2626    frame::Line2 {
2627      origin: self.base,
2628      basis:  self.direction.into()
2629    }
2630  }
2631  #[inline]
2632  pub fn nearest_point (self, point : Point2 <S>) -> Line2Point <S> {
2633    project_2d_point_on_line (point, self)
2634  }
2635  #[inline]
2636  pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2637    -> Option <(Line2Point <S>, Line2Point <S>)>
2638  {
2639    intersect::continuous_line2_aabb2 (self, aabb)
2640  }
2641  #[inline]
2642  pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2643    -> Option <(Line2Point <S>, Line2Point <S>)>
2644  where S : Field + Sqrt {
2645    intersect::continuous_line2_sphere2 (self, sphere)
2646  }
2647}
2648impl <S : Real> Default for Line2 <S> {
2649  fn default() -> Self {
2650    Line2 {
2651      base:      Point2::origin(),
2652      direction: Unit2::axis_y()
2653    }
2654  }
2655}
2656impl <S> Primitive <S, Point2 <S>> for Line2 <S> where
2657  S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2658{
2659  fn translate (&mut self, displacement : Vector2 <S>) {
2660    self.base += displacement;
2661  }
2662  #[expect(unused_variables)]
2663  fn scale (&mut self, scale : Positive <S>) {
2664    /* no-op */
2665  }
2666  fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2667    let dot = self.direction.dot (*direction);
2668    let out = if approx::abs_diff_eq!(dot, S::zero()) {
2669      self.base
2670    } else if dot > S::zero() {
2671      Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2672        S::zero()
2673      } else {
2674        x * S::infinity()
2675      }))
2676    } else {
2677      debug_assert!(dot < S::zero());
2678      Point2 ((-self.direction).sigvec().map (|x| if x == S::zero() {
2679        S::zero()
2680      } else {
2681        x * S::infinity()
2682      }))
2683    };
2684    (out, out.0.dot (*direction))
2685  }
2686}
2687impl <S> From <Ray2 <S>> for Line2 <S> {
2688  fn from (Ray2 { base, direction } : Ray2 <S>) -> Line2 <S> {
2689    Line2 { base, direction }
2690  }
2691}
2692
2693impl_numcast_fields!(Ray2, base, direction);
2694impl <S : OrderedRing> Ray2 <S> {
2695  /// Construct a new 2D ray
2696  #[inline]
2697  pub const fn new (base : Point2 <S>, direction : Unit2 <S>) -> Self {
2698    Ray2 { base, direction }
2699  }
2700  #[inline]
2701  pub fn point (self, t : S) -> Point2 <S> {
2702    self.base + *self.direction * t
2703  }
2704  #[inline]
2705  pub fn affine_line (self) -> frame::Line2 <S> {
2706    frame::Line2 {
2707      origin: self.base,
2708      basis:  self.direction.into()
2709    }
2710  }
2711  pub fn nearest_point (self, point : Point2 <S>) -> Ray2Point <S> {
2712    use num::Zero;
2713    let (t, point) = Line2::from (self).nearest_point (point);
2714    if t < S::zero() {
2715      (NonNegative::zero(), self.base)
2716    } else {
2717      (NonNegative::unchecked (t), point)
2718    }
2719  }
2720  #[inline]
2721  pub fn intersect_aabb (self, aabb : Aabb2 <S>)
2722    -> Option <(Ray2Point <S>, Ray2Point <S>)>
2723  {
2724    intersect::continuous_ray2_aabb2 (self, aabb)
2725  }
2726  #[inline]
2727  pub fn intersect_sphere (self, sphere : Sphere2 <S>)
2728    -> Option <(Ray2Point <S>, Ray2Point <S>)>
2729  {
2730    intersect::continuous_ray2_sphere2 (self, sphere)
2731  }
2732}
2733impl <S : Real> Default for Ray2 <S> {
2734  fn default() -> Self {
2735    Ray2 {
2736      base:      Point2::origin(),
2737      direction: Unit2::axis_y()
2738    }
2739  }
2740}
2741impl <S> Primitive <S, Point2 <S>> for Ray2 <S> where
2742  S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2743{
2744  fn translate (&mut self, displacement : Vector2 <S>) {
2745    self.base += displacement;
2746  }
2747  #[expect(unused_variables)]
2748  fn scale (&mut self, scale : Positive <S>) {
2749    /* no-op */
2750  }
2751  fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
2752    let dot = self.direction.dot (*direction);
2753    let out = if dot < S::default_epsilon() {
2754      self.base
2755    } else {
2756      Point2 (self.direction.sigvec().map (|x| if x == S::zero() {
2757        S::zero()
2758      } else {
2759        x * S::infinity()
2760      }))
2761    };
2762    (out, out.0.dot (*direction))
2763  }
2764}
2765impl <S> From <Line2 <S>> for Ray2 <S> {
2766  fn from (Line2 { base, direction } : Line2 <S>) -> Ray2 <S> {
2767    Ray2 { base, direction }
2768  }
2769}
2770
2771impl_numcast_fields!(Line3, base, direction);
2772impl <S : OrderedRing> Line3 <S> {
2773  /// Consructs a new 3D line with given base point and direction vector
2774  #[inline]
2775  pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2776    Line3 { base, direction }
2777  }
2778  #[inline]
2779  pub fn point (self, t : S) -> Point3 <S> {
2780    self.base + *self.direction * t
2781  }
2782  #[inline]
2783  pub fn affine_line (self) -> frame::Line3 <S> {
2784    frame::Line3 {
2785      origin: self.base,
2786      basis:  self.direction.into()
2787    }
2788  }
2789  #[inline]
2790  pub fn nearest_point (self, point : Point3 <S>) -> Line3Point <S> {
2791    project_3d_point_on_line (point, self)
2792  }
2793  #[inline]
2794  pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Line3Point <S>> where
2795    S : Field + approx::RelativeEq
2796  {
2797    intersect::continuous_line3_plane3 (self, plane)
2798  }
2799  #[inline]
2800  pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2801    -> Option <(Line3Point <S>, Line3Point <S>)>
2802  where S : num::Float + approx::RelativeEq <Epsilon=S> {
2803    intersect::continuous_line3_aabb3 (self, aabb)
2804  }
2805  #[inline]
2806  pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2807    -> Option <(Line3Point <S>, Line3Point <S>)>
2808  where S : Field + Sqrt {
2809    intersect::continuous_line3_sphere3 (self, sphere)
2810  }
2811  #[inline]
2812  pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2813    -> Option <Line3Point <S>>
2814  where S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2815    intersect::continuous_line3_triangle3 (self, triangle)
2816  }
2817}
2818impl <S : Real> Default for Line3 <S> {
2819  fn default() -> Self {
2820    Line3 {
2821      base:      Point3::origin(),
2822      direction: Unit3::axis_z()
2823    }
2824  }
2825}
2826impl <S> Primitive <S, Point3 <S>> for Line3 <S> where
2827  S : OrderedField + num::float::FloatCore + approx::AbsDiffEq
2828{
2829  fn translate (&mut self, displacement : Vector3 <S>) {
2830    self.base += displacement;
2831  }
2832  #[expect(unused_variables)]
2833  fn scale (&mut self, scale : Positive <S>) {
2834    /* no-op */
2835  }
2836  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2837    let dot = self.direction.dot (*direction);
2838    let out = if approx::abs_diff_eq!(dot, S::zero()) {
2839      self.base
2840    } else if dot > S::zero() {
2841      Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2842        S::zero()
2843      } else {
2844        x * S::infinity()
2845      }))
2846    } else {
2847      debug_assert!(dot < S::zero());
2848      Point3 (-self.direction.sigvec().map (|x| if x == S::zero() {
2849        S::zero()
2850      } else {
2851        x * S::infinity()
2852      }))
2853    };
2854    (out, out.0.dot (*direction))
2855  }
2856}
2857impl <S> From <Ray3 <S>> for Line3 <S> {
2858  fn from (Ray3 { base, direction } : Ray3 <S>) -> Line3 <S> {
2859    Line3 { base, direction }
2860  }
2861}
2862
2863impl_numcast_fields!(Ray3, base, direction);
2864impl <S : OrderedRing> Ray3 <S> {
2865  /// Consructs a new 3D ray with given base point and direction vector
2866  #[inline]
2867  pub const fn new (base : Point3 <S>, direction : Unit3 <S>) -> Self {
2868    Ray3 { base, direction }
2869  }
2870  #[inline]
2871  pub fn point (self, t : S) -> Point3 <S> {
2872    self.base + *self.direction * t
2873  }
2874  #[inline]
2875  pub fn affine_line (self) -> frame::Line3 <S> {
2876    frame::Line3 {
2877      origin: self.base,
2878      basis:  self.direction.into()
2879    }
2880  }
2881  pub fn nearest_point (self, point : Point3 <S>) -> Ray3Point <S> {
2882    use num::Zero;
2883    let (t, point) = Line3::from (self).nearest_point (point);
2884    if t < S::zero() {
2885      (NonNegative::zero(), self.base)
2886    } else {
2887      (NonNegative::unchecked (t), point)
2888    }
2889  }
2890  #[inline]
2891  pub fn intersect_plane (self, plane : Plane3 <S>) -> Option <Ray3Point <S>> where
2892    S : approx::RelativeEq
2893  {
2894    intersect::continuous_ray3_plane3 (self, plane)
2895  }
2896  #[inline]
2897  pub fn intersect_aabb (self, aabb : Aabb3 <S>)
2898    -> Option <(Ray3Point <S>, Ray3Point <S>)>
2899  where S : num::Float + approx::RelativeEq <Epsilon=S> {
2900    intersect::continuous_ray3_aabb3 (self, aabb)
2901  }
2902  #[inline]
2903  pub fn intersect_sphere (self, sphere : Sphere3 <S>)
2904    -> Option <(Ray3Point <S>, Ray3Point <S>)>
2905  {
2906    intersect::continuous_ray3_sphere3 (self, sphere)
2907  }
2908  #[inline]
2909  pub fn intersect_triangle (self, triangle : Triangle3 <S>)
2910    -> Option <Ray3Point <S>>
2911  where S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
2912    intersect::continuous_ray3_triangle3 (self, triangle)
2913  }
2914}
2915impl <S : Real> Default for Ray3 <S> {
2916  fn default() -> Self {
2917    Ray3 {
2918      base:      Point3::origin(),
2919      direction: Unit3::axis_z()
2920    }
2921  }
2922}
2923impl <S> Primitive <S, Point3 <S>> for Ray3 <S> where
2924  S : OrderedField + num::float::FloatCore + approx::AbsDiffEq <Epsilon = S>
2925{
2926  fn translate (&mut self, displacement : Vector3 <S>) {
2927    self.base += displacement;
2928  }
2929  #[expect(unused_variables)]
2930  fn scale (&mut self, scale : Positive <S>) {
2931    /* no-op */
2932  }
2933  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2934    let dot = self.direction.dot (*direction);
2935    let out = if dot < S::default_epsilon() {
2936      self.base
2937    } else {
2938      Point3 (self.direction.sigvec().map (|x| if x == S::zero() {
2939        S::zero()
2940      } else {
2941        x * S::infinity()
2942      }))
2943    };
2944    (out, out.0.dot (*direction))
2945  }
2946}
2947impl <S> From <Line3 <S>> for Ray3 <S> {
2948  fn from (Line3 { base, direction } : Line3 <S>) -> Ray3 <S> {
2949    Ray3 { base, direction }
2950  }
2951}
2952
2953impl_numcast_fields!(Plane3, base, normal);
2954impl <S> Plane3 <S> {
2955  /// Consructs a new 3D plane with given base point and normal vector
2956  #[inline]
2957  pub const fn new (base : Point3 <S>, normal : Unit3 <S>) -> Self {
2958    Plane3 { base, normal }
2959  }
2960}
2961impl <S : Real> Default for Plane3 <S> {
2962  fn default() -> Self {
2963    Plane3 {
2964      base:   Point3::origin(),
2965      normal: Unit3::axis_z()
2966    }
2967  }
2968}
2969impl <S> Primitive <S, Point3 <S>> for Plane3 <S> where
2970  S : Real + num::float::FloatCore + approx::RelativeEq
2971{
2972  fn translate (&mut self, displacement : Vector3 <S>) {
2973    self.base += displacement;
2974  }
2975  #[expect(unused_variables)]
2976  fn scale (&mut self, scale : Positive <S>) {
2977    /* no-op */
2978  }
2979  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
2980    let dot = self.normal.dot (*direction);
2981    let out = if approx::relative_eq!(dot, S::one()) {
2982      self.base
2983    } else {
2984      let projected =
2985        project_3d_point_on_plane (Point3 (self.base.0 + *direction), *self).0;
2986      Point3 (projected.sigvec().map (|x| if x == S::zero() {
2987        S::zero()
2988      } else {
2989        x * S::infinity()
2990      }))
2991    };
2992    (out, out.0.dot (*direction))
2993  }
2994}
2995
2996impl_numcast_fields!(Sphere2, center, radius);
2997impl <S : OrderedRing> Sphere2 <S> {
2998  /// Unit circle
2999  #[inline]
3000  pub fn unit() -> Self {
3001    Sphere2 {
3002      center: Point2::origin(),
3003      radius: num::One::one()
3004    }
3005  }
3006  /// Discrete intersection test with another sphere
3007  #[inline]
3008  pub fn intersects (self, other : Self) -> bool {
3009    intersect::discrete_sphere2_sphere2 (self, other)
3010  }
3011}
3012impl <S> Primitive <S, Point2 <S>> for Sphere2 <S> where
3013  S : Real + num::real::Real + MaybeSerDes
3014{
3015  fn translate (&mut self, displacement : Vector2 <S>) {
3016    self.center += displacement;
3017  }
3018  fn scale (&mut self, scale : Positive <S>) {
3019    self.radius *= scale;
3020  }
3021  fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
3022    let out = self.center + direction.normalized() * *self.radius;
3023    (out, out.0.dot (*direction))
3024  }
3025}
3026
3027impl_numcast_fields!(Sphere3, center, radius);
3028impl <S : OrderedRing> Sphere3 <S> {
3029  /// Unit sphere
3030  #[inline]
3031  pub fn unit() -> Self where S : Field {
3032    Sphere3 {
3033      center: Point3::origin(),
3034      radius: num::One::one()
3035    }
3036  }
3037  /// Discrete intersection test with another sphere
3038  #[inline]
3039  pub fn intersects (self, other : Self) -> bool {
3040    intersect::discrete_sphere3_sphere3 (self, other)
3041  }
3042}
3043impl <S> Primitive <S, Point3 <S>> for Sphere3 <S> where
3044  S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes
3045{
3046  fn translate (&mut self, displacement : Vector3 <S>) {
3047    self.center += displacement;
3048  }
3049  fn scale (&mut self, scale : Positive <S>) {
3050    self.radius *= scale;
3051  }
3052  fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
3053    let out = self.center + direction.normalized() * *self.radius;
3054    (out, out.0.dot (*direction))
3055  }
3056}
3057
3058////////////////////////////////////////////////////////////////////////////////////////
3059//  tests                                                                             //
3060////////////////////////////////////////////////////////////////////////////////////////
3061
3062#[cfg(test)]
3063mod tests {
3064  use approx::{assert_relative_eq, assert_ulps_eq};
3065  use rand;
3066  use rand_distr;
3067  use rand_xorshift;
3068  use sorted_vec::partial::SortedSet;
3069  use super::*;
3070
3071  #[test]
3072  fn colinear_2() {
3073    use rand::{RngExt, SeedableRng};
3074    use rand_xorshift::XorShiftRng;
3075    use rand_distr::Distribution;
3076    macro test ($t:ty) {
3077      let mut rng = XorShiftRng::seed_from_u64 (0);
3078      let normal  = rand_distr::StandardNormal;
3079      let cauchy  = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3080      let randn   = |rng : &mut XorShiftRng| normal.sample (rng);
3081      let randf   = |rng : &mut XorShiftRng| cauchy.sample (rng);
3082      // colinear
3083      for _ in 0..100000 {
3084        let dir  = Unit2::<$t>::normalize ([randn (&mut rng), randn (&mut rng)].into());
3085        let base = point2 (randf (&mut rng), randf (&mut rng));
3086        let line = Line2::new (base, dir);
3087        let p1   = line.point (randf (&mut rng));
3088        let p2   = line.point (randf (&mut rng));
3089        let p3   = line.point (randf (&mut rng));
3090        assert!(colinear_2d (p1, p2, p3));
3091      }
3092      // not colinear
3093      for _ in 0..100000 {
3094        let p1        = point2 (randf (&mut rng), randf (&mut rng));
3095        let v         = p1.0;
3096        let len       = 0.1 + randf (&mut rng).abs();
3097        let angle     = Deg (rng.random_range (-180.0..180.0)).into();
3098        let rotation  = Rotation2::from_angle (angle);
3099        let p2        = p1 + rotation * (v.normalized() * len);
3100        let v         = p2 - p1;
3101        let len       = 0.1 + randf (&mut rng).abs();
3102        let mut angle = rng.random_range (5.0..175.0);
3103        if rng.random_bool (0.5) {
3104          angle = -angle;
3105        }
3106        let rotation = Rotation2::from_angle (Deg (angle).into());
3107        let p3       = p2 + rotation * v * len;
3108        assert!(!colinear_2d (p1, p2, p3));
3109      }
3110    }
3111    test!(f32);
3112    test!(f64);
3113  }
3114
3115  #[test]
3116  fn colinear_3() {
3117    use rand::{RngExt, SeedableRng};
3118    use rand_xorshift::XorShiftRng;
3119    use rand_distr::Distribution;
3120    macro test ($t:ty) {
3121      let mut rng = XorShiftRng::seed_from_u64 (0);
3122      let normal  = rand_distr::StandardNormal;
3123      let cauchy  = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3124      let randf   = |rng : &mut XorShiftRng| cauchy.sample (rng);
3125      let randn   = |rng : &mut XorShiftRng| normal.sample (rng);
3126      // colinear
3127      for _ in 0..50000 {
3128        let dir = Unit3::<$t>::normalize (
3129          [randn (&mut rng), randn (&mut rng), randn (&mut rng)].into());
3130        let base = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3131        let line = Line3::new (base, dir);
3132        let p1   = line.point (randf (&mut rng));
3133        let p2   = line.point (randf (&mut rng));
3134        let p3   = line.point (randf (&mut rng));
3135        assert!(colinear_3d (p1, p2, p3));
3136      }
3137      // not colinear
3138      for _ in 0..50000 {
3139        let p1       = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3140        let v        = p1.0;
3141        let len      = 0.1 + randf (&mut rng).abs();
3142        let axis     = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3143        let angle    = Deg (rng.random_range (-180.0..180.0)).into();
3144        let rotation = Rotation3::from_axis_angle (axis, angle);
3145        let p2       = p1 + rotation * (v.normalized() * len);
3146        let v        = p2 - p1;
3147        let len      = 0.1 + randf (&mut rng).abs();
3148        let axis     = loop {
3149          let axis = vector3 (randn (&mut rng), randn (&mut rng), randn (&mut rng));
3150          if axis.normalized().cross (v.normalized()).magnitude() > 0.1 {
3151            break axis
3152          }
3153        };
3154        let mut angle = rng.random_range (5.0..175.0);
3155        if rng.random_bool (0.5) {
3156          angle = -angle;
3157        }
3158        let rotation = Rotation3::from_axis_angle (axis, Deg (angle).into());
3159        let p3       = p2 + rotation * v * len;
3160        assert!(!colinear_3d (p1, p2, p3));
3161      }
3162    }
3163    test!(f32);
3164    test!(f64);
3165  }
3166
3167  #[test]
3168  fn coplanar_3() {
3169    use rand::SeedableRng;
3170    use rand_xorshift::XorShiftRng;
3171    use rand_distr::Distribution;
3172    macro test ($t:ty) {
3173      let mut rng = XorShiftRng::seed_from_u64 (0);
3174      let cauchy  = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
3175      let randf   = |rng : &mut XorShiftRng| cauchy.sample (rng);
3176      // coplanar
3177      for _ in 0..50000 {
3178        let p1   = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3179        let p2   = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3180        let p3   = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3181        let s    = randf (&mut rng);
3182        let t    = randf (&mut rng);
3183        let p4   = p1 + (p2 - p1) * s + (p3 - p1) * t;
3184        assert!(coplanar_3d::<$t> (p1, p2, p3, p4));
3185      }
3186      // not coplanar
3187      for _ in 0..50000 {
3188        let p1    = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3189        let p2    = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3190        let p3    = point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3191        let s     = randf (&mut rng);
3192        let t     = randf (&mut rng);
3193        let e1    = p2 - p1;
3194        let e2    = p3 - p1;
3195        let n     = e1.cross (e2).normalized();
3196        let rn    = randf (&mut rng);
3197        let pnew  = p1 + e1 * s + e2 * t;
3198        let n_len = (0.3 * (pnew - p1).magnitude()).mul_add (rn.signum(), rn);
3199        //let n_len = rn + 0.3 * (pnew - p1).magnitude() * rn.signum();
3200        let p4    = pnew + n * n_len;
3201        assert!(!coplanar_3d::<$t> (p1, p2, p3, p4));
3202      }
3203    }
3204    test!(f32);
3205    test!(f64);
3206  }
3207
3208  #[test]
3209  fn triangle_area_squared() {
3210    assert_relative_eq!(
3211      81.0,
3212      *triangle_3d_area_squared (
3213        [-2.0, -1.0,  0.0].into(),
3214        [ 1.0, -1.0,  0.0].into(),
3215        [ 1.0,  5.0,  0.0].into())
3216    );
3217    assert_relative_eq!(
3218      20.25,
3219      *triangle_3d_area_squared (
3220        [-2.0, -1.0,  0.0].into(),
3221        [ 1.0, -1.0,  0.0].into(),
3222        [ 1.0,  2.0,  0.0].into())
3223    );
3224    assert_relative_eq!(
3225      20.25,
3226      *triangle_3d_area_squared (
3227        [ 1.0, -1.0,  0.0].into(),
3228        [-2.0, -1.0,  0.0].into(),
3229        [ 1.0,  2.0,  0.0].into())
3230    );
3231    assert_relative_eq!(
3232      20.25,
3233      *triangle_3d_area_squared (
3234        [ 1.0, -1.0,  0.0].into(),
3235        [ 1.0,  2.0,  0.0].into(),
3236        [-2.0, -1.0,  0.0].into())
3237    );
3238  }
3239
3240  #[test]
3241  fn project_2d_point_on_line() {
3242    use super::project_2d_point_on_line;
3243    use Unit2;
3244    let point : Point2 <f64> = [2.0, 2.0].into();
3245    let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_x());
3246    assert_eq!(project_2d_point_on_line (point, line).1, [2.0, 0.0].into());
3247    let line = Line2::<f64>::new ([0.0, 0.0].into(), Unit2::axis_y());
3248    assert_eq!(project_2d_point_on_line (point, line).1, [0.0, 2.0].into());
3249    let point : Point2 <f64> = [0.0, 1.0].into();
3250    let line = Line2::<f64>::new (
3251      [0.0, -1.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3252    assert_relative_eq!(
3253      project_2d_point_on_line (point, line).1, [1.0, 0.0].into());
3254    // the answer should be the same for lines with equivalent definitions
3255    let point : Point2 <f64> = [1.0, 3.0].into();
3256    let line_a = Line2::<f64>::new (
3257      [0.0, -1.0].into(), Unit2::normalize ([2.0, 1.0].into()));
3258    let line_b = Line2::<f64>::new (
3259      [2.0, 0.0].into(),  Unit2::normalize ([2.0, 1.0].into()));
3260    assert_relative_eq!(
3261      project_2d_point_on_line (point, line_a).1,
3262      project_2d_point_on_line (point, line_b).1);
3263    let line_a = Line2::<f64>::new (
3264      [0.0, 0.0].into(),   Unit2::normalize ([1.0, 1.0].into()));
3265    let line_b = Line2::<f64>::new (
3266      [-2.0, -2.0].into(), Unit2::normalize ([1.0, 1.0].into()));
3267    assert_ulps_eq!(
3268      project_2d_point_on_line (point, line_a).1,
3269      project_2d_point_on_line (point, line_b).1
3270    );
3271    assert_relative_eq!(
3272      project_2d_point_on_line (point, line_a).1,
3273      [2.0, 2.0].into());
3274  }
3275
3276  #[test]
3277  fn project_3d_point_on_line() {
3278    use Unit3;
3279    use super::project_3d_point_on_line;
3280    // all the tests from 2d projection with 0.0 for the Z component
3281    let point : Point3 <f64> = [2.0, 2.0, 0.0].into();
3282    let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
3283    assert_eq!(project_3d_point_on_line (point, line).1, [2.0, 0.0, 0.0].into());
3284    let line = Line3::<f64>::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
3285    assert_eq!(project_3d_point_on_line (point, line).1, [0.0, 2.0, 0.0].into());
3286    let point : Point3 <f64> = [0.0, 1.0, 0.0].into();
3287    let line = Line3::<f64>::new (
3288      [0.0, -1.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3289    assert_relative_eq!(
3290      project_3d_point_on_line (point, line).1, [1.0, 0.0, 0.0].into());
3291    // the answer should be the same for lines with equivalent definitions
3292    let point : Point3 <f64> = [1.0, 3.0, 0.0].into();
3293    let line_a = Line3::<f64>::new (
3294      [0.0, -1.0, 0.0].into(), Unit3::normalize ([2.0, 1.0, 0.0].into()));
3295    let line_b = Line3::<f64>::new (
3296      [2.0, 0.0, 0.0].into(),  Unit3::normalize ([2.0, 1.0, 0.0].into()));
3297    assert_relative_eq!(
3298      project_3d_point_on_line (point, line_a).1,
3299      project_3d_point_on_line (point, line_b).1);
3300    let line_a = Line3::<f64>::new (
3301      [0.0, 0.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3302    let line_b = Line3::<f64>::new (
3303      [2.0, 2.0, 0.0].into(), Unit3::normalize ([1.0, 1.0, 0.0].into()));
3304    assert_relative_eq!(
3305      project_3d_point_on_line (point, line_a).1,
3306      project_3d_point_on_line (point, line_b).1);
3307    assert_relative_eq!(
3308      project_3d_point_on_line (point, line_a).1,
3309      [2.0, 2.0, 0.0].into());
3310    // more tests
3311    let point : Point3 <f64> = [0.0, 0.0, 2.0].into();
3312    let line_a = Line3::<f64>::new (
3313      [-4.0, -4.0, -4.0].into(), Unit3::normalize ([1.0, 1.0, 1.0].into()));
3314    let line_b = Line3::<f64>::new (
3315      [4.0, 4.0, 4.0].into(),    Unit3::normalize ([1.0, 1.0, 1.0].into()));
3316    assert_relative_eq!(
3317      project_3d_point_on_line (point, line_a).1,
3318      project_3d_point_on_line (point, line_b).1,
3319      epsilon = 0.000_000_000_000_01
3320    );
3321    assert_relative_eq!(
3322      project_3d_point_on_line (point, line_a).1,
3323      [2.0/3.0, 2.0/3.0, 2.0/3.0].into(),
3324      epsilon = 0.000_000_000_000_01
3325    );
3326  }
3327
3328  #[test]
3329  fn smallest_rect() {
3330    use super::smallest_rect;
3331    // points are in counter-clockwise order
3332    let points : Vec <Point2 <f32>> = [
3333      [-3.0, -3.0],
3334      [ 3.0, -3.0],
3335      [ 3.0,  3.0],
3336      [ 0.0,  6.0],
3337      [-3.0,  3.0]
3338    ].map (Point2::from).into_iter().collect();
3339    let hull = Hull2::from_points (points.as_slice()).unwrap();
3340    assert_eq!(hull.points(), points);
3341    let rect = smallest_rect (0, 1, &hull);
3342    assert_eq!(rect.area, 54.0);
3343    // points are in counter-clockwise order
3344    let points : Vec <Point2 <f32>> = [
3345      [-1.0, -4.0],
3346      [ 2.0,  2.0],
3347      [-4.0, -1.0]
3348    ].map (Point2::from).into_iter().collect();
3349    let hull = Hull2::from_points (points.as_slice()).unwrap();
3350    assert_eq!(hull.points(), points);
3351    let rect = smallest_rect (0, 1, &hull);
3352    assert_eq!(rect.area, 27.0);
3353  }
3354
3355  #[test]
3356  fn capsule3() {
3357    use num::One;
3358    let capsule : Capsule3 <f32> = Capsule3 {
3359      center:      Point3::origin(),
3360      radius:      Positive::noisy (2.0),
3361      half_height: Positive::one()
3362    };
3363    let support = capsule.support (Unit3::axis_z().into()).0;
3364    assert_eq!(support, point3 (0.0, 0.0, 3.0));
3365    let support = capsule.support (Unit3::axis_x().into()).0;
3366    assert_eq!(support, point3 (2.0, 0.0, 0.0));
3367    let support = capsule.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3368    assert_eq!(support,
3369      point3 (0.0, 0.0, 1.0) + *Unit3::normalize (vector3 (1.0, 0.0, 1.0)) * 2.0);
3370  }
3371
3372  #[test]
3373  fn cylinder3() {
3374    use num::One;
3375    let cylinder : Cylinder3 <f32> = Cylinder3 {
3376      center:      Point3::origin(),
3377      radius:      Positive::noisy (2.0),
3378      half_height: Positive::one()
3379    };
3380    let support = cylinder.support (Unit3::axis_z().into()).0;
3381    assert_eq!(support, point3 (0.0, 0.0, 1.0));
3382    let support = cylinder.support (Unit3::axis_x().into()).0;
3383    assert_eq!(support, point3 (2.0, 0.0, 0.0));
3384    let support = cylinder.support (NonZero3::noisy (vector3 (1.0, 0.0, 1.0))).0;
3385    assert_eq!(support, point3 (2.0, 0.0, 1.0));
3386  }
3387
3388  #[test]
3389  fn obb2() {
3390    let points : Vec <Point2 <f32>> = [
3391      [-1.0, -4.0],
3392      [ 2.0,  2.0],
3393      [-4.0, -1.0]
3394    ].map (Point2::from).into_iter().collect();
3395    let obb = Obb2::containing_points (&points).unwrap();
3396    let corners = obb.corners();
3397    assert_eq!(obb.center, [-0.25, -0.25].into());
3398    approx::assert_relative_eq!(point2 (-4.0, -1.0), corners[0], max_relative = 0.00001);
3399    approx::assert_relative_eq!(point2 ( 0.5,  3.5), corners[1], max_relative = 0.00001);
3400    approx::assert_relative_eq!(point2 (-1.0, -4.0), corners[2], max_relative = 0.00001);
3401    approx::assert_relative_eq!(point2 ( 3.5,  0.5), corners[3], max_relative = 0.00001);
3402    // test support
3403    let points : Vec <Point2 <f32>> = [
3404      [-2.0,  0.0],
3405      [ 0.0,  2.0],
3406      [ 2.0,  0.0],
3407      [ 0.0, -2.0],
3408    ].map (Point2::from).into_iter().collect();
3409    let obb = Obb2::containing_points (&points).unwrap();
3410    let support = obb.support (Unit2::axis_y().into()).0;
3411    approx::assert_relative_eq!(support, point2 (0.0, 2.0), epsilon = 0.000_001);
3412  }
3413
3414  #[test]
3415  #[expect(clippy::suboptimal_flops)]
3416  fn obb3() {
3417    use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
3418    use approx::AbsDiffEq;
3419
3420    let points = [
3421      [ 1.0, -1.0, -1.0],
3422      [ 1.0, -1.0,  1.0],
3423      [ 1.0,  1.0, -1.0],
3424      [ 1.0,  1.0,  1.0],
3425      [-1.0, -1.0, -1.0],
3426      [-1.0, -1.0,  1.0],
3427      [-1.0,  1.0, -1.0],
3428      [-1.0,  1.0,  1.0]
3429    ].map (Point3::<f32>::from);
3430    let obb1 = Obb3::containing_points_with_orientation (&points, Angles3::zero())
3431      .unwrap();
3432    assert_eq!(
3433      SortedSet::from_unsorted (obb1.corners().to_vec()),
3434      SortedSet::from_unsorted (
3435        Aabb3::containing (&points).unwrap().corners().to_vec()));
3436    let obb2 = Obb3::containing_points_approx (&points).unwrap();
3437    assert_eq!(obb1, obb2);
3438
3439    let rotation = Rotation3::from_angle_y (FRAC_PI_4.into());
3440    let points = points.map (|p| rotation.rotate (p));
3441    let obb1 = Obb3::containing_points_with_orientation (&points,
3442      // because the cube is symmetrical, it gets oriented by one of the faces
3443      Angles3::wrap (
3444        (3.0 * FRAC_PI_2).into(),
3445        (2.0 * PI - FRAC_PI_4).into(),
3446        FRAC_PI_2.into())
3447    ).unwrap();
3448    let obb2 = Obb3::containing_points_approx (&points).unwrap();
3449    approx::assert_abs_diff_eq!(obb1.center, obb2.center);
3450    assert_eq!(obb1.orientation, obb2.orientation);
3451    approx::assert_abs_diff_eq!(*obb1.extents.x, *obb2.extents.x,
3452      epsilon=4.0 * f32::default_epsilon());
3453    approx::assert_abs_diff_eq!(*obb1.extents.y, *obb2.extents.y,
3454      epsilon=4.0 * f32::default_epsilon());
3455    approx::assert_abs_diff_eq!(*obb1.extents.z, *obb2.extents.z,
3456      epsilon=2.0 * f32::default_epsilon());
3457    // because of rounding errors we can't sort the points to compare
3458    let corners = obb2.corners();
3459    approx::assert_abs_diff_eq!(corners[0], points[7],
3460      epsilon=8.0 * f32::default_epsilon());
3461    approx::assert_abs_diff_eq!(corners[1], points[5],
3462      epsilon=8.0 * f32::default_epsilon());
3463    approx::assert_abs_diff_eq!(corners[2], points[3],
3464      epsilon=8.0 * f32::default_epsilon());
3465    approx::assert_abs_diff_eq!(corners[3], points[1],
3466      epsilon=8.0 * f32::default_epsilon());
3467    approx::assert_abs_diff_eq!(corners[4], points[6],
3468      epsilon=8.0 * f32::default_epsilon());
3469    approx::assert_abs_diff_eq!(corners[5], points[4],
3470      epsilon=8.0 * f32::default_epsilon());
3471    approx::assert_abs_diff_eq!(corners[6], points[2],
3472      epsilon=8.0 * f32::default_epsilon());
3473    approx::assert_abs_diff_eq!(corners[7], points[0],
3474      epsilon=8.0 * f32::default_epsilon());
3475
3476    // test support
3477    let points : Vec <Point3 <f32>> = [
3478      [-2.0,  0.0, -2.0],
3479      [ 0.0,  2.0, -2.0],
3480      [ 2.0,  0.0, -2.0],
3481      [ 0.0, -2.0, -2.0],
3482      [-2.0,  0.0,  2.0],
3483      [ 0.0,  2.0,  2.0],
3484      [ 2.0,  0.0,  2.0],
3485      [ 0.0, -2.0,  2.0]
3486    ].map (Point3::from).into_iter().collect();
3487    let obb = Obb3::containing_points_approx (&points).unwrap();
3488    let support = obb.support (Unit3::axis_y().into()).0;
3489    approx::assert_relative_eq!(support, point3 (0.0, 2.0, 0.0), epsilon = 0.000_001);
3490    let support = obb.support (NonZero3::noisy (vector3 (0.0, 1.0, 1.0))).0;
3491    approx::assert_relative_eq!(support, point3 (0.0, 2.0, 2.0), epsilon = 0.000_001);
3492  }
3493
3494  #[test]
3495  fn support_aabb3() {
3496    let aabb = Aabb3::with_minmax (
3497      [-1.0, -2.0, -3.0].into(),
3498      [ 1.0,  2.0,  3.0].into()).unwrap();
3499    assert_eq!(
3500      [-1.0, -2.0, -3.0],
3501      aabb.support (NonZero3::noisy ([-1.0, -1.0, -1.0].into())).0.0.into_array());
3502    assert_eq!(
3503      [-1.0, -2.0,  3.0],
3504      aabb.support (NonZero3::noisy ([-1.0, -1.0,  1.0].into())).0.0.into_array());
3505    assert_eq!(
3506      [-1.0,  2.0, -3.0],
3507      aabb.support (NonZero3::noisy ([-1.0,  1.0, -1.0].into())).0.0.into_array());
3508    assert_eq!(
3509      [-1.0,  2.0,  3.0],
3510      aabb.support (NonZero3::noisy ([-1.0,  1.0,  1.0].into())).0.0.into_array());
3511    assert_eq!(
3512      [ 1.0, -2.0, -3.0],
3513      aabb.support (NonZero3::noisy ([ 1.0, -1.0, -1.0].into())).0.0.into_array());
3514    assert_eq!(
3515      [ 1.0, -2.0,  3.0],
3516      aabb.support (NonZero3::noisy ([ 1.0, -1.0,  1.0].into())).0.0.into_array());
3517    assert_eq!(
3518      [ 1.0,  2.0, -3.0],
3519      aabb.support (NonZero3::noisy ([ 1.0,  1.0, -1.0].into())).0.0.into_array());
3520    assert_eq!(
3521      [ 1.0,  2.0,  3.0],
3522      aabb.support (NonZero3::noisy ([ 1.0,  1.0,  1.0].into())).0.0.into_array());
3523
3524    assert_eq!(
3525      [-1.0, -2.0,  0.0],
3526      aabb.support (NonZero3::noisy ([-1.0, -1.0,  0.0].into())).0.0.into_array());
3527    assert_eq!(
3528      [-1.0,  2.0,  0.0],
3529      aabb.support (NonZero3::noisy ([-1.0,  1.0,  0.0].into())).0.0.into_array());
3530    assert_eq!(
3531      [ 1.0, -2.0,  0.0],
3532      aabb.support (NonZero3::noisy ([ 1.0, -1.0,  0.0].into())).0.0.into_array());
3533    assert_eq!(
3534      [ 1.0,  2.0,  0.0],
3535      aabb.support (NonZero3::noisy ([ 1.0,  1.0,  0.0].into())).0.0.into_array());
3536
3537    assert_eq!(
3538      [-1.0,  0.0, -3.0],
3539      aabb.support (NonZero3::noisy ([-1.0,  0.0, -1.0].into())).0.0.into_array());
3540    assert_eq!(
3541      [-1.0,  0.0,  3.0],
3542      aabb.support (NonZero3::noisy ([-1.0,  0.0,  1.0].into())).0.0.into_array());
3543    assert_eq!(
3544      [ 1.0,  0.0, -3.0],
3545      aabb.support (NonZero3::noisy ([ 1.0,  0.0, -1.0].into())).0.0.into_array());
3546    assert_eq!(
3547      [ 1.0,  0.0,  3.0],
3548      aabb.support (NonZero3::noisy ([ 1.0,  0.0,  1.0].into())).0.0.into_array());
3549
3550    assert_eq!(
3551      [ 0.0, -2.0, -3.0],
3552      aabb.support (NonZero3::noisy ([ 0.0, -1.0, -1.0].into())).0.0.into_array());
3553    assert_eq!(
3554      [ 0.0, -2.0,  3.0],
3555      aabb.support (NonZero3::noisy ([ 0.0, -1.0,  1.0].into())).0.0.into_array());
3556    assert_eq!(
3557      [ 0.0,  2.0, -3.0],
3558      aabb.support (NonZero3::noisy ([ 0.0,  1.0, -1.0].into())).0.0.into_array());
3559    assert_eq!(
3560      [ 0.0,  2.0,  3.0],
3561      aabb.support (NonZero3::noisy ([ 0.0,  1.0,  1.0].into())).0.0.into_array());
3562
3563    assert_eq!(
3564      [-1.0,  0.0,  0.0],
3565      aabb.support (NonZero3::noisy ([-1.0,  0.0,  0.0].into())).0.0.into_array());
3566    assert_eq!(
3567      [ 1.0,  0.0,  0.0],
3568      aabb.support (NonZero3::noisy ([ 1.0,  0.0,  0.0].into())).0.0.into_array());
3569    assert_eq!(
3570      [ 0.0, -2.0,  0.0],
3571      aabb.support (NonZero3::noisy ([ 0.0, -1.0,  0.0].into())).0.0.into_array());
3572    assert_eq!(
3573      [ 0.0,  2.0,  0.0],
3574      aabb.support (NonZero3::noisy ([ 0.0,  1.0,  0.0].into())).0.0.into_array());
3575    assert_eq!(
3576      [ 0.0,  0.0, -3.0],
3577      aabb.support (NonZero3::noisy ([ 0.0,  0.0, -1.0].into())).0.0.into_array());
3578    assert_eq!(
3579      [ 0.0,  0.0,  3.0],
3580      aabb.support (NonZero3::noisy ([ 0.0,  0.0,  1.0].into())).0.0.into_array());
3581  }
3582} // end tests