patterns: move tests from doctest to unit.
This commit is contained in:
parent
3aea76b35c
commit
5d6b3e6d57
@ -64,112 +64,30 @@ where
|
||||
|
||||
/// 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
|
||||
/// ```
|
||||
/// use rtchallenge::{
|
||||
/// matrices::Matrix4x4,
|
||||
/// patterns::{test_pattern, ColorMapper},
|
||||
/// BLACK, WHITE,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
///
|
||||
/// let pattern = test_pattern().build()?;
|
||||
/// assert_eq!(pattern.transform(), Matrix4x4::identity());
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
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 })
|
||||
}
|
||||
|
||||
/// 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<std::error::Error>> {
|
||||
///
|
||||
/// 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 })
|
||||
}
|
||||
|
||||
/// Builder for creating a material pattern that alternates between the given colors along the X, Y
|
||||
/// and Z pattern. Creates traditional ray tracer tile floor pattern.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{
|
||||
/// patterns::{checkers_pattern, ColorMapper},
|
||||
/// BLACK, WHITE,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
///
|
||||
/// let pattern = checkers_pattern(BLACK, WHITE).build()?;
|
||||
/// assert_eq!(pattern.color, ColorMapper::Checkers { a: BLACK, b: WHITE });
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn checkers_pattern(a: Color, b: Color) -> PatternBuilder {
|
||||
PatternBuilder::default().color(ColorMapper::Checkers { a, b })
|
||||
}
|
||||
@ -178,23 +96,6 @@ pub fn checkers_pattern(a: Color, b: Color) -> PatternBuilder {
|
||||
impl Pattern {
|
||||
/// 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 {
|
||||
color: ColorMapper::TestPattern,
|
||||
@ -204,17 +105,6 @@ 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 },
|
||||
@ -223,17 +113,6 @@ impl Pattern {
|
||||
}
|
||||
|
||||
/// Create a pattern that gradually blends between the given colors along the X-axis.
|
||||
///
|
||||
/// # 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 },
|
||||
@ -242,17 +121,6 @@ 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 },
|
||||
@ -261,17 +129,6 @@ impl Pattern {
|
||||
}
|
||||
|
||||
/// Create a pattern that alternates between the given colors along the X, Y and Z axis.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{
|
||||
/// patterns::{ColorMapper, Pattern},
|
||||
/// BLACK, WHITE,
|
||||
/// };
|
||||
///
|
||||
/// let pattern = Pattern::checkers(BLACK, WHITE);
|
||||
/// assert_eq!(pattern.color, ColorMapper::Checkers { a: BLACK, b: WHITE });
|
||||
/// ```
|
||||
pub fn checkers(a: Color, b: Color) -> Pattern {
|
||||
Pattern {
|
||||
color: ColorMapper::Checkers { a, b },
|
||||
@ -280,80 +137,6 @@ impl Pattern {
|
||||
}
|
||||
|
||||
/// Sample the color at the given point in untranslated object space.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{patterns::Pattern, tuples::point, BLACK, WHITE};
|
||||
///
|
||||
/// // 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),
|
||||
/// (point(0., 1., 0.), WHITE),
|
||||
/// (point(0., 2., 0.), WHITE),
|
||||
/// // A stripe pattern is constant in z.
|
||||
/// (point(0., 0., 0.), WHITE),
|
||||
/// (point(0., 0., 1.), WHITE),
|
||||
/// (point(0., 0., 2.), WHITE),
|
||||
/// // A stripe pattern alternates in z.
|
||||
/// (point(0., 0., 0.), WHITE),
|
||||
/// (point(0.9, 0., 0.), WHITE),
|
||||
/// (point(1., 0., 0.), BLACK),
|
||||
/// (point(-0.1, 0., 0.), BLACK),
|
||||
/// (point(-1., 0., 0.), BLACK),
|
||||
/// (point(-1.1, 0., 0.), WHITE),
|
||||
/// ] {
|
||||
/// assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p);
|
||||
/// }
|
||||
///
|
||||
/// // 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()
|
||||
/// );
|
||||
///
|
||||
/// // 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);
|
||||
///
|
||||
/// // Checkers should repeat along X-axis.
|
||||
/// let pattern = Pattern::checkers(WHITE, BLACK);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
|
||||
/// assert_eq!(pattern.pattern_at(point(0.99, 0., 0.)), WHITE);
|
||||
/// assert_eq!(pattern.pattern_at(point(1.01, 0., 0.)), BLACK);
|
||||
///
|
||||
/// // Checkers should repeat along Y-axis.
|
||||
/// let pattern = Pattern::checkers(WHITE, BLACK);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 0.99, 0.)), WHITE);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 1.01, 0.)), BLACK);
|
||||
///
|
||||
/// // Checkers should repeat along Z-axis.
|
||||
/// let pattern = Pattern::checkers(WHITE, BLACK);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 0., 0.99)), WHITE);
|
||||
/// assert_eq!(pattern.pattern_at(point(0., 0., 1.01)), BLACK);
|
||||
/// ```
|
||||
pub fn pattern_at(&self, point: Tuple) -> Color {
|
||||
match self.color {
|
||||
ColorMapper::TestPattern => [point.x, point.y, point.z].into(),
|
||||
@ -392,37 +175,6 @@ impl Pattern {
|
||||
}
|
||||
/// Sample the color at the given world point on the given object.
|
||||
/// This function respects the object and the pattern's transform matrix.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rtchallenge::{
|
||||
/// matrices::scaling,
|
||||
/// patterns::stripe_pattern,
|
||||
/// shapes::{Shape, ShapeBuilder},
|
||||
/// tuples::point,
|
||||
/// BLACK, WHITE,
|
||||
/// };
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
///
|
||||
/// // Stripes with an object transformation.
|
||||
/// let object = ShapeBuilder::sphere()
|
||||
/// .transform(scaling(2., 2., 2.))
|
||||
/// .build()?;
|
||||
/// 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).build()?;
|
||||
/// pattern.set_transform(scaling(2., 2., 2.));
|
||||
/// let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.));
|
||||
/// assert_eq!(c, WHITE);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
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;
|
||||
@ -441,3 +193,169 @@ impl Pattern {
|
||||
self.inverse_transform = t.inverse();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
matrices::identity,
|
||||
patterns::{ColorMapper, Pattern},
|
||||
BLACK, WHITE,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let pattern = Pattern::test();
|
||||
assert_eq!(pattern.transform(), identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stripe_create() {
|
||||
let pattern = Pattern::stripe(BLACK, WHITE);
|
||||
assert_eq!(pattern.color, ColorMapper::Stripe { a: BLACK, b: WHITE });
|
||||
}
|
||||
#[test]
|
||||
fn gradient_create() {
|
||||
let pattern = Pattern::gradient(BLACK, WHITE);
|
||||
assert_eq!(pattern.color, ColorMapper::Gradient { a: BLACK, b: WHITE });
|
||||
}
|
||||
#[test]
|
||||
fn ring_create() {
|
||||
let pattern = Pattern::ring(BLACK, WHITE);
|
||||
assert_eq!(pattern.color, ColorMapper::Ring { a: BLACK, b: WHITE });
|
||||
}
|
||||
#[test]
|
||||
fn checkers_create() {
|
||||
let pattern = Pattern::checkers(BLACK, WHITE);
|
||||
assert_eq!(pattern.color, ColorMapper::Checkers { a: BLACK, b: WHITE });
|
||||
}
|
||||
|
||||
mod pattern_at {
|
||||
use super::*;
|
||||
use crate::tuples::point;
|
||||
|
||||
#[test]
|
||||
fn test_returns_coordinates() {
|
||||
// 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());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stripe_alternates_between_two_colors() {
|
||||
// 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),
|
||||
(point(0., 1., 0.), WHITE),
|
||||
(point(0., 2., 0.), WHITE),
|
||||
// A stripe pattern is constant in z.
|
||||
(point(0., 0., 0.), WHITE),
|
||||
(point(0., 0., 1.), WHITE),
|
||||
(point(0., 0., 2.), WHITE),
|
||||
// A stripe pattern alternates in z.
|
||||
(point(0., 0., 0.), WHITE),
|
||||
(point(0.9, 0., 0.), WHITE),
|
||||
(point(1., 0., 0.), BLACK),
|
||||
(point(-0.1, 0., 0.), BLACK),
|
||||
(point(-1., 0., 0.), BLACK),
|
||||
(point(-1.1, 0., 0.), WHITE),
|
||||
] {
|
||||
assert_eq!(pattern.pattern_at(*p), *want, "{:?}", p);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gradient_linearly_interpolates_between_colors() {
|
||||
// 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()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ring_extend_in_x_and_z() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checkers_repeat_along_x_axis() {
|
||||
// Checkers should repeat along X-axis.
|
||||
let pattern = Pattern::checkers(WHITE, BLACK);
|
||||
assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
|
||||
assert_eq!(pattern.pattern_at(point(0.99, 0., 0.)), WHITE);
|
||||
assert_eq!(pattern.pattern_at(point(1.01, 0., 0.)), BLACK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checkers_repeat_along_y_axis() {
|
||||
// Checkers should repeat along Y-axis.
|
||||
let pattern = Pattern::checkers(WHITE, BLACK);
|
||||
assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
|
||||
assert_eq!(pattern.pattern_at(point(0., 0.99, 0.)), WHITE);
|
||||
assert_eq!(pattern.pattern_at(point(0., 1.01, 0.)), BLACK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checkers_repeat_along_z_axis() {
|
||||
// Checkers should repeat along Z-axis.
|
||||
let pattern = Pattern::checkers(WHITE, BLACK);
|
||||
assert_eq!(pattern.pattern_at(point(0., 0., 0.)), WHITE);
|
||||
assert_eq!(pattern.pattern_at(point(0., 0., 0.99)), WHITE);
|
||||
assert_eq!(pattern.pattern_at(point(0., 0., 1.01)), BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
mod pattern_at_object {
|
||||
use std::error::Error;
|
||||
|
||||
use crate::{
|
||||
matrices::scaling,
|
||||
patterns::stripe_pattern,
|
||||
shapes::{Shape, ShapeBuilder},
|
||||
tuples::point,
|
||||
BLACK, WHITE,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn stripes_with_an_object_transformation() -> Result<(), Box<dyn Error>> {
|
||||
// Stripes with an object transformation.
|
||||
let object = ShapeBuilder::sphere()
|
||||
.transform(scaling(2., 2., 2.))
|
||||
.build()?;
|
||||
let pattern = stripe_pattern(WHITE, BLACK).build()?;
|
||||
let c = pattern.pattern_at_object(&object, point(1.5, 0., 0.));
|
||||
assert_eq!(c, WHITE);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stripes_with_a_pattern_transformation() -> Result<(), Box<dyn Error>> {
|
||||
// Stripes with a pattern transformation.
|
||||
let object = Shape::sphere();
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user