gistools/geometry/s2/
point.rs

1use core::cmp::Ordering;
2use core::fmt::Debug;
3use core::ops::{Add, Div, Mul, Neg, Sub};
4
5use libm::{fabs, sqrt};
6
7use crate::{s2::xyz_to_face_uv, wm::LonLat, xyz_to_face_st};
8
9use super::S2CellId;
10
11/// An S2Point represents a point on the unit sphere as a 3D vector. Usually
12/// points are normalized to be unit length, but some methods do not require
13/// this.  See util/math/vector.h for the methods available.  Among other
14/// things, there are overloaded operators that make it convenient to write
15/// arithmetic expressions (e.g. (1-x)*p1 + x*p2).
16/// NOTE: asumes only f64 or greater is used.
17#[derive(Debug, Copy, Clone, Default, PartialEq)]
18pub struct S2Point {
19    /// The x component.
20    pub x: f64,
21    /// The y component.
22    pub y: f64,
23    /// The z component.
24    pub z: f64,
25}
26impl S2Point {
27    /// Creates a new S2Point.
28    pub fn new(x: f64, y: f64, z: f64) -> Self {
29        S2Point { x, y, z }
30    }
31
32    /// Returns true if the point is the zero vector.
33    pub fn is_empty(&self) -> bool {
34        let zero = f64::default();
35        self.x == zero && self.y == zero && self.z == zero
36    }
37
38    /// Returns the S2 face assocated with this point
39    pub fn face(&self, f: u8) -> f64 {
40        if f == 0 {
41            self.x
42        } else if f == 1 {
43            self.y
44        } else {
45            self.z
46        }
47    }
48
49    /// Returns a Face-ST representation of this point
50    pub fn to_face_st(&self) -> (u8, f64, f64) {
51        xyz_to_face_st(self)
52    }
53
54    /// Returns the S2 face assocated with this point
55    pub fn get_face(&self) -> u8 {
56        xyz_to_face_uv(self).0
57    }
58
59    /// dot returns the standard dot product of v and ov.
60    pub fn dot(&self, b: &Self) -> f64 {
61        self.x * b.x + self.y * b.y + self.z * b.z
62    }
63
64    /// Returns the absolute value of the point.
65    pub fn abs(&self) -> Self {
66        Self::new(fabs(self.x), fabs(self.y), fabs(self.z))
67    }
68
69    /// Returns the length of the point.
70    pub fn len(&self) -> f64 {
71        sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
72    }
73
74    /// return the distance from this point to the other point
75    pub fn distance(&self, b: &Self) -> f64 {
76        let d = *self - *b;
77        d.len()
78    }
79
80    /// Returns the largest absolute component of the point.
81    pub fn largest_abs_component(&self) -> u8 {
82        let tmp = self.abs();
83        if tmp.x > tmp.y {
84            if tmp.x > tmp.z {
85                0
86            } else {
87                2
88            }
89        } else if tmp.y > tmp.z {
90            1
91        } else {
92            2
93        }
94    }
95
96    /// Normalize this point to unit length.
97    pub fn normalize(&mut self) {
98        let mut len = self.x * self.x + self.y * self.y + self.z * self.z;
99        if len > 0.0 {
100            len = sqrt(len);
101            self.x /= len;
102            self.y /= len;
103            self.z /= len;
104        }
105    }
106}
107impl From<&LonLat> for S2Point {
108    fn from(lonlat: &LonLat) -> Self {
109        lonlat.to_point()
110    }
111}
112impl From<&S2CellId> for S2Point {
113    fn from(cellid: &S2CellId) -> Self {
114        cellid.to_point()
115    }
116}
117// Implementing the Add trait for S2Point
118impl Add<S2Point> for S2Point {
119    type Output = Self;
120    fn add(self, other: Self) -> Self::Output {
121        S2Point { x: self.x + other.x, y: self.y + other.y, z: self.z + other.z }
122    }
123}
124impl Add<f64> for S2Point {
125    type Output = Self;
126    fn add(self, other: f64) -> Self::Output {
127        S2Point { x: self.x + other, y: self.y + other, z: self.z + other }
128    }
129}
130// Implementing the Sub trait for S2Point
131impl Sub<S2Point> for S2Point {
132    type Output = Self;
133    fn sub(self, other: Self) -> Self::Output {
134        S2Point { x: self.x - other.x, y: self.y - other.y, z: self.z - other.z }
135    }
136}
137impl Sub<f64> for S2Point {
138    type Output = Self;
139    fn sub(self, other: f64) -> Self::Output {
140        S2Point { x: self.x - other, y: self.y - other, z: self.z - other }
141    }
142}
143// Implementing the Neg trait for S2Point
144impl Neg for S2Point {
145    type Output = Self;
146    fn neg(self) -> Self::Output {
147        S2Point { x: -self.x, y: -self.y, z: -self.z }
148    }
149}
150// Implementing the Div trait for S2Point
151impl Div<S2Point> for S2Point {
152    type Output = Self;
153    fn div(self, other: Self) -> Self::Output {
154        S2Point { x: self.x / other.x, y: self.y / other.y, z: self.z / other.z }
155    }
156}
157impl Div<f64> for S2Point {
158    type Output = Self;
159    fn div(self, other: f64) -> Self::Output {
160        S2Point { x: self.x / other, y: self.y / other, z: self.z / other }
161    }
162}
163// Implementing the Mul trait for S2Point
164impl Mul<S2Point> for S2Point {
165    type Output = Self;
166    fn mul(self, other: Self) -> Self::Output {
167        S2Point { x: self.x * other.x, y: self.y * other.y, z: self.z * other.z }
168    }
169}
170impl Mul<f64> for S2Point {
171    type Output = Self;
172    fn mul(self, other: f64) -> Self::Output {
173        S2Point { x: self.x * other, y: self.y * other, z: self.z * other }
174    }
175}
176impl Eq for S2Point {}
177impl Ord for S2Point {
178    fn cmp(&self, other: &Self) -> Ordering {
179        match self.x.partial_cmp(&other.x) {
180            Some(Ordering::Equal) => {}
181            other => return other.unwrap(), // Handle cases where `x` comparison is not equal
182        }
183        match self.y.partial_cmp(&other.y) {
184            Some(Ordering::Equal) => {}
185            other => return other.unwrap(), // Handle cases where `y` comparison is not equal
186        }
187        match self.z.partial_cmp(&other.z) {
188            Some(order) => order,
189            None => Ordering::Equal, // This handles the NaN case safely
190        }
191    }
192}
193impl PartialOrd for S2Point {
194    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
195        Some(self.cmp(other))
196    }
197}
198
199//     /// norm returns the vector's norm.
200//     pub fn norm(&self) T {
201//         return @sqrt(self.norm2());
202//     }
203
204//     /// norm2 returns the square of the norm.
205//     pub fn norm2(&self) T {
206//         return self.dot(self);
207//     }
208
209//     pub fn normalize(&self) S2PointType(T) {
210//         const len = self.norm();
211//         return Init(self.x / len, self.y / len, self.z / len);
212//     }
213
214//     pub fn distance(a: *const Self, b: *const S2PointType(T)) T {
215//         return @sqrt(pow(@abs(b.x - a.x), 2) + pow(@abs(b.y - a.y), 2) + pow(@abs(b.z - a.z), 2));
216//     }
217
218//     pub fn cross(&self, b: *const S2PointType(T)) S2PointType(T) {
219//         return Init(
220//             self.y * b.z - self.z * b.y,
221//             self.z * b.x - self.x * b.z,
222//             self.x * b.y - self.y * b.x,
223//         );
224//     }
225
226//     pub fn intermediate(&self, b: *const S2PointType(T), t: T) S2PointType(T) {
227//         var c = .{
228//             .x = (self.x) + ((b.x - self.x) * (1 - t)),
229//             .y = (self.y) + ((b.y - self.y) * (1 - t)),
230//             .z = (self.z) + ((b.z - self.z) * (1 - t)),
231//         };
232//         return c.normalize();
233//     }
234
235//     /// Returns the angle between "this" and v in radians, in the range [0, pi]. If
236//     /// either vector is zero-length, or nearly zero-length, the result will be
237//     /// zero, regardless of the other value.
238//     pub fn angle(&self, b: *const S2PointType(T)) T {
239//         return atan2(T, self.cross(b).norm(), self.dot(b));
240//     }
241// };
242// }