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