282 Zeilen
8,7 KiB
Rust
282 Zeilen
8,7 KiB
Rust
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<T>
|
|
where
|
|
T: Integer,
|
|
{
|
|
pub x: T,
|
|
pub y: T,
|
|
}
|
|
|
|
impl<T: Integer> Kartesian<T> {
|
|
pub const fn new(x: T, y: T) -> Self {
|
|
Self {
|
|
x,
|
|
y,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn wrap<T: Integer + Rem + MaximumValue + From<u32> + 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<T: Integer + Eq + Debug + CheckedBasicMath + Default + Copy> Kartesian<T> {
|
|
pub fn checked_add_max(self, rhs: Kartesian<T>, max: Kartesian<T>) -> Option<Kartesian<T>> {
|
|
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<T>) -> Option<Kartesian<T>> {
|
|
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<T>) -> (Kartesian<T>, KartesianDirection) {
|
|
let mut relative = Kartesian::<T>::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<T>, dir: KartesianDirection) -> Option<Kartesian<T>> {
|
|
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<T>, dir: KartesianDirection, max: Kartesian<T>) -> Option<Kartesian<T>> {
|
|
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<usize> {
|
|
pub fn get_value<T: Copy>(self, map: &Vec<Vec<T>>) -> Option<T> {
|
|
if map.len() > self.x {
|
|
if map[self.x].len() > self.y {
|
|
return Some(map[self.x][self.y]);
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|
|
|
|
impl<T: Integer + Unsigned + CheckedBasicMath> Kartesian<T> {
|
|
#[must_use]
|
|
pub fn move_velocity(self, velocity: Velocity<T>, max: Kartesian<T>) -> Option<Kartesian<T>> {
|
|
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<T: Integer + From<u16> + Copy + Add + Roots + Pow<T, Output = T>> Kartesian<T> {
|
|
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<T: Integer + Display + Unsigned + WrappingAdd + WrappingSub + MaximumValue + From<u32> + Copy> Kartesian<T> {
|
|
pub fn wrapping_move_velocity(self, velocity: Velocity<T>, max: Kartesian<T>) -> Kartesian<T> {
|
|
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<T: Integer + Unsigned> {
|
|
speed: Kartesian<T>,
|
|
direction: KartesianDirection,
|
|
}
|
|
|
|
impl<T: Integer + Unsigned> Velocity<T> {
|
|
pub fn new(x: T, y: T, dir: KartesianDirection) -> Self {
|
|
Velocity {
|
|
speed: Kartesian {
|
|
x,
|
|
y,
|
|
},
|
|
direction: dir,
|
|
}
|
|
}
|
|
}
|