bs-trace 0.3.0

Free RayTracing software
Documentation
use const_default::ConstDefault;
use std::fmt::Display;

#[derive(Debug, Clone, Copy)]
/// An RGB pixel with 24-bit depth.
pub struct Pixel {
    /// Red value of this pixel.
    pub r: u8,
    /// Green value of this pixel.
    pub g: u8,
    /// Blue value of this pixel.
    pub b: u8,
}

impl ConstDefault for Pixel {
    /// Black.
    const DEFAULT: Pixel = Pixel::new(0, 0, 0);
}

impl Display for Pixel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}\t{}\t{}", self.r, self.g, self.b)
    }
}

/// A NetPBM Portable PixMap (P3; PPM) image.
pub struct Ppm<const W: usize, const H: usize> {
    rows: Vec<Vec<Pixel>>,
}

impl Pixel {
    /// Constructs a new pixel from the given red, green, and blue values.
    pub const fn new(r: u8, g: u8, b: u8) -> Self {
        Self { r, g, b }
    }
}

impl<const W: usize, const H: usize> Ppm<W, H> {
    /// Constructs a PPM image from the given row data.
    ///
    /// This method will panic if there are not precisely `H` rows, or any of the rows do not contain precisely `W` pixels.
    pub fn new(rows: Vec<Vec<Pixel>>) -> Self {
        assert_eq!(rows.len(), H);
        for row in rows.iter() {
            assert_eq!(row.len(), W);
        }

        unsafe { Self::new_unchecked(rows) }
    }

    /// Constructs a PPM image from the given row data.
    ///
    /// # Safety
    ///
    /// It is the responsibility of the caller to ensure that `rows` contains precisely `H`
    /// rows, and that each row contains precisely `W` pixels.
    pub unsafe fn new_unchecked(rows: Vec<Vec<Pixel>>) -> Self {
        Self { rows }
    }

    /// Generates a PPM image using the given function in each pixel's (x,y) coordinates -- the
    /// top-left corner of the image is (0,0), x grows to the right in the positive direction,
    /// and y grows downwards in the positive direction.
    pub fn gen<F>(f: F) -> Self
    where
        F: Fn(usize, usize) -> Pixel,
    {
        let mut rows = Vec::<Vec<Pixel>>::with_capacity(H);
        for y in 0..H {
            let mut row_data = Vec::<Pixel>::with_capacity(W);
            for x in 0..W {
                row_data.push(f(x, y));
            }
            rows.push(row_data);
        }

        unsafe { Self::new_unchecked(rows) }
    }
}

impl<const W: usize, const H: usize> Display for Ppm<W, H> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "P3")?;
        writeln!(f, "{}\t{}", W, H)?;
        writeln!(f, "255")?;
        for row in self.rows.iter() {
            for pixel in row.iter() {
                writeln!(f, "{}", pixel)?;
            }
        }

        Ok(())
    }
}

impl IntoIterator for Pixel {
    type Item = u8;
    type IntoIter = std::array::IntoIter<u8, 3>;

    fn into_iter(self) -> Self::IntoIter {
        [self.r, self.g, self.b].into_iter()
    }
}