truster/
texture.rs

1//! Holds the [Texture] trait, as well as some common textures which implement it.
2
3use std::rc::Rc;
4
5use crate::color::Color;
6use crate::matrix::Matrix;
7use crate::shape::Shape;
8use crate::tuple::Tuple;
9
10pub mod solid_color;
11pub mod stripe;
12
13/// A basic texture implementation. There is no UV mapping or anything like that. The method
14/// [Texture::color_at] should just map a point in 3D space to a color. Textures can be
15/// transformed, so color_at_shape should return the color as if the texture were not transformed.
16/// [Texture::color_at_shape] will perform the transformation, so it should not be overwritten.
17/// [Texture::transform] should return the texture transform matrix,
18/// [Texture::transform_inverse] should return it's inverse. [Texture::set_transform] should set
19/// the texture transform to be `transform`.
20pub trait Texture {
21    fn color_at_shape(&self, point: Tuple, shape: Rc<dyn Shape>) -> Color {
22        let point = shape.transform_inverse() * point;
23        let point = self.transform_inverse() * point;
24        self.color_at(point)
25    }
26    fn color_at_texture(&self, point: Tuple) -> Color {
27        let point = self.transform_inverse() * point;
28        self.color_at(point)
29    }
30    fn color_at(&self, point: Tuple) -> Color;
31
32    fn transform(&self) -> &Matrix;
33    fn transform_inverse(&self) -> &Matrix;
34    fn set_transform(&mut self, transform: Matrix);
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use crate::shape::sphere::Sphere;
41
42    struct MockTexture {
43        transform: Matrix,
44        transform_inverse: Matrix,
45    }
46
47    impl MockTexture {
48        fn new() -> Self {
49            Self {
50                transform: Matrix::eye(),
51                transform_inverse: Matrix::eye(),
52            }
53        }
54    }
55
56    impl Texture for MockTexture {
57        fn color_at(&self, point: Tuple) -> Color {
58            Color::new(point.x(), point.y(), point.z())
59        }
60
61        fn transform(&self) -> &Matrix {
62            &self.transform
63        }
64
65        fn transform_inverse(&self) -> &Matrix {
66            &self.transform_inverse
67        }
68
69        fn set_transform(&mut self, transform: Matrix) {
70            self.transform_inverse = transform.inverse();
71            self.transform = transform;
72        }
73    }
74
75    #[test]
76    fn color_at_shape_with_shape_transformation() {
77        let mut shape = Sphere::new();
78        shape.set_transform(Matrix::scaling(2.0, 2.0, 2.0));
79        let texture = MockTexture::new();
80        let color = texture.color_at_shape(Tuple::point(2.0, 3.0, 4.0), Rc::new(shape));
81        assert_eq!(color, Color::new(1.0, 1.5, 2.0));
82    }
83
84    #[test]
85    fn color_at_shape_with_texture_transformation() {
86        let shape = Sphere::new();
87        let mut texture = MockTexture::new();
88        texture.set_transform(Matrix::scaling(2.0, 2.0, 2.0));
89        let color = texture.color_at_shape(Tuple::point(2.0, 3.0, 4.0), Rc::new(shape));
90        assert_eq!(color, Color::new(1.0, 1.5, 2.0));
91    }
92
93    #[test]
94    fn color_at_shape_with_shape_and_texture_transformation() {
95        let mut shape = Sphere::new();
96        shape.set_transform(Matrix::scaling(2.0, 2.0, 2.0));
97        let mut texture = MockTexture::new();
98        texture.set_transform(Matrix::translation(0.5, 1.0, 1.5));
99        let color = texture.color_at_shape(Tuple::point(2.5, 3.0, 3.5), Rc::new(shape));
100        assert_eq!(color, Color::new(0.75, 0.5, 0.25));
101    }
102}