summaryrefslogtreecommitdiff
path: root/src/geometry
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-04-22 14:38:40 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-04-22 14:38:40 +0200
commit3e54b01003aa9d27de8f4ca13c9240fe785ec0e1 (patch)
tree0dfe803ba790951e29cba25f791e31418eff5b05 /src/geometry
parent29a323e0a3bd3ab3e6109b23e15bb5f9e88398e3 (diff)
Structures representing game state
Diffstat (limited to 'src/geometry')
-rw-r--r--src/geometry/point.rs84
-rw-r--r--src/geometry/rect.rs19
-rw-r--r--src/geometry/vec.rs154
3 files changed, 257 insertions, 0 deletions
diff --git a/src/geometry/point.rs b/src/geometry/point.rs
new file mode 100644
index 0000000..c34d00f
--- /dev/null
+++ b/src/geometry/point.rs
@@ -0,0 +1,84 @@
+use crate::geometry::vec::*;
+
+use std::ops::*;
+use num_traits::{NumOps, NumAssignOps};
+use num_traits::pow::Pow;
+use num_traits::real::Real;
+
+macro_rules! impl_point {
+ ($PointN:ident { $($field:ident),+ }, $VecN:ident) => {
+ #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
+ pub struct $PointN<T> {
+ $(pub $field: T),+
+ }
+
+ impl<T> $PointN<T> {
+ pub fn new($($field: T),+) -> $PointN<T> {
+ $PointN { $($field),+ }
+ }
+ }
+
+ impl<T: NumOps + Pow<u8, Output=T> + Copy> $PointN<T> {
+ pub fn distance_squared(self, other: $PointN<T>) -> T {
+ (self - other).magnitude_squared()
+ }
+ }
+
+
+ impl<T: Real + Pow<u8, Output=T>> $PointN<T> {
+ pub fn distance(self, other: $PointN<T>) -> T {
+ (self - other).magnitude()
+ }
+ }
+
+ impl<T: NumOps> Add<$VecN<T>> for $PointN<T> {
+ type Output = Self;
+
+ fn add(self, other: $VecN<T>) -> Self {
+ $PointN {
+ $($field: self.$field + other.$field),+
+ }
+ }
+ }
+
+ impl<T: NumAssignOps> AddAssign<$VecN<T>> for $PointN<T> {
+ fn add_assign(&mut self, other: $VecN<T>) {
+ $(self.$field += other.$field);+
+ }
+ }
+
+ impl<T: NumOps> Sub for $PointN<T> {
+ type Output = $VecN<T>;
+
+ fn sub(self, other: Self) -> $VecN<T> {
+ $VecN {
+ $($field: self.$field - other.$field),+
+ }
+ }
+ }
+ }
+}
+
+impl_point!(Point2d { x, y }, Vec2d);
+impl_point!(Point3d { x, y, z }, Vec3d);
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn distance_in_x_dimension_example() {
+ let a = Point2d::new(1., 1.);
+ let b = Point2d::new(3., 1.);
+ assert_eq!(a.distance(b), 2.);
+ assert_eq!(a.distance_squared(b), 4.);
+ }
+
+ #[test]
+ fn distance_in_y_dimension_example() {
+ let a = Point2d::new(1., 1.);
+ let b = Point2d::new(1., 3.);
+ assert_eq!(b.distance(a), 2.);
+ assert_eq!(b.distance_squared(a), 4.);
+ }
+}
diff --git a/src/geometry/rect.rs b/src/geometry/rect.rs
new file mode 100644
index 0000000..e2b1882
--- /dev/null
+++ b/src/geometry/rect.rs
@@ -0,0 +1,19 @@
+use crate::geometry::Point2d;
+use crate::geometry::Vec2d;
+
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
+pub struct Rectangle<T> {
+ pub pos: Point2d<T>,
+ pub size: Vec2d<T>,
+}
+
+impl<T> Rectangle<T> {
+ pub fn new(x: T, y: T, w: T, h: T) -> Rectangle<T> {
+ Rectangle {
+ pos: Point2d::new(x, y),
+ size: Vec2d::new(w, h),
+ }
+ }
+
+ // TODO: Constructor to build a rectangle centered at an x,y
+}
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<T> {
+ $(pub $field: T),+
+ }
+
+ impl<T> $VecN<T> {
+ pub fn new($($field: T),+) -> $VecN<T> {
+ $VecN { $($field),+ }
+ }
+ }
+
+ impl<T: NumOps + Pow<u8, Output=T> + Copy> $VecN<T> {
+ pub fn magnitude_squared(&self) -> T {
+ fold_array!(add, { $(self.$field.pow(2)),+ })
+ }
+ }
+
+ impl<T: Real + Pow<u8, Output=T>> $VecN<T> {
+ pub fn magnitude(&self) -> T {
+ self.magnitude_squared().sqrt()
+ }
+
+ pub fn unit(&self) -> $VecN<T> {
+ let mag = self.magnitude();
+ $VecN {
+ $($field: self.$field / mag),+
+ }
+ }
+ }
+
+ impl<T: NumOps + Copy> $VecN<T> {
+ pub fn dot(&self, other: $VecN<T>) -> T {
+ fold_array!(add, { $(self.$field * other.$field),+ })
+ }
+ }
+
+ impl<T: NumOps> Add for $VecN<T> {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ $VecN {
+ $($field: self.$field + other.$field),+
+ }
+ }
+ }
+
+ impl<T: NumAssignOps> AddAssign for $VecN<T> {
+ fn add_assign(&mut self, other: Self) {
+ $(self.$field += other.$field);+
+ }
+ }
+
+ impl<T: NumOps> Sub for $VecN<T> {
+ type Output = Self;
+
+ fn sub(self, other: Self) -> Self {
+ $VecN {
+ $($field: self.$field - other.$field),+
+ }
+ }
+ }
+
+ impl<T: NumOps + Copy> Mul<T> for $VecN<T> {
+ type Output = Self;
+
+ fn mul(self, rhs: T) -> Self {
+ $VecN {
+ $($field: self.$field * rhs),+
+ }
+ }
+ }
+
+ impl<T: Neg<Output=T>> Neg for $VecN<T> {
+ type Output = Self;
+
+ fn neg(self) -> Self {
+ $VecN {
+ $($field: -self.$field),+
+ }
+ }
+ }
+
+ }
+}
+
+impl_vector!(Vec2d { x, y });
+impl_vector!(Vec3d { x, y, z });
+
+impl<T: Real + Pow<u8, Output=T>> Vec2d<T> {
+ 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());
+ }
+}