From 3e54b01003aa9d27de8f4ca13c9240fe785ec0e1 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Mon, 22 Apr 2019 14:38:40 +0200 Subject: Structures representing game state --- src/geometry/vec.rs | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/geometry/vec.rs (limited to 'src/geometry/vec.rs') diff --git a/src/geometry/vec.rs b/src/geometry/vec.rs new file mode 100644 index 0000000..f6bd29f --- /dev/null +++ b/src/geometry/vec.rs @@ -0,0 +1,154 @@ +use std::ops::*; +use num_traits::{NumOps, NumAssignOps}; +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 fn new($($field: T),+) -> $VecN { + $VecN { $($field),+ } + } + } + + 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()); + } +} -- cgit v1.2.3