Skip to main content

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>(
14    #[deprecated(
15        since = "0.7.19",
16        note = "Use Triangle::new for construction and triangle.v1() to retrieve the value."
17    )]
18    pub Coord<T>,
19    #[deprecated(
20        since = "0.7.19",
21        note = "Use Triangle::new for construction and triangle.v2() to retrieve the value."
22    )]
23    pub Coord<T>,
24    #[deprecated(
25        since = "0.7.19",
26        note = "Use Triangle::new for construction and triangle.v3() to retrieve the value."
27    )]
28    pub Coord<T>,
29);
30
31#[allow(deprecated)]
32impl<T: CoordNum> Triangle<T> {
33    /// Create a new [`Triangle`], enforcing default (counter clockwise) winding order.
34    ///
35    /// If the vertices are given in clockwise order, they will be reversed.
36    /// Degenerate triangles (collinear or identical vertices) are stored as-is.
37    pub fn new(v1: Coord<T>, v2: Coord<T>, v3: Coord<T>) -> Self {
38        // determine cross product of input points. NB: non-robust
39        let orientation = Point::from(v1).cross_prod(v2.into(), v3.into());
40        match orientation.partial_cmp(&T::zero()) {
41            Some(Ordering::Greater) => Self(v1, v2, v3),
42            Some(Ordering::Less) => Self(v3, v2, v1),
43            // degenerate: collinear or identical — store as-is
44            _ => Self(v1, v2, v3),
45        }
46    }
47
48    /// Create a [`Triangle`] without normalising the winding order.
49    ///
50    /// Use this when the caller has already verified CCW winding, or when the
51    /// calling code does not depend on a particular winding order (e.g. inside
52    /// tight loops where the orientation check would be wasteful).
53    ///
54    /// Unlike [`Triangle::new`], vertices are stored in the order given.
55    pub fn unchecked_winding(v1: Coord<T>, v2: Coord<T>, v3: Coord<T>) -> Self {
56        Self(v1, v2, v3)
57    }
58
59    /// Return the first vertex of this triangle.
60    pub fn v1(&self) -> Coord<T> {
61        self.0
62    }
63
64    /// Return the second vertex of this triangle.
65    pub fn v2(&self) -> Coord<T> {
66        self.1
67    }
68
69    /// Return the third vertex of this triangle.
70    pub fn v3(&self) -> Coord<T> {
71        self.2
72    }
73
74    pub fn to_array(&self) -> [Coord<T>; 3] {
75        [self.0, self.1, self.2]
76    }
77
78    pub fn to_lines(&self) -> [Line<T>; 3] {
79        [
80            Line::new(self.0, self.1),
81            Line::new(self.1, self.2),
82            Line::new(self.2, self.0),
83        ]
84    }
85
86    /// Create a `Polygon` from the `Triangle`.
87    ///
88    /// # Examples
89    ///
90    /// ```rust
91    /// use geo_types::{coord, Triangle, polygon};
92    ///
93    /// // Input is CW
94    /// let triangle = Triangle::new(
95    ///     coord! { x: 0., y: 0. },
96    ///     coord! { x: 10., y: 20. },
97    ///     coord! { x: 20., y: -10. },
98    /// );
99    ///
100    /// // Output is CCW
101    /// assert_eq!(
102    ///     triangle.to_polygon(),
103    ///     polygon![
104    ///         (x: 20., y: -10.),
105    ///         (x: 10., y: 20.),
106    ///         (x: 0., y: 0.),
107    ///         (x: 20., y: -10.),
108    ///     ],
109    /// );
110    /// ```
111    pub fn to_polygon(self) -> Polygon<T> {
112        polygon![self.0, self.1, self.2, self.0]
113    }
114}
115
116impl<IC: Into<Coord<T>> + Copy, T: CoordNum> From<[IC; 3]> for Triangle<T> {
117    fn from(array: [IC; 3]) -> Self {
118        Self::new(array[0].into(), array[1].into(), array[2].into())
119    }
120}
121
122#[cfg(any(feature = "approx", test))]
123mod approx_integration {
124    use super::*;
125    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
126
127    impl<T> RelativeEq for Triangle<T>
128    where
129        T: CoordNum + RelativeEq<Epsilon = T>,
130    {
131        #[inline]
132        fn default_max_relative() -> Self::Epsilon {
133            T::default_max_relative()
134        }
135
136        /// Equality assertion within a relative limit.
137        ///
138        /// # Examples
139        ///
140        /// ```
141        /// use geo_types::{point, Triangle};
142        ///
143        /// let a = Triangle::new((0.0, 0.0).into(), (10.0, 10.0).into(), (0.0, 5.0).into());
144        /// let b = Triangle::new((0.0, 0.0).into(), (10.01, 10.0).into(), (0.0, 5.0).into());
145        ///
146        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
147        /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
148        /// ```
149        #[inline]
150        fn relative_eq(
151            &self,
152            other: &Self,
153            epsilon: Self::Epsilon,
154            max_relative: Self::Epsilon,
155        ) -> bool {
156            if !self.v1().relative_eq(&other.v1(), epsilon, max_relative) {
157                return false;
158            }
159            if !self.v2().relative_eq(&other.v2(), epsilon, max_relative) {
160                return false;
161            }
162            if !self.v3().relative_eq(&other.v3(), epsilon, max_relative) {
163                return false;
164            }
165
166            true
167        }
168    }
169
170    impl<T> AbsDiffEq for Triangle<T>
171    where
172        T: CoordNum + AbsDiffEq<Epsilon = T>,
173    {
174        type Epsilon = T;
175
176        #[inline]
177        fn default_epsilon() -> Self::Epsilon {
178            T::default_epsilon()
179        }
180
181        /// Equality assertion with an absolute limit.
182        ///
183        /// # Examples
184        ///
185        /// ```
186        /// use geo_types::{point, Triangle};
187        ///
188        /// let a = Triangle::new((0.0, 0.0).into(), (10.0, 10.0).into(), (0.0, 5.0).into());
189        /// let b = Triangle::new((0.0, 0.0).into(), (10.01, 10.0).into(), (0.0, 5.0).into());
190        ///
191        /// approx::abs_diff_eq!(a, b, epsilon=0.1);
192        /// approx::abs_diff_ne!(a, b, epsilon=0.001);
193        /// ```
194        #[inline]
195        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
196            if !self.v1().abs_diff_eq(&other.v1(), epsilon) {
197                return false;
198            }
199            if !self.v2().abs_diff_eq(&other.v2(), epsilon) {
200                return false;
201            }
202            if !self.v3().abs_diff_eq(&other.v3(), epsilon) {
203                return false;
204            }
205
206            true
207        }
208    }
209
210    impl<T> UlpsEq for Triangle<T>
211    where
212        T: CoordNum + UlpsEq<Epsilon = T>,
213    {
214        fn default_max_ulps() -> u32 {
215            T::default_max_ulps()
216        }
217
218        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
219            if !self.v1().ulps_eq(&other.v1(), epsilon, max_ulps) {
220                return false;
221            }
222            if !self.v2().ulps_eq(&other.v2(), epsilon, max_ulps) {
223                return false;
224            }
225            if !self.v3().ulps_eq(&other.v3(), epsilon, max_ulps) {
226                return false;
227            }
228            true
229        }
230    }
231}
232
233#[cfg(any(
234    feature = "rstar_0_8",
235    feature = "rstar_0_9",
236    feature = "rstar_0_10",
237    feature = "rstar_0_11",
238    feature = "rstar_0_12"
239))]
240macro_rules! impl_rstar_triangle {
241    ($rstar:ident) => {
242        impl<T> ::$rstar::RTreeObject for Triangle<T>
243        where
244            T: ::num_traits::Float + ::$rstar::RTreeNum,
245        {
246            type Envelope = ::$rstar::AABB<Point<T>>;
247
248            fn envelope(&self) -> Self::Envelope {
249                let bounding_rect =
250                    crate::private_utils::get_bounding_rect(self.to_array()).unwrap();
251                ::$rstar::AABB::from_corners(bounding_rect.min().into(), bounding_rect.max().into())
252            }
253        }
254    };
255}
256
257#[cfg(feature = "rstar_0_8")]
258impl_rstar_triangle!(rstar_0_8);
259
260#[cfg(feature = "rstar_0_9")]
261impl_rstar_triangle!(rstar_0_9);
262
263#[cfg(feature = "rstar_0_10")]
264impl_rstar_triangle!(rstar_0_10);
265
266#[cfg(feature = "rstar_0_11")]
267impl_rstar_triangle!(rstar_0_11);
268
269#[cfg(feature = "rstar_0_12")]
270impl_rstar_triangle!(rstar_0_12);