implicit3d/
transformer.rs

1use crate::{BoundingBox, Object, PrimitiveParameters, RealField};
2use num_traits::Float;
3
4#[derive(Clone, Debug)]
5/// AffineTransformer is a primitive that takes an object as input and allows to modify it using
6/// affine transforms.
7/// Usually it is used indirectly through ```Object::scale()```, ```Object::translate()``` or ```Object::rotate()```.
8pub struct AffineTransformer<S: RealField> {
9    object: Box<dyn Object<S>>,
10    transform: na::Matrix4<S>,
11    transposed3x3: na::Matrix3<S>,
12    scale_min: S,
13    bbox: BoundingBox<S>,
14}
15
16impl<S: RealField + Float + From<f32>> Object<S> for AffineTransformer<S> {
17    fn approx_value(&self, p: &na::Point3<S>, slack: S) -> S {
18        let approx = self.bbox.distance(p);
19        if approx <= slack {
20            self.object
21                .approx_value(&self.transform.transform_point(&p), slack / self.scale_min)
22                * self.scale_min
23        } else {
24            approx
25        }
26    }
27    fn bbox(&self) -> &BoundingBox<S> {
28        &self.bbox
29    }
30    fn set_parameters(&mut self, p: &PrimitiveParameters<S>) {
31        self.object.set_parameters(p);
32    }
33    fn normal(&self, p: &na::Point3<S>) -> na::Vector3<S> {
34        let normal_at_p = self.object.normal(&self.transform.transform_point(&p));
35        let transformed_normal = self.transposed3x3 * normal_at_p;
36        transformed_normal.normalize()
37    }
38    fn translate(&self, v: &na::Vector3<S>) -> Box<dyn Object<S>> {
39        let new_trans = self.transform.prepend_translation(&-v);
40        Box::new(AffineTransformer::new_with_scaler(
41            self.object.clone(),
42            new_trans,
43            self.scale_min,
44        ))
45    }
46    fn rotate(&self, r: &na::Vector3<S>) -> Box<dyn Object<S>> {
47        let euler = ::na::Rotation::from_euler_angles(r.x, r.y, r.z).to_homogeneous();
48        let new_trans = self.transform * euler;
49        Box::new(AffineTransformer::new_with_scaler(
50            self.object.clone(),
51            new_trans,
52            self.scale_min,
53        ))
54    }
55    fn scale(&self, s: &na::Vector3<S>) -> Box<dyn Object<S>> {
56        let one: S = From::from(1f32);
57        let new_trans = self.transform.prepend_nonuniform_scaling(&na::Vector3::new(
58            one / s.x,
59            one / s.y,
60            one / s.z,
61        ));
62        Box::new(AffineTransformer::new_with_scaler(
63            self.object.clone(),
64            new_trans,
65            self.scale_min * Float::min(s.x, Float::min(s.y, s.z)),
66        ))
67    }
68}
69
70impl<S: RealField + Float + From<f32>> AffineTransformer<S> {
71    fn identity(o: Box<dyn Object<S>>) -> Self {
72        AffineTransformer::new(o, na::Matrix4::identity())
73    }
74    fn new(o: Box<dyn Object<S>>, t: na::Matrix4<S>) -> Self {
75        let one: S = From::from(1f32);
76        AffineTransformer::new_with_scaler(o, t, one)
77    }
78    fn new_with_scaler(o: Box<dyn Object<S>>, t: na::Matrix4<S>, scale_min: S) -> Self {
79        // TODO: Calculate scale_min from t.
80        // This should be something similar to
81        // 1./Vector::new(t.x.x, t.y.x, t.z.x).magnitude().min(
82        // 1./Vector::new(t.x.y, t.y.y, t.z.y).magnitude().min(
83        // 1./Vector::new(t.x.z, t.y.z, t.z.z).magnitude()))
84        match t.try_inverse() {
85            None => panic!("Failed to invert {:?}", t),
86            Some(ref t_inv) => {
87                let bbox = o.bbox().transform(t_inv);
88                let transposed3x3 = t
89                    .fixed_slice::<::na::core::dimension::U3, ::na::core::dimension::U3>(0, 0)
90                    .transpose();
91                AffineTransformer {
92                    object: o,
93                    transform: t,
94                    transposed3x3,
95                    scale_min,
96                    bbox,
97                }
98            }
99        }
100    }
101    /// Create a new translated version of the input.
102    pub fn new_translate(o: Box<dyn Object<S>>, v: &na::Vector3<S>) -> Box<dyn Object<S>> {
103        AffineTransformer::identity(o).translate(v)
104    }
105    /// Create a new rotated version of the input.
106    pub fn new_rotate(o: Box<dyn Object<S>>, r: &na::Vector3<S>) -> Box<dyn Object<S>> {
107        AffineTransformer::identity(o).rotate(r)
108    }
109    /// Create a new scaled version of the input.
110    pub fn new_scale(o: Box<dyn Object<S>>, s: &na::Vector3<S>) -> Box<dyn Object<S>> {
111        AffineTransformer::identity(o).scale(s)
112    }
113}
114
115#[cfg(test)]
116mod test {
117    use crate::test::MockObject;
118    use crate::Object;
119
120    #[test]
121    fn translate() {
122        let normal = na::Vector3::new(1.0, 0.0, 0.0);
123        let mut mock_object = MockObject::new(1.0, normal);
124        let receiver = mock_object.add_normal_call_recorder(1);
125        let translation = na::Vector3::new(0.0001, 0.0, 0.0);
126        let translated = mock_object.translate(&translation);
127        let p = na::Point3::new(1.0, 0.0, 0.0);
128        assert_eq!(translated.normal(&p), normal);
129        assert_eq!(receiver.recv().unwrap(), p - translation);
130    }
131
132    #[test]
133    fn scale() {
134        let normal = na::Vector3::new(1.0, 0.0, 0.0);
135        let mut mock_object = MockObject::new(1.0, normal);
136        let receiver = mock_object.add_normal_call_recorder(1);
137        let scale = na::Vector3::new(0.1, 0.1, 0.1);
138        let scaled = mock_object.scale(&scale);
139        let p = na::Point3::new(1.0, 0.0, 0.0);
140        assert_eq!(scaled.normal(&p), normal);
141        assert_eq!(receiver.recv().unwrap(), p / 0.1);
142    }
143
144    #[test]
145    fn rotate() {
146        let normal = na::Vector3::new(1.0, 0.0, 0.0);
147        let mut mock_object = MockObject::new(1.0, normal);
148        let receiver = mock_object.add_normal_call_recorder(1);
149        let rotation = na::Vector3::new(0.0, 0.0, ::std::f64::consts::PI / 6.0);
150        let rotated = mock_object.rotate(&rotation);
151        let p = na::Point3::new(1.0, 0.0, 0.0);
152
153        assert_relative_eq!(
154            rotated.normal(&p),
155            na::Vector3::new(num_traits::Float::sqrt(3.0) / 2.0, -0.5, 0.0)
156        );
157        assert_relative_eq!(
158            receiver.try_recv().unwrap(),
159            na::Point3::new(num_traits::Float::sqrt(3.0) / 2.0, 0.5, 0.0)
160        );
161    }
162
163    #[test]
164    fn scale_and_translate() {
165        let normal = na::Vector3::new(1.0, 0.0, 0.0);
166        let mut mock_object = MockObject::new(1.0, normal);
167        let receiver = mock_object.add_normal_call_recorder(1);
168        let scale = na::Vector3::new(0.1, 0.1, 0.1);
169        let scaled = mock_object.scale(&scale);
170        let translation = na::Vector3::new(5.0, 0.0, 0.0);
171        let translated = scaled.translate(&translation);
172        let p = na::Point3::new(1.0, 0.0, 0.0);
173        assert_eq!(translated.normal(&p), normal);
174        assert_eq!(receiver.recv().unwrap(), (p - translation) / 0.1);
175    }
176
177    #[test]
178    fn translate_and_scale() {
179        let normal = na::Vector3::new(1.0, 0.0, 0.0);
180        let mut mock_object = MockObject::new(1.0, normal);
181        let receiver = mock_object.add_normal_call_recorder(1);
182        let translation = na::Vector3::new(5.0, 0.0, 0.0);
183        let translated = mock_object.translate(&translation);
184        let scale = na::Vector3::new(0.1, 0.1, 0.1);
185        let scaled = translated.scale(&scale);
186        let p = na::Point3::new(1.0, 0.0, 0.0);
187        assert_eq!(scaled.normal(&p), normal);
188        assert_eq!(receiver.recv().unwrap(), p / 0.1 - translation);
189    }
190
191    #[test]
192    fn rotate_and_translate() {
193        let normal = na::Vector3::new(1.0, 0.0, 0.0);
194        let mut mock_object = MockObject::new(1.0, normal);
195        let receiver = mock_object.add_normal_call_recorder(1);
196        let rotation = na::Vector3::new(0.0, 0.0, ::std::f64::consts::PI / 2.0);
197        let rotated = mock_object.rotate(&rotation);
198        let translation = na::Vector3::new(5.0, 0.0, 0.0);
199        let translated = rotated.translate(&translation);
200        let p = na::Point3::new(1.0, 0.0, 0.0);
201        translated.normal(&p);
202        assert_relative_eq!(
203            receiver.recv().unwrap(),
204            na::Point3::new(
205                p.y - translation.y,
206                p.x - translation.x,
207                p.z - translation.z
208            ),
209            epsilon = 10e-10
210        );
211    }
212
213    #[test]
214    fn translate_and_rotate() {
215        let normal = na::Vector3::new(1.0, 0.0, 0.0);
216        let mut mock_object = MockObject::new(1.0, normal);
217        let receiver = mock_object.add_normal_call_recorder(1);
218        let translation = na::Vector3::new(5.0, 0.0, 0.0);
219        let translated = mock_object.translate(&translation);
220        let rotation = na::Vector3::new(0.0, 0.0, ::std::f64::consts::PI / 2.0);
221        let rotated = translated.rotate(&rotation);
222        let p = na::Point3::new(1.0, 0.0, 0.0);
223        rotated.normal(&p);
224        assert_relative_eq!(
225            receiver.recv().unwrap(),
226            na::Point3::new(p.y, p.x, p.z) - translation,
227            epsilon = 10e-10
228        );
229    }
230}