From bdcee49d5ad92832a689205ac248dee69e3efbcf Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 25 Jul 2021 13:50:13 -0700 Subject: [PATCH] patterns: add builder pattern for creating Patterns. --- rtchallenge/src/materials.rs | 4 +- rtchallenge/src/patterns.rs | 102 ++++++++++++++++++++++++++++------- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/rtchallenge/src/materials.rs b/rtchallenge/src/materials.rs index 79598a1..cfec864 100644 --- a/rtchallenge/src/materials.rs +++ b/rtchallenge/src/materials.rs @@ -56,7 +56,7 @@ impl Default for Material { /// use rtchallenge::{ /// lights::PointLight, /// materials::{lighting, Material}, -/// patterns::stripe_pattern, +/// patterns::Pattern, /// shapes::Shape, /// tuples::{point, vector, Color}, /// Float, BLACK, WHITE, @@ -113,7 +113,7 @@ impl Default for Material { /// // Lighting with a pattern applied. /// /// let m = Material { -/// color: stripe_pattern(WHITE, BLACK).into(), +/// color: Pattern::stripe(WHITE, BLACK), /// ambient: 1., /// diffuse: 0., /// specular: 0., diff --git a/rtchallenge/src/patterns.rs b/rtchallenge/src/patterns.rs index f586a0a..17b47f7 100644 --- a/rtchallenge/src/patterns.rs +++ b/rtchallenge/src/patterns.rs @@ -1,3 +1,5 @@ +use derive_builder::Builder; + use crate::{ matrices::Matrix4x4, shapes::Shape, @@ -5,7 +7,7 @@ use crate::{ WHITE, }; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum ColorMapper { /// TestPattern the color returned is the pattern space point after going through world->object and object->pattern space translation. TestPattern, @@ -16,13 +18,21 @@ pub enum ColorMapper { Stripe { a: Color, b: Color }, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Builder, Debug, PartialEq, Clone)] +#[builder(default, pattern = "owned")] pub struct Pattern { pub color: ColorMapper, transform: Matrix4x4, + #[builder(private, default = "self.default_inverse_transform()?")] inverse_transform: Matrix4x4, } +impl PatternBuilder { + fn default_inverse_transform(&self) -> Result { + Ok(self.transform.unwrap_or(Matrix4x4::identity()).inverse()) + } +} + impl Default for Pattern { fn default() -> Pattern { Pattern { @@ -46,7 +56,7 @@ where } } -/// Create a material pattern that alternates between the given colors along each unit of the +/// 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 @@ -56,17 +66,19 @@ where /// BLACK, WHITE, /// }; /// -/// let pattern = stripe_pattern(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) -> Pattern { - Pattern { - color: ColorMapper::Stripe { a, b }, - ..Pattern::default() - } +pub fn stripe_pattern(a: Color, b: Color) -> PatternBuilder { + PatternBuilder::default().color(ColorMapper::Stripe { a, b }) } -/// Create a material pattern used for testing. The color returned is the pattern space point +/// 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 @@ -77,25 +89,74 @@ pub fn stripe_pattern(a: Color, b: Color) -> Pattern { /// BLACK, WHITE, /// }; /// -/// let pattern = test_pattern(); +/// # fn main() -> Result<(), Box> { +/// +/// let pattern = test_pattern().build()?; /// assert_eq!(pattern.transform(), Matrix4x4::identity()); +/// +/// # Ok(()) +/// # } /// ``` -pub fn test_pattern() -> Pattern { - Pattern { - color: ColorMapper::TestPattern, - ..Pattern::default() - } +pub fn test_pattern() -> PatternBuilder { + PatternBuilder::default().color(ColorMapper::TestPattern) } /// Generic implementation for mapping points to colors according to the given [ColorMapper]. 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 }, + ..Pattern::default() + } + } + + /// 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, + ..Pattern::default() + } + } /// Sample the color at the given point in untranslated object space. /// /// # Examples /// ``` /// use rtchallenge::{patterns::stripe_pattern, tuples::point, BLACK, WHITE}; /// - /// let pattern = stripe_pattern(WHITE, BLACK); + /// # fn main() -> Result<(), Box> { + /// + /// let pattern = stripe_pattern(WHITE, BLACK).build()?; /// /// for (p, want) in &[ /// // A stripe pattern is constant in y. @@ -116,6 +177,9 @@ impl Pattern { /// ] { /// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p); /// } + /// + /// # Ok(()) + /// # } /// ``` pub fn pattern_at(&self, point: Tuple) -> Color { match self.color { @@ -158,13 +222,13 @@ impl Pattern { /// let object = ShapeBuilder::sphere() /// .transform(scaling(2., 2., 2.)) /// .build()?; - /// let pattern = stripe_pattern(WHITE, BLACK); + /// 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); + /// 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);