diff --git a/rtchallenge/src/lib.rs b/rtchallenge/src/lib.rs index b89954b..c3d8815 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, stripe_pattern}, + patterns::{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 2f216d8..36f80f9 100644 --- a/rtchallenge/src/patterns.rs +++ b/rtchallenge/src/patterns.rs @@ -18,6 +18,8 @@ pub enum ColorMapper { Stripe { a: Color, b: Color }, /// Linear blend between `a` and `b` along the X-axis. Gradient { a: Color, b: Color }, + /// Bullseye pattern in the XZ plane. + Ring { a: Color, b: Color }, } #[derive(Builder, Debug, PartialEq, Clone)] @@ -126,6 +128,28 @@ 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 }) +} + /// 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 @@ -174,8 +198,7 @@ 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. + /// Create a pattern that gradually blends between the given colors along the X-axis. /// /// # Examples /// ``` @@ -194,6 +217,25 @@ 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 }, + ..Pattern::default() + } + } + /// Sample the color at the given point in untranslated object space. /// /// # Examples @@ -242,22 +284,22 @@ impl Pattern { /// 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); /// ``` pub fn pattern_at(&self, point: Tuple) -> Color { match self.color { ColorMapper::TestPattern => [point.x, point.y, point.z].into(), ColorMapper::Constant(c) => c, ColorMapper::Stripe { a, b } => { - let x = point.x.floor() as i64; - /* - // Shift negative valued to get correct striping. - if x < 0 { - x - 1 - } else { - x - }; - */ - if x % 2 == 0 { + let x = point.x.floor(); + if x % 2. == 0. { a } else { b @@ -268,6 +310,15 @@ impl Pattern { let fraction = point.x - point.x.floor(); a + distance * fraction } + ColorMapper::Ring { a, b } => { + let px = point.x; + let pz = point.z; + if (px * px + pz * pz).sqrt().floor() % 2. == 0. { + a + } else { + b + } + } } } /// Sample the color at the given world point on the given object.