patterns: add builder pattern for creating Patterns.

This commit is contained in:
Bill Thiede 2021-07-25 13:50:13 -07:00
parent 2e4e8b3dcd
commit bdcee49d5a
2 changed files with 85 additions and 21 deletions

View File

@ -56,7 +56,7 @@ impl Default for Material {
/// use rtchallenge::{ /// use rtchallenge::{
/// lights::PointLight, /// lights::PointLight,
/// materials::{lighting, Material}, /// materials::{lighting, Material},
/// patterns::stripe_pattern, /// patterns::Pattern,
/// shapes::Shape, /// shapes::Shape,
/// tuples::{point, vector, Color}, /// tuples::{point, vector, Color},
/// Float, BLACK, WHITE, /// Float, BLACK, WHITE,
@ -113,7 +113,7 @@ impl Default for Material {
/// // Lighting with a pattern applied. /// // Lighting with a pattern applied.
/// ///
/// let m = Material { /// let m = Material {
/// color: stripe_pattern(WHITE, BLACK).into(), /// color: Pattern::stripe(WHITE, BLACK),
/// ambient: 1., /// ambient: 1.,
/// diffuse: 0., /// diffuse: 0.,
/// specular: 0., /// specular: 0.,

View File

@ -1,3 +1,5 @@
use derive_builder::Builder;
use crate::{ use crate::{
matrices::Matrix4x4, matrices::Matrix4x4,
shapes::Shape, shapes::Shape,
@ -5,7 +7,7 @@ use crate::{
WHITE, WHITE,
}; };
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
pub enum ColorMapper { pub enum ColorMapper {
/// TestPattern the color returned is the pattern space point after going through world->object and object->pattern space translation. /// TestPattern the color returned is the pattern space point after going through world->object and object->pattern space translation.
TestPattern, TestPattern,
@ -16,13 +18,21 @@ pub enum ColorMapper {
Stripe { a: Color, b: Color }, Stripe { a: Color, b: Color },
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Builder, Debug, PartialEq, Clone)]
#[builder(default, pattern = "owned")]
pub struct Pattern { pub struct Pattern {
pub color: ColorMapper, pub color: ColorMapper,
transform: Matrix4x4, transform: Matrix4x4,
#[builder(private, default = "self.default_inverse_transform()?")]
inverse_transform: Matrix4x4, inverse_transform: Matrix4x4,
} }
impl PatternBuilder {
fn default_inverse_transform(&self) -> Result<Matrix4x4, String> {
Ok(self.transform.unwrap_or(Matrix4x4::identity()).inverse())
}
}
impl Default for Pattern { impl Default for Pattern {
fn default() -> Pattern { fn default() -> Pattern {
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. /// X-axis. The strip extends infinitely in the positive and negative Y and Z axes.
/// ///
/// # Examples /// # Examples
@ -56,17 +66,19 @@ where
/// BLACK, WHITE, /// BLACK, WHITE,
/// }; /// };
/// ///
/// let pattern = stripe_pattern(BLACK, WHITE); /// # fn main() -> Result<(), Box<std::error::Error>> {
///
/// let pattern = stripe_pattern(BLACK, WHITE).build()?;
/// assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE }); /// assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE });
///
/// # Ok(())
/// # }
/// ``` /// ```
pub fn stripe_pattern(a: Color, b: Color) -> Pattern { pub fn stripe_pattern(a: Color, b: Color) -> PatternBuilder {
Pattern { PatternBuilder::default().color(ColorMapper::Stripe { a, b })
color: ColorMapper::Stripe { a, b },
..Pattern::default()
}
} }
/// 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. /// after going through world->object and object->pattern space translation.
/// ///
/// # Examples /// # Examples
@ -77,25 +89,74 @@ pub fn stripe_pattern(a: Color, b: Color) -> Pattern {
/// BLACK, WHITE, /// BLACK, WHITE,
/// }; /// };
/// ///
/// let pattern = test_pattern(); /// # fn main() -> Result<(), Box<std::error::Error>> {
///
/// let pattern = test_pattern().build()?;
/// assert_eq!(pattern.transform(), Matrix4x4::identity()); /// assert_eq!(pattern.transform(), Matrix4x4::identity());
///
/// # Ok(())
/// # }
/// ``` /// ```
pub fn test_pattern() -> Pattern { 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<std::error::Error>> {
///
/// let pattern = Pattern::test();
/// assert_eq!(pattern.transform(), Matrix4x4::identity());
///
/// # Ok(())
/// # }
/// ```
pub fn test() -> Pattern {
Pattern { Pattern {
color: ColorMapper::TestPattern, color: ColorMapper::TestPattern,
..Pattern::default() ..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. /// Sample the color at the given point in untranslated object space.
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use rtchallenge::{patterns::stripe_pattern, tuples::point, BLACK, WHITE}; /// use rtchallenge::{patterns::stripe_pattern, tuples::point, BLACK, WHITE};
/// ///
/// let pattern = stripe_pattern(WHITE, BLACK); /// # fn main() -> Result<(), Box<std::error::Error>> {
///
/// let pattern = stripe_pattern(WHITE, BLACK).build()?;
/// ///
/// for (p, want) in &[ /// for (p, want) in &[
/// // A stripe pattern is constant in y. /// // A stripe pattern is constant in y.
@ -116,6 +177,9 @@ impl Pattern {
/// ] { /// ] {
/// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p); /// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p);
/// } /// }
///
/// # Ok(())
/// # }
/// ``` /// ```
pub fn pattern_at(&self, point: Tuple) -> Color { pub fn pattern_at(&self, point: Tuple) -> Color {
match self.color { match self.color {
@ -158,13 +222,13 @@ impl Pattern {
/// let object = ShapeBuilder::sphere() /// let object = ShapeBuilder::sphere()
/// .transform(scaling(2., 2., 2.)) /// .transform(scaling(2., 2., 2.))
/// .build()?; /// .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.)); /// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.));
/// assert_eq!(c, WHITE); /// assert_eq!(c, WHITE);
/// ///
/// // Stripes with an pattern transformation. /// // Stripes with an pattern transformation.
/// let object = Shape::sphere(); /// 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.)); /// pattern.set_transform(scaling(2., 2., 2.));
/// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.)); /// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.));
/// assert_eq!(c, WHITE); /// assert_eq!(c, WHITE);