1 // This is core/vgl/algo/vgl_rotation_3d.h
2 #ifndef vgl_rotation_3d_h_
3 #define vgl_rotation_3d_h_
4 //:
5 // \file
6 // \brief A class representing a 3d rotation.
7 // \author Thomas Pollard
8 // \date 2005-03-13
9 //
10 // This is a class for storing and doing conversions with 3d rotation transforms
11 // specified by any of the following parameters: quaternions, Euler angles,
12 // Rodrigues vector, an orthonormal 3x3 matrix (with determinant = 1), or a
13 // vgl_h_matrix of proper form.
14 //
15 // \verbatim
16 // Modifications
17 // M. J. Leotta 2007-03-14 Moved from VPGL and implemented member functions
18 // Peter Vanroose 2009-06-11 Robustified the 2-vector constructors: input no longer needs to be unit-norm
19 // Peter Vanroose 2010-10-24 mutators and setters now return *this
20 // \endverbatim
21
22 #include <cmath>
23 #include <vector>
24 #include <iostream>
25 #include <vnl/vnl_vector_fixed.h>
26 #include <vnl/vnl_matrix_fixed.h>
27 #include <vnl/vnl_quaternion.h>
28 #include <vnl/vnl_cross.h>
29 #include <vnl/vnl_math.h>
30 #include <vgl/vgl_fwd.h>
31 #include <vgl/vgl_point_3d.h>
32 #include <vgl/vgl_homg_point_3d.h>
33 #include <vgl/algo/vgl_h_matrix_3d.h>
34 #include <vgl/vgl_tolerance.h>
35 #ifdef _MSC_VER
36 # include <vcl_msvc_warnings.h>
37 #endif
38
39 template <class T>
40 class vgl_rotation_3d
41 {
42 public:
43 // Constructors:-------------------------------------
44
45 //: Construct the identity rotation
vgl_rotation_3d()46 vgl_rotation_3d() : q_(0,0,0,1) {}
47
48 //: Construct from a quaternion.
vgl_rotation_3d(vnl_quaternion<T> const & q)49 vgl_rotation_3d( vnl_quaternion<T> const& q ) : q_(q) { q_.normalize(); }
50
51 //: Construct from Euler angles.
vgl_rotation_3d(T const & rx,T const & ry,T const & rz)52 vgl_rotation_3d( T const& rx, T const& ry, T const& rz ) : q_(rx,ry,rz) {}
53
54 //: Construct from a Rodrigues vector.
vgl_rotation_3d(vnl_vector_fixed<T,3> const & rvector)55 explicit vgl_rotation_3d( vnl_vector_fixed<T,3> const& rvector )
56 {
57 T mag = rvector.magnitude();
58 if (mag > T(0))
59 q_ = vnl_quaternion<T>(rvector/mag,mag);
60 else // identity rotation is a special case
61 q_ = vnl_quaternion<T>(0,0,0,1);
62 }
63
64 //: Construct from a Rodrigues vector.
65 vgl_rotation_3d& operator=( vnl_vector_fixed<T,3> const& rvector )
66 {
67 T mag = rvector.magnitude();
68 if (mag > T(0))
69 q_ = vnl_quaternion<T>(rvector/mag,mag);
70 else // identity rotation is a special case
71 q_ = vnl_quaternion<T>(0,0,0,1);
72 return *this;
73 }
74
75 //: Construct from a 3x3 rotation matrix
vgl_rotation_3d(vnl_matrix_fixed<T,3,3> const & matrix)76 explicit vgl_rotation_3d( vnl_matrix_fixed<T,3,3> const& matrix )
77 : q_(matrix.transpose()) {}
78
79 //: Construct from a 3x3 rotation matrix
80 vgl_rotation_3d& operator=( vnl_matrix_fixed<T,3,3> const& matrix )
81 { q_ = matrix.transpose(); return *this; }
82
83 //: Construct from a vgl_h_matrix_3d.
vgl_rotation_3d(vgl_h_matrix_3d<T> const & h)84 explicit vgl_rotation_3d( vgl_h_matrix_3d<T> const& h )
85 : q_(h.get_upper_3x3_matrix().transpose()) { assert(h.is_rotation()); }
86
87 //: Construct from a vgl_h_matrix_3d.
88 vgl_rotation_3d& operator=( vgl_h_matrix_3d<T> const& h )
89 { assert(h.is_rotation()); q_ = h.get_upper_3x3_matrix().transpose(); return *this; }
90
91 //: Construct to rotate (direction of) vector a to vector b
92 // The input vectors need not be of unit length
vgl_rotation_3d(vnl_vector_fixed<T,3> const & a,vnl_vector_fixed<T,3> const & b)93 vgl_rotation_3d(vnl_vector_fixed<T,3> const& a,
94 vnl_vector_fixed<T,3> const& b)
95 {
96 vnl_vector_fixed<T,3> c = vnl_cross_3d(a, b);
97 double aa = 0.0; if (dot_product(a, b) < 0) { aa = vnl_math::pi; c=-c; }
98 double cmag = static_cast<double>(c.magnitude())
99 / static_cast<double>(a.magnitude())
100 / static_cast<double>(b.magnitude());
101 // cross product of unit vectors is at most a unit vector:
102 if (cmag>1.0) cmag=1.0;
103 // if the vectors have the same direction, then set to identity rotation:
104 if (cmag<vgl_tolerance<double>::position)
105 {
106 if (aa!=vnl_math::pi) {
107 q_ = vnl_quaternion<T>(0, 0, 0, 1);
108 return;
109 }
110 else { // rotation axis not defined for rotation of pi
111 // construct a vector v along the min component axis of a
112 double ax = std::fabs(static_cast<double>(a[0]));
113 double ay = std::fabs(static_cast<double>(a[1]));
114 double az = std::fabs(static_cast<double>(a[2]));
115 vnl_vector_fixed<T,3> v(T(0));
116 double amin = ax; v[0]=T(1);
117 if (ay<amin) { amin = ay; v[0]=T(0); v[1]=T(1); }
118 if (az<amin) { v[0]=T(0); v[1]=T(0); v[2]=T(1); }
119 // define the pi rotation axis perpendicular to both v and a
120 vnl_vector_fixed<T,3> pi_axis = vnl_cross_3d(v, a);
121 q_ = vnl_quaternion<T>(pi_axis/pi_axis.magnitude(), aa);
122 return;
123 }
124 }
125 double angl = std::asin(cmag)+aa;
126 q_ = vnl_quaternion<T>(c/c.magnitude(), angl);
127 }
128
129 //: Construct to rotate (direction of) vector a to vector b
130 // The input vectors need not be of unit length
vgl_rotation_3d(vgl_vector_3d<T> const & a,vgl_vector_3d<T> const & b)131 vgl_rotation_3d(vgl_vector_3d<T> const& a,
132 vgl_vector_3d<T> const& b)
133 {
134 vnl_vector_fixed<T,3> na(a.x(), a.y(), a.z());
135 vnl_vector_fixed<T,3> nb(b.x(), b.y(), b.z());
136 *this = vgl_rotation_3d<T>(na, nb);
137 }
138
139 // Conversions:--------------------------------------
140
141 //: Output unit quaternion.
as_quaternion()142 vnl_quaternion<T> as_quaternion() const
143 {
144 return q_;
145 }
146
147 //: Output Euler angles.
148 // The first element is the rotation about the x-axis
149 // The second element is the rotation about the y-axis
150 // The third element is the rotation about the z-axis
151 // The total rotation is a composition of these rotations in this order
as_euler_angles()152 vnl_vector_fixed<T,3> as_euler_angles() const
153 {
154 return q_.rotation_euler_angles();
155 }
156
157 //: Output Rodrigues vector.
158 // The direction of this vector is the axis of rotation
159 // The length of this vector is the angle of rotation in radians
as_rodrigues()160 vnl_vector_fixed<T,3> as_rodrigues() const
161 {
162 double ang = q_.angle();
163 if (ang == 0.0)
164 return vnl_vector_fixed<T,3>(T(0));
165 return q_.axis()*T(ang);
166 }
167
168 //: Output the matrix representation of this rotation in 3x3 form.
as_matrix()169 vnl_matrix_fixed<T,3,3> as_matrix() const
170 {
171 return q_.rotation_matrix_transpose().transpose();
172 }
173
174 //: Output the matrix representation of this rotation in 4x4 form.
as_h_matrix_3d()175 vgl_h_matrix_3d<T> as_h_matrix_3d() const
176 {
177 return vgl_h_matrix_3d<T>(q_.rotation_matrix_transpose_4().transpose());
178 }
179
180 //: Returns the axis of rotation (unit vector)
axis()181 vnl_vector_fixed<T,3> axis() const { return q_.axis(); }
182
183 //: Returns the angle of rotation on the range [ 0 360 ] in radians
angle()184 double angle() const { return q_.angle(); }
185
186 // Operations:----------------------------------------
187
188 //: Make the rotation the identity (i.e. no rotation)
set_identity()189 vgl_rotation_3d& set_identity() { q_[0]=0; q_[1]=0; q_[2]=0; q_[3]=1; return *this; }
190
191 //: The inverse rotation
inverse()192 vgl_rotation_3d<T> inverse() const { return vgl_rotation_3d<T>(q_.conjugate()); }
193
194 //: The transpose or conjugate of the rotation
transpose()195 vgl_rotation_3d<T> transpose() const { return this->inverse(); }
196
197 //: Composition of two rotations.
198 vgl_rotation_3d<T> operator*( vgl_rotation_3d<T> const& first_rotation ) const
199 {
200 return vgl_rotation_3d<T>( q_*first_rotation.q_ );
201 }
202
203 //: Rotate a homogeneous point.
204 vgl_homg_point_3d<T> operator*( vgl_homg_point_3d<T> const& p ) const
205 {
206 vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.x(),p.y(),p.z()));
207 return vgl_homg_point_3d<T>(rp[0],rp[1],rp[2],p.w());
208 }
209
210 //: Rotate a homogeneous plane.
211 vgl_homg_plane_3d<T> operator*( vgl_homg_plane_3d<T> const& p ) const
212 {
213 vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.a(),p.b(),p.c()));
214 return vgl_homg_plane_3d<T>(rp[0],rp[1],rp[2],p.d());
215 }
216
217 //: Rotate a homogeneous line.
218 vgl_homg_line_3d_2_points<T> operator*( vgl_homg_line_3d_2_points<T> const& l ) const
219 {
220 return vgl_homg_line_3d_2_points<T>(this->operator*(l.point_finite()),
221 this->operator*(l.point_infinite()));
222 }
223
224 //: Rotate a point.
225 vgl_point_3d<T> operator*( vgl_point_3d<T> const& p ) const
226 {
227 vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.x(),p.y(),p.z()));
228 return vgl_point_3d<T>(rp[0],rp[1],rp[2]);
229 }
230
231 //: Rotate a plane.
232 vgl_plane_3d<T> operator*( vgl_plane_3d<T> const& p ) const
233 {
234 vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.a(),p.b(),p.c()));
235 return vgl_plane_3d<T>(rp[0],rp[1],rp[2],p.d());
236 }
237
238 //: Rotate a line.
239 vgl_line_3d_2_points<T> operator*( vgl_line_3d_2_points<T> const& l ) const
240 {
241 return vgl_line_3d_2_points<T>(this->operator*(l.point1()),
242 this->operator*(l.point2()));
243 }
244
245 //: Rotate a line segment.
246 vgl_line_segment_3d<T> operator*( vgl_line_segment_3d<T> const& l ) const
247 {
248 return vgl_line_segment_3d<T>(this->operator*(l.point1()),
249 this->operator*(l.point2()));
250 }
251
252 //: Rotate a vgl vector.
253 vgl_vector_3d<T> operator*( vgl_vector_3d<T> const& v ) const
254 {
255 vnl_vector_fixed<T,3> rv = q_.rotate(vnl_vector_fixed<T,3>(v.x(),v.y(),v.z()));
256 return vgl_vector_3d<T>(rv[0],rv[1],rv[2]);
257 }
258
259 //: Rotate a vnl vector.
260 vnl_vector_fixed<T, 3> operator*( vnl_vector_fixed<T,3> const& v ) const
261 {
262 return q_.rotate(v);
263 }
264
265 //: comparison operator
266 bool operator==(vgl_rotation_3d<T> const& r) const { return q_ == r.as_quaternion(); }
267
268 protected:
269 //: The internal representation of the rotation is a quaternion.
270 vnl_quaternion<T> q_;
271 };
272
273
274 // External methods for stream I/O
275 // ----------------------------------------------------------------
276
277 template <class T>
278 std::istream& operator>>(std::istream& s, vgl_rotation_3d<T> &R)
279 {
280 vnl_quaternion<T> q;
281 s >> q;
282 R = vgl_rotation_3d<T>(q);
283 return s;
284 }
285
286 template <class T>
287 std::ostream& operator<<(std::ostream& s, vgl_rotation_3d<T> const& R)
288 {
289 return s << R.as_quaternion();
290 }
291
292 // External methods for more efficient rotation of multiple objects
293 // ----------------------------------------------------------------
294
295 //: In-place rotation of a vector of homogeneous points
296 // \note This is more efficient than calling vgl_rotation_3d::rotate() on each
297 template <class T> inline
vgl_rotate_3d(vgl_rotation_3d<T> const & rot,std::vector<vgl_homg_point_3d<T>> & pts)298 void vgl_rotate_3d(vgl_rotation_3d<T> const& rot, std::vector<vgl_homg_point_3d<T> >& pts)
299 {
300 vnl_matrix_fixed<T,3,3> R = rot.as_3matrix();
301 for (typename std::vector<vgl_homg_point_3d<T> >::iterator itr = pts.begin();
302 itr != pts.end(); ++itr)
303 {
304 vgl_homg_point_3d<T>& p = *itr;
305 p.set(R[0][0]*p.x()+R[0][1]*p.y()+R[0][2]*p.z(),
306 R[1][0]*p.x()+R[1][1]*p.y()+R[1][2]*p.z(),
307 R[2][0]*p.x()+R[2][1]*p.y()+R[2][2]*p.z(), p.w());
308 }
309 }
310
311 #endif // vgl_rotation_3d_h_
312