geo_buf/util/coordinate/
mod.rs

1use core::ops::{Add, Div, Mul, Sub};
2
3use crate::util::{feq, Ray};
4
5/// This structure conceptually represents a point or a vector on
6/// the 2-dimensional Cartesian plane.
7///
8/// It may be vary on the context which represents which.
9#[derive(Clone, Default, Debug, Copy, PartialEq, PartialOrd)]
10pub struct Coordinate(
11    /// x-component of the Cartesian coordinates.
12    pub f64,
13    /// y-component of the Cartesian coordinates.
14    pub f64,
15);
16
17impl From<Coordinate> for (f64, f64) {
18    fn from(item: Coordinate) -> (f64, f64) {
19        (item.0, item.1)
20    }
21}
22
23impl From<(f64, f64)> for Coordinate {
24    fn from(item: (f64, f64)) -> Coordinate {
25        Coordinate(item.0, item.1)
26    }
27}
28
29impl From<geo_types::Coord<f64>> for Coordinate {
30    fn from(value: geo_types::Coord<f64>) -> Self {
31        Coordinate(value.x, value.y)
32    }
33}
34
35impl From<Coordinate> for geo_types::Coord<f64> {
36    fn from(value: Coordinate) -> geo_types::Coord<f64> {
37        geo_types::geometry::Coord {
38            x: value.0,
39            y: value.1,
40        }
41    }
42}
43
44impl Add for Coordinate {
45    type Output = Self;
46    fn add(self, rhs: Self) -> Self {
47        Self(self.0 + rhs.0, self.1 + rhs.1)
48    }
49}
50
51impl Sub for Coordinate {
52    type Output = Self;
53    fn sub(self, rhs: Self) -> Self {
54        Self(self.0 - rhs.0, self.1 - rhs.1)
55    }
56}
57
58impl Mul<f64> for Coordinate {
59    type Output = Self;
60    fn mul(self, rhs: f64) -> Self::Output {
61        Self(self.0 * rhs, self.1 * rhs)
62    }
63}
64
65impl Div<f64> for Coordinate {
66    type Output = Self;
67    fn div(self, rhs: f64) -> Self::Output {
68        if rhs == 0. {
69            return self;
70        }
71        Self(self.0 / rhs, self.1 / rhs)
72    }
73}
74
75impl Div<Coordinate> for Coordinate {
76    type Output = f64;
77    fn div(self, rhs: Self) -> Self::Output {
78        if rhs.0 == 0. && rhs.1 == 0. {
79            0.
80        } else if rhs.1 == 0. {
81            return self.0 / rhs.0;
82        } else {
83            return self.1 / rhs.1;
84        }
85    }
86}
87
88impl Coordinate {
89    /// Creates and returns a [Coordinate] w.r.t. the given argument.
90    ///
91    /// # Argument
92    ///
93    /// + `x`: x-component of the `Coordinate`.
94    /// + `y`: y-component of the `Coordinate`.
95    ///
96    /// # Example
97    ///
98    /// ```
99    /// let c1 = geo_buf::Coordinate::new(3., 4.);
100    /// assert_eq!(c1, (3., 4.).into());
101    /// ```
102    pub fn new(x: f64, y: f64) -> Self {
103        Self(x, y)
104    }
105
106    /// Returns a tuple wihch has values of each component.
107    ///
108    /// # Example
109    ///
110    /// ```
111    /// let c1 = geo_buf::Coordinate::new(3., 4.);
112    /// let t1 = c1.get_val();
113    /// assert_eq!(t1, (3., 4.));
114    /// ```
115    pub fn get_val(&self) -> (f64, f64) {
116        (self.0, self.1)
117    }
118
119    /// Returns a value of inner product (i.e. dot product) of the Cartesian coordinates of
120    /// two vectors.
121    ///
122    /// # Argument
123    ///
124    /// + `self`: The Cartesian coordinates of the first vector, **a**.
125    /// + `rhs`: The Cartesian coordinates of the second vector, **b**.
126    ///
127    /// # Return
128    ///
129    /// **a** · **b**
130    ///
131    /// # Example
132    ///
133    /// ```
134    /// let c1 = geo_buf::Coordinate::new(1., 2.);
135    /// let c2 = geo_buf::Coordinate::new(3., 4.);
136    /// let ip = c1.inner_product(&c2);
137    /// assert_eq!(ip, 11.);
138    /// ```
139    ///
140    /// # Notes
141    ///
142    /// + This operation is linear.
143    /// + This operation is commutative.
144    ///
145    pub fn inner_product(&self, rhs: &Self) -> f64 {
146        self.0 * rhs.0 + self.1 * rhs.1
147    }
148
149    /// Returns a value of the magnitude of cross product of the Cartesian coordinates of
150    /// two vectors.
151    ///
152    /// # Argument
153    ///
154    /// + `self`: The Cartesian coordinates of the first vector, **a**.
155    /// + `rhs`: The Cartesian coordinates of the second vector, **b**.
156    ///
157    /// # Return
158    ///
159    /// **a** × **b**
160    ///
161    /// # Example
162    ///
163    /// ```
164    /// let c1 = geo_buf::Coordinate::new(1., 2.);
165    /// let c2 = geo_buf::Coordinate::new(3., 4.);
166    /// let op = c1.outer_product(&c2);
167    /// assert_eq!(op, -2.);
168    /// ```
169    ///
170    /// # Notes
171    ///
172    /// + This operation is linear.
173    /// + This operation is *not* commutative. (More precisely, it is anti-commutative.)
174    /// + The sign of cross product indicates the orientation of **a** and **b**. If **a** lies before **b** in
175    /// the counter-clockwise (CCW for short) ordering, the sign of the result will be positive. If **a** lies after **b** in CCW ordering,
176    /// the sign will be negative. The result will be zero if two vectors are colinear. (I.e. lay on the same line.)
177    ///
178    pub fn outer_product(&self, rhs: &Self) -> f64 {
179        self.0 * rhs.1 - self.1 * rhs.0
180    }
181
182    /// Returns the Euclidean norm (i.e. magnitude, or L2 norm) of the given vector.
183    ///
184    /// # Example
185    ///
186    /// ```
187    /// let c1 = geo_buf::Coordinate::new(3., 4.);
188    /// assert_eq!(c1.norm(), 5.);
189    /// ```
190    pub fn norm(&self) -> f64 {
191        self.inner_product(self).sqrt()
192    }
193
194    /// Returns the distance between two Cartesian coordinates.
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// let c1 = geo_buf::Coordinate::new(3., 4.);
200    /// let c2 = geo_buf::Coordinate::new(7., 7.);
201    /// assert_eq!(c1.dist_coord(&c2), 5.);
202    /// ```
203    pub fn dist_coord(&self, rhs: &Coordinate) -> f64 {
204        f64::sqrt((self.0 - rhs.0) * (self.0 - rhs.0) + (self.1 - rhs.1) * (self.1 - rhs.1))
205    }
206
207    /// Returns the distance from `self` to the given ray.
208    ///
209    /// Note that this function considers the given ray as a open-ended line.
210    /// That is, the foot of perpendicular may lay on the extended line of the given ray.
211    ///
212    /// # Example
213    ///
214    /// ```
215    /// use geo_buf::{Coordinate, Ray};
216    ///
217    /// let r1 = Ray::new((0., 3.).into(), (4., 0.).into());
218    /// let c1 = Coordinate::new(0., 0.);
219    /// assert_eq!(c1.dist_ray(&r1), 2.4);
220    /// ```
221    ///
222    pub fn dist_ray(&self, rhs: &Ray) -> f64 {
223        if rhs.is_degenerated() {
224            return self.dist_coord(&rhs.origin);
225        }
226        f64::abs((*self - rhs.origin).outer_product(&rhs.angle)) / rhs.angle.norm()
227    }
228
229    /// Checks whether the given two Cartesian coordinates are the same (by the equality test with a small epsilon).
230    ///
231    /// # Result
232    ///
233    /// + `true` if the given coordinates are the same.
234    /// + `false` otherwise.
235    ///
236    /// # Example
237    ///
238    /// ```
239    /// let c1 = geo_buf::Coordinate::new(0.1, 0.2);
240    /// let c2 = geo_buf::Coordinate::new(0.2, 0.3);
241    /// let c3 = geo_buf::Coordinate::new(0.3, 0.5);
242    /// let c4 = c1 + c2;
243    /// assert!(c3.eq(&c4));
244    /// ```
245    ///
246    /// # Example (this example panics)
247    ///
248    /// ```should_panic
249    /// let c1 = geo_buf::Coordinate::new(0.1, 0.2);
250    /// let c2 = geo_buf::Coordinate::new(0.2, 0.3);
251    /// let c3 = geo_buf::Coordinate::new(0.3, 0.5);
252    /// let c4 = c1 + c2;
253    /// assert_eq!(c3, c4); // should panic since 0.1 + 0.2 != 0.3 due to floating point errors
254    /// ```
255    pub fn eq(&self, rhs: &Self) -> bool {
256        feq(self.0, rhs.0) && feq(self.1, rhs.1)
257    }
258}