rusvid_core/
point.rs

1use approx::{AbsDiffEq, RelativeEq, UlpsEq};
2use glam::DVec2;
3
4/// A 2-dimensional vector.
5#[derive(Debug, Clone, Copy, PartialEq, Default)]
6pub struct Point(DVec2);
7
8impl Point {
9    /// All zeroes.
10    pub const ZERO: Self = Self::new_symmetric(0.0);
11
12    /// All ones.
13    pub const ONE: Self = Self::new_symmetric(1.0);
14
15    /// All negative ones.
16    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
171/// Trait to transform struct into a [crate::types::Point]
172pub trait AsPoint {
173    /// Returns values of the struct as [crate::types::Point].
174    ///
175    /// Used to calculate with the values more easily
176    /// ```rust
177    /// use rusvid_core::point::{AsPoint, Point};
178    ///
179    /// struct Resolution((f64, f64));
180    ///
181    /// impl AsPoint for Resolution {
182    ///     fn as_point(&self) -> Point {
183    ///         Point::new(self.0.0, self.0.1)
184    ///     }
185    /// }
186    ///
187    /// let res = Resolution((100.0, 100.0));
188    /// assert_eq!(res.as_point(), Point::new(100.0, 100.0));
189    /// assert_eq!(res.as_point() * Point::NEG_ONE, Point::new(-100.0, -100.0));
190    /// ```
191    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}