geo_buffer/util/coordinate/
mod.rs

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