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}