//! Types representing directions and locations in 2d and 3d space. //! //! //! This module contains 3 major types: //! - [`Vec2`], a 2d float vector //! - [`Vec2Int`], a 2d integer vector //! - [`Direction`], a 2d cardinal direction //! //! All the types implement the expected [`From`]s and all the relevant operator traits. use std::{ fmt::Debug, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; use sdl2::rect::Point; // Direction /// A cardinal direction in a 2d plane. /// /// Conversions to a [`Vec2`] or [`Vec2Int`] assume that East is positive-x and South is positive-y. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Direction { /// North, or Vec2::from((0, -1)) North, /// North, or Vec2::from((0, 1)) South, /// North, or Vec2::from((1, 0)) East, /// North, or Vec2::from((-1, 0)) West, } #[allow(clippy::enum_glob_use)] impl Direction { /// Flips this `Direction` around both the x- and y-axes. #[must_use] pub fn flipped(self) -> Self { self.flip_x().flip_y() } /// Flips this `Direction` around the x-axis. #[must_use] pub fn flip_x(self) -> Self { use Direction::*; match self { East => West, West => East, v => v, } } /// Flips this `Direction` around the y-axis. #[must_use] pub fn flip_y(self) -> Self { use Direction::*; match self { North => South, South => North, v => v, } } } // ...and related op impls impl Neg for Direction { type Output = Self; fn neg(self) -> Self::Output { self.flipped() } } #[allow(clippy::enum_glob_use)] impl From for Vec2 { fn from(v: Direction) -> Self { use Direction::*; match v { North => (0.0, -1.0).into(), South => (0.0, 1.0).into(), East => (1.0, 0.0).into(), West => (-1.0, 0.0).into(), } } } #[allow(clippy::enum_glob_use)] impl From for Vec2Int { fn from(v: Direction) -> Self { use Direction::*; match v { North => (0, -1).into(), South => (0, 1).into(), East => (1, 0).into(), West => (-1, 0).into(), } } } impl From for Vec2 { fn from(p: Point) -> Self { let x: (i32, i32) = p.into(); x.into() } } impl From for Vec2Int { fn from(p: Point) -> Self { let x: (i32, i32) = p.into(); x.into() } } impl Mul for Direction { type Output = Vec2; fn mul(self, rhs: f32) -> Self::Output { Vec2::from(self) * rhs } } impl Mul for Direction { type Output = Vec2Int; fn mul(self, rhs: i32) -> Self::Output { Vec2Int::from(self) * rhs } } // Vec2 /// A set of 2 [`f32`]s representing a location or direction in the 2d plane. #[derive(Clone, Copy, Default, PartialEq)] pub struct Vec2 { /// The x component of the vector. pub x: f32, /// The y component of the vector. pub y: f32, } impl Debug for Vec2 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Vec2").field(&self.x).field(&self.y).finish() } } impl Vec2 { /// Creates a new `Vec2` with the given x- and y-values. /// /// It is often simpler, and preferred, to just write `(x, y).into()`. #[must_use] pub const fn new(x: f32, y: f32) -> Vec2 { Self { x, y } } /// Gets the squared magnitude of the vector. /// /// Useful for comparisons as it is faster to calculate than `magnitude`. #[must_use] pub fn sq_magnitude(self) -> f32 { self.x * self.x + self.y * self.y } /// Gets the magnitude of the vector. #[must_use] pub fn magnitude(self) -> f32 { self.sq_magnitude().sqrt() } /// Gets the squared distance from this vector to `rhs`. /// /// Useful for comparisons as it is faster to calculate than `dist`. #[must_use] pub fn sq_dist(self, rhs: Self) -> f32 { (self - rhs).sq_magnitude() } /// Gets the distance from this vector to `rhs`. #[must_use] pub fn dist(self, rhs: Self) -> f32 { (self - rhs).magnitude() } /// Normalizes the vector, making its magnitude `1`. #[must_use] pub fn normalized(self) -> Self { self / self.magnitude() } /// Rounds the vector to a [`Vec2Int`]. /// /// This uses `as i32` under the hood, and as such comes with all the same unfortunate edge cases. Beware. #[must_use] pub fn rounded(self) -> Vec2Int { #[allow(clippy::cast_possible_truncation)] Vec2Int { x: self.x as i32, y: self.y as i32, } } } impl From<(i32, i32)> for Vec2 { fn from(v: (i32, i32)) -> Self { Vec2Int::from(v).to_f32() } } impl From<(f32, f32)> for Vec2 { fn from(v: (f32, f32)) -> Self { Self { x: v.0, y: v.1 } } } impl From for (f32, f32) { fn from(v: Vec2) -> Self { (v.x, v.y) } } impl PartialEq<(i32, i32)> for Vec2 { fn eq(&self, other: &(i32, i32)) -> bool { self == &Self::from(*other) } } impl PartialEq<(f32, f32)> for Vec2 { fn eq(&self, other: &(f32, f32)) -> bool { self == &Self::from(*other) } } // ...and related op impls impl Neg for Vec2 { type Output = Self; fn neg(self) -> Self::Output { self * -1.0 } } impl Add for Vec2 { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self { x: self.x + rhs.x, y: self.y + rhs.y, } } } impl Add for Vec2 { type Output = Self; fn add(self, rhs: Direction) -> Self::Output { self + Self::from(rhs) } } impl AddAssign for Vec2 where Vec2: Add, { fn add_assign(&mut self, rhs: T) { *self = *self + rhs; } } impl Sub for Vec2 where Vec2: Add, { type Output = Self; fn sub(self, rhs: T) -> Self::Output { -(-self + rhs) } } impl SubAssign for Vec2 where Vec2: Sub, { fn sub_assign(&mut self, rhs: T) { *self = *self - rhs; } } impl Mul for Vec2 { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { Self { x: self.x * rhs, y: self.y * rhs, } } } impl Div for Vec2 { type Output = Self; fn div(self, rhs: f32) -> Self::Output { Self { x: self.x / rhs, y: self.y / rhs, } } } impl MulAssign for Vec2 { fn mul_assign(&mut self, rhs: f32) { *self = *self * rhs; } } impl DivAssign for Vec2 { fn div_assign(&mut self, rhs: f32) { *self = *self / rhs; } } // Vec2Int /// A set of 2 [`i32`]s representing a location or direction in the 2d plane. #[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] pub struct Vec2Int { /// The x component of the vector. pub x: i32, /// The y component of the vector. pub y: i32, } impl Debug for Vec2Int { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Vec2Int") .field(&self.x) .field(&self.y) .finish() } } impl Vec2Int { /// Creates a new `Vec2` with the given x- and y-values. /// /// It is often simpler, and preferred, to just write `(x, y).into()`. #[must_use] pub const fn new(x: i32, y: i32) -> Vec2Int { Self { x, y } } /// Gets the squared magnitude of the vector. /// /// Useful for comparisons as it is faster to calculate than `magnitude`. #[must_use] pub fn sq_magnitude(self) -> i32 { self.x * self.x + self.y * self.y } /// Gets the magnitude of the vector. #[must_use] pub fn magnitude(self) -> f32 { #[allow(clippy::cast_precision_loss)] (self.sq_magnitude() as f32).sqrt() } /// Gets the squared distance from this vector to `rhs`. /// /// Useful for comparisons as it is faster to calculate than `dist`. #[must_use] pub fn sq_dist(self, rhs: Self) -> i32 { (self - rhs).sq_magnitude() } /// Gets the distance from this vector to `rhs`. #[must_use] pub fn dist(self, rhs: Self) -> f32 { (self - rhs).magnitude() } /// Casts this vector to a [`Vec2`]. /// /// This uses `as f32` under the hood, and as such comes with all the same unfortunate edge cases. Beware. #[must_use] pub fn to_f32(self) -> Vec2 { #[allow(clippy::cast_precision_loss)] Vec2 { x: self.x as f32, y: self.y as f32, } } } impl From<(i32, i32)> for Vec2Int { fn from(v: (i32, i32)) -> Self { Self { x: v.0, y: v.1 } } } impl From for (i32, i32) { fn from(v: Vec2Int) -> Self { (v.x, v.y) } } impl PartialEq<(i32, i32)> for Vec2Int { fn eq(&self, other: &(i32, i32)) -> bool { self == &Self::from(*other) } } // ...and related op impls impl Neg for Vec2Int { type Output = Self; fn neg(self) -> Self::Output { self * -1 } } impl Add for Vec2Int { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self { x: self.x + rhs.x, y: self.y + rhs.y, } } } impl Add for Vec2Int { type Output = Self; fn add(self, rhs: Direction) -> Self::Output { self + Self::from(rhs) } } impl AddAssign for Vec2Int where Vec2Int: Add, { fn add_assign(&mut self, rhs: T) { *self = *self + rhs; } } impl Sub for Vec2Int where Vec2Int: Add, { type Output = Self; fn sub(self, rhs: T) -> Self::Output { -(-self + rhs) } } impl SubAssign for Vec2Int where Vec2Int: Sub, { fn sub_assign(&mut self, rhs: T) { *self = *self - rhs; } } impl Mul for Vec2Int { type Output = Self; fn mul(self, rhs: i32) -> Self::Output { Self { x: self.x * rhs, y: self.y * rhs, } } } impl Div for Vec2Int { type Output = Self; fn div(self, rhs: i32) -> Self::Output { Self { x: self.x / rhs, y: self.y / rhs, } } } impl MulAssign for Vec2Int { fn mul_assign(&mut self, rhs: i32) { *self = *self * rhs; } } impl DivAssign for Vec2Int { fn div_assign(&mut self, rhs: i32) { *self = *self / rhs; } }