diff --git a/rtchallenge/src/patterns.rs b/rtchallenge/src/patterns.rs index e91485d..fbb83d6 100644 --- a/rtchallenge/src/patterns.rs +++ b/rtchallenge/src/patterns.rs @@ -64,112 +64,30 @@ where /// Builder for creating a material pattern used for testing. The color returned is the pattern space point /// after going through world->object and object->pattern space translation. -/// -/// # Examples -/// ``` -/// use rtchallenge::{ -/// matrices::Matrix4x4, -/// patterns::{test_pattern, ColorMapper}, -/// BLACK, WHITE, -/// }; -/// -/// # fn main() -> Result<(), Box> { -/// -/// let pattern = test_pattern().build()?; -/// assert_eq!(pattern.transform(), Matrix4x4::identity()); -/// -/// # Ok(()) -/// # } -/// ``` pub fn test_pattern() -> PatternBuilder { PatternBuilder::default().color(ColorMapper::TestPattern) } /// Builder for creating a material pattern that alternates between the given colors along each unit of the /// X-axis. The strip extends infinitely in the positive and negative Y and Z axes. -/// -/// # Examples -/// ``` -/// use rtchallenge::{ -/// patterns::{stripe_pattern, ColorMapper}, -/// BLACK, WHITE, -/// }; -/// -/// # fn main() -> Result<(), Box> { -/// -/// let pattern = stripe_pattern(BLACK, WHITE).build()?; -/// assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE }); -/// -/// # Ok(()) -/// # } -/// ``` pub fn stripe_pattern(a: Color, b: Color) -> PatternBuilder { PatternBuilder::default().color(ColorMapper::Stripe { a, b }) } /// Builder for creating a material pattern that gradually blends between the given colors along /// the X-axis. -/// -/// # Examples -/// ``` -/// use rtchallenge::{ -/// matrices::Matrix4x4, -/// patterns::{gradient_pattern, ColorMapper}, -/// BLACK, WHITE, -/// }; -/// -/// # fn main() -> Result<(), Box> { -/// -/// let pattern = gradient_pattern(WHITE, BLACK).build()?; -/// assert_eq!(pattern.color, ColorMapper::Gradient { a: WHITE, b: BLACK }); -/// -/// # Ok(()) -/// # } -/// ``` pub fn gradient_pattern(a: Color, b: Color) -> PatternBuilder { PatternBuilder::default().color(ColorMapper::Gradient { a, b }) } /// Builder for creating a material pattern that alternates between the given colors in a ring /// shape in the XZ plane. -/// -/// # Examples -/// ``` -/// use rtchallenge::{ -/// patterns::{ring_pattern, ColorMapper}, -/// BLACK, WHITE, -/// }; -/// -/// # fn main() -> Result<(), Box> { -/// -/// let pattern = ring_pattern(BLACK, WHITE).build()?; -/// assert_eq!(pattern.color, ColorMapper::Ring { a: BLACK, b: WHITE }); -/// -/// # Ok(()) -/// # } -/// ``` pub fn ring_pattern(a: Color, b: Color) -> PatternBuilder { PatternBuilder::default().color(ColorMapper::Ring { a, b }) } /// Builder for creating a material pattern that alternates between the given colors along the X, Y /// and Z pattern. Creates traditional ray tracer tile floor pattern. -/// -/// # Examples -/// ``` -/// use rtchallenge::{ -/// patterns::{checkers_pattern, ColorMapper}, -/// BLACK, WHITE, -/// }; -/// -/// # fn main() -> Result<(), Box> { -/// -/// let pattern = checkers_pattern(BLACK, WHITE).build()?; -/// assert_eq!(pattern.color, ColorMapper::Checkers { a: BLACK, b: WHITE }); -/// -/// # Ok(()) -/// # } -/// ``` pub fn checkers_pattern(a: Color, b: Color) -> PatternBuilder { PatternBuilder::default().color(ColorMapper::Checkers { a, b }) } @@ -178,23 +96,6 @@ pub fn checkers_pattern(a: Color, b: Color) -> PatternBuilder { impl Pattern { /// Create a pattern used for testing. The color returned is the pattern space point /// after going through world->object and object->pattern space translation. - /// - /// # Examples - /// ``` - /// use rtchallenge::{ - /// matrices::Matrix4x4, - /// patterns::{ColorMapper, Pattern}, - /// BLACK, WHITE, - /// }; - /// - /// # fn main() -> Result<(), Box> { - /// - /// let pattern = Pattern::test(); - /// assert_eq!(pattern.transform(), Matrix4x4::identity()); - /// - /// # Ok(()) - /// # } - /// ``` pub fn test() -> Pattern { Pattern { color: ColorMapper::TestPattern, @@ -204,17 +105,6 @@ impl Pattern { /// Create a pattern that alternates between the given colors along each unit of the /// X-axis. The strip extends infinitely in the positive and negative Y and Z axes. - /// - /// # Examples - /// ``` - /// use rtchallenge::{ - /// patterns::{ColorMapper, Pattern}, - /// BLACK, WHITE, - /// }; - /// - /// let pattern = Pattern::stripe(BLACK, WHITE); - /// assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE }); - /// ``` pub fn stripe(a: Color, b: Color) -> Pattern { Pattern { color: ColorMapper::Stripe { a, b }, @@ -223,17 +113,6 @@ impl Pattern { } /// Create a pattern that gradually blends between the given colors along the X-axis. - /// - /// # Examples - /// ``` - /// use rtchallenge::{ - /// patterns::{ColorMapper, Pattern}, - /// BLACK, WHITE, - /// }; - /// - /// let pattern = Pattern::gradient(BLACK, WHITE); - /// assert_eq!(pattern.color, ColorMapper::Gradient { a: BLACK, b: WHITE }); - /// ``` pub fn gradient(a: Color, b: Color) -> Pattern { Pattern { color: ColorMapper::Gradient { a, b }, @@ -242,17 +121,6 @@ impl Pattern { } /// Create a pattern that alternates between the given colors in a ring in the XZ plane. - /// - /// # Examples - /// ``` - /// use rtchallenge::{ - /// patterns::{ColorMapper, Pattern}, - /// BLACK, WHITE, - /// }; - /// - /// let pattern = Pattern::ring(BLACK, WHITE); - /// assert_eq!(pattern.color, ColorMapper::Ring { a: BLACK, b: WHITE }); - /// ``` pub fn ring(a: Color, b: Color) -> Pattern { Pattern { color: ColorMapper::Ring { a, b }, @@ -261,17 +129,6 @@ impl Pattern { } /// Create a pattern that alternates between the given colors along the X, Y and Z axis. - /// - /// # Examples - /// ``` - /// use rtchallenge::{ - /// patterns::{ColorMapper, Pattern}, - /// BLACK, WHITE, - /// }; - /// - /// let pattern = Pattern::checkers(BLACK, WHITE); - /// assert_eq!(pattern.color, ColorMapper::Checkers { a: BLACK, b: WHITE }); - /// ``` pub fn checkers(a: Color, b: Color) -> Pattern { Pattern { color: ColorMapper::Checkers { a, b }, @@ -280,80 +137,6 @@ impl Pattern { } /// Sample the color at the given point in untranslated object space. - /// - /// # Examples - /// ``` - /// use rtchallenge::{patterns::Pattern, tuples::point, BLACK, WHITE}; - /// - /// // A test returns the pattern space coordinates of the point. - /// let pattern = Pattern::test(); - /// let p = point(1., 2., 3.); - /// assert_eq!(pattern.pattern_at(p), [p.x, p.y, p.z].into()); - /// - /// // A stripe alternates between two colors. - /// let pattern = Pattern::stripe(WHITE, BLACK); - /// for (p, want) in &[ - /// // A stripe pattern is constant in y. - /// (point(0., 0., 0.), WHITE), - /// (point(0., 1., 0.), WHITE), - /// (point(0., 2., 0.), WHITE), - /// // A stripe pattern is constant in z. - /// (point(0., 0., 0.), WHITE), - /// (point(0., 0., 1.), WHITE), - /// (point(0., 0., 2.), WHITE), - /// // A stripe pattern alternates in z. - /// (point(0., 0., 0.), WHITE), - /// (point(0.9, 0., 0.), WHITE), - /// (point(1., 0., 0.), BLACK), - /// (point(-0.1, 0., 0.), BLACK), - /// (point(-1., 0., 0.), BLACK), - /// (point(-1.1, 0., 0.), WHITE), - /// ] { - /// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p); - /// } - /// - /// // A gradient linearly interpolates between two colors. - /// let pattern = Pattern::gradient(WHITE, BLACK); - /// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); - /// assert_eq!( - /// pattern.pattern_at(point(0.25, 0., 0.)), - /// [0.75, 0.75, 0.75].into() - /// ); - /// assert_eq!( - /// pattern.pattern_at(point(0.5, 0., 0.)), - /// [0.5, 0.5, 0.5].into() - /// ); - /// assert_eq!( - /// pattern.pattern_at(point(0.75, 0., 0.)), - /// [0.25, 0.25, 0.25].into() - /// ); - /// - /// // A ring should extend both in x and z. - /// let pattern = Pattern::ring(WHITE, BLACK); - /// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); - /// assert_eq!(pattern.pattern_at(point(1., 0., 0.)), BLACK); - /// assert_eq!(pattern.pattern_at(point(0., 0., 1.)), BLACK); - /// // 0.708 is slight more than 2.sqrt()/2. - /// assert_eq!(pattern.pattern_at(point(0.708, 0., 0.708)), BLACK); - /// - /// // Checkers should repeat along X-axis. - /// let pattern = Pattern::checkers(WHITE, BLACK); - /// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); - /// assert_eq!(pattern.pattern_at(point(0.99, 0., 0.)), WHITE); - /// assert_eq!(pattern.pattern_at(point(1.01, 0., 0.)), BLACK); - /// - /// // Checkers should repeat along Y-axis. - /// let pattern = Pattern::checkers(WHITE, BLACK); - /// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); - /// assert_eq!(pattern.pattern_at(point(0., 0.99, 0.)), WHITE); - /// assert_eq!(pattern.pattern_at(point(0., 1.01, 0.)), BLACK); - /// - /// // Checkers should repeat along Z-axis. - /// let pattern = Pattern::checkers(WHITE, BLACK); - /// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); - /// assert_eq!(pattern.pattern_at(point(0., 0., 0.99)), WHITE); - /// assert_eq!(pattern.pattern_at(point(0., 0., 1.01)), BLACK); - /// ``` pub fn pattern_at(&self, point: Tuple) -> Color { match self.color { ColorMapper::TestPattern => [point.x, point.y, point.z].into(), @@ -392,37 +175,6 @@ impl Pattern { } /// Sample the color at the given world point on the given object. /// This function respects the object and the pattern's transform matrix. - /// - /// # Examples - /// ``` - /// use rtchallenge::{ - /// matrices::scaling, - /// patterns::stripe_pattern, - /// shapes::{Shape, ShapeBuilder}, - /// tuples::point, - /// BLACK, WHITE, - /// }; - /// - /// # fn main() -> Result<(), Box> { - /// - /// // Stripes with an object transformation. - /// let object = ShapeBuilder::sphere() - /// .transform(scaling(2., 2., 2.)) - /// .build()?; - /// let pattern = stripe_pattern(WHITE, BLACK).build()?; - /// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.)); - /// assert_eq!(c, WHITE); - /// - /// // Stripes with an pattern transformation. - /// let object = Shape::sphere(); - /// let mut pattern = stripe_pattern(WHITE, BLACK).build()?; - /// pattern.set_transform(scaling(2., 2., 2.)); - /// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.)); - /// assert_eq!(c, WHITE); - /// - /// # Ok(()) - /// # } - /// ``` pub fn pattern_at_object(&self, object: &Shape, world_point: Tuple) -> Color { let object_point = object.inverse_transform() * world_point; let pattern_point = self.inverse_transform * object_point; @@ -441,3 +193,169 @@ impl Pattern { self.inverse_transform = t.inverse(); } } + +#[cfg(test)] +mod tests { + use crate::{ + matrices::identity, + patterns::{ColorMapper, Pattern}, + BLACK, WHITE, + }; + + #[test] + fn test_create() { + let pattern = Pattern::test(); + assert_eq!(pattern.transform(), identity()); + } + + #[test] + fn stripe_create() { + let pattern = Pattern::stripe(BLACK, WHITE); + assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE }); + } + #[test] + fn gradient_create() { + let pattern = Pattern::gradient(BLACK, WHITE); + assert_eq!(pattern.color, ColorMapper::Gradient { a: BLACK, b: WHITE }); + } + #[test] + fn ring_create() { + let pattern = Pattern::ring(BLACK, WHITE); + assert_eq!(pattern.color, ColorMapper::Ring { a: BLACK, b: WHITE }); + } + #[test] + fn checkers_create() { + let pattern = Pattern::checkers(BLACK, WHITE); + assert_eq!(pattern.color, ColorMapper::Checkers { a: BLACK, b: WHITE }); + } + + mod pattern_at { + use super::*; + use crate::tuples::point; + + #[test] + fn test_returns_coordinates() { + // A test returns the pattern space coordinates of the point. + let pattern = Pattern::test(); + let p = point(1., 2., 3.); + assert_eq!(pattern.pattern_at(p), [p.x, p.y, p.z].into()); + } + + #[test] + fn stripe_alternates_between_two_colors() { + // A stripe alternates between two colors. + let pattern = Pattern::stripe(WHITE, BLACK); + for (p, want) in &[ + // A stripe pattern is constant in y. + (point(0., 0., 0.), WHITE), + (point(0., 1., 0.), WHITE), + (point(0., 2., 0.), WHITE), + // A stripe pattern is constant in z. + (point(0., 0., 0.), WHITE), + (point(0., 0., 1.), WHITE), + (point(0., 0., 2.), WHITE), + // A stripe pattern alternates in z. + (point(0., 0., 0.), WHITE), + (point(0.9, 0., 0.), WHITE), + (point(1., 0., 0.), BLACK), + (point(-0.1, 0., 0.), BLACK), + (point(-1., 0., 0.), BLACK), + (point(-1.1, 0., 0.), WHITE), + ] { + assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p); + } + } + + #[test] + fn gradient_linearly_interpolates_between_colors() { + // A gradient linearly interpolates between two colors. + let pattern = Pattern::gradient(WHITE, BLACK); + assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); + assert_eq!( + pattern.pattern_at(point(0.25, 0., 0.)), + [0.75, 0.75, 0.75].into() + ); + assert_eq!( + pattern.pattern_at(point(0.5, 0., 0.)), + [0.5, 0.5, 0.5].into() + ); + assert_eq!( + pattern.pattern_at(point(0.75, 0., 0.)), + [0.25, 0.25, 0.25].into() + ); + } + + #[test] + fn ring_extend_in_x_and_z() { + // A ring should extend both in x and z. + let pattern = Pattern::ring(WHITE, BLACK); + assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); + assert_eq!(pattern.pattern_at(point(1., 0., 0.)), BLACK); + assert_eq!(pattern.pattern_at(point(0., 0., 1.)), BLACK); + // 0.708 is slight more than 2.sqrt()/2. + assert_eq!(pattern.pattern_at(point(0.708, 0., 0.708)), BLACK); + } + + #[test] + fn checkers_repeat_along_x_axis() { + // Checkers should repeat along X-axis. + let pattern = Pattern::checkers(WHITE, BLACK); + assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); + assert_eq!(pattern.pattern_at(point(0.99, 0., 0.)), WHITE); + assert_eq!(pattern.pattern_at(point(1.01, 0., 0.)), BLACK); + } + + #[test] + fn checkers_repeat_along_y_axis() { + // Checkers should repeat along Y-axis. + let pattern = Pattern::checkers(WHITE, BLACK); + assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); + assert_eq!(pattern.pattern_at(point(0., 0.99, 0.)), WHITE); + assert_eq!(pattern.pattern_at(point(0., 1.01, 0.)), BLACK); + } + + #[test] + fn checkers_repeat_along_z_axis() { + // Checkers should repeat along Z-axis. + let pattern = Pattern::checkers(WHITE, BLACK); + assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE); + assert_eq!(pattern.pattern_at(point(0., 0., 0.99)), WHITE); + assert_eq!(pattern.pattern_at(point(0., 0., 1.01)), BLACK); + } + } + + mod pattern_at_object { + use std::error::Error; + + use crate::{ + matrices::scaling, + patterns::stripe_pattern, + shapes::{Shape, ShapeBuilder}, + tuples::point, + BLACK, WHITE, + }; + + #[test] + fn stripes_with_an_object_transformation() -> Result<(), Box> { + // Stripes with an object transformation. + let object = ShapeBuilder::sphere() + .transform(scaling(2., 2., 2.)) + .build()?; + let pattern = stripe_pattern(WHITE, BLACK).build()?; + let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.)); + assert_eq!(c, WHITE); + Ok(()) + } + + #[test] + fn stripes_with_a_pattern_transformation() -> Result<(), Box> { + // Stripes with a pattern transformation. + let object = Shape::sphere(); + let mut pattern = stripe_pattern(WHITE, BLACK).build()?; + pattern.set_transform(scaling(2., 2., 2.)); + let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.)); + assert_eq!(c, WHITE); + Ok(()) + } + } +}