ceratophrys 0.0.3

Library to draw pixel art as code
Documentation
use crate::Position;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Size {
    pub width: u16,
    pub height: u16,
}

impl Size {
    pub const EMPTY: Self = Self::new(0, 0);

    pub const fn new(width: u16, height: u16) -> Self {
        Self { width, height }
    }

    pub const fn square(side: u16) -> Self {
        Self::new(side, side)
    }

    pub const fn is_empty(self) -> bool {
        self.width == 0 || self.height == 0
    }

    pub fn area(self) -> u32 {
        u32::from(self.width) * u32::from(self.height)
    }

    pub fn contains(self, position: Position) -> bool {
        if position.x < 0 || position.y < 0 {
            return false;
        }
        (0..self.height).contains(&(position.y as u16))
            && (0..self.width).contains(&(position.x as u16))
    }

    pub fn max(self, other: Self) -> Self {
        Self::new(self.width.max(other.width), self.height.max(other.height))
    }

    pub fn min(self, other: Self) -> Self {
        Self::new(self.width.min(other.width), self.height.min(other.height))
    }

    pub fn positions(self) -> impl Iterator<Item = Position> {
        let w = i16::try_from(self.width).expect("width is too large");
        let h = i16::try_from(self.height).expect("height is too large");
        (0..h).flat_map(move |y| (0..w).map(move |x| Position::xy(x, y)))
    }

    pub fn edge_positions(self) -> impl Iterator<Item = Position> {
        fn if_iter(
            cond: bool,
            iter: impl Iterator<Item = Position>,
        ) -> impl Iterator<Item = Position> {
            cond.then_some(iter).into_iter().flatten()
        }

        let w = i16::try_from(self.width).expect("width is too large");
        let h = i16::try_from(self.height).expect("height is too large");

        let iter0 = if_iter(h > 0, (0..w).map(move |x| Position::xy(x, 0)));
        let iter1 = if_iter(h > 1, (0..w).map(move |x| Position::xy(x, h - 1)));
        let iter2 = if_iter(w > 0, (1..h - 1).map(move |y| Position::xy(0, y)));
        let iter3 = if_iter(w > 1, (1..h - 1).map(move |y| Position::xy(w - 1, y)));

        iter0.chain(iter1).chain(iter2).chain(iter3)
    }
}

impl FromIterator<Position> for Size {
    fn from_iter<T: IntoIterator<Item = Position>>(iter: T) -> Self {
        let mut min = Position::MAX;
        let mut max = Position::MIN;

        for position in iter {
            min = min.min(position);
            max = max.max(position);
        }

        if max < min {
            Self::EMPTY
        } else {
            let size = max - min;
            Self::new(size.x as u16 + 1, size.y as u16 + 1)
        }
    }
}