use core::ops;
use crate::{
Size,
int::{Int, SignedInt},
internal,
};
#[macro_export]
macro_rules! pos {
($x:expr, $y:expr) => {
Pos::new($x, $y)
};
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)]
#[allow(private_bounds)]
pub struct Pos<T: Int = i32> {
pub x: T,
pub y: T,
}
#[allow(private_bounds)]
impl<T: Int> Pos<T> {
pub const ORIGIN: Self = Self {
x: T::ZERO,
y: T::ZERO,
};
pub const MIN: Self = Self {
x: T::MIN,
y: T::MIN,
};
pub const MAX: Self = Self {
x: T::MAX,
y: T::MAX,
};
pub const X: Self = Self {
x: T::ONE,
y: T::ZERO,
};
pub const Y: Self = Self {
x: T::ZERO,
y: T::ONE,
};
#[must_use]
pub const fn new(x: T, y: T) -> Self {
Self { x, y }
}
#[must_use]
pub fn normalized_approx(&self) -> Self {
if self == &Self::ORIGIN {
Self::ORIGIN
} else {
let gcd = internal::gcd(self.x, self.y);
Self {
x: self.x / gcd,
y: self.y / gcd,
}
}
}
}
impl<T: SignedInt> Pos<T> {
pub const NEG_X: Self = Self {
x: T::NEG_ONE,
y: T::ZERO,
};
pub const NEG_Y: Self = Self {
x: T::ZERO,
y: T::NEG_ONE,
};
}
impl<T: Int> Default for Pos<T> {
fn default() -> Self {
Self::ORIGIN
}
}
impl<T: SignedInt> ops::Neg for Pos<T> {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
x: -self.x,
y: -self.y,
}
}
}
impl<T: Int> ops::Add<Pos<T>> for Pos<T> {
type Output = Self;
fn add(self, rhs: Pos<T>) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl<T: Int> ops::AddAssign<Pos<T>> for Pos<T> {
fn add_assign(&mut self, rhs: Pos<T>) {
self.x += rhs.x;
self.y += rhs.y;
}
}
impl<T: Int> ops::Sub<Pos<T>> for Pos<T> {
type Output = Self;
fn sub(self, rhs: Pos<T>) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl<T: Int> ops::SubAssign<Pos<T>> for Pos<T> {
fn sub_assign(&mut self, rhs: Pos<T>) {
self.x -= rhs.x;
self.y -= rhs.y;
}
}
impl<T: Int> ops::Mul<T> for Pos<T> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl<T: Int> ops::MulAssign<T> for Pos<T> {
fn mul_assign(&mut self, rhs: T) {
self.x *= rhs;
self.y *= rhs;
}
}
impl<T: Int> ops::Mul<Pos<T>> for Pos<T> {
type Output = Self;
fn mul(self, rhs: Pos<T>) -> Self::Output {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
}
}
}
impl<T: Int> ops::MulAssign<Pos<T>> for Pos<T> {
fn mul_assign(&mut self, rhs: Pos<T>) {
self.x *= rhs.x;
self.y *= rhs.y;
}
}
impl<T: Int> ops::Div<T> for Pos<T> {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl<T: Int> ops::DivAssign<T> for Pos<T> {
fn div_assign(&mut self, rhs: T) {
self.x /= rhs;
self.y /= rhs;
}
}
impl<T: Int> ops::Div<Pos<T>> for Pos<T> {
type Output = Self;
fn div(self, rhs: Pos<T>) -> Self::Output {
Self {
x: self.x / rhs.x,
y: self.y / rhs.y,
}
}
}
impl<T: Int> ops::DivAssign<Pos<T>> for Pos<T> {
fn div_assign(&mut self, rhs: Pos<T>) {
self.x /= rhs.x;
self.y /= rhs.y;
}
}
impl<T: Int> From<(T, T)> for Pos<T> {
fn from(value: (T, T)) -> Self {
Self::new(value.0, value.1)
}
}
impl<T: Int> From<Pos<T>> for (T, T) {
fn from(pos: Pos<T>) -> Self {
(pos.x, pos.y)
}
}
impl<T: Int> From<[T; 2]> for Pos<T> {
fn from(value: [T; 2]) -> Self {
Self::new(value[0], value[1])
}
}
impl<T: Int> From<Pos<T>> for [T; 2] {
fn from(pos: Pos<T>) -> Self {
[pos.x, pos.y]
}
}
pub trait TryFromPos<T: Int>: Sized {
fn try_from_pos(value: Pos<T>) -> Result<Self, TryFromPosError>;
}
pub trait TryIntoPos<T: Int>: Sized {
fn try_into_pos(self) -> Result<Pos<T>, TryFromPosError>;
}
impl<T, U> TryIntoPos<U> for Pos<T>
where
Pos<U>: TryFromPos<T>,
U: Int,
T: Int,
{
fn try_into_pos(self) -> Result<Pos<U>, TryFromPosError> {
Pos::<U>::try_from_pos(self)
}
}
impl<T: Int> AsRef<[T; 2]> for Pos<T> {
fn as_ref(&self) -> &[T; 2] {
unsafe { &*core::ptr::from_ref::<Pos<T>>(self).cast::<[T; 2]>() }
}
}
impl<T: Int> AsRef<(T, T)> for Pos<T> {
fn as_ref(&self) -> &(T, T) {
unsafe { &*core::ptr::from_ref::<Pos<T>>(self).cast::<(T, T)>() }
}
}
impl<T: Int> AsMut<[T; 2]> for Pos<T> {
fn as_mut(&mut self) -> &mut [T; 2] {
unsafe { &mut *core::ptr::from_mut::<Pos<T>>(self).cast::<[T; 2]>() }
}
}
impl<T: Int> AsMut<(T, T)> for Pos<T> {
fn as_mut(&mut self) -> &mut (T, T) {
unsafe { &mut *(core::ptr::from_mut::<Pos<T>>(self)).cast::<(T, T)>() }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryFromPosError {
OutOfRange,
}
impl<S: Int, T: Int + TryFrom<S>> TryFromPos<S> for Pos<T> {
fn try_from_pos(value: Pos<S>) -> Result<Self, TryFromPosError> {
let x = T::try_from(value.x).map_err(|_| TryFromPosError::OutOfRange)?;
let y = T::try_from(value.y).map_err(|_| TryFromPosError::OutOfRange)?;
Ok(Pos::new(x, y))
}
}
impl<T: Int> TryFrom<Pos<T>> for Size {
type Error = TryFromPosError;
fn try_from(value: Pos<T>) -> Result<Self, TryFromPosError> {
let width = value
.x
.checked_to_usize()
.ok_or(TryFromPosError::OutOfRange)?;
let height = value
.y
.checked_to_usize()
.ok_or(TryFromPosError::OutOfRange)?;
Ok(Size::new(width, height))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layout_is_c_struct() {
struct CPos {
x: i32,
y: i32,
}
let pos = Pos::<i32> { x: 1, y: 2 };
#[allow(unsafe_code, reason = "Test")]
let c_pos: CPos = unsafe { core::mem::transmute(pos) };
assert_eq!(c_pos.x, 1);
assert_eq!(c_pos.y, 2);
}
#[test]
fn pos_macro() {
const P: Pos<i32> = pos!(3, 4);
assert_eq!(P.x, 3);
assert_eq!(P.y, 4);
}
#[test]
fn ord() {
assert!(Pos::new(1, 2) < Pos::new(2, 1));
assert!(Pos::new(1, 2) < Pos::new(1, 3));
assert!(Pos::new(1, 2) < Pos::new(2, 2));
assert!(Pos::new(2, 2) > Pos::new(1, 2));
assert!(Pos::new(2, 1) > Pos::new(1, 2));
}
#[test]
fn generic_t_defaults_to_i32() {
let p: Pos = Pos::default();
assert_eq!(p, Pos::<i32>::ORIGIN);
}
#[test]
fn origin_is_0_0() {
assert_eq!(Pos::ORIGIN, Pos::new(0, 0));
}
#[test]
fn min_is_min_min() {
assert_eq!(Pos::MIN, Pos::new(i32::MIN, i32::MIN));
}
#[test]
fn max_is_max_max() {
assert_eq!(Pos::MAX, Pos::new(i32::MAX, i32::MAX));
}
#[test]
fn x_is_1_0() {
assert_eq!(Pos::X, Pos::new(1, 0));
}
#[test]
fn y_is_0_1() {
assert_eq!(Pos::Y, Pos::new(0, 1));
}
#[test]
fn new_x_y() {
let p = Pos::new(3, 4);
assert_eq!(p.x, 3);
assert_eq!(p.y, 4);
}
#[test]
fn default_is_origin() {
let p: Pos<i32> = Pos::default();
assert_eq!(p, Pos::ORIGIN);
}
#[test]
fn negate() {
let p = Pos::new(3, 4);
assert_eq!(-p, Pos::new(-3, -4));
}
#[test]
fn mul_by_scalar() {
let p = Pos::new(3, 4) * 2;
assert_eq!(p, Pos::new(6, 8));
}
#[test]
fn mul_assign_by_scalar() {
let mut p = Pos::new(3, 4);
p *= 2;
assert_eq!(p, Pos::new(6, 8));
}
#[test]
fn from_tuple() {
let pos = Pos::from((3, 4));
assert_eq!(pos.x, 3);
assert_eq!(pos.y, 4);
}
#[test]
fn from_array() {
let pos = Pos::from([3, 4]);
assert_eq!(pos.x, 3);
assert_eq!(pos.y, 4);
}
#[test]
fn into_tuple() {
let pos = Pos::new(3, 4);
let tuple: (i32, i32) = pos.into();
assert_eq!(tuple, (3, 4));
}
#[test]
fn into_array() {
let pos = Pos::new(3, 4);
let array: [i32; 2] = pos.into();
assert_eq!(array, [3, 4]);
}
#[test]
fn try_from_pos_ok() {
let source: Pos<u8> = Pos::new(3, 4);
let convert = Pos::<i32>::try_from_pos(source).unwrap();
assert_eq!(convert.x, 3);
assert_eq!(convert.y, 4);
}
#[test]
fn try_from_pos_out_of_range() {
let source: Pos<u16> = Pos::new(7000, 8000);
let result = Pos::<u8>::try_from_pos(source);
assert!(result.is_err());
}
#[test]
fn try_into_pos_ok() {
let source: Pos<u8> = Pos::new(3, 4);
let convert: Pos<i32> = source.try_into_pos().unwrap();
assert_eq!(convert.x, 3);
assert_eq!(convert.y, 4);
}
#[test]
fn try_into_pos_out_of_range() {
let source: Pos<u16> = Pos::new(7000, 8000);
let result: Result<Pos<u8>, TryFromPosError> = source.try_into_pos();
assert!(result.is_err());
}
#[test]
fn as_ref_array() {
let pos = Pos::new(3, 4);
let arr: &[i32; 2] = pos.as_ref();
assert_eq!(arr, &[3, 4]);
}
#[test]
fn as_mut_array() {
let mut pos = Pos::new(3, 4);
let arr: &mut [i32; 2] = pos.as_mut();
arr[0] = 5;
arr[1] = 6;
assert_eq!(pos, Pos::new(5, 6));
}
#[test]
fn as_ref_tuple() {
let pos = Pos::new(3, 4);
let (x, y) = pos.as_ref();
assert_eq!(x, &3);
assert_eq!(y, &4);
}
#[test]
fn as_mut_tuple() {
let mut pos = Pos::new(3, 4);
let (x, y) = pos.as_mut();
*x = 5;
*y = 6;
assert_eq!(pos, Pos::new(5, 6));
}
#[test]
fn add_pos() {
let p1 = Pos::new(3, 4);
let p2 = Pos::new(1, 2);
assert_eq!(p1 + p2, Pos::new(4, 6));
}
#[test]
fn add_assign_pos() {
let mut p1 = Pos::new(3, 4);
let p2 = Pos::new(1, 2);
p1 += p2;
assert_eq!(p1, Pos::new(4, 6));
}
#[test]
fn into_size() {
let pos = Pos::new(3, 4);
let size = Size::try_from(pos).unwrap();
assert_eq!(size.width, 3);
assert_eq!(size.height, 4);
}
#[test]
fn into_size_wrapped() {
let pos = Pos::new(-3, -4);
let size = Size::try_from(pos);
assert!(size.is_err());
}
#[test]
fn normalized() {
assert_eq!(Pos::new(0, 0).normalized_approx(), Pos::ORIGIN);
assert_eq!(Pos::new(1, 0).normalized_approx(), Pos::X);
assert_eq!(Pos::new(0, 1).normalized_approx(), Pos::Y);
assert_eq!(Pos::new(2, 0).normalized_approx(), Pos::X);
assert_eq!(Pos::new(0, 2).normalized_approx(), Pos::Y);
assert_eq!(
Pos::new(6, 8).normalized_approx(),
Pos::new(3, 4).normalized_approx()
);
}
#[test]
fn mul_scalar() {
let p = Pos::new(3, 4) * 2;
assert_eq!(p, Pos::new(6, 8));
}
#[test]
fn mul_assign_scalar() {
let mut p = Pos::new(3, 4);
p *= 2;
assert_eq!(p, Pos::new(6, 8));
}
#[test]
fn mul_pos() {
let p1 = Pos::new(3, 4);
let p2 = Pos::new(2, 3);
assert_eq!(p1 * p2, Pos::new(6, 12));
}
#[test]
fn mul_assign_pos() {
let mut p1 = Pos::new(3, 4);
let p2 = Pos::new(2, 3);
p1 *= p2;
assert_eq!(p1, Pos::new(6, 12));
}
#[test]
fn div_scalar() {
let p = Pos::new(6, 8) / 2;
assert_eq!(p, Pos::new(3, 4));
}
#[test]
fn div_assign_scalar() {
let mut p = Pos::new(6, 8);
p /= 2;
assert_eq!(p, Pos::new(3, 4));
}
#[test]
fn div_pos() {
let p1 = Pos::new(6, 8);
let p2 = Pos::new(2, 4);
assert_eq!(p1 / p2, Pos::new(3, 2));
}
#[test]
fn div_assign_pos() {
let mut p1 = Pos::new(6, 8);
let p2 = Pos::new(2, 4);
p1 /= p2;
assert_eq!(p1, Pos::new(3, 2));
}
}