1use crate::{BoundingBox, Object, PrimitiveParameters, RealField};
2use nalgebra as na;
3use num_traits::Float;
4
5#[derive(Clone, Debug)]
6pub 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 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 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 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 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}