geo_types/geometry/
triangle.rs

1use crate::{polygon, Coord, CoordNum, Line, Point, Polygon};
2use core::cmp::Ordering;
3
4/// A bounded 2D area whose three vertices are defined by
5/// `Coord`s. The semantics and validity are that of
6/// the equivalent [`Polygon`]; in addition, the three
7/// vertices **must not** be collinear and they *must* be distinct.
8///
9/// # Notes
10/// Irrespective of input order the resulting geometry has ccw order and its vertices are yielded in ccw order by iterators
11#[derive(Copy, Clone, Hash, Eq, PartialEq)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub struct Triangle<T: CoordNum = f64>(pub Coord<T>, pub Coord<T>, pub Coord<T>);
14
15impl<T: CoordNum> Triangle<T> {
16    /// Instantiate Self from the raw content value
17    pub fn new(v1: Coord<T>, v2: Coord<T>, v3: Coord<T>) -> Self {
18        // determine cross product of input points. NB: non-robust
19        let orientation = Point::from(v1).cross_prod(v2.into(), v3.into());
20        match orientation.partial_cmp(&T::zero()) {
21            Some(Ordering::Greater) => Self(v1, v2, v3),
22            Some(Ordering::Less) => Self(v3, v2, v1),
23            // we told you not to do this!
24            _ => Self(v1, v2, v3),
25        }
26    }
27
28    pub fn to_array(&self) -> [Coord<T>; 3] {
29        [self.0, self.1, self.2]
30    }
31
32    pub fn to_lines(&self) -> [Line<T>; 3] {
33        [
34            Line::new(self.0, self.1),
35            Line::new(self.1, self.2),
36            Line::new(self.2, self.0),
37        ]
38    }
39
40    /// Create a `Polygon` from the `Triangle`.
41    ///
42    /// # Examples
43    ///
44    /// ```rust
45    /// use geo_types::{coord, Triangle, polygon};
46    ///
47    /// // Input is CW
48    /// let triangle = Triangle::new(
49    ///     coord! { x: 0., y: 0. },
50    ///     coord! { x: 10., y: 20. },
51    ///     coord! { x: 20., y: -10. },
52    /// );
53    ///
54    /// // Output is CCW
55    /// assert_eq!(
56    ///     triangle.to_polygon(),
57    ///     polygon![
58    ///         (x: 20., y: -10.),
59    ///         (x: 10., y: 20.),
60    ///         (x: 0., y: 0.),
61    ///         (x: 20., y: -10.),
62    ///     ],
63    /// );
64    /// ```
65    pub fn to_polygon(self) -> Polygon<T> {
66        polygon![self.0, self.1, self.2, self.0]
67    }
68}
69
70impl<IC: Into<Coord<T>> + Copy, T: CoordNum> From<[IC; 3]> for Triangle<T> {
71    fn from(array: [IC; 3]) -> Self {
72        Self(array[0].into(), array[1].into(), array[2].into())
73    }
74}
75
76#[cfg(any(feature = "approx", test))]
77mod approx_integration {
78    use super::*;
79    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
80
81    impl<T> RelativeEq for Triangle<T>
82    where
83        T: CoordNum + RelativeEq<Epsilon = T>,
84    {
85        #[inline]
86        fn default_max_relative() -> Self::Epsilon {
87            T::default_max_relative()
88        }
89
90        /// Equality assertion within a relative limit.
91        ///
92        /// # Examples
93        ///
94        /// ```
95        /// use geo_types::{point, Triangle};
96        ///
97        /// let a = Triangle::new((0.0, 0.0).into(), (10.0, 10.0).into(), (0.0, 5.0).into());
98        /// let b = Triangle::new((0.0, 0.0).into(), (10.01, 10.0).into(), (0.0, 5.0).into());
99        ///
100        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
101        /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
102        /// ```
103        #[inline]
104        fn relative_eq(
105            &self,
106            other: &Self,
107            epsilon: Self::Epsilon,
108            max_relative: Self::Epsilon,
109        ) -> bool {
110            if !self.0.relative_eq(&other.0, epsilon, max_relative) {
111                return false;
112            }
113            if !self.1.relative_eq(&other.1, epsilon, max_relative) {
114                return false;
115            }
116            if !self.2.relative_eq(&other.2, epsilon, max_relative) {
117                return false;
118            }
119
120            true
121        }
122    }
123
124    impl<T> AbsDiffEq for Triangle<T>
125    where
126        T: CoordNum + AbsDiffEq<Epsilon = T>,
127    {
128        type Epsilon = T;
129
130        #[inline]
131        fn default_epsilon() -> Self::Epsilon {
132            T::default_epsilon()
133        }
134
135        /// Equality assertion with an absolute limit.
136        ///
137        /// # Examples
138        ///
139        /// ```
140        /// use geo_types::{point, Triangle};
141        ///
142        /// let a = Triangle::new((0.0, 0.0).into(), (10.0, 10.0).into(), (0.0, 5.0).into());
143        /// let b = Triangle::new((0.0, 0.0).into(), (10.01, 10.0).into(), (0.0, 5.0).into());
144        ///
145        /// approx::abs_diff_eq!(a, b, epsilon=0.1);
146        /// approx::abs_diff_ne!(a, b, epsilon=0.001);
147        /// ```
148        #[inline]
149        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
150            if !self.0.abs_diff_eq(&other.0, epsilon) {
151                return false;
152            }
153            if !self.1.abs_diff_eq(&other.1, epsilon) {
154                return false;
155            }
156            if !self.2.abs_diff_eq(&other.2, epsilon) {
157                return false;
158            }
159
160            true
161        }
162    }
163
164    impl<T> UlpsEq for Triangle<T>
165    where
166        T: CoordNum + UlpsEq<Epsilon = T>,
167    {
168        fn default_max_ulps() -> u32 {
169            T::default_max_ulps()
170        }
171
172        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
173            if !self.0.ulps_eq(&other.0, epsilon, max_ulps) {
174                return false;
175            }
176            if !self.1.ulps_eq(&other.1, epsilon, max_ulps) {
177                return false;
178            }
179            if !self.2.ulps_eq(&other.2, epsilon, max_ulps) {
180                return false;
181            }
182            true
183        }
184    }
185}
186
187#[cfg(any(
188    feature = "rstar_0_8",
189    feature = "rstar_0_9",
190    feature = "rstar_0_10",
191    feature = "rstar_0_11",
192    feature = "rstar_0_12"
193))]
194macro_rules! impl_rstar_triangle {
195    ($rstar:ident) => {
196        impl<T> ::$rstar::RTreeObject for Triangle<T>
197        where
198            T: ::num_traits::Float + ::$rstar::RTreeNum,
199        {
200            type Envelope = ::$rstar::AABB<Point<T>>;
201
202            fn envelope(&self) -> Self::Envelope {
203                let bounding_rect =
204                    crate::private_utils::get_bounding_rect(self.to_array()).unwrap();
205                ::$rstar::AABB::from_corners(bounding_rect.min().into(), bounding_rect.max().into())
206            }
207        }
208    };
209}
210
211#[cfg(feature = "rstar_0_8")]
212impl_rstar_triangle!(rstar_0_8);
213
214#[cfg(feature = "rstar_0_9")]
215impl_rstar_triangle!(rstar_0_9);
216
217#[cfg(feature = "rstar_0_10")]
218impl_rstar_triangle!(rstar_0_10);
219
220#[cfg(feature = "rstar_0_11")]
221impl_rstar_triangle!(rstar_0_11);
222
223#[cfg(feature = "rstar_0_12")]
224impl_rstar_triangle!(rstar_0_12);