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