Added a hashmap macro
[kaka/rust-sdl-test.git] / src / common.rs
1 use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Neg};
2
3 #[macro_export]
4 macro_rules! point {
5     ( $x:expr, $y:expr ) => {
6         Point2D { x: $x, y: $y }
7     };
8 }
9
10 #[derive(Debug, Default, Copy, Clone, PartialEq)]
11 pub struct Point2D<T> {
12     pub x: T,
13     pub y: T,
14 }
15
16 impl Point2D<f64> {
17     pub fn length(self) -> f64 {
18         ((self.x * self.x) + (self.y * self.y)).sqrt()
19     }
20
21     pub fn to_i32(self) -> Point2D<i32> {
22         Point2D {
23             x: self.x as i32,
24             y: self.y as i32,
25         }
26     }
27 }
28
29 macro_rules! point_op {
30     ($op:tt, $trait:ident($fn:ident), $trait_assign:ident($fn_assign:ident), $rhs:ident = $Rhs:ty => $x:expr, $y:expr) => {
31         impl<T: $trait<Output = T>> $trait<$Rhs> for Point2D<T> {
32             type Output = Self;
33
34             fn $fn(self, $rhs: $Rhs) -> Self {
35                 Self {
36                     x: self.x $op $x,
37                     y: self.y $op $y,
38                 }
39             }
40         }
41
42         impl<T: $trait<Output = T> + Copy> $trait_assign<$Rhs> for Point2D<T> {
43             fn $fn_assign(&mut self, $rhs: $Rhs) {
44                 *self = Self {
45                     x: self.x $op $x,
46                     y: self.y $op $y,
47                 }
48             }
49         }
50     }
51 }
52
53 point_op!(+, Add(add), AddAssign(add_assign), rhs = Point2D<T> => rhs.x, rhs.y);
54 point_op!(-, Sub(sub), SubAssign(sub_assign), rhs = Point2D<T> => rhs.x, rhs.y);
55 point_op!(*, Mul(mul), MulAssign(mul_assign), rhs = Point2D<T> => rhs.x, rhs.y);
56 point_op!(/, Div(div), DivAssign(div_assign), rhs = Point2D<T> => rhs.x, rhs.y);
57 point_op!(+, Add(add), AddAssign(add_assign), rhs = (T, T) => rhs.0, rhs.1);
58 point_op!(-, Sub(sub), SubAssign(sub_assign), rhs = (T, T) => rhs.0, rhs.1);
59 point_op!(*, Mul(mul), MulAssign(mul_assign), rhs = (T, T) => rhs.0, rhs.1);
60 point_op!(/, Div(div), DivAssign(div_assign), rhs = (T, T) => rhs.0, rhs.1);
61
62 ////////// multiply point with scalar //////////////////////////////////////////
63 impl<T: Mul<Output = T> + Copy> Mul<T> for Point2D<T> {
64     type Output = Self;
65
66     fn mul(self, rhs: T) -> Self {
67         Self {
68             x: self.x * rhs,
69             y: self.y * rhs,
70         }
71     }
72 }
73
74 impl<T: Mul<Output = T> + Copy> MulAssign<T> for Point2D<T> {
75     fn mul_assign(&mut self, rhs: T) {
76         *self = Self {
77             x: self.x * rhs,
78             y: self.y * rhs,
79         }
80     }
81 }
82
83 ////////// divide point with scalar ////////////////////////////////////////////
84 impl<T: Div<Output = T> + Copy> Div<T> for Point2D<T> {
85     type Output = Self;
86
87     fn div(self, rhs: T) -> Self {
88         Self {
89             x: self.x / rhs,
90             y: self.y / rhs,
91         }
92     }
93 }
94
95 impl<T: Div<Output = T> + Copy> DivAssign<T> for Point2D<T> {
96     fn div_assign(&mut self, rhs: T) {
97         *self = Self {
98             x: self.x / rhs,
99             y: self.y / rhs,
100         }
101     }
102 }
103
104 impl<T: Neg<Output = T>> Neg for Point2D<T> {
105     type Output = Self;
106
107     fn neg(self) -> Self {
108         Self {
109             x: -self.x,
110             y: -self.y,
111         }
112     }
113 }
114
115 impl<T> From<(T, T)> for Point2D<T> {
116     fn from(item: (T, T)) -> Self {
117         Point2D {
118             x: item.0,
119             y: item.1,
120         }
121     }
122 }
123
124 impl<T> From<Point2D<T>> for (T, T) {
125     fn from(item: Point2D<T>) -> Self {
126         (item.x, item.y)
127     }
128 }
129
130 impl From<Degrees> for Point2D<f64> {
131     fn from(item: Degrees) -> Self {
132         Point2D {
133             x: (item.0 * std::f64::consts::PI / 180.0).cos(),
134             y: (item.0 * std::f64::consts::PI / 180.0).sin(),
135         }
136     }
137 }
138
139 impl From<Radians> for Point2D<f64> {
140     fn from(item: Radians) -> Self {
141         Point2D {
142             x: item.0.cos(),
143             y: item.0.sin(),
144         }
145     }
146 }
147
148 #[derive(Debug, Default, PartialEq, Clone, Copy)]
149 pub struct Degrees(pub f64);
150 #[derive(Debug, Default, PartialEq, Clone, Copy)]
151 pub struct Radians(pub f64);
152
153 impl Degrees {
154     #[allow(dead_code)]
155     fn to_radians(&self) -> Radians {
156         Radians(self.0 * std::f64::consts::PI / 180.0)
157     }
158 }
159
160 impl Radians {
161     #[allow(dead_code)]
162     fn to_degrees(&self) -> Degrees {
163         Degrees(self.0 * 180.0 * std::f64::consts::FRAC_1_PI)
164     }
165 }
166
167 #[macro_export]
168 macro_rules! rect {
169     ( $x:expr, $y:expr ) => {
170         Rect { x: $x, y: $y }
171     };
172 }
173
174 #[derive(Default)]
175 pub struct Rect<T> {
176     pub width: T,
177     pub height: T,
178 }
179
180 impl<T: Mul<Output = T> + Copy> Rect<T> {
181     #[allow(dead_code)]
182     pub fn area(&self) -> T {
183         self.width * self.height
184     }
185 }
186
187 impl<T> From<(T, T)> for Rect<T> {
188     fn from(item: (T, T)) -> Self {
189         Rect {
190             width: item.0,
191             height: item.1,
192         }
193     }
194 }
195
196 #[macro_export]
197 macro_rules! hashmap {
198     ($($k:expr => $v:expr),*) => {
199         {
200             let mut map = std::collections::HashMap::new();
201             $(map.insert($k, $v);)*
202             map
203         }
204     }
205 }
206
207 #[cfg(test)]
208 mod tests {
209     use super::*;
210
211     #[test]
212     fn immutable_copy_of_point() {
213         let a = point!(0, 0);
214         let mut b = a; // Copy
215         assert_eq!(a, b); // PartialEq
216         b.x = 1;
217         assert_ne!(a, b); // PartialEq
218     }
219
220     #[test]
221     fn add_points() {
222         let mut a = point!(1, 0);
223         assert_eq!(a + point!(2, 2), point!(3, 2)); // Add
224         a += point!(2, 2); // AddAssign
225         assert_eq!(a, point!(3, 2));
226         assert_eq!(point!(1, 0) + (2, 3), point!(3, 3));
227     }
228
229     #[test]
230     fn sub_points() {
231         let mut a = point!(1, 0);
232         assert_eq!(a - point!(2, 2), point!(-1, -2));
233         a -= point!(2, 2);
234         assert_eq!(a, point!(-1, -2));
235         assert_eq!(point!(1, 0) - (2, 3), point!(-1, -3));
236     }
237
238     #[test]
239     fn mul_points() {
240         let mut a = point!(1, 2);
241         assert_eq!(a * 2, point!(2, 4));
242         assert_eq!(a * point!(2, 3), point!(2, 6));
243         a *= 2;
244         assert_eq!(a, point!(2, 4));
245         a *= point!(3, 1);
246         assert_eq!(a, point!(6, 4));
247         assert_eq!(point!(1, 0) * (2, 3), point!(2, 0));
248     }
249
250     #[test]
251     fn div_points() {
252         let mut a = point!(4, 8);
253         assert_eq!(a / 2, point!(2, 4));
254         assert_eq!(a / point!(2, 4), point!(2, 2));
255         a /= 2;
256         assert_eq!(a, point!(2, 4));
257         a /= point!(2, 4);
258         assert_eq!(a, point!(1, 1));
259         assert_eq!(point!(6, 3) / (2, 3), point!(3, 1));
260     }
261
262     #[test]
263     fn neg_point() {
264         assert_eq!(point!(1, 1), -point!(-1, -1));
265     }
266
267     #[test]
268     fn angles() {
269         assert_eq!(Radians(0.0).to_degrees(), Degrees(0.0));
270         assert_eq!(Radians(std::f64::consts::PI).to_degrees(), Degrees(180.0));
271         assert_eq!(Degrees(180.0).to_radians(), Radians(std::f64::consts::PI));
272         assert!((Point2D::from(Degrees(90.0)) - point!(0.0, 1.0)).length() < 0.001);
273         assert!((Point2D::from(Radians(std::f64::consts::FRAC_PI_2)) - point!(0.0, 1.0)).length() < 0.001);
274     }
275
276     #[test]
277     fn area_for_rect_of_multipliable_type() {
278         let r: Rect<_> = (30, 20).into(); // the Into trait uses the From trait
279         assert_eq!(r.area(), 30 * 20);
280         // let a = Rect::from(("a".to_string(), "b".to_string())).area(); // this doesn't work, because area() is not implemented for String
281     }
282 }