Skip to main content

implicit3d/
transformer.rs

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