patterns: add Gradient pattern.

This commit is contained in:
Bill Thiede 2021-07-25 14:11:20 -07:00
parent bdcee49d5a
commit 74fe69188a
2 changed files with 116 additions and 49 deletions

View File

@ -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::stripe_pattern,
patterns::{gradient_pattern, stripe_pattern},
shapes::{plane, sphere, test_shape},
transformations::view_transform,
tuples::{point, vector, Color},

View File

@ -16,6 +16,8 @@ pub enum ColorMapper {
/// 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 },
/// Linear blend between `a` and `b` along the X-axis.
Gradient { a: Color, b: Color },
}
#[derive(Builder, Debug, PartialEq, Clone)]
@ -56,28 +58,6 @@ where
}
}
/// 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
/// ```
/// use rtchallenge::{
/// patterns::{stripe_pattern, ColorMapper},
/// 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 });
///
/// # Ok(())
/// # }
/// ```
pub fn stripe_pattern(a: Color, b: Color) -> PatternBuilder {
PatternBuilder::default().color(ColorMapper::Stripe { a, b })
}
/// 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.
///
@ -101,28 +81,53 @@ pub fn test_pattern() -> PatternBuilder {
PatternBuilder::default().color(ColorMapper::TestPattern)
}
/// 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
/// ```
/// use rtchallenge::{
/// patterns::{stripe_pattern, ColorMapper},
/// 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 });
///
/// # Ok(())
/// # }
/// ```
pub fn stripe_pattern(a: Color, b: Color) -> PatternBuilder {
PatternBuilder::default().color(ColorMapper::Stripe { a, b })
}
/// Builder for creating a material pattern that gradually blends between the given colors along
/// the X-axis.
///
/// # Examples
/// ```
/// use rtchallenge::{
/// matrices::Matrix4x4,
/// patterns::{gradient_pattern, ColorMapper},
/// BLACK, WHITE,
/// };
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
///
/// let pattern = gradient_pattern(WHITE, BLACK).build()?;
/// assert_eq!(pattern.color, ColorMapper::Gradient { a: WHITE, b: BLACK });
///
/// # Ok(())
/// # }
/// ```
pub fn gradient_pattern(a: Color, b: Color) -> PatternBuilder {
PatternBuilder::default().color(ColorMapper::Gradient { a, b })
}
/// 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.
///
@ -148,16 +153,60 @@ impl Pattern {
..Pattern::default()
}
}
/// 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 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::gradient(BLACK, WHITE);
/// assert_eq!(pattern.color, ColorMapper::Gradient { a: BLACK, b: WHITE });
/// ```
pub fn gradient(a: Color, b: Color) -> Pattern {
Pattern {
color: ColorMapper::Gradient { a, b },
..Pattern::default()
}
}
/// Sample the color at the given point in untranslated object space.
///
/// # Examples
/// ```
/// use rtchallenge::{patterns::stripe_pattern, tuples::point, BLACK, WHITE};
/// use rtchallenge::{patterns::Pattern, tuples::point, BLACK, WHITE};
///
/// # fn main() -> Result<(), Box<std::error::Error>> {
///
/// let pattern = stripe_pattern(WHITE, BLACK).build()?;
/// // A test returns the pattern space coordinates of the point.
/// let pattern = Pattern::test();
/// let p = point(1., 2., 3.);
/// assert_eq!(pattern.pattern_at(p), [p.x, p.y, p.z].into());
///
/// // A stripe alternates between two colors.
/// let pattern = Pattern::stripe(WHITE, BLACK);
/// for (p, want) in &[
/// // A stripe pattern is constant in y.
/// (point(0., 0., 0.), WHITE),
@ -178,8 +227,21 @@ impl Pattern {
/// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p);
/// }
///
/// # Ok(())
/// # }
/// // A gradient linearly interpolates between two colors.
/// let pattern = Pattern::gradient(WHITE, BLACK);
/// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
/// assert_eq!(
/// pattern.pattern_at(point(0.25, 0., 0.)),
/// [0.75, 0.75, 0.75].into()
/// );
/// assert_eq!(
/// pattern.pattern_at(point(0.5, 0., 0.)),
/// [0.5, 0.5, 0.5].into()
/// );
/// assert_eq!(
/// pattern.pattern_at(point(0.75, 0., 0.)),
/// [0.25, 0.25, 0.25].into()
/// );
/// ```
pub fn pattern_at(&self, point: Tuple) -> Color {
match self.color {
@ -201,6 +263,11 @@ impl Pattern {
b
}
}
ColorMapper::Gradient { a, b } => {
let distance = b - a;
let fraction = point.x - point.x.floor();
a + distance * fraction
}
}
}
/// Sample the color at the given world point on the given object.