math_utils/geometry/primitive/
simplex.rs

1pub use self::simplex2::Simplex2;
2pub use self::simplex3::Simplex3;
3
4/// 2D simplex types
5pub mod simplex2 {
6  #[cfg(feature = "derive_serdes")]
7  use serde::{Deserialize, Serialize};
8
9  use crate::*;
10  use crate::geometry::*;
11
12  /// A $n<3$-simplex in 2-dimensional space.
13  ///
14  /// Individual simplex variants should fail to construct in debug builds when
15  /// points are degenerate.
16  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
17  #[derive(Clone, Copy, Debug, PartialEq)]
18  pub enum Simplex2 <S> {
19    Segment  (Segment  <S>),
20    Triangle (Triangle <S>)
21  }
22
23  /// A 1-simplex or line segment in 2D space.
24  ///
25  /// Creation methods should fail with a debug assertion if the points are
26  /// identical.
27  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
28  #[derive(Clone, Copy, Debug, PartialEq)]
29  pub struct Segment <S> {
30    a : Point2 <S>,
31    b : Point2 <S>
32  }
33
34  /// A 2-simplex or triangle in 2D space
35  ///
36  /// Creation methods should fail with a debug assertion if the points are
37  /// colinear.
38  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
39  #[derive(Clone, Copy, Debug, PartialEq)]
40  pub struct Triangle <S> {
41    a : Point2 <S>,
42    b : Point2 <S>,
43    c : Point2 <S>
44  }
45
46  impl <S : Ring> Segment <S> {
47    /// Panics if the points are identical:
48    ///
49    /// ```should_panic
50    /// # use math_utils::geometry::simplex2::Segment;
51    /// let s = Segment::new ([0.0, 0.0].into(), [0.0, 0.0].into());
52    /// ```
53    pub fn new (
54      a : Point2 <S>,
55      b : Point2 <S>
56    ) -> Self where S : std::fmt::Debug {
57      assert_ne!(a, b);
58      Segment { a, b }
59    }
60    /// Debug panic if the points are identical:
61    ///
62    /// ```should_panic
63    /// # use math_utils::geometry::simplex2::Segment;
64    /// let s = Segment::unchecked ([0.0, 0.0].into(), [0.0, 0.0].into());
65    /// ```
66    pub fn unchecked (
67      a : Point2 <S>,
68      b : Point2 <S>
69    ) -> Self where S : std::fmt::Debug {
70      debug_assert_ne!(a, b);
71      Segment { a, b }
72    }
73    #[inline]
74    pub fn numcast <T> (self) -> Option <Segment <T>> where
75      S : num_traits::NumCast,
76      T : num_traits::NumCast
77    {
78      Some (Segment {
79        a: self.a.numcast()?,
80        b: self.b.numcast()?
81      })
82    }
83    #[inline]
84    pub fn point_a (&self) -> &Point2 <S> {
85      &self.a
86    }
87    #[inline]
88    pub fn point_b (&self) -> &Point2 <S> {
89      &self.b
90    }
91    #[inline]
92    pub fn length (&self) -> S where S : Field + Sqrt {
93      self.vector().norm()
94    }
95    #[inline]
96    pub fn length2 (&self) -> S {
97      self.vector().self_dot()
98    }
99    /// Returns `point_b() - point_a()`
100    #[inline]
101    pub fn vector (&self) -> Vector2 <S> {
102      self.b - self.a
103    }
104    #[inline]
105    pub fn aabb2 (&self) -> Aabb2 <S> where S : std::fmt::Debug {
106      Aabb2::from_points (self.a, self.b)
107    }
108    #[inline]
109    pub fn line2 (&self) -> Line2 <S> where S : Real {
110      Line2::new (self.a, Unit2::normalize (self.vector()))
111    }
112    #[inline]
113    #[allow(clippy::type_complexity)]
114    pub fn intersect_aabb (&self, aabb : &Aabb2 <S>)
115      -> Option <((S, Point2 <S>), (S, Point2 <S>))>
116    where S : Real + std::fmt::Debug {
117      intersect::continuous_segment2_aabb2 (self, aabb)
118    }
119    #[inline]
120    #[allow(clippy::type_complexity)]
121    pub fn intersect_sphere (&self, sphere : &Sphere2 <S>)
122      -> Option <((S, Point2 <S>), (S, Point2 <S>))>
123    where S : Real + std::fmt::Debug {
124      intersect::continuous_segment2_sphere2 (self, sphere)
125    }
126  }
127  impl <S : Field> Default for Segment <S> {
128    /// A default simplex is arbitrarily chosen to be the simplex from -1.0 to 1.0
129    /// lying on the X axis:
130    ///
131    /// ```
132    /// # use math_utils::geometry::simplex2::Segment;
133    /// assert_eq!(
134    ///   Segment::default(),
135    ///   Segment::new ([-1.0, 0.0].into(), [1.0, 0.0].into())
136    /// );
137    /// ```
138    fn default() -> Self {
139      Segment {
140        a: [-S::one(), S::zero()].into(),
141        b: [ S::one(), S::zero()].into()
142      }
143    }
144  }
145
146  impl <S : Field> Triangle <S> {
147    /// Panics if the points are colinear:
148    ///
149    /// ```should_panic
150    /// # use math_utils::geometry::simplex2::Triangle;
151    /// let s = Triangle::new (
152    ///   [-1.0, -1.0].into(),
153    ///   [ 0.0,  0.0].into(),
154    ///   [ 1.0,  1.0].into());
155    /// ```
156    pub fn new (
157      a : Point2 <S>,
158      b : Point2 <S>,
159      c : Point2 <S>
160    ) -> Self where S : approx::RelativeEq + std::fmt::Debug {
161      debug_assert_ne!(a, b);
162      debug_assert_ne!(a, c);
163      debug_assert_ne!(b, c);
164      assert!(!colinear_2d (&a, &b, &c));
165      Triangle { a, b, c }
166    }
167    /// Debug panic if the points are colinear:
168    ///
169    /// ```should_panic
170    /// # use math_utils::geometry::simplex2::Triangle;
171    /// let s = Triangle::unchecked (
172    ///   [-1.0, -1.0].into(),
173    ///   [ 0.0,  0.0].into(),
174    ///   [ 1.0,  1.0].into());
175    /// ```
176    pub fn unchecked (
177      a : Point2 <S>,
178      b : Point2 <S>,
179      c : Point2 <S>
180    ) -> Self where S : approx::RelativeEq + std::fmt::Debug {
181      debug_assert_ne!(a, b);
182      debug_assert_ne!(a, c);
183      debug_assert_ne!(b, c);
184      debug_assert!(!colinear_2d (&a, &b, &c));
185      Triangle { a, b, c }
186    }
187    #[inline]
188    pub fn point_a (&self) -> &Point2 <S> {
189      &self.a
190    }
191    #[inline]
192    pub fn point_b (&self) -> &Point2 <S> {
193      &self.b
194    }
195    #[inline]
196    pub fn point_c (&self) -> &Point2 <S> {
197      &self.c
198    }
199  }
200  impl <S : Field + Sqrt> Default for Triangle <S> {
201    /// A default triangle is arbitrarily chosen to be the equilateral triangle
202    /// with point at 1.0 on the Y axis:
203    ///
204    /// ```
205    /// # use math_utils::approx;
206    /// # use math_utils::geometry::simplex2::Triangle;
207    /// use std::f64::consts::FRAC_1_SQRT_2;
208    /// let s = Triangle::default();
209    /// let t = Triangle::new (
210    ///   [           0.0,            1.0].into(),
211    ///   [-FRAC_1_SQRT_2, -FRAC_1_SQRT_2].into(),
212    ///   [ FRAC_1_SQRT_2, -FRAC_1_SQRT_2].into()
213    /// );
214    /// approx::assert_relative_eq!(s.point_a(), t.point_a());
215    /// approx::assert_relative_eq!(s.point_b(), t.point_b());
216    /// approx::assert_relative_eq!(s.point_c(), t.point_c());
217    /// ```
218    fn default() -> Self {
219      let frac_1_sqrt_2 = S::one() / (S::one() + S::one()).sqrt();
220      Triangle {
221        a: [     S::zero(),       S::one()].into(),
222        b: [-frac_1_sqrt_2, -frac_1_sqrt_2].into(),
223        c: [ frac_1_sqrt_2, -frac_1_sqrt_2].into()
224      }
225    }
226  }
227}
228
229/// 3D simplex types
230pub mod simplex3 {
231  #[cfg(feature = "derive_serdes")]
232  use serde::{Deserialize, Serialize};
233
234  use crate::*;
235  use crate::geometry::*;
236  /// A $n<4$-simplex in 3-dimensional space.
237  ///
238  /// Individual simplex variants should fail to construct in debug builds when
239  /// points are degenerate.
240  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
241  #[derive(Clone, Copy, Debug, PartialEq)]
242  pub enum Simplex3 <S> {
243    Segment     (Segment     <S>),
244    Triangle    (Triangle    <S>),
245    Tetrahedron (Tetrahedron <S>)
246  }
247
248  /// A 1-simplex or line segment in 3D space.
249  ///
250  /// Creation methods should fail with a debug assertion if the points are
251  /// identical.
252  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
253  #[derive(Clone, Copy, Debug, PartialEq)]
254  pub struct Segment <S> {
255    a : Point3 <S>,
256    b : Point3 <S>
257  }
258
259  /// A 2-simplex or triangle in 3D space
260  ///
261  /// Creation methods should fail with a debug assertion if the points are
262  /// colinear.
263  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
264  #[derive(Clone, Copy, Debug, PartialEq)]
265  pub struct Triangle <S> {
266    a : Point3 <S>,
267    b : Point3 <S>,
268    c : Point3 <S>
269  }
270
271  /// A 3-simplex or tetrahedron in 3D space
272  ///
273  /// Creation methods will fail with a debug assertion if the points are
274  /// coplanar.
275  #[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
276  #[derive(Clone, Copy, Debug, PartialEq)]
277  pub struct Tetrahedron <S> {
278    a : Point3 <S>,
279    b : Point3 <S>,
280    c : Point3 <S>,
281    d : Point3 <S>
282  }
283
284  impl <S : Ring> Segment <S> {
285    /// Panics if the points are identical:
286    ///
287    /// ```should_panic
288    /// # use math_utils::geometry::simplex3::Segment;
289    /// let s = Segment::new ([0.0, 0.0, 1.0].into(), [0.0, 0.0, 1.0].into());
290    /// ```
291    pub fn new (
292      a : Point3 <S>,
293      b : Point3 <S>
294    ) -> Self where S : std::fmt::Debug {
295      assert_ne!(a, b);
296      Segment { a, b }
297    }
298    /// Debug panic if the points are identical:
299    ///
300    /// ```should_panic
301    /// # use math_utils::geometry::simplex3::Segment;
302    /// let s = Segment::unchecked ([0.0, 0.0, 1.0].into(), [0.0, 0.0, 1.0].into());
303    /// ```
304    pub fn unchecked (
305      a : Point3 <S>,
306      b : Point3 <S>
307    ) -> Self where S : std::fmt::Debug {
308      debug_assert_ne!(a, b);
309      Segment { a, b }
310    }
311    #[inline]
312    pub fn numcast <T> (self) -> Option <Segment <T>> where
313      S : num_traits::NumCast,
314      T : num_traits::NumCast
315    {
316      Some (Segment {
317        a: self.a.numcast()?,
318        b: self.b.numcast()?
319      })
320    }
321    #[inline]
322    pub fn point_a (&self) -> &Point3 <S> {
323      &self.a
324    }
325    #[inline]
326    pub fn point_b (&self) -> &Point3 <S> {
327      &self.b
328    }
329    #[inline]
330    pub fn length (&self) -> S where S : Field + Sqrt {
331      self.vector().norm()
332    }
333    #[inline]
334    pub fn length2 (&self) -> S {
335      self.vector().self_dot()
336    }
337    /// Returns `point_b() - point_a()`
338    #[inline]
339    pub fn vector (&self) -> Vector3 <S> {
340      self.b - self.a
341    }
342    #[inline]
343    pub fn aabb3 (&self) -> Aabb3 <S> where S : std::fmt::Debug {
344      Aabb3::from_points (self.a, self.b)
345    }
346    #[inline]
347    pub fn line3 (&self) -> Line3 <S> where S : Real {
348      Line3::new (self.a, Unit3::normalize (self.vector()))
349    }
350    #[inline]
351    #[allow(clippy::type_complexity)]
352    pub fn intersect_aabb (&self, aabb : &Aabb3 <S>)
353      -> Option <((S, Point3 <S>), (S, Point3 <S>))>
354    where S : Real + num_traits::Float + approx::RelativeEq <Epsilon=S> +
355      std::fmt::Debug
356    {
357      intersect::continuous_segment3_aabb3 (self, aabb)
358    }
359    #[inline]
360    #[allow(clippy::type_complexity)]
361    pub fn intersect_sphere (&self, sphere : &Sphere3 <S>)
362      -> Option <((S, Point3 <S>), (S, Point3 <S>))>
363    where S : Field + Sqrt {
364      intersect::continuous_segment3_sphere3 (self, sphere)
365    }
366    #[inline]
367    #[allow(clippy::type_complexity)]
368    pub fn intersect_cylinder (&self, sphere : &Cylinder3 <S>)
369      -> Option <((S, Point3 <S>), (S, Point3 <S>))>
370    where S : Real + std::fmt::Debug {
371      intersect::continuous_segment3_cylinder3 (self, sphere)
372    }
373    #[inline]
374    #[allow(clippy::type_complexity)]
375    pub fn intersect_capsule (&self, capsule : &Capsule3 <S>)
376      -> Option <((S, Point3 <S>), (S, Point3 <S>))>
377    where S : Real + std::fmt::Debug {
378      intersect::continuous_segment3_capsule3 (self, capsule)
379    }
380  }
381  impl <S : Field> Default for Segment <S> {
382    /// A default simplex is arbitrarily chosen to be the simplex from -1.0 to 1.0
383    /// lying on the X axis:
384    ///
385    /// ```
386    /// # use math_utils::geometry::simplex3::Segment;
387    /// assert_eq!(
388    ///   Segment::default(),
389    ///   Segment::new ([-1.0, 0.0, 0.0].into(), [1.0, 0.0, 0.0].into())
390    /// );
391    /// ```
392    fn default() -> Self {
393      Segment {
394        a: [-S::one(), S::zero(), S::zero()].into(),
395        b: [ S::one(), S::zero(), S::zero()].into()
396      }
397    }
398  }
399
400  impl <S : Ring> Triangle <S> {
401    /// Panics if the points are colinear:
402    ///
403    /// ```should_panic
404    /// # use math_utils::geometry::simplex3::Triangle;
405    /// let s = Triangle::new (
406    ///   [-1.0, -1.0, -1.0].into(),
407    ///   [ 0.0,  0.0,  0.0].into(),
408    ///   [ 1.0,  1.0,  1.0].into());
409    /// ```
410    pub fn new (
411      a : Point3 <S>,
412      b : Point3 <S>,
413      c : Point3 <S>
414    ) -> Self where
415      S : Field + Sqrt + approx::RelativeEq + std::fmt::Debug
416    {
417      debug_assert_ne!(a, b);
418      debug_assert_ne!(a, c);
419      debug_assert_ne!(b, c);
420      assert!(!colinear_3d (&a, &b, &c));
421      Triangle { a, b, c }
422    }
423    /// Debug panic if the points are colinear:
424    ///
425    /// ```should_panic
426    /// # use math_utils::geometry::simplex3::Triangle;
427    /// let s = Triangle::unchecked (
428    ///   [-1.0, -1.0, -1.0].into(),
429    ///   [ 0.0,  0.0,  0.0].into(),
430    ///   [ 1.0,  1.0,  1.0].into());
431    /// ```
432    pub fn unchecked (
433      a : Point3 <S>,
434      b : Point3 <S>,
435      c : Point3 <S>
436    ) -> Self where
437      S : Field + Sqrt + approx::RelativeEq + std::fmt::Debug
438    {
439      debug_assert_ne!(a, b);
440      debug_assert_ne!(a, c);
441      debug_assert_ne!(b, c);
442      debug_assert!(!colinear_3d (&a, &b, &c));
443      Triangle { a, b, c }
444    }
445    #[inline]
446    pub fn point_a (&self) -> &Point3 <S> {
447      &self.a
448    }
449    #[inline]
450    pub fn point_b (&self) -> &Point3 <S> {
451      &self.b
452    }
453    #[inline]
454    pub fn point_c (&self) -> &Point3 <S> {
455      &self.c
456    }
457  }
458  impl <S : Field + Sqrt> Default for Triangle <S> {
459    /// A default simplex is arbitrarily chosen to be the simplex on the XY plane
460    /// formed by an equilateral triangle with point at 1.0 on the Y axis:
461    ///
462    /// ```
463    /// # use math_utils::approx;
464    /// # use math_utils::geometry::simplex3::Triangle;
465    /// # use math_utils::*;
466    /// use std::f64::consts::FRAC_1_SQRT_2;
467    /// let s = Triangle::default();
468    /// let t = Triangle::new (
469    ///   [           0.0,            1.0, 0.0].into(),
470    ///   [-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0].into(),
471    ///   [ FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0].into()
472    /// );
473    /// approx::assert_relative_eq!(
474    ///   Matrix3::from_col_arrays ([
475    ///     s.point_a().0.into_array(),
476    ///     s.point_b().0.into_array(),
477    ///     s.point_c().0.into_array()
478    ///   ]),
479    ///   Matrix3::from_col_arrays ([
480    ///     t.point_a().0.into_array(),
481    ///     t.point_b().0.into_array(),
482    ///     t.point_c().0.into_array()
483    ///   ]));
484    /// ```
485    fn default() -> Self {
486      let frac_1_sqrt_2 = S::one() / (S::one() + S::one()).sqrt();
487      Triangle {
488        a: [     S::zero(),       S::one(), S::zero()].into(),
489        b: [-frac_1_sqrt_2, -frac_1_sqrt_2, S::zero()].into(),
490        c: [ frac_1_sqrt_2, -frac_1_sqrt_2, S::zero()].into()
491      }
492    }
493  }
494
495  impl <S : Ring> Tetrahedron <S> {
496    /// Panics if the points are coplanar:
497    ///
498    /// ```should_panic
499    /// # use math_utils::geometry::simplex3::Tetrahedron;
500    /// let s = Tetrahedron::new (
501    ///   [-1.0, -1.0, -1.0].into(),
502    ///   [ 1.0,  1.0,  1.0].into(),
503    ///   [-1.0,  1.0,  0.0].into(),
504    ///   [ 1.0, -1.0,  0.0].into());
505    /// ```
506    pub fn new (
507      a : Point3 <S>,
508      b : Point3 <S>,
509      c : Point3 <S>,
510      d : Point3 <S>
511    ) -> Self where
512      S : approx::RelativeEq
513    {
514      assert!(!coplanar_3d (&a, &b, &c, &d));
515      Tetrahedron { a, b, c, d }
516    }
517    /// Debug panic if the points are coplanar:
518    ///
519    /// ```should_panic
520    /// # use math_utils::geometry::simplex3::Tetrahedron;
521    /// let s = Tetrahedron::unchecked (
522    ///   [-1.0, -1.0, -1.0].into(),
523    ///   [ 1.0,  1.0,  1.0].into(),
524    ///   [-1.0,  1.0,  0.0].into(),
525    ///   [ 1.0, -1.0,  0.0].into());
526    /// ```
527    pub fn unchecked (
528      a : Point3 <S>,
529      b : Point3 <S>,
530      c : Point3 <S>,
531      d : Point3 <S>
532    ) -> Self where
533      S : approx::RelativeEq
534    {
535      debug_assert!(!coplanar_3d (&a, &b, &c, &d));
536      Tetrahedron { a, b, c, d }
537    }
538    #[inline]
539    pub fn point_a (&self) -> &Point3 <S> {
540      &self.a
541    }
542    #[inline]
543    pub fn point_b (&self) -> &Point3 <S> {
544      &self.b
545    }
546    #[inline]
547    pub fn point_c (&self) -> &Point3 <S> {
548      &self.c
549    }
550    #[inline]
551    pub fn point_d (&self) -> &Point3 <S> {
552      &self.d
553    }
554  }
555  impl <S : Field + Sqrt> Default for Tetrahedron <S> {
556    /// A default simplex is arbitrarily chosen to be the simplex with vertices at
557    /// unit distance from the origin with A at `[0.0, 0.0, 1.0]` and B at
558    /// `[0.0, sqrt(8.0/9.0), -1.0/3.0]`, and points C and D at
559    /// `[ sqrt(2.0/3.0), -sqrt(2.0/9.0), -1.0/3.0]` and
560    /// `[-sqrt(2.0/3.0), -sqrt(2.0/9.0), -1.0/3.0]`.
561    ///
562    /// ```
563    /// # use math_utils::approx;
564    /// # use math_utils::geometry::simplex3::Tetrahedron;
565    /// # use math_utils::*;
566    /// let s = Tetrahedron::default();
567    /// let t = Tetrahedron::new (
568    ///   [                0.0,                 0.0,      1.0].into(),
569    ///   [                0.0,  f64::sqrt(8.0/9.0), -1.0/3.0].into(),
570    ///   [ f64::sqrt(2.0/3.0), -f64::sqrt(2.0/9.0), -1.0/3.0].into(),
571    ///   [-f64::sqrt(2.0/3.0), -f64::sqrt(2.0/9.0), -1.0/3.0].into());
572    /// approx::assert_relative_eq!(
573    ///   Matrix4::from_col_arrays ([
574    ///     s.point_a().0.with_w (0.0).into_array(),
575    ///     s.point_b().0.with_w (0.0).into_array(),
576    ///     s.point_c().0.with_w (0.0).into_array(),
577    ///     s.point_d().0.with_w (0.0).into_array()
578    ///   ]),
579    ///   Matrix4::from_col_arrays ([
580    ///     t.point_a().0.with_w (0.0).into_array(),
581    ///     t.point_b().0.with_w (0.0).into_array(),
582    ///     t.point_c().0.with_w (0.0).into_array(),
583    ///     t.point_d().0.with_w (0.0).into_array()
584    ///   ]));
585    /// ```
586    fn default() -> Self {
587      let frac_1_3 = S::one()    / S::three();
588      let sqrt_2_3 = (S::two()   / S::three()).sqrt();
589      let sqrt_8_9 = (S::eight() / S::nine()).sqrt();
590      let sqrt_2_9 = (S::two()   / S::nine()).sqrt();
591      Tetrahedron {
592        a: [S::zero(), S::zero(),  S::one()].into(),
593        b: [S::zero(),  sqrt_8_9, -frac_1_3].into(),
594        c: [ sqrt_2_3, -sqrt_2_9, -frac_1_3].into(),
595        d: [-sqrt_2_3, -sqrt_2_9, -frac_1_3].into()
596      }
597    }
598  }
599}