From b9f2c3f0eccce309442895333266445e833e3f71 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sun, 25 Jul 2021 13:30:40 -0700 Subject: [PATCH] patterns: create generic Pattern modeled after StripePattern. Add TestPattern to validate generic implementation. Make Material.color use Pattern. --- rtchallenge/src/materials.rs | 36 +--------- rtchallenge/src/patterns.rs | 132 +++++++++++++++++++++++++++-------- 2 files changed, 104 insertions(+), 64 deletions(-) diff --git a/rtchallenge/src/materials.rs b/rtchallenge/src/materials.rs index 6dc8adf..79598a1 100644 --- a/rtchallenge/src/materials.rs +++ b/rtchallenge/src/materials.rs @@ -2,44 +2,17 @@ use derive_builder::Builder; use crate::{ lights::PointLight, - patterns::StripePattern, + patterns::Pattern, shapes::Shape, tuples::{dot, reflect, Color, Tuple}, Float, BLACK, WHITE, }; -#[derive(Debug, PartialEq, Clone)] -pub enum ColorMapper { - Constant(Color), - StripePattern(StripePattern), -} - -/// Creates a [ColorMapper::Constant] from the given [Color] -impl From for ColorMapper { - fn from(c: Color) -> Self { - ColorMapper::Constant(c) - } -} - -/// Creates a [ColorMapper::Constant] from the given array. -impl From<[Float; 3]> for ColorMapper { - fn from(rgb: [Float; 3]) -> Self { - ColorMapper::Constant(rgb.into()) - } -} - -/// Creates a [ColorMapper::StripePattern] from the given [Color] -impl From for ColorMapper { - fn from(sp: StripePattern) -> Self { - ColorMapper::StripePattern(sp) - } -} - #[derive(Builder, Debug, PartialEq, Clone)] #[builder(default)] pub struct Material { #[builder(setter(into))] - pub color: ColorMapper, + pub color: Pattern, pub ambient: Float, pub diffuse: Float, pub specular: Float, @@ -180,10 +153,7 @@ pub fn lighting( in_shadow: bool, ) -> Color { // Combine the surface color with the light's color. - let color = match &material.color { - ColorMapper::Constant(color) => *color, - ColorMapper::StripePattern(pat) => pat.stripe_at_object(object, point), - }; + let color = material.color.pattern_at_object(object, point); let effective_color = color * light.intensity; // Find the direciton of the light source. let lightv = (light.position - point).normalize(); diff --git a/rtchallenge/src/patterns.rs b/rtchallenge/src/patterns.rs index 438c9a3..72025f3 100644 --- a/rtchallenge/src/patterns.rs +++ b/rtchallenge/src/patterns.rs @@ -2,38 +2,100 @@ use crate::{ matrices::Matrix4x4, shapes::Shape, tuples::{Color, Tuple}, + Float, WHITE, }; #[derive(Debug, PartialEq, Clone)] -pub struct StripePattern { - pub a: Color, - pub b: Color, +pub enum ColorMapper { + /// TestPattern the color returned is the pattern space point after going through world->object and object->pattern space translation. + TestPattern, + /// Solid color, the same sampled every where. + Constant(Color), + /// 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. + Stripe { a: Color, b: Color }, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Pattern { + pub color: ColorMapper, transform: Matrix4x4, inverse_transform: Matrix4x4, } +impl Default for Pattern { + fn default() -> Pattern { + Pattern { + color: ColorMapper::Constant(WHITE), + transform: Matrix4x4::identity(), + inverse_transform: Matrix4x4::identity(), + } + } +} + +/// Creates a [Pattern] with a color type of [ColorMapper::Constant] from the given [Color] +impl From<[Float; 3]> for Pattern { + fn from(rgb: [Float; 3]) -> Self { + Pattern { + color: ColorMapper::Constant(rgb.into()), + ..Pattern::default() + } + } +} +/// Creates a [Pattern] with a color type of [ColorMapper::Constant] from the given [Color] +impl From for Pattern { + fn from(c: Color) -> Self { + Pattern { + color: ColorMapper::Constant(c), + ..Pattern::default() + } + } +} + /// Create 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, BLACK, WHITE}; +/// use rtchallenge::{ +/// patterns::{stripe_pattern, ColorMapper}, +/// BLACK, WHITE, +/// }; /// /// let pattern = stripe_pattern(BLACK, WHITE); -/// assert_eq!(pattern.a, BLACK); -/// assert_eq!(pattern.b, WHITE); +/// assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE }); /// ``` -pub fn stripe_pattern(a: Color, b: Color) -> StripePattern { - StripePattern { - a, - b, - transform: Matrix4x4::identity(), - inverse_transform: Matrix4x4::identity(), +pub fn stripe_pattern(a: Color, b: Color) -> Pattern { + Pattern { + color: ColorMapper::Stripe { a, b }, + ..Pattern::default() } } -impl StripePattern { - /// Sample the color at the given point. +/// Create 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, +/// }; +/// +/// let pattern = test_pattern(); +/// assert_eq!(pattern.transform(), Matrix4x4::identity()); +/// ``` +pub fn test_pattern() -> Pattern { + Pattern { + color: ColorMapper::TestPattern, + ..Pattern::default() + } +} + +/// Generic implementation for mapping points to colors according to the given [ColorMapper]. +impl Pattern { + /// Sample the color at the given point in untranslated object space. /// /// # Examples /// ``` @@ -58,21 +120,29 @@ impl StripePattern { /// (point(-1., 0., 0.), BLACK), /// (point(-1.1, 0., 0.), WHITE), /// ] { - /// assert_eq!(pattern.stripe_at(*p), *want, "{:?}", p); + /// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p); /// } /// ``` - pub fn stripe_at(&self, point: Tuple) -> Color { - 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 { - self.a - } else { - self.b + 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 { + a + } else { + b + } + } } } /// Sample the color at the given world point on the given object. @@ -95,23 +165,23 @@ impl StripePattern { /// .transform(scaling(2., 2., 2.)) /// .build()?; /// let pattern = stripe_pattern(WHITE, BLACK); - /// let c = pattern.stripe_at_object(&object, point(1.5, 0., 0.)); + /// 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); /// pattern.set_transform(scaling(2., 2., 2.)); - /// let c = pattern.stripe_at_object(&object, point(1.5, 0., 0.)); + /// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.)); /// assert_eq!(c, WHITE); /// /// # Ok(()) /// # } /// ``` - pub fn stripe_at_object(&self, object: &Shape, world_point: Tuple) -> Color { + 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; - self.stripe_at(pattern_point) + self.pattern_at(pattern_point) } pub fn transform(&self) -> Matrix4x4 { self.transform