diff --git a/ray-tracer/src/types/canvas.rs b/ray-tracer/src/types/canvas.rs new file mode 100644 index 0000000..82c0310 --- /dev/null +++ b/ray-tracer/src/types/canvas.rs @@ -0,0 +1,41 @@ +use crate::types::Color; + +pub struct Canvas { + width: usize, + height: usize, + pixels: Vec, +} + +impl Canvas { + pub fn new(width: usize, height: usize) -> Self { + Self { + width, + height, + pixels: vec![Color::default(); width * height], + } + } + + #[inline] + pub fn width(&self) -> usize { + self.width + } + + #[inline] + pub fn height(&self) -> usize { + self.height + } + + pub fn pixel(&self, row: usize, column: usize) -> &Color { + &self.pixels[self.addr(row, column)] + } + + pub fn pixel_mut<'a>(&'a mut self, row: usize, column: usize) -> &'a mut Color { + let addr = self.addr(row, column); + &mut self.pixels[addr] + } + + #[inline] + fn addr(&self, row: usize, column: usize) -> usize { + row * self.width() + column + } +} diff --git a/ray-tracer/src/types/color.rs b/ray-tracer/src/types/color.rs new file mode 100644 index 0000000..50c8586 --- /dev/null +++ b/ray-tracer/src/types/color.rs @@ -0,0 +1,87 @@ +use crate::types::Tuple; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Color(Tuple); + +impl Color { + pub fn new(red: f64, green: f64, blue: f64) -> Self { + Self(Tuple(red, green, blue, 0.)) + } + + #[inline] + pub fn red(&self) -> f64 { + self.0.0 + } + + #[inline] + pub fn green(&self) -> f64 { + self.0.1 + } + + #[inline] + pub fn blue(&self) -> f64 { + self.0.2 + } + +} + +impl Default for Color { + fn default() -> Self { + Self::new(0., 0., 0.) + } +} + +impl From for Color { + fn from(tuple: Tuple) -> Self { + assert_eq!(tuple.3, 0.0); + Self(tuple) + } +} + +impl std::ops::Deref for Color { + type Target = Tuple; + fn deref(&self) -> &Tuple { + &self.0 + } +} + +impl std::ops::Add for Color { + type Output = Color; + fn add(self, r: Self) -> Self { + Color::from(self.0 + r.0) + } +} + +impl std::ops::Sub for Color { + type Output = Color; + fn sub(self, r: Self) -> Self { + Color::from(self.0 - r.0) + } +} + +impl std::ops::Neg for Color { + type Output = Color; + fn neg(self) -> Self::Output { + let mut t = -self.0; + t.0 = 0.; + Color::from(t) + } +} + +impl std::ops::Mul for Color { + type Output = Color; + fn mul(self, r: Color) -> Self { + let red = self.red() * r.red(); + let green = self.green() * r.green(); + let blue = self.blue() * r.blue(); + Self::new(red, green, blue) + } +} + +impl std::ops::Mul for Color { + type Output = Color; + fn mul(self, r: f64) -> Self { + Color::from(self.0 * r) + } +} + diff --git a/ray-tracer/src/types/mod.rs b/ray-tracer/src/types/mod.rs index e501927..7f60b97 100644 --- a/ray-tracer/src/types/mod.rs +++ b/ray-tracer/src/types/mod.rs @@ -1,7 +1,11 @@ +mod canvas; +mod color; mod point; mod tuple; mod vector; +pub use canvas::Canvas; +pub use color::Color; pub use point::Point; pub use tuple::Tuple; pub use vector::Vector; @@ -116,4 +120,32 @@ mod tests { Vector::new(1., -2., 1.) ); } + + #[test] + fn multiply_colors() { + let c1 = Color::new(1., 0.2, 0.4); + let c2 = Color::new(0.9, 1., 0.1); + + assert_eq!(c1 * c2, Color::new(0.9, 0.2, 0.04)); + } + + #[test] + fn it_creates_a_canvas() { + let c = Canvas::new(10, 20); + assert_eq!(c.width(), 10); + assert_eq!(c.height(), 20); + for row in 0..20 { + for col in 0..10 { + assert_eq!(*c.pixel(row, col), Color::default()); + } + } + } + + #[test] + fn it_can_write_pixel() { + let mut c = Canvas::new(10, 20); + let red = Color::new(1., 0., 0.); + *c.pixel_mut(2, 3) = red; + assert_eq!(*c.pixel(2, 3), red); + } }