physdes/
point.rs

1// #![no_std]
2
3use super::Vector2;
4#[cfg(any(test, feature = "std"))]
5#[cfg(test)]
6use core::hash;
7use core::ops::{Add, Neg, Sub};
8use num_traits::Num;
9
10/// The code defines a generic Point struct with x and y coordinates.
11///
12/// Properties:
13///
14/// * `xcoord`: The `xcoord` property represents the x-coordinate of a point in a two-dimensional space.
15/// It is a generic type `T`, which means it can be any type that implements the necessary traits for
16/// the `Point` struct.
17/// * `ycoord`: The `ycoord` property represents the y-coordinate of a point in a two-dimensional space.
18/// It is a generic type `T`, which means it can be any type that implements the necessary traits for
19/// the `Point` struct.
20#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Default)]
21pub struct Point<T> {
22    /// x portion of the Point object
23    pub xcoord: T,
24    /// y portion of the Point object
25    pub ycoord: T,
26}
27
28impl<T> Point<T> {
29    /// The `new` function creates a new `Point` with the given x and y coordinates.
30    ///
31    /// Arguments:
32    ///
33    /// * `xcoord`: The `xcoord` parameter represents the x-coordinate of the point. It is of type `T`,
34    /// which means it can be any type that implements the necessary traits for mathematical operations.
35    /// * `ycoord`: The `ycoord` parameter represents the y-coordinate of the point. It is used to
36    /// specify the vertical position of the point in a two-dimensional coordinate system.
37    ///
38    /// Returns:
39    ///
40    /// The `new` function returns a new instance of the `Point` struct with the specified `xcoord` and
41    /// `ycoord` values.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use physdes::point::Point;
47    /// assert_eq!(Point::new(3, 4).xcoord, 3);
48    /// assert_eq!(Point::new(3, 4).ycoord, 4);
49    /// ```
50    #[inline]
51    pub const fn new(xcoord: T, ycoord: T) -> Self {
52        Point { xcoord, ycoord }
53    }
54}
55
56macro_rules! forward_xf_xf_binop {
57    (impl $imp:ident, $method:ident) => {
58        impl<'a, 'b, T: Clone + Num> $imp<&'b Vector2<T>> for &'a Point<T> {
59            type Output = Point<T>;
60
61            #[inline]
62            fn $method(self, other: &Vector2<T>) -> Self::Output {
63                self.clone().$method(other.clone())
64            }
65        }
66    };
67}
68
69macro_rules! forward_xf_val_binop {
70    (impl $imp:ident, $method:ident) => {
71        impl<'a, T: Clone + Num> $imp<Vector2<T>> for &'a Point<T> {
72            type Output = Point<T>;
73
74            #[inline]
75            fn $method(self, other: Vector2<T>) -> Self::Output {
76                self.clone().$method(other)
77            }
78        }
79    };
80}
81
82macro_rules! forward_val_xf_binop {
83    (impl $imp:ident, $method:ident) => {
84        impl<'a, T: Clone + Num> $imp<&'a Vector2<T>> for Point<T> {
85            type Output = Point<T>;
86
87            #[inline]
88            fn $method(self, other: &Vector2<T>) -> Self::Output {
89                self.$method(other.clone())
90            }
91        }
92    };
93}
94
95macro_rules! forward_all_binop {
96    (impl $imp:ident, $method:ident) => {
97        forward_xf_xf_binop!(impl $imp, $method);
98        forward_xf_val_binop!(impl $imp, $method);
99        forward_val_xf_binop!(impl $imp, $method);
100    };
101}
102
103// arithmetic
104forward_all_binop!(impl Add, add);
105
106// (a, b) + (c, d) == (a + c), (b + d)
107impl<T: Clone + Num> Add<Vector2<T>> for Point<T> {
108    type Output = Self;
109
110    /// Translate a new Point
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use physdes::point::Point;
116    /// use physdes::vector2::Vector2;
117    ///
118    /// assert_eq!(Point::new(3, 4) + Vector2::new(5, 3), Point::new(8, 7));
119    /// assert_eq!(Point::new(3, 4) + Vector2::new(-5, -3), Point::new(-2, 1));
120    /// assert_eq!(Point::new(3, 4) + Vector2::new(5, -3), Point::new(8, 1));
121    /// assert_eq!(Point::new(3, 4) + Vector2::new(-5, 3), Point::new(-2, 7));
122    /// assert_eq!(Point::new(3, 4) + Vector2::new(0, 0), Point::new(3, 4));
123    /// assert_eq!(Point::new(3, 4) + Vector2::new(0, 5), Point::new(3, 9));
124    /// ```
125    #[inline]
126    fn add(self, other: Vector2<T>) -> Self::Output {
127        Self::Output::new(self.xcoord + other.x_, self.ycoord + other.y_)
128    }
129}
130
131forward_all_binop!(impl Sub, sub);
132
133// (a, b) - (c, d) == (a - c), (b - d)
134impl<T: Clone + Num> Sub<Vector2<T>> for Point<T> {
135    type Output = Self;
136
137    /// Translate a new Point
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// use physdes::point::Point;
143    /// use physdes::vector2::Vector2;
144    /// assert_eq!(Point::new(3, 4) - Vector2::new(5, 3), Point::new(-2, 1));
145    /// assert_eq!(Point::new(3, 4) - Vector2::new(-5, -3), Point::new(8, 7));
146    /// assert_eq!(Point::new(3, 4) - Vector2::new(5, -3), Point::new(-2, 7));
147    /// assert_eq!(Point::new(3, 4) - Vector2::new(-5, 3), Point::new(8, 1));
148    /// assert_eq!(Point::new(3, 4) - Vector2::new(0, 0), Point::new(3, 4));
149    /// assert_eq!(Point::new(3, 4) - Vector2::new(0, 5), Point::new(3, -1));
150    /// assert_eq!(Point::new(3, 4) - Vector2::new(5, 0), Point::new(-2, 4));
151    /// ```
152    #[inline]
153    fn sub(self, other: Vector2<T>) -> Self::Output {
154        Self::Output::new(self.xcoord - other.x_, self.ycoord - other.y_)
155    }
156}
157
158macro_rules! forward_xf_xf_binop2 {
159    (impl $imp:ident, $method:ident) => {
160        impl<'a, 'b, T: Clone + Num> $imp<&'b Point<T>> for &'a Point<T> {
161            type Output = Vector2<T>;
162
163            #[inline]
164            fn $method(self, other: &Point<T>) -> Self::Output {
165                self.clone().$method(other.clone())
166            }
167        }
168    };
169}
170
171macro_rules! forward_xf_val_binop2 {
172    (impl $imp:ident, $method:ident) => {
173        impl<'a, T: Clone + Num> $imp<Point<T>> for &'a Point<T> {
174            type Output = Vector2<T>;
175
176            #[inline]
177            fn $method(self, other: Point<T>) -> Self::Output {
178                self.clone().$method(other)
179            }
180        }
181    };
182}
183
184macro_rules! forward_val_xf_binop2 {
185    (impl $imp:ident, $method:ident) => {
186        impl<'a, T: Clone + Num> $imp<&'a Point<T>> for Point<T> {
187            type Output = Vector2<T>;
188
189            #[inline]
190            fn $method(self, other: &Point<T>) -> Self::Output {
191                self.$method(other.clone())
192            }
193        }
194    };
195}
196
197macro_rules! forward_all_binop2 {
198    (impl $imp:ident, $method:ident) => {
199        forward_xf_xf_binop2!(impl $imp, $method);
200        forward_xf_val_binop2!(impl $imp, $method);
201        forward_val_xf_binop2!(impl $imp, $method);
202    };
203}
204
205// arithmetic
206forward_all_binop2!(impl Sub, sub);
207
208impl<T: Clone + Num> Sub for Point<T> {
209    type Output = Vector2<T>;
210
211    /// Displacement of two Points
212    ///
213    /// Arguments:
214    ///
215    /// * `other`: The `other` parameter is of the same type as `self` and represents the other object
216    /// that you want to subtract from `self`.
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// use physdes::point::Point;
222    /// use physdes::vector2::Vector2;
223    ///
224    /// assert_eq!(Point::new(3, 4) - Point::new(5, 3), Vector2::new(-2, 1));
225    /// assert_eq!(Point::new(3, 4) - Point::new(-5, -3), Vector2::new(8, 7));
226    /// assert_eq!(Point::new(3, 4) - Point::new(5, -3), Vector2::new(-2, 7));
227    /// assert_eq!(Point::new(3, 4) - Point::new(-5, 3), Vector2::new(8, 1));
228    /// assert_eq!(Point::new(3, 4) - Point::new(0, 0), Vector2::new(3, 4));
229    #[inline]
230    fn sub(self, other: Self) -> Self::Output {
231        Self::Output::new(self.xcoord - other.xcoord, self.ycoord - other.ycoord)
232    }
233}
234
235// Op Assign
236
237mod opassign {
238    use core::ops::{AddAssign, SubAssign};
239
240    use num_traits::NumAssign;
241
242    use crate::Point;
243    use crate::Vector2;
244
245    impl<T: Clone + NumAssign> AddAssign<Vector2<T>> for Point<T> {
246        fn add_assign(&mut self, other: Vector2<T>) {
247            self.xcoord += other.x_;
248            self.ycoord += other.y_;
249        }
250    }
251
252    impl<T: Clone + NumAssign> SubAssign<Vector2<T>> for Point<T> {
253        fn sub_assign(&mut self, other: Vector2<T>) {
254            self.xcoord -= other.x_;
255            self.ycoord -= other.y_;
256        }
257    }
258
259    macro_rules! forward_op_assign {
260        (impl $imp:ident, $method:ident) => {
261            impl<'a, T: Clone + NumAssign> $imp<&'a Vector2<T>> for Point<T> {
262                #[inline]
263                fn $method(&mut self, other: &Vector2<T>) {
264                    self.$method(other.clone())
265                }
266            }
267        };
268    }
269
270    forward_op_assign!(impl AddAssign, add_assign);
271    forward_op_assign!(impl SubAssign, sub_assign);
272}
273
274impl<T: Clone + Num + Neg<Output = T>> Neg for Point<T> {
275    type Output = Self;
276
277    /// Negate a Points
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use physdes::point::Point;
283    ///
284    /// assert_eq!(-Point::new(3, 4), Point::new(-3, -4));
285    /// assert_eq!(-Point::new(0, 0), Point::new(0, 0));
286    #[inline]
287    fn neg(self) -> Self::Output {
288        Self::Output::new(-self.xcoord, -self.ycoord)
289    }
290}
291
292impl<'a, T: Clone + Num + Neg<Output = T>> Neg for &'a Point<T> {
293    type Output = Point<T>;
294
295    #[inline]
296    fn neg(self) -> Self::Output {
297        -self.clone()
298    }
299}
300
301#[cfg(test)]
302fn hash<T: hash::Hash>(x: &T) -> u64 {
303    use std::collections::hash_map::RandomState;
304    use std::hash::{BuildHasher, Hasher};
305    let mut hasher = <RandomState as BuildHasher>::Hasher::new();
306    x.hash(&mut hasher);
307    hasher.finish()
308}
309
310#[cfg(test)]
311mod test {
312    #![allow(non_upper_case_globals)]
313
314    use super::{hash, Point, Vector2};
315    use core::i32;
316
317    pub const _0_0p: Point<i32> = Point {
318        xcoord: 0,
319        ycoord: 0,
320    };
321    pub const _1_0p: Point<i32> = Point {
322        xcoord: 1,
323        ycoord: 0,
324    };
325    pub const _1_1p: Point<i32> = Point {
326        xcoord: 1,
327        ycoord: 1,
328    };
329    pub const _0_1p: Point<i32> = Point {
330        xcoord: 0,
331        ycoord: 1,
332    };
333    pub const _neg1_1p: Point<i32> = Point {
334        xcoord: -1,
335        ycoord: 1,
336    };
337    // pub const all_consts: [Point<i32>; 4] = [_0_0p, _1_0p, _1_1p, _neg1_1p];
338    pub const _4_2p: Point<i32> = Point {
339        xcoord: 4,
340        ycoord: 2,
341    };
342
343    #[test]
344    fn test_consts() {
345        // check our constants are what Point::new creates
346        fn test(c: Point<i32>, r: i32, i: i32) {
347            assert_eq!(c, Point::new(r, i));
348        }
349        test(_0_0p, 0, 0);
350        test(_1_0p, 1, 0);
351        test(_1_1p, 1, 1);
352        test(_neg1_1p, -1, 1);
353    }
354
355    #[test]
356    fn test_hash() {
357        let a = Point::new(0i32, 0i32);
358        let b = Point::new(1i32, 0i32);
359        let c = Point::new(0i32, 1i32);
360        assert!(hash(&a) != hash(&b));
361        assert!(hash(&b) != hash(&c));
362        assert!(hash(&c) != hash(&a));
363    }
364
365    #[test]
366    fn test_add() {
367        let a = Point::new(0i32, 0i32);
368        let b = Point::new(1i32, 0i32);
369        let v = Vector2::new(5i32, 6i32);
370        assert_eq!(a, a + v - v);
371        assert_eq!(b, b - v + v);
372    }
373}