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