use std::{fmt, ops::*};
pub type Coord = usize;
macro_rules! assign {
( $for:ident: $(
$trait:ident$(<$rhs:ty>)?::$method:ident via $normal:ident
);* $(;)? ) => { $(
impl $trait$(<$rhs>)? for $for {
fn $method(&mut self, rhs: assign!(@first $($rhs,)? Self,)) {
*self = self.$normal(rhs);
}
}
)* };
( @first $res:ty, $($other:tt)* ) => { $res }
}
macro_rules! peq_from_into {
( $type:ty, $rhs:ty ) => {
impl PartialEq<$rhs> for $type {
fn eq(&self, other: &$rhs) -> bool {
self == &Into::<$type>::into(*other)
}
}
impl PartialEq<$rhs> for &$type {
fn eq(&self, other: &$rhs) -> bool {
*self == &Into::<$type>::into(*other)
}
}
};
}
macro_rules! int_wrap {
( $(
$(#[$attr:meta])*
$pub:vis struct $name:ident($fpub:vis $wrapping:ty)
);* $(;)? ) => { $(
$(#[$attr])*
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
$pub struct $name($fpub $wrapping);
impl From<usize> for $name {
fn from(v: usize) -> Self {
Self(v)
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Add for $name {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Sub for $name {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
assign! {
$name:
AddAssign::add_assign via add;
SubAssign::sub_assign via sub;
}
peq_from_into!($name, usize);
impl PartialOrd<usize> for $name {
fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> {
self.0.partial_cmp(other)
}
}
)* }
}
int_wrap! {
pub struct X(pub Coord);
pub struct Y(pub Coord);
}
macro_rules! pair_wrap {
( $(
$(#[$attr:meta])*
$pub:vis struct $name:ident($fpub:vis $x:ident, $y:ident)
);* $(;)? ) => { $(
$(#[$attr])*
#[derive(Clone, Copy, Default, PartialEq, Eq)]
$pub struct $name {
#[allow(missing_docs)]
$fpub $x: X,
#[allow(missing_docs)]
$fpub $y: Y,
}
impl $name {
#[doc = concat!("create a new ", stringify!($name), " with the given X/Y")]
pub const fn new($x: X, $y: Y) -> Self {
Self { $x, $y }
}
#[doc = concat!("create a new ", stringify!($name), " with the given X/Y usizes")]
pub const fn rnew($x: usize, $y: usize) -> Self {
Self { $x: X($x), $y: Y($y) }
}
#[doc = concat!("the zero ", stringify!($name))]
pub const ZERO: Self = Self { $x: X(0), $y: Y(0) };
}
impl From<(usize, usize)> for $name {
fn from((x, y): (usize, usize)) -> Self {
Self::rnew(x, y)
}
}
impl From<(X, Y)> for $name {
fn from((x, y): (X, Y)) -> Self {
Self::new(x, y)
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "({}, {})"), self.$x, self.$y)
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
impl Add<Size> for $name {
type Output = Self;
fn add(self, rhs: Size) -> Self {
Self {
$x: self.$x + rhs.width,
$y: self.$y + rhs.height,
}
}
}
impl Sub<Size> for $name {
type Output = Self;
fn sub(self, rhs: Size) -> Self {
Self {
$x: self.$x - rhs.width,
$y: self.$y - rhs.height,
}
}
}
impl Add<X> for $name {
type Output = Self;
fn add(self, rhs: X) -> Self {
Self {
$x: self.$x + rhs,
$y: self.$y,
}
}
}
impl Sub<X> for $name {
type Output = Self;
fn sub(self, rhs: X) -> Self {
Self {
$x: self.$x - rhs,
$y: self.$y,
}
}
}
impl Add<Y> for $name {
type Output = Self;
fn add(self, rhs: Y) -> Self {
Self {
$x: self.$x,
$y: self.$y + rhs,
}
}
}
impl Sub<Y> for $name {
type Output = Self;
fn sub(self, rhs: Y) -> Self {
Self {
$x: self.$x,
$y: self.$y - rhs,
}
}
}
assign! {
$name:
AddAssign<Size>::add_assign via add;
AddAssign<X>::add_assign via add;
AddAssign<Y>::add_assign via add;
SubAssign<Size>::sub_assign via sub;
SubAssign<X>::sub_assign via sub;
SubAssign<Y>::sub_assign via sub;
}
peq_from_into!($name, (usize, usize));
peq_from_into!($name, (X, Y));
)* };
}
pair_wrap! {
pub struct Pos(pub x, y);
pub struct Size(pub width, height);
}
impl Sub<Pos> for Pos {
type Output = Size;
fn sub(self, rhs: Pos) -> Size {
Size { width: self.x - rhs.x, height: self.y - rhs.y }
}
}
impl Size {
pub fn is_empty(&self) -> bool {
self.width.0 == 0 || self.height.0 == 0
}
pub fn area(&self) -> usize {
self.width.0 * self.height.0
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct Rect {
pub tl: Pos,
pub size: Size,
}
impl Rect {
pub const fn tlbr(tl: Pos, br: Pos) -> Self {
let size = Size { width: X(br.x.0 - tl.x.0), height: Y(br.y.0 - tl.y.0) };
Self { tl, size }
}
pub const fn tlsz(tl: Pos, size: Size) -> Self {
Self { tl, size }
}
pub const EMPTY: Self = Self::tlsz(Pos::ZERO, Size::ZERO);
#[inline]
pub fn br(&self) -> Pos {
self.tl + self.size - Size { width: X(1), height: Y(1) }
}
pub fn contains(&self, pos: Pos) -> bool {
self.tl.x <= pos.x
&& pos.x < self.tl.x + self.size.width
&& self.tl.y <= pos.y
&& pos.y < self.tl.y + self.size.height
}
pub fn rel(&self, pos: Pos) -> Option<Pos> {
let pos =
Pos { x: X(pos.x.0.checked_sub(self.tl.x.0)?), y: Y(pos.y.0.checked_sub(self.tl.y.0)?) };
if pos.x < self.size.width && pos.y < self.size.height { Some(pos) } else { None }
}
pub fn rows(&self) -> impl Iterator<Item = Y> {
(self.tl.y.0..self.br().y.0).map(Y)
}
pub fn is_empty(&self) -> bool {
self.size.is_empty()
}
pub fn split_h(self, col: X) -> (Self, Self) {
(
Self { tl: self.tl, size: Size { width: col, height: self.size.height } },
Self { tl: self.tl + col, size: self.size - col },
)
}
pub fn split_v(self, row: Y) -> (Self, Self) {
(
Self { tl: self.tl, size: Size { width: self.size.width, height: row } },
Self { tl: self.tl + row, size: self.size - row },
)
}
}
impl fmt::Debug for Rect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Rect[{},{}+{},{}]", self.tl.x, self.tl.y, self.size.width, self.size.height)
}
}