1use approx::{AbsDiffEq, RelativeEq, UlpsEq};
2use glam::DVec2;
3
4#[derive(Debug, Clone, Copy, PartialEq, Default)]
6pub struct Point(DVec2);
7
8impl Point {
9 pub const ZERO: Self = Self::new_symmetric(0.0);
11
12 pub const ONE: Self = Self::new_symmetric(1.0);
14
15 pub const NEG_ONE: Self = Self::new_symmetric(-1.0);
17
18 pub const fn new(x: f64, y: f64) -> Self {
19 Self::from_dvec(DVec2::new(x, y))
20 }
21
22 pub const fn new_symmetric(v: f64) -> Self {
23 Self::new(v, v)
24 }
25
26 const fn from_dvec(dvec: DVec2) -> Self {
27 Point(dvec)
28 }
29
30 pub fn x(&self) -> f64 {
31 self.0.x
32 }
33
34 pub fn y(&self) -> f64 {
35 self.0.y
36 }
37
38 pub fn x_mut(&mut self) -> &mut f64 {
39 &mut self.0.x
40 }
41
42 pub fn y_mut(&mut self) -> &mut f64 {
43 &mut self.0.y
44 }
45}
46
47impl From<(f64, f64)> for Point {
48 fn from(raw: (f64, f64)) -> Self {
49 Point::new(raw.0, raw.1)
50 }
51}
52
53impl AsRef<[f64; 2]> for Point {
54 fn as_ref(&self) -> &[f64; 2] {
55 self.0.as_ref()
56 }
57}
58
59impl AbsDiffEq for Point {
60 type Epsilon = <f64 as AbsDiffEq>::Epsilon;
61
62 #[cfg_attr(coverage_nightly, no_coverage)]
63 fn default_epsilon() -> Self::Epsilon {
64 f64::default_epsilon()
65 }
66
67 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
68 self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
69 }
70}
71
72impl RelativeEq for Point {
73 #[cfg_attr(coverage_nightly, no_coverage)]
74 fn default_max_relative() -> Self::Epsilon {
75 f64::default_max_relative()
76 }
77
78 fn relative_eq(
79 &self,
80 other: &Self,
81 epsilon: Self::Epsilon,
82 max_relative: Self::Epsilon,
83 ) -> bool {
84 self.as_ref()
85 .relative_eq(other.as_ref(), epsilon, max_relative)
86 }
87}
88
89impl UlpsEq for Point {
90 #[cfg_attr(coverage_nightly, no_coverage)]
91 fn default_max_ulps() -> u32 {
92 f64::default_max_ulps()
93 }
94
95 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
96 self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
97 }
98}
99
100macro_rules! implement_math_operator {
101 ($operant:ident, $fct:ident, Point for Point) => {
102 impl std::ops::$operant<Point> for Point {
103 type Output = Point;
104 fn $fct(self, rhs: Self) -> Self::Output {
105 Point(self.0.$fct(rhs.0))
106 }
107 }
108 };
109 ($operant:ident, $fct:ident, f64 for Point) => {
110 impl std::ops::$operant<f64> for Point {
111 type Output = Point;
112 fn $fct(self, rhs: f64) -> Self::Output {
113 Point(self.0.$fct(rhs))
114 }
115 }
116 };
117 ($operant:ident, $fct:ident, Point for f64) => {
118 impl std::ops::$operant<Point> for f64 {
119 type Output = Point;
120 fn $fct(self, rhs: Self::Output) -> Self::Output {
121 Point(DVec2 {
122 x: self.$fct(rhs.x()),
123 y: self.$fct(rhs.y()),
124 })
125 }
126 }
127 };
128 ($operant:ident, $fct:ident, Point for assign $other_type:ident) => {
129 concat_idents::concat_idents!(method_name = $fct, _assign {
130 impl std::ops::$operant<$other_type> for Point {
131 fn method_name(&mut self, other: $other_type) {
132 use std::ops::*;
133
134 *self = self.$fct(other);
135 }
136 }
137 });
138 };
139}
140
141implement_math_operator!(Add, add, f64 for Point);
142implement_math_operator!(Add, add, Point for f64);
143implement_math_operator!(Add, add, Point for Point);
144implement_math_operator!(AddAssign, add, Point for assign f64);
145implement_math_operator!(AddAssign, add, Point for assign Point);
146
147implement_math_operator!(Div, div, f64 for Point);
148implement_math_operator!(Div, div, Point for f64);
149implement_math_operator!(Div, div, Point for Point);
150implement_math_operator!(DivAssign, div, Point for assign f64);
151implement_math_operator!(DivAssign, div, Point for assign Point);
152
153implement_math_operator!(Mul, mul, f64 for Point);
154implement_math_operator!(Mul, mul, Point for f64);
155implement_math_operator!(Mul, mul, Point for Point);
156implement_math_operator!(MulAssign, mul, Point for assign f64);
157implement_math_operator!(MulAssign, mul, Point for assign Point);
158
159implement_math_operator!(Rem, rem, f64 for Point);
160implement_math_operator!(Rem, rem, Point for f64);
161implement_math_operator!(Rem, rem, Point for Point);
162implement_math_operator!(RemAssign, rem, Point for assign f64);
163implement_math_operator!(RemAssign, rem, Point for assign Point);
164
165implement_math_operator!(Sub, sub, f64 for Point);
166implement_math_operator!(Sub, sub, Point for f64);
167implement_math_operator!(Sub, sub, Point for Point);
168implement_math_operator!(SubAssign, sub, Point for assign f64);
169implement_math_operator!(SubAssign, sub, Point for assign Point);
170
171pub trait AsPoint {
173 fn as_point(&self) -> Point;
192}
193
194#[cfg(test)]
195mod tests {
196 use super::Point;
197
198 #[test]
199 fn coordinate_mut() {
200 let mut p = Point::new(25.0, 50.0);
201
202 assert_eq!(p.x(), 25.0);
203 *p.x_mut() = 75.0;
204 assert_eq!(p.x(), 75.0);
205
206 assert_eq!(p.y(), 50.0);
207 *p.y_mut() = 25.0;
208 assert_eq!(p.y(), 25.0);
209 }
210
211 #[test]
212 fn from_tuple() {
213 let tuple = (50.0, -10.0);
214 let p = Point::from(tuple);
215
216 assert_eq!(tuple.0, p.x());
217 assert_eq!(tuple.1, p.y());
218 }
219
220 #[test]
221 fn as_ref() {
222 let p = Point::new(250.0, -300.0);
223 let slice = p.as_ref();
224
225 assert_eq!(p.x(), slice[0]);
226 assert_eq!(p.y(), slice[1]);
227 }
228
229 mod approx {
230 use approx::*;
231
232 use crate::point::Point;
233
234 #[test]
235 fn abs_diff_eq() {
236 let p1 = Point::new(100.0, 200.0);
237 let p2 = Point::new(100.0001, 199.9999);
238 let p3 = Point::new(75.0, 220.0);
239
240 assert_eq!(p1.abs_diff_eq(&p2, 0.0), false);
241 assert_eq!(p1.abs_diff_eq(&p3, 0.0), false);
242
243 assert_eq!(p1.abs_diff_eq(&p2, 0.001), true);
244 assert_eq!(p1.abs_diff_eq(&p3, 0.001), false);
245
246 assert_eq!(p1.abs_diff_eq(&p2, 30.0), true);
247 assert_eq!(p1.abs_diff_eq(&p3, 30.0), true);
248 }
249
250 #[test]
251 fn relative_eq() {
252 let p1 = Point::new(100.0, 200.0);
253 let p2 = Point::new(100.0001, 199.9999);
254 let p3 = Point::new(75.0, 220.0);
255
256 assert_eq!(p1.relative_eq(&p2, 0.0, 0.0), false);
257 assert_eq!(p1.relative_eq(&p3, 0.0, 0.0), false);
258
259 assert_eq!(p1.relative_eq(&p2, 0.001, 0.0), true);
260 assert_eq!(p1.relative_eq(&p3, 0.001, 0.0), false);
261
262 assert_eq!(p1.relative_eq(&p3, 30.0, 0.0), true);
263 assert_eq!(p1.relative_eq(&p2, 30.0, 0.0), true);
264 }
265 }
266
267 mod math {
268 use crate::point::Point;
269
270 #[test]
271 fn add() {
272 let p1 = Point::new(25.0, 50.0);
273 let p2 = Point::new_symmetric(10.0);
274
275 assert_eq!(10.0 + p1, Point::new(35.0, 60.0));
276 assert_eq!(p1 + 10.0, Point::new(35.0, 60.0));
277 assert_eq!(p1 + p2, Point::new(35.0, 60.0));
278
279 let mut p3 = Point::new(35.0, -10.0);
280 p3 += 10.0;
281 assert_eq!(p3, Point::new(45.0, 0.0));
282 p3 += Point::new(-35.0, 100.0);
283 assert_eq!(p3, Point::new(10.0, 100.0));
284 }
285
286 #[test]
287 fn sub() {
288 let p1 = Point::new(25.0, 50.0);
289 let p2 = Point::new_symmetric(10.0);
290
291 assert_eq!(10.0 - p1, Point::new(-15.0, -40.0));
292 assert_eq!(p1 - 10.0, Point::new(15.0, 40.0));
293 assert_eq!(p1 - p2, Point::new(15.0, 40.0));
294
295 let mut p3 = Point::new(35.0, -10.0);
296 p3 -= 10.0;
297 assert_eq!(p3, Point::new(25.0, -20.0));
298 p3 -= Point::new(-35.0, 100.0);
299 assert_eq!(p3, Point::new(60.0, -120.0));
300 }
301
302 #[test]
303 fn mul() {
304 let p1 = Point::new(25.0, 50.0);
305 let p2 = Point::new_symmetric(10.0);
306
307 assert_eq!(10.0 * p1, Point::new(250.0, 500.0));
308 assert_eq!(p1 * 10.0, Point::new(250.0, 500.0));
309 assert_eq!(p1 * p2, Point::new(250.0, 500.0));
310
311 let mut p3 = Point::new(35.0, -10.0);
312 p3 *= 5.7;
313 assert_eq!(p3, Point::new(199.5, -57.0));
314 p3 *= Point::new(-35.0, 100.0);
315 assert_eq!(p3, Point::new(-6982.5, -5700.0));
316 }
317 }
318}