use crate::{color::Color, ppm::PNM, shapes::Shape, vec::Vec2d};
pub struct Image {
cols: usize,
rows: usize,
pixels: Vec<Vec<Color>>,
}
impl Image {
pub fn new(cols: usize, rows: usize) -> Self {
let pixels = vec![vec![Color::default(); cols]; rows];
Self { cols, rows, pixels }
}
pub fn cols(&self) -> usize {
self.cols
}
pub fn rows(&self) -> usize {
self.rows
}
pub fn get(&self, x: usize, y: usize) -> Option<Color> {
let Some(row) = self.pixels.get(y) else {
return None;
};
return row.get(x).copied();
}
pub fn set(&mut self, x: usize, y: usize, color: &Color) {
let Some(row) = self.pixels.get_mut(y) else {
return;
};
if x < row.len() {
row[x] = *color;
} else {
}
}
pub fn at(&self, vector: Vec2d) -> Option<Color> {
match (vector.x, vector.y) {
(x, y) if x >= 0.0 && y >= 0.0 => self.get(x as usize, y as usize),
_ => None,
}
}
pub fn fill_with(&mut self, color: &Color) {
self.pixels = vec![vec![*color; self.cols]; self.rows];
}
pub fn draw(&mut self, shape: &dyn Shape) {
shape.draw(self);
}
}
impl PNM for Image {
fn to_pnm_p3(&self) -> String {
let mut ppm = "P3\n".to_string();
ppm.push_str(&format!("{} {}\n", self.cols, self.rows));
ppm.push_str(&format!("{}\n", u8::max_value()));
for row in &self.pixels {
let mut row_str = "".to_string();
for pixel in row {
row_str.push_str(&format!("{} ", pixel.to_pnm_p3()));
}
ppm.push_str(&format!("{}\n", row_str));
}
ppm
}
fn to_pnm_p6(&self) -> Vec<u8> {
let mut ppm = "P6\n".to_string();
ppm.push_str(&format!("{} {}\n", self.cols, self.rows));
ppm.push_str(&format!("{}\n", u8::max_value()));
let mut ppm = ppm.as_bytes().to_owned();
for row in &self.pixels {
let mut row_vec = vec![];
for pixel in row {
row_vec.append(&mut pixel.to_pnm_p6());
}
ppm.append(&mut row_vec);
}
ppm
}
}
#[cfg(test)]
mod tests {
use crate::{rgb, vec2};
use super::*;
#[test]
fn test_image_new() {
let img = Image::new(42, 17);
assert_eq!(img.cols(), 42);
assert_eq!(img.rows(), 17);
assert_eq!(img.pixels, vec![vec![Color::splat(0); 42]; 17]);
}
#[test]
fn test_image_set() {
let mut img = Image::new(42, 17);
img.set(10, 10, &rgb!(42, 42, 17));
let pixel = img.get(10, 10);
assert_eq!(pixel, Some(rgb!(42, 42, 17)));
let pixel = img.at(vec2![10.0]);
assert_eq!(pixel, Some(rgb!(42, 42, 17)));
}
#[test]
fn test_image_get_out_of_bounds() {
let img = Image::new(42, 17);
let pixel = img.get(100, 100);
assert_eq!(pixel, None);
}
#[test]
fn test_image_at_out_of_bounds() {
let img = Image::new(42, 17);
let pixel = img.at(vec2![100.0]);
assert_eq!(pixel, None);
}
#[test]
fn test_image_fill() {
let mut img = Image::new(42, 17);
img.fill_with(&rgb!(17, 120, 42));
assert_eq!(img.pixels, vec![vec![rgb!(17, 120, 42); 42]; 17]);
}
}