use std::{fmt, ops};
use crate::{Coordinate, Size2D};
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct Size {
pub x: u32,
pub y: u32,
pub z: u32,
}
impl Size {
pub const fn new(x: u32, y: u32, z: u32) -> Self {
Self { x, y, z }
}
pub fn contains(self, coordinate: impl Into<Coordinate>) -> bool {
let coordinate = coordinate.into();
(0..self.x as i32).contains(&coordinate.x)
&& (0..self.y as i32).contains(&coordinate.y)
&& (0..self.z as i32).contains(&coordinate.z)
}
pub const fn index_to_offset(self, index: usize) -> Coordinate {
let z = (index % self.z as usize) as i32;
let xy = index / self.z as usize;
let x = (xy % self.x as usize) as i32;
let y = (xy / self.x as usize) as i32;
Coordinate { x, y, z }
}
pub fn offset_to_index(self, coordinate: impl Into<Coordinate>) -> usize {
let coordinate = coordinate.into();
let [x, y, z] = [
coordinate.x as usize,
coordinate.y as usize,
coordinate.z as usize,
];
z + (x + y * self.x as usize) * self.z as usize
}
pub fn volume(self) -> usize {
self.x as usize * self.y as usize * self.z as usize
}
pub fn flat(self) -> Size2D {
self.into()
}
}
impl fmt::Display for Size {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}x{}x{}", self.x, self.y, self.z)
}
}
impl fmt::Debug for Size {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Size({}, {}, {})", self.x, self.y, self.z)
}
}
impl<T> ops::Add<T> for Size
where
T: Into<Self>,
{
type Output = Self;
fn add(self, rhs: T) -> Self::Output {
let rhs = rhs.into();
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl<T> ops::Sub<T> for Size
where
T: Into<Self>,
{
type Output = Self;
fn sub(self, rhs: T) -> Self::Output {
let rhs = rhs.into();
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
z: self.z - rhs.z,
}
}
}
impl<T> ops::Mul<T> for Size
where
T: Into<Self>,
{
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
let rhs = rhs.into();
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
z: self.z * rhs.z,
}
}
}
impl ops::Mul<u32> for Size {
type Output = Self;
fn mul(self, rhs: u32) -> Self::Output {
Self {
x: self.x * rhs,
y: self.y * rhs,
z: self.z * rhs,
}
}
}
impl ops::Div<u32> for Size {
type Output = Self;
fn div(self, rhs: u32) -> Self::Output {
Self {
x: self.x / rhs,
y: self.y / rhs,
z: self.z / rhs,
}
}
}
impl ops::Add<Size2D> for Size {
type Output = Self;
fn add(self, rhs: Size2D) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y,
z: self.z + rhs.z,
}
}
}
impl ops::Sub<Size2D> for Size {
type Output = Self;
fn sub(self, rhs: Size2D) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y,
z: self.z - rhs.z,
}
}
}
impl From<[u32; 3]> for Size {
fn from(value: [u32; 3]) -> Self {
Self {
x: value[0],
y: value[1],
z: value[2],
}
}
}
impl From<(u32, u32, u32)> for Size {
fn from(value: (u32, u32, u32)) -> Self {
Self {
x: value.0,
y: value.1,
z: value.2,
}
}
}