use std::ops::*; use num_traits::{NumOps, NumAssignOps, Signed}; use num_traits::pow::Pow; use num_traits::real::Real; macro_rules! fold_array { ($method:ident, { $x:expr }) => { $x }; ($method:ident, { $x:expr, $y:expr }) => { $x.$method($y) }; ($method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method($y).$method($z) }; } macro_rules! impl_vector { ($VecN:ident { $($field:ident),+ }) => { #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] pub struct $VecN { $(pub $field: T),+ } impl $VecN { pub const fn new($($field: T),+) -> $VecN { $VecN { $($field),+ } } } impl $VecN { pub fn walking_distance(&self) -> T { fold_array!(max, { $(self.$field.abs()),+ }) } } impl + Copy> $VecN { pub fn magnitude_squared(&self) -> T { fold_array!(add, { $(self.$field.pow(2)),+ }) } } impl> $VecN { pub fn magnitude(&self) -> T { self.magnitude_squared().sqrt() } pub fn unit(&self) -> $VecN { let mag = self.magnitude(); $VecN { $($field: self.$field / mag),+ } } } impl $VecN { pub fn dot(&self, other: $VecN) -> T { fold_array!(add, { $(self.$field * other.$field),+ }) } } impl Add for $VecN { type Output = Self; fn add(self, other: Self) -> Self { $VecN { $($field: self.$field + other.$field),+ } } } impl AddAssign for $VecN { fn add_assign(&mut self, other: Self) { $(self.$field += other.$field);+ } } impl Sub for $VecN { type Output = Self; fn sub(self, other: Self) -> Self { $VecN { $($field: self.$field - other.$field),+ } } } impl Mul for $VecN { type Output = Self; fn mul(self, rhs: T) -> Self { $VecN { $($field: self.$field * rhs),+ } } } impl> Neg for $VecN { type Output = Self; fn neg(self) -> Self { $VecN { $($field: -self.$field),+ } } } } } impl_vector!(Vec2d { x, y }); impl_vector!(Vec3d { x, y, z }); impl> Vec2d { pub fn angle(&self) -> T { self.y.atan2(self.x) } } #[cfg(test)] mod tests { use super::*; #[test] fn right_angles_in_2d_vectors() { use std::f32::consts::{FRAC_PI_2, PI}; assert_eq!(0., Vec2d::new(1., 0.).angle()); assert_eq!(FRAC_PI_2, Vec2d::new(0., 1.).angle()); assert_eq!(PI, Vec2d::new(-1., 0.).angle()); assert_eq!(-FRAC_PI_2, Vec2d::new(0., -1.).angle()); } #[test] fn unit_normalizes_2d_vector() { let before = Vec2d::new(2., 10.); let after = before.unit(); assert_eq!(1., after.magnitude()); assert_eq!(before.angle(), after.angle()); } #[test] fn unit_normalizes_3d_vector() { let before = Vec3d::new(2., 10., -5.); let after = before.unit(); assert_eq!(1., after.magnitude()); // How to define 3d angle? 2 angles I suppose? // assert_eq!(before.angle(), after.angle()); } #[test] fn dot_product_example_2d() { let a = Vec2d::new(-6., 8.); let b = Vec2d::new(5., 12.); assert_eq!(66., a.dot(b)); } #[test] fn magnitude_squared_of_an_integer() { let a = Vec2d::new(3, 4); assert_eq!(25, a.magnitude_squared()); } }