core/vgl/algo/vgl_rotation_3d.h
Go to the documentation of this file.
00001 // This is core/vgl/algo/vgl_rotation_3d.h
00002 #ifndef vgl_rotation_3d_h_
00003 #define vgl_rotation_3d_h_
00004 //:
00005 // \file
00006 // \brief A class representing a 3d rotation.
00007 // \author Thomas Pollard
00008 // \date 2005-03-13
00009 //
00010 // This is a class for storing and doing conversions with 3d rotation transforms
00011 // specified by any of the following parameters: quaternions, Euler angles,
00012 // Rodrigues vector, an orthonormal 3x3 matrix (with determinant = 1), or a
00013 // vgl_h_matrix of proper form.
00014 //
00015 // \verbatim
00016 //  Modifications
00017 //   M. J. Leotta   2007-03-14 Moved from VPGL and implemented member functions
00018 //   Peter Vanroose 2009-06-11 Robustified the 2-vector constructors: input no longer needs to be unit-norm
00019 // \endverbatim
00020 
00021 #include <vnl/vnl_vector_fixed.h>
00022 #include <vnl/vnl_matrix_fixed.h>
00023 #include <vnl/vnl_quaternion.h>
00024 #include <vnl/vnl_cross.h>
00025 #include <vnl/vnl_math.h>
00026 #include <vcl_cmath.h>
00027 #include <vgl/vgl_fwd.h>
00028 #include <vgl/vgl_point_3d.h>
00029 #include <vgl/vgl_homg_point_3d.h>
00030 #include <vgl/algo/vgl_h_matrix_3d.h>
00031 #include <vgl/vgl_tolerance.h>
00032 #include <vcl_vector.h>
00033 #include <vcl_iostream.h>
00034 
00035 template <class T>
00036 class vgl_rotation_3d
00037 {
00038  public:
00039   // Constructors:-------------------------------------
00040 
00041   //: Construct the identity rotation
00042   vgl_rotation_3d() : q_(0,0,0,1) {}
00043 
00044   //: Construct from a quaternion.
00045   vgl_rotation_3d( const vnl_quaternion<T>& q ) : q_(q) { q_.normalize(); }
00046 
00047   //: Construct from Euler angles.
00048   vgl_rotation_3d( const T& rx, const T& ry, const T& rz ) : q_(rx,ry,rz) {}
00049 
00050   //: Construct from a Rodrigues vector.
00051   explicit vgl_rotation_3d( const vnl_vector_fixed<T,3>& rvector )
00052   {
00053     T mag = rvector.magnitude();
00054     if (mag > T(0))
00055       q_ = vnl_quaternion<T>(rvector/mag,mag);
00056     else // identity rotation is a special case
00057       q_ = vnl_quaternion<T>(0,0,0,1);
00058   }
00059 
00060   //: Construct from a 3x3 rotation matrix
00061   explicit vgl_rotation_3d( const vnl_matrix_fixed<T,3,3>& matrix )
00062     : q_(matrix.transpose()) {}
00063 
00064   //: Construct from a vgl_h_matrix_3d.
00065   explicit vgl_rotation_3d( const vgl_h_matrix_3d<T>& h )
00066     : q_(h.get_upper_3x3_matrix().transpose()) { assert(h.is_rotation()); }
00067 
00068   //: Construct to rotate (direction of) vector a to vector b
00069   //  The input vectors need not be of unit length
00070   vgl_rotation_3d(const vnl_vector_fixed<T,3>& a,
00071                   const vnl_vector_fixed<T,3>& b)
00072   {
00073     vnl_vector_fixed<T,3> c = vnl_cross_3d(a, b);
00074     double aa = 0.0; if (dot_product(a, b) < 0) { aa = vnl_math::pi; c=-c; }
00075     double cmag = static_cast<double>(c.magnitude())
00076                 / static_cast<double>(a.magnitude())
00077                 / static_cast<double>(b.magnitude());
00078     // cross product of unit vectors is at most a unit vector:
00079     if (cmag>1.0) cmag=1.0;
00080     // if the vectors have the same direction, then set to identity rotation:
00081     if (cmag<vgl_tolerance<double>::position)
00082     {
00083       if (aa!=vnl_math::pi) {
00084         q_ = vnl_quaternion<T>(0, 0, 0, 1);
00085         return;
00086       }
00087       else { // rotation axis not defined for rotation of pi
00088         // construct a vector v along the min component axis of a
00089         double ax = static_cast<double>(a[0]),ay = static_cast<double>(a[1]),
00090                az = static_cast<double>(a[2]);
00091         vnl_vector_fixed<T,3> v(T(0));
00092         double amin = ax; v[0]=T(1);
00093         if (ay<amin) { amin = ay; v[0]=T(0); v[1]=T(1); }
00094         if (az<amin) { v[0]=T(0); v[1]=T(0); v[2]=T(1); }
00095         // define the pi rotation axis perpendicular to both v and a
00096         vnl_vector_fixed<T,3> pi_axis = vnl_cross_3d(v, a);
00097         q_ = vnl_quaternion<T>(pi_axis/pi_axis.magnitude(), aa);
00098         return;
00099       }
00100     }
00101     double angl = vcl_asin(cmag)+aa;
00102     q_ = vnl_quaternion<T>(c/c.magnitude(), angl);
00103   }
00104 
00105   //: Construct to rotate (direction of) vector a to vector b
00106   //  The input vectors need not be of unit length
00107   explicit vgl_rotation_3d(const vgl_vector_3d<T>& a,
00108                            const vgl_vector_3d<T>& b)
00109   {
00110     vnl_vector_fixed<T,3> na(a.x(), a.y(), a.z());
00111     vnl_vector_fixed<T,3> nb(b.x(), b.y(), b.z());
00112     *this = vgl_rotation_3d<T>(na, nb);
00113   }
00114 
00115   // Conversions:--------------------------------------
00116 
00117   //: Output unit quaternion.
00118   vnl_quaternion<T> as_quaternion() const
00119   {
00120     return q_;
00121   }
00122 
00123   //: Output Euler angles.
00124   //  The first element is the rotation about the x-axis
00125   //  The second element is the rotation about the y-axis
00126   //  The third element is the rotation about the z-axis
00127   //  The total rotation is a composition of these rotations in this order
00128   vnl_vector_fixed<T,3> as_euler_angles() const
00129   {
00130     return q_.rotation_euler_angles();
00131   }
00132 
00133   //: Output Rodrigues vector.
00134   //  The direction of this vector is the axis of rotation
00135   //  The length of this vector is the angle of rotation in radians
00136   vnl_vector_fixed<T,3> as_rodrigues() const
00137   {
00138     double ang = q_.angle();
00139     if (ang == 0.0)
00140       return vnl_vector_fixed<T,3>(T(0));
00141     return q_.axis()*T(ang);
00142   }
00143 
00144   //: Output the matrix representation of this rotation in 3x3 form.
00145   vnl_matrix_fixed<T,3,3> as_matrix() const
00146   {
00147     return q_.rotation_matrix_transpose().transpose();
00148   }
00149 
00150   //: Output the matrix representation of this rotation in 4x4 form.
00151   vgl_h_matrix_3d<T> as_h_matrix_3d() const
00152   {
00153     return vgl_h_matrix_3d<T>(q_.rotation_matrix_transpose_4().transpose());
00154   }
00155 
00156   //: Returns the axis of rotation (unit vector)
00157   vnl_vector_fixed<T,3> axis() const { return q_.axis(); }
00158 
00159   //: Returns the magnitude of the angle of rotation
00160   double angle() const { return q_.angle(); }
00161 
00162   // Operations:----------------------------------------
00163 
00164   //: Make the rotation the identity (i.e. no rotation)
00165   void set_identity() { q_[0]=0; q_[1]=0; q_[2]=0; q_[3]=1; }
00166 
00167   //: The inverse rotation
00168   vgl_rotation_3d<T> inverse() const { return vgl_rotation_3d<T>(q_.conjugate()); }
00169   //:the transpose or congugate of the rotation
00170   vgl_rotation_3d<T> transpose() const
00171   { return this->inverse(); }
00172 
00173   //: Composition of two rotations.
00174   vgl_rotation_3d<T> operator*( const vgl_rotation_3d<T>& first_rotation ) const
00175   {
00176     return vgl_rotation_3d<T>( q_*first_rotation.q_ );
00177   }
00178 
00179   //: Rotate a homogeneous point.
00180   vgl_homg_point_3d<T> operator*( const vgl_homg_point_3d<T>& p ) const
00181   {
00182     vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.x(),p.y(),p.z()));
00183     return vgl_homg_point_3d<T>(rp[0],rp[1],rp[2],p.w());
00184   }
00185 
00186   //: Rotate a homogeneous plane.
00187   vgl_homg_plane_3d<T> operator*( const vgl_homg_plane_3d<T>& p ) const
00188   {
00189     vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.a(),p.b(),p.c()));
00190     return vgl_homg_plane_3d<T>(rp[0],rp[1],rp[2],p.d());
00191   }
00192 
00193   //: Rotate a homogeneous line.
00194   vgl_homg_line_3d_2_points<T> operator*( const vgl_homg_line_3d_2_points<T>& l ) const
00195   {
00196     return vgl_homg_line_3d_2_points<T>(this->operator*(l.point_finite()),
00197                                         this->operator*(l.point_infinite()));
00198   }
00199 
00200   //: Rotate a point.
00201   vgl_point_3d<T> operator*( const vgl_point_3d<T>& p ) const
00202   {
00203     vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.x(),p.y(),p.z()));
00204     return vgl_point_3d<T>(rp[0],rp[1],rp[2]);
00205   }
00206 
00207   //: Rotate a plane.
00208   vgl_plane_3d<T> operator*( const vgl_plane_3d<T>& p ) const
00209   {
00210     vnl_vector_fixed<T,3> rp = q_.rotate(vnl_vector_fixed<T,3>(p.a(),p.b(),p.c()));
00211     return vgl_plane_3d<T>(rp[0],rp[1],rp[2],p.d());
00212   }
00213 
00214   //: Rotate a line.
00215   vgl_line_3d_2_points<T> operator*( const vgl_line_3d_2_points<T>& l ) const
00216   {
00217     return vgl_line_3d_2_points<T>(this->operator*(l.point1()),
00218                                    this->operator*(l.point2()));
00219   }
00220 
00221   //: Rotate a line segment.
00222   vgl_line_segment_3d<T> operator*( const vgl_line_segment_3d<T>& l ) const
00223   {
00224     return vgl_line_segment_3d<T>(this->operator*(l.point1()),
00225                                   this->operator*(l.point2()));
00226   }
00227 
00228   //: Rotate a vgl vector.
00229   vgl_vector_3d<T> operator*( const vgl_vector_3d<T>& v ) const
00230   {
00231     vnl_vector_fixed<T,3> rv = q_.rotate(vnl_vector_fixed<T,3>(v.x(),v.y(),v.z()));
00232     return vgl_vector_3d<T>(rv[0],rv[1],rv[2]);
00233   }
00234 
00235   //: Rotate a vnl vector.
00236   vnl_vector_fixed<T, 3> operator*( const vnl_vector_fixed<T,3>& v ) const
00237   {
00238     return q_.rotate(v);
00239   }
00240 
00241   //: comparison operator
00242   bool operator==(vgl_rotation_3d<T> const& r) const { return q_ == r.as_quaternion(); }
00243 
00244  protected:
00245   //: The internal representation of the rotation is a quaternion.
00246   vnl_quaternion<T> q_;
00247 };
00248 
00249 
00250 // External methods for stream I/O
00251 // ----------------------------------------------------------------
00252 
00253 template <class T>
00254 vcl_ostream& operator<<(vcl_ostream& s, vgl_rotation_3d<T> const& R)
00255 {
00256   return s << R.as_quaternion();
00257 }
00258 
00259 // External methods for more efficient rotation of multiple objects
00260 // ----------------------------------------------------------------
00261 
00262 //: In-place rotation of a vector of homogeneous points
00263 // \note This is more efficient than calling vgl_rotation_3d::rotate() on each
00264 template <class T> inline
00265 void vgl_rotate_3d(const vgl_rotation_3d<T>& rot, vcl_vector<vgl_homg_point_3d<T> >& pts)
00266 {
00267   vnl_matrix_fixed<T,3,3> R = rot.as_3matrix();
00268   for (typename vcl_vector<vgl_homg_point_3d<T> >::iterator itr = pts.begin();
00269        itr != pts.end();  ++itr)
00270   {
00271     vgl_homg_point_3d<T>& p = *itr;
00272     p.set(R[0][0]*p.x()+R[0][1]*p.y()+R[0][2]*p.z(),
00273           R[1][0]*p.x()+R[1][1]*p.y()+R[1][2]*p.z(),
00274           R[2][0]*p.x()+R[2][1]*p.y()+R[2][2]*p.z(), p.w());
00275   }
00276 }
00277 
00278 #endif // vgl_rotation_3d_h_