#![doc = include_str!("../README.md")]
mod area;
mod direction;
mod grid;
mod lines;
pub use area::*;
pub use direction::*;
pub use grid::*;
pub use lines::*;
pub use enumflags2::{BitFlag, BitFlags};
use std::{
convert::{TryFrom, TryInto},
fmt::Display,
num::TryFromIntError,
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Coord {
pub x: u32,
pub y: u32,
}
impl Coord {
pub const ZERO: Coord = Coord::new(0, 0);
pub const fn new(x: u32, y: u32) -> Self {
Self { x, y }
}
pub fn to_2d_idx(self, width: u32) -> u32 {
self.y * width + self.x
}
pub fn to_icoord(self) -> CoordVec {
self.into()
}
pub fn neighbors4(self) -> Vec<Coord> {
Direction4::DIRECTIONS
.iter()
.filter_map(|dir| {
let iself = self.to_icoord();
let ineighbor = iself + *dir;
ineighbor.to_coord() })
.collect()
}
pub fn neighbors8(self) -> Vec<Coord> {
Direction8::DIRECTIONS
.iter()
.filter_map(|dir| {
let iself = self.to_icoord();
let ineighbor = iself + *dir;
ineighbor.to_coord() })
.collect()
}
pub fn area(self, width: u32, height: u32) -> Area {
Area::new(self, width, height)
}
pub fn offset4(self, offset: Direction4) -> Option<Self> {
let deltas = offset.deltas();
let x = (self.x as i32 + deltas.x).try_into().ok()?;
let y = (self.y as i32 + deltas.y).try_into().ok()?;
Some(Coord::new(x, y))
}
pub fn offset8(self, offset: Direction8) -> Option<Self> {
let deltas = offset.deltas();
let x = (self.x as i32 + deltas.x).try_into().ok()?;
let y = (self.y as i32 + deltas.y).try_into().ok()?;
Some(Coord::new(x, y))
}
pub fn offset9(self, offset: Direction9) -> Option<Self> {
let deltas = offset.deltas();
let x = (self.x as i32 + deltas.x).try_into().ok()?;
let y = (self.y as i32 + deltas.y).try_into().ok()?;
Some(Coord::new(x, y))
}
}
impl Add for Coord {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl AddAssign for Coord {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl Sub for Coord {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl SubAssign for Coord {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl Mul<u32> for Coord {
type Output = Self;
fn mul(self, rhs: u32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl MulAssign<u32> for Coord {
fn mul_assign(&mut self, rhs: u32) {
self.x *= rhs;
self.y *= rhs;
}
}
impl Mul<Coord> for Coord {
type Output = Self;
fn mul(self, rhs: Coord) -> Self::Output {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
}
}
}
impl MulAssign<Coord> for Coord {
fn mul_assign(&mut self, rhs: Coord) {
self.x *= rhs.x;
self.y *= rhs.y;
}
}
impl TryFrom<CoordVec> for Coord {
type Error = TryFromIntError;
fn try_from(value: CoordVec) -> Result<Self, Self::Error> {
Ok(Self {
x: value.x.try_into()?,
y: value.y.try_into()?,
})
}
}
impl Display for Coord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CoordVec {
pub x: i32,
pub y: i32,
}
impl CoordVec {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
pub fn quadrant(self) -> u32 {
match (self.x >= 0, self.y >= 0) {
(true, true) => 1,
(false, true) => 2,
(false, false) => 3,
(true, false) => 4,
}
}
pub fn to_coord(self) -> Option<Coord> {
self.try_into().ok()
}
pub fn neighbors4(self) -> [CoordVec; 4] {
[
self + Direction4::North,
self + Direction4::East,
self + Direction4::South,
self + Direction4::West,
]
}
pub fn neighbors8(self) -> [CoordVec; 8] {
[
self + Direction8::North,
self + Direction8::NorthEast,
self + Direction8::East,
self + Direction8::SouthEast,
self + Direction8::South,
self + Direction8::SouthWest,
self + Direction8::West,
self + Direction8::NorthWest,
]
}
pub fn point9(self) -> Direction9 {
if self.x == 0 && self.y == 0 {
return Direction9::Center;
}
let angle =
(-self.y as f32).atan2(self.x as f32) + std::f32::consts::PI;
match angle / std::f32::consts::TAU * 16.0 {
a if a < 1.0 => Direction9::East,
a if a < 3.0 => Direction9::NorthEast,
a if a < 5.0 => Direction9::North,
a if a < 7.0 => Direction9::NorthWest,
a if a < 9.0 => Direction9::West,
a if a < 11.0 => Direction9::SouthWest,
a if a < 13.0 => Direction9::South,
a if a < 15.0 => Direction9::SouthEast,
_ => Direction9::East,
}
}
}
impl Add for CoordVec {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl AddAssign for CoordVec {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl Sub for CoordVec {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl SubAssign for CoordVec {
fn sub_assign(&mut self, rhs: Self) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl Add<Direction4> for CoordVec {
type Output = Self;
fn add(self, rhs: Direction4) -> Self::Output {
self + rhs.deltas()
}
}
impl AddAssign<Direction4> for CoordVec {
fn add_assign(&mut self, rhs: Direction4) {
*self += rhs.deltas();
}
}
impl Add<Direction8> for CoordVec {
type Output = Self;
fn add(self, rhs: Direction8) -> Self::Output {
self + rhs.deltas()
}
}
impl AddAssign<Direction8> for CoordVec {
fn add_assign(&mut self, rhs: Direction8) {
*self += rhs.deltas();
}
}
impl Mul<i32> for CoordVec {
type Output = Self;
fn mul(self, rhs: i32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl MulAssign<i32> for CoordVec {
fn mul_assign(&mut self, rhs: i32) {
self.x *= rhs;
self.y *= rhs;
}
}
impl From<Coord> for CoordVec {
fn from(value: Coord) -> Self {
Self {
x: value.x as i32,
y: value.y as i32,
}
}
}
impl Display for CoordVec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}