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