geo_types/geometry/
line.rs

1use crate::{Coord, CoordNum, Point};
2
3/// A line segment made up of exactly two
4/// [`Coord`]s.
5///
6/// # Semantics
7///
8/// The _interior_ and _boundary_ are defined as with a
9/// `LineString` with the two end points.
10#[derive(Eq, PartialEq, Clone, Copy, Hash)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct Line<T: CoordNum = f64> {
13    pub start: Coord<T>,
14    pub end: Coord<T>,
15}
16
17impl<T: CoordNum> Line<T> {
18    /// Creates a new line segment.
19    ///
20    /// # Examples
21    ///
22    /// ```
23    /// use geo_types::{coord, Line};
24    ///
25    /// let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. });
26    ///
27    /// assert_eq!(line.start, coord! { x: 0., y: 0. });
28    /// assert_eq!(line.end, coord! { x: 1., y: 2. });
29    /// ```
30    pub fn new<C>(start: C, end: C) -> Self
31    where
32        C: Into<Coord<T>>,
33    {
34        Self {
35            start: start.into(),
36            end: end.into(),
37        }
38    }
39
40    /// Calculate the difference in coordinates (Δx, Δy).
41    pub fn delta(&self) -> Coord<T> {
42        self.end - self.start
43    }
44
45    /// Calculate the difference in ‘x’ components (Δx).
46    ///
47    /// Equivalent to:
48    ///
49    /// ```rust
50    /// # use geo_types::{Line, point};
51    /// # let line = Line::new(
52    /// #     point! { x: 4., y: -12. },
53    /// #     point! { x: 0., y: 9. },
54    /// # );
55    /// # assert_eq!(
56    /// #     line.dx(),
57    /// line.end.x - line.start.x
58    /// # );
59    /// ```
60    pub fn dx(&self) -> T {
61        self.delta().x
62    }
63
64    /// Calculate the difference in ‘y’ components (Δy).
65    ///
66    /// Equivalent to:
67    ///
68    /// ```rust
69    /// # use geo_types::{Line, point};
70    /// # let line = Line::new(
71    /// #     point! { x: 4., y: -12. },
72    /// #     point! { x: 0., y: 9. },
73    /// # );
74    /// # assert_eq!(
75    /// #     line.dy(),
76    /// line.end.y - line.start.y
77    /// # );
78    /// ```
79    pub fn dy(&self) -> T {
80        self.delta().y
81    }
82
83    /// Calculate the slope (Δy/Δx).
84    ///
85    /// Equivalent to:
86    ///
87    /// ```rust
88    /// # use geo_types::{Line, point};
89    /// # let line = Line::new(
90    /// #     point! { x: 4., y: -12. },
91    /// #     point! { x: 0., y: 9. },
92    /// # );
93    /// # assert_eq!(
94    /// #     line.slope(),
95    /// line.dy() / line.dx()
96    /// # );
97    /// ```
98    ///
99    /// Note that:
100    ///
101    /// ```rust
102    /// # use geo_types::{Line, point};
103    /// # let a = point! { x: 4., y: -12. };
104    /// # let b = point! { x: 0., y: 9. };
105    /// # assert!(
106    /// Line::new(a, b).slope() == Line::new(b, a).slope()
107    /// # );
108    /// ```
109    pub fn slope(&self) -> T {
110        self.dy() / self.dx()
111    }
112
113    /// Calculate the [determinant](https://en.wikipedia.org/wiki/Determinant) of the line.
114    ///
115    /// Equivalent to:
116    ///
117    /// ```rust
118    /// # use geo_types::{Line, point};
119    /// # let line = Line::new(
120    /// #     point! { x: 4., y: -12. },
121    /// #     point! { x: 0., y: 9. },
122    /// # );
123    /// # assert_eq!(
124    /// #     line.determinant(),
125    /// line.start.x * line.end.y - line.start.y * line.end.x
126    /// # );
127    /// ```
128    ///
129    /// Note that:
130    ///
131    /// ```rust
132    /// # use geo_types::{Line, point};
133    /// # let a = point! { x: 4., y: -12. };
134    /// # let b = point! { x: 0., y: 9. };
135    /// # assert!(
136    /// Line::new(a, b).determinant() == -Line::new(b, a).determinant()
137    /// # );
138    /// ```
139    pub fn determinant(&self) -> T {
140        self.start.x * self.end.y - self.start.y * self.end.x
141    }
142
143    pub fn start_point(&self) -> Point<T> {
144        Point::from(self.start)
145    }
146
147    pub fn end_point(&self) -> Point<T> {
148        Point::from(self.end)
149    }
150
151    pub fn points(&self) -> (Point<T>, Point<T>) {
152        (self.start_point(), self.end_point())
153    }
154}
155
156impl<T: CoordNum> From<[(T, T); 2]> for Line<T> {
157    fn from(coord: [(T, T); 2]) -> Self {
158        Line::new(coord[0], coord[1])
159    }
160}
161
162#[cfg(any(feature = "approx", test))]
163mod approx_integration {
164    use super::*;
165    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
166
167    impl<T> RelativeEq for Line<T>
168    where
169        T: CoordNum + RelativeEq<Epsilon = T>,
170    {
171        #[inline]
172        fn default_max_relative() -> Self::Epsilon {
173            T::default_max_relative()
174        }
175
176        /// Equality assertion within a relative limit.
177        ///
178        /// # Examples
179        ///
180        /// ```
181        /// use geo_types::{coord, Line};
182        ///
183        /// let a = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
184        /// let b = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1.001, y: 1. });
185        ///
186        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
187        /// ```
188        #[inline]
189        fn relative_eq(
190            &self,
191            other: &Self,
192            epsilon: Self::Epsilon,
193            max_relative: Self::Epsilon,
194        ) -> bool {
195            self.start.relative_eq(&other.start, epsilon, max_relative)
196                && self.end.relative_eq(&other.end, epsilon, max_relative)
197        }
198    }
199
200    impl<T> AbsDiffEq for Line<T>
201    where
202        T: CoordNum + AbsDiffEq<Epsilon = T>,
203    {
204        type Epsilon = T;
205
206        #[inline]
207        fn default_epsilon() -> Self::Epsilon {
208            T::default_epsilon()
209        }
210
211        /// Equality assertion with an absolute limit.
212        ///
213        /// # Examples
214        ///
215        /// ```
216        /// use geo_types::{coord, Line};
217        ///
218        /// let a = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
219        /// let b = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1.001, y: 1. });
220        ///
221        /// approx::assert_abs_diff_eq!(a, b, epsilon=0.1);
222        /// ```
223        #[inline]
224        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
225            self.start.abs_diff_eq(&other.start, epsilon)
226                && self.end.abs_diff_eq(&other.end, epsilon)
227        }
228    }
229
230    impl<T> UlpsEq for Line<T>
231    where
232        T: CoordNum + UlpsEq<Epsilon = T>,
233    {
234        fn default_max_ulps() -> u32 {
235            T::default_max_ulps()
236        }
237
238        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
239            self.start.ulps_eq(&other.start, epsilon, max_ulps)
240                && self.end.ulps_eq(&other.end, epsilon, max_ulps)
241        }
242    }
243}
244
245#[cfg(any(
246    feature = "rstar_0_8",
247    feature = "rstar_0_9",
248    feature = "rstar_0_10",
249    feature = "rstar_0_11",
250    feature = "rstar_0_12"
251))]
252macro_rules! impl_rstar_line {
253    ($rstar:ident) => {
254        impl<T> ::$rstar::RTreeObject for Line<T>
255        where
256            T: ::num_traits::Float + ::$rstar::RTreeNum,
257        {
258            type Envelope = ::$rstar::AABB<Point<T>>;
259
260            fn envelope(&self) -> Self::Envelope {
261                ::$rstar::AABB::from_corners(self.start_point(), self.end_point())
262            }
263        }
264
265        impl<T> ::$rstar::PointDistance for Line<T>
266        where
267            T: ::num_traits::Float + ::$rstar::RTreeNum,
268        {
269            fn distance_2(&self, point: &Point<T>) -> T {
270                let d = crate::private_utils::point_line_euclidean_distance(*point, *self);
271                d.powi(2)
272            }
273        }
274    };
275}
276
277#[cfg(feature = "rstar_0_8")]
278impl_rstar_line!(rstar_0_8);
279
280#[cfg(feature = "rstar_0_9")]
281impl_rstar_line!(rstar_0_9);
282
283#[cfg(feature = "rstar_0_10")]
284impl_rstar_line!(rstar_0_10);
285
286#[cfg(feature = "rstar_0_11")]
287impl_rstar_line!(rstar_0_11);
288
289#[cfg(feature = "rstar_0_12")]
290impl_rstar_line!(rstar_0_12);
291
292#[cfg(test)]
293mod test {
294    use super::*;
295    use crate::{coord, point};
296    use approx::{AbsDiffEq, RelativeEq};
297
298    #[test]
299    fn test_abs_diff_eq() {
300        let delta = 1e-6;
301        let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
302        let line_start_x = Line::new(
303            point! {
304                x: 0. + delta,
305                y: 0.,
306            },
307            point! { x: 1., y: 1. },
308        );
309        assert!(line.abs_diff_eq(&line_start_x, 1e-2));
310        assert!(line.abs_diff_ne(&line_start_x, 1e-12));
311
312        let line_start_y = Line::new(
313            coord! {
314                x: 0.,
315                y: 0. + delta,
316            },
317            coord! { x: 1., y: 1. },
318        );
319        assert!(line.abs_diff_eq(&line_start_y, 1e-2));
320        assert!(line.abs_diff_ne(&line_start_y, 1e-12));
321
322        let line_end_x = Line::new(
323            coord! { x: 0., y: 0. },
324            coord! {
325                x: 1. + delta,
326                y: 1.,
327            },
328        );
329
330        assert!(line.abs_diff_eq(&line_end_x, 1e-2));
331        assert!(line.abs_diff_ne(&line_end_x, 1e-12));
332
333        let line_end_y = Line::new(
334            coord! { x: 0., y: 0. },
335            coord! {
336                x: 1.,
337                y: 1. + delta,
338            },
339        );
340
341        assert!(line.abs_diff_eq(&line_end_y, 1e-2));
342        assert!(line.abs_diff_ne(&line_end_y, 1e-12));
343    }
344
345    #[test]
346    fn test_relative_eq() {
347        let delta = 1e-6;
348
349        let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
350        let line_start_x = Line::new(
351            point! {
352                x: 0. + delta,
353                y: 0.,
354            },
355            point! { x: 1., y: 1. },
356        );
357        let line_start_y = Line::new(
358            coord! {
359                x: 0.,
360                y: 0. + delta,
361            },
362            coord! { x: 1., y: 1. },
363        );
364
365        assert!(line.relative_eq(&line_start_x, 1e-2, 1e-2));
366        assert!(line.relative_ne(&line_start_x, 1e-12, 1e-12));
367
368        assert!(line.relative_eq(&line_start_y, 1e-2, 1e-2));
369        assert!(line.relative_ne(&line_start_y, 1e-12, 1e-12));
370    }
371}