diff --git a/rtchallenge/src/lib.rs b/rtchallenge/src/lib.rs index c3d8815..9c3a0a0 100644 --- a/rtchallenge/src/lib.rs +++ b/rtchallenge/src/lib.rs @@ -45,7 +45,7 @@ pub mod prelude { lights::{PointLight, PointLightBuilder}, materials::{Material, MaterialBuilder}, matrices::{identity, rotation_x, rotation_y, rotation_z, scaling, shearing, translation}, - patterns::{gradient_pattern, ring_pattern, stripe_pattern}, + patterns::{checkers_pattern, gradient_pattern, ring_pattern, stripe_pattern}, shapes::{plane, sphere, test_shape}, transformations::view_transform, tuples::{point, vector, Color}, diff --git a/rtchallenge/src/patterns.rs b/rtchallenge/src/patterns.rs index 36f80f9..e91485d 100644 --- a/rtchallenge/src/patterns.rs +++ b/rtchallenge/src/patterns.rs @@ -20,6 +20,8 @@ pub enum ColorMapper { Gradient { a: Color, b: Color }, /// Bullseye pattern in the XZ plane. Ring { a: Color, b: Color }, + /// Traditional ray tracer tile floor pattern. + Checkers { a: Color, b: Color }, } #[derive(Builder, Debug, PartialEq, Clone)] @@ -150,6 +152,28 @@ 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 }) +} + /// Generic implementation for mapping points to colors according to the given [ColorMapper]. impl Pattern { /// Create a pattern used for testing. The color returned is the pattern space point @@ -236,6 +260,25 @@ 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 }, + ..Pattern::default() + } + } + /// Sample the color at the given point in untranslated object space. /// /// # Examples @@ -292,6 +335,24 @@ impl Pattern { /// 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 { @@ -319,6 +380,14 @@ impl Pattern { b } } + ColorMapper::Checkers { a, b } => { + let d = point.x.floor() + point.y.floor() + point.z.floor(); + if d % 2. == 0. { + a + } else { + b + } + } } } /// Sample the color at the given world point on the given object.