use log::debug; use num::{integer::*, traits::*}; use std::{ fmt::{Debug, Display}, ops::*, }; pub mod direction; pub mod external_traits; pub mod map; pub mod maximum; mod test; pub mod traits; pub use direction::*; pub use map::*; pub use maximum::*; pub use traits::*; pub type KD = KartesianDirection; #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Kartesian where T: Integer, { pub x: T, pub y: T, } impl Kartesian { pub const fn new(x: T, y: T) -> Self { Self { x, y, } } } fn wrap + Copy + MaximumValue + Display, const FACTOR: u32>(v: T, max: T) -> T { if v < max { v } else if v < max * T::from(FACTOR) { v % max } else { max - (T::MAX - v) } } impl Kartesian { pub fn checked_add_max(self, rhs: Kartesian, max: Kartesian) -> Option> { let mut new = Kartesian::default(); new.x = match self.x.checked_add(&rhs.x) { None => return None, Some(x) => { if x < T::default() || x >= max.x { return None; } x }, }; new.y = match self.y.checked_add(&rhs.y) { None => return None, Some(y) => { if y < T::default() || y >= max.y { return None; } y }, }; Some(new) } pub fn checked_sub(self, rhs: Kartesian) -> Option> { let mut new = Kartesian::default(); new.x = match self.x.checked_sub(&rhs.x) { None => return None, Some(x) => x, }; new.y = match self.y.checked_sub(&rhs.y) { None => return None, Some(y) => y, }; Some(new) } pub fn diff(self, rhs: Kartesian) -> (Kartesian, KartesianDirection) { let mut relative = Kartesian::::default(); let mut dir = KartesianDirection::None; debug!("{:?} <> {:?}", self.x, rhs.x); if self.x < rhs.x { dir = dir.down(); relative.x = rhs.x - self.x; } else if self.x > rhs.x { dir = dir.up(); relative.x = self.x - rhs.x; } debug!("{:?} <> {:?}", self.x, rhs.x); if self.y < rhs.y { dir = dir.right(); relative.y = rhs.y - self.y; } else if self.y > rhs.y { dir = dir.left(); relative.y = self.y - rhs.y; } (relative, dir) } #[inline] #[must_use] pub fn move_dir(self, vector: Kartesian, dir: KartesianDirection) -> Option> { let mut new = self; match dir { KartesianDirection::TopLeft | KartesianDirection::Top | KartesianDirection::TopRight => { new.x = match new.x.checked_sub(&vector.x) { None => return None, Some(d) => d, } }, KartesianDirection::BottomLeft | KartesianDirection::Bottom | KartesianDirection::BottomRight => { new.x = match new.x.checked_add(&vector.x) { None => return None, Some(d) => d, } }, _ => {}, } match dir { KartesianDirection::TopLeft | KartesianDirection::Left | KartesianDirection::BottomLeft => { new.y = match new.y.checked_sub(&vector.y) { None => return None, Some(d) => d, } }, KartesianDirection::TopRight | KartesianDirection::Right | KartesianDirection::BottomRight => { new.y = match new.y.checked_add(&vector.y) { None => return None, Some(d) => d, } }, _ => {}, } Some(new) } #[inline] #[must_use] pub fn move_dir_max(self, vector: Kartesian, dir: KartesianDirection, max: Kartesian) -> Option> { match self.move_dir(vector, dir) { None => None, Some(new) => { if new.x < max.x && new.y < max.y { Some(new) } else { None } }, } } } impl Kartesian { pub fn get_value(self, map: &Vec>) -> Option { if map.len() > self.x { if map[self.x].len() > self.y { return Some(map[self.x][self.y]); } } None } } impl Kartesian { #[must_use] pub fn move_velocity(self, velocity: Velocity, max: Kartesian) -> Option> { if velocity.direction == KD::None { return Some(self); } Some(Kartesian { x: match velocity.direction { KD::Left | KD::Right => self.x, KD::TopLeft | KD::Top | KD::TopRight => match self.x.checked_sub(&velocity.speed.x) { None => return None, Some(x) => x, }, KD::BottomLeft | KD::Bottom | KD::BottomRight => { let x = self.x + velocity.speed.x; if x >= max.x { return None; } x }, _ => unreachable!(), }, y: match velocity.direction { KD::Top | KD::Bottom => self.y, KD::TopLeft | KD::Left | KD::BottomLeft => match self.y.checked_sub(&velocity.speed.y) { None => return None, Some(y) => y, }, KD::TopRight | KD::Right | KD::BottomRight => { let y = self.y + velocity.speed.y; if y >= max.y { return None; } y }, _ => unreachable!(), }, }) } } impl + Copy + Add + Roots + Pow> Kartesian { pub fn distance(self, other: Self) -> T { let power = T::from(2); let x = if self.x > other.x { self.x - other.x } else { other.x - self.x }; let y = if self.y > other.y { self.y - other.y } else { other.y - self.y }; (x.pow(power) + y.pow(power)).sqrt() } } impl + Copy> Kartesian { pub fn wrapping_move_velocity(self, velocity: Velocity, max: Kartesian) -> Kartesian { Kartesian { x: match velocity.direction { KartesianDirection::TopLeft | KartesianDirection::Top | KartesianDirection::TopRight => { if self.x < velocity.speed.x { max.x - (velocity.speed.x - self.x) } else { self.x - velocity.speed.x } }, KartesianDirection::Left | KartesianDirection::None | KartesianDirection::Right => self.x, KartesianDirection::BottomLeft | KartesianDirection::Bottom | KartesianDirection::BottomRight => (self.x + velocity.speed.x) % max.x, }, y: match velocity.direction { KartesianDirection::TopLeft | KartesianDirection::Left | KartesianDirection::BottomLeft => { if self.y < velocity.speed.y { max.y - (velocity.speed.y - self.y) } else { self.y - velocity.speed.y } }, KartesianDirection::Top | KartesianDirection::None | KartesianDirection::Bottom => self.y, KartesianDirection::TopRight | KartesianDirection::Right | KartesianDirection::BottomRight => (self.y + velocity.speed.y) % max.y, }, } } } pub struct KartesianIterator { i: u8, diagonal: bool, none: bool, straight: bool, } #[derive(Debug, Default, Clone, Copy)] pub struct Velocity { speed: Kartesian, direction: KartesianDirection, } impl Velocity { pub fn new(x: T, y: T, dir: KartesianDirection) -> Self { Velocity { speed: Kartesian { x, y, }, direction: dir, } } }