Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

fmatSpatial.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_fmatSpatial_h_
00003 #define INCLUDED_fmatSpatial_h_
00004 
00005 #include "fmatCore.h"
00006 
00007 namespace fmat {
00008   
00009   template<typename R> class TransformT;
00010   
00011   // ************************* rotation matrices ******************** //
00012   
00013   //! returns 2x2 rotation matrix (i.e. implied rotation about Z), for angle @a rad in radians
00014   template<typename R>
00015   inline Matrix<2,2,R> rotation2DT(R rad) {
00016     R s = std::sin(rad), c = std::cos(rad);
00017     R dat[4] = { c, s, -s, c };
00018     return Matrix<2,2,R>(dat);
00019   }
00020   //! returns 2x2 rotation matrix for angle @a rad (in radians) about Z axis
00021   inline Matrix<2,2,fmatReal> rotation2D(fmatReal rad) { return rotation2DT<fmatReal>(rad); }
00022   
00023   //! returns NxN rotation matrix for angle @a rad (in radians) about X axis (only uses upper 3x3)
00024   template<size_t N, typename R>
00025   inline Matrix<N,N,R> rotationXN(R rad) {
00026     (void)sizeof(fmat_internal::CompileTimeAssert<Rotation_requires_larger_dimensions,N>=3>);
00027     Matrix<N,N,R> ans = Matrix<N,N,R>::identity();
00028     ans(1,1) = ans(2,2) = std::cos(rad);
00029     ans(1,2) = -(ans(2,1) = std::sin(rad));
00030     return ans;
00031   }
00032   //! returns 3x3 rotation matrix for angle @a rad (in radians) about X axis
00033   inline Matrix<3,3,fmatReal> rotationX(fmatReal rad) { return rotationXN<3,fmatReal>(rad); }
00034 
00035   //! returns NxN rotation matrix for angle @a rad (in radians) about Y axis (only uses upper 3x3)
00036   template<size_t N, typename R>
00037   inline Matrix<N,N,R> rotationYN(R rad) {
00038     (void)sizeof(fmat_internal::CompileTimeAssert<Rotation_requires_larger_dimensions,N>=3>);
00039     Matrix<N,N,R> ans = Matrix<N,N,R>::identity();
00040     ans(0,0) = ans(2,2) = std::cos(rad);
00041     ans(2,0) = -(ans(0,2) = std::sin(rad));
00042     return ans;
00043   }
00044   //! returns 3x3 rotation matrix for angle @a rad (in radians) about Z axis
00045   inline Matrix<3,3,fmatReal> rotationY(fmatReal rad) { return rotationYN<3,fmatReal>(rad); }
00046   
00047   //! returns NxN rotation matrix for angle @a rad (in radians) about Z axis (only uses upper 2x2)
00048   template<size_t N, typename R>
00049   inline Matrix<N,N,R> rotationZN(R rad) {
00050     (void)sizeof(fmat_internal::CompileTimeAssert<Rotation_requires_larger_dimensions,N>=2>);
00051     if(N==2) {
00052       R s = std::sin(rad), c = std::cos(rad);
00053       R dat[4] = { c, s, -s, c };
00054       return Matrix<N,N,R>(dat);
00055     } else {
00056       Matrix<N,N,R> ans = Matrix<N,N,R>::identity();
00057       ans(0,0) = ans(1,1) = std::cos(rad);
00058       ans(0,1) = -(ans(1,0) = std::sin(rad));
00059       return ans;
00060     }
00061   }
00062   //! returns 3x3 rotation matrix for angle @a rad (in radians) about Z axis
00063   inline Matrix<3,3,fmatReal> rotationZ(fmatReal rad) { return rotationZN<3,fmatReal>(rad); }
00064   
00065   
00066   
00067   // ************************* Quaternion ******************** //
00068   
00069   //! Quaternions can be more efficient and more stable for a series of rotations than a corresponding 3x3 matrix, also more compact storage
00070   template<class R=fmatReal>
00071   class QuaternionT {
00072   public:
00073     //! Default constructor, initialize to identity (0 rotation)
00074     QuaternionT() : w(1), x(0), y(0), z(0) {}
00075     
00076     //! Explicit construction from elements (careful, does not check normalization!)
00077     QuaternionT(R w_, R x_, R y_, R z_) : w(w_), x(x_), y(y_), z(z_) {}
00078     
00079     //! copies from another representation, assuming operator[] is supported and w is index 0
00080     template<typename T> static QuaternionT from(const T& q) { return QuaternionT(q[0],q[1],q[2],q[3]); }
00081     //! copies from another representation, assuming operator[] is supported and w is index 0
00082     template<typename T> QuaternionT& importFrom(const T& q) { w=q[0]; x=q[1]; y=q[2]; z=q[3]; return *this; }
00083     //! copies into another representation, assuming operator[] is supported and w is index 0
00084     template<typename T> T exportTo() const { T q; q[0]=w; q[1]=x; q[2]=y; q[3]=z; return q; }
00085     //! copies into another representation, assuming operator[] is supported and w is index 0
00086     template<typename T> T& exportTo(T& q) const { q[0]=w; q[1]=x; q[2]=y; q[3]=z; return q; }
00087     //! unpacks into specified storage
00088     template<typename T> void exportTo(T& w_, T& x_, T& y_, T& z_) const { w_=w; x_=x; y_=y; z_=z; }
00089     //! unpacks into specified storage, skipping #w which can be reconstitued from √(1-x^2-y^2-z^2), see fromAxis()
00090     template<typename T> void exportTo(T& x_, T& y_, T& z_) const { if(w<0) { x_=-x; y_=-y; z_=-z; } else { x_=x; y_=y; z_=z; } }
00091     
00092     R getW() const { return w; } //!< returns #w
00093     R getX() const { return x; } //!< returns #x
00094     R getY() const { return y; } //!< returns #y
00095     R getZ() const { return z; } //!< returns #z
00096     
00097     //! returns a no-op quaternion (0 rotation), the same as the default constructor, but can re-use this instance instead of creating new ones all the time
00098     static const QuaternionT& identity() { return IDENTITY; }
00099     static const QuaternionT IDENTITY; //!< identity instance
00100     
00101     //! generate quaternion representing rotation of @a rad radians about X axis
00102     static QuaternionT<R> aboutX(R rad) { return fmat::QuaternionT<R>(std::cos(rad/2),std::sin(rad/2),0,0); }
00103     //! generate quaternion representing rotation of @a rad radians about Y axis
00104     static QuaternionT<R> aboutY(R rad) { return fmat::QuaternionT<R>(std::cos(rad/2),0,std::sin(rad/2),0); }
00105     //! generate quaternion representing rotation of @a rad radians about Z axis
00106     static QuaternionT<R> aboutZ(R rad) { return fmat::QuaternionT<R>(std::cos(rad/2),0,0,std::sin(rad/2)); }
00107     
00108     //! Generate quaternion from axis-angle representation (@a angle in radians, @a axis will be re-normalized if non-zero @a angle)
00109     /*! If axis is 0 length, initializes to identity (0 rotation) */
00110     template<class T>
00111     static QuaternionT fromAxisAngle(const T& axis, R angle) {
00112       if(std::abs(angle) <= std::numeric_limits<R>::epsilon())
00113         return QuaternionT(); // 0 rotation, axis does not matter
00114       R n = axis.sumSq();
00115       if(n <= std::numeric_limits<R>::epsilon())
00116         return QuaternionT(); // invalid axis, just use identity
00117       R s = std::sin(angle/2)/std::sqrt(n);
00118       return QuaternionT(std::cos(angle/2), axis[0]*s, axis[1]*s, axis[2]*s);
00119     }
00120     
00121     //! Generate quaternion from just the axis component of another quaternion (i.e. @a axis magnitude should be less than 1; 0 magnitude means no rotation)
00122     /*! This method requires only basic operations on the input type, so you can pass a raw array or plist::Point... */
00123     template<class T>
00124     static QuaternionT fromAxis(const T& axis) {
00125       R n2 = static_cast<R>(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
00126       if(n2>1) {
00127         if(n2>1+std::numeric_limits<R>::epsilon()*10) {
00128           R n = std::sqrt(n2);
00129           std::cerr << "Warning: fmat::Quaternion axis constructor passed non-normalized |" << axis[0] << ',' << axis[1] << ',' << axis[2] << "| = " << n << " (err " << (n-1) << ')' << std::endl;
00130           R s = 1/n;
00131           return QuaternionT(0, axis[0]*s, axis[1]*s, axis[2]*s);
00132         } else {
00133           // minor rounding error
00134           R s = 1/n2; // not bothering with sqrt, ensures it goes below 1
00135           return QuaternionT(0, axis[0]*s, axis[1]*s, axis[2]*s);
00136         }
00137       } else {
00138         return QuaternionT(std::sqrt(1-n2),axis[0],axis[1],axis[2]);
00139       }
00140     }
00141     
00142     //! Generate quaternion from rotation matrix, this assumes the rotation matrix is well-formed
00143     /*! This implementation is based on cross pollination between "Angel"'s code on this page:
00144      *    http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
00145      *  and the Ogre3D Quaternion implementation, which itself references:
00146      *    Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
00147      *    article "Quaternion Calculus and Fast Animation". */
00148     template<class T>
00149     static QuaternionT fromMatrix(const T& rot) {
00150       QuaternionT quat = fmat_internal::NoInit();
00151       R tracem1 = rot(0,0) + rot(1,1) + rot(2,2);
00152       if( tracem1 >= 0 ) {
00153         const R r = std::sqrt(tracem1 + 1);
00154         quat.w = (R)0.5 * r;
00155         const R s = (R)0.5 / r;
00156         quat.x = ( rot(2,1) - rot(1,2) ) * s;
00157         quat.y = ( rot(0,2) - rot(2,0) ) * s;
00158         quat.z = ( rot(1,0) - rot(0,1) ) * s;
00159       } else {
00160         if ( rot(0,0) > rot(1,1) && rot(0,0) > rot(2,2) ) {
00161           const R r = std::sqrt( 1.0f + rot(0,0) - rot(1,1) - rot(2,2));
00162           quat.x = (R)0.5 * r;
00163           const R s = (R)0.5 / r;
00164           quat.w = (rot(2,1) - rot(1,2) ) * s;
00165           quat.y = (rot(0,1) + rot(1,0) ) * s;
00166           quat.z = (rot(0,2) + rot(2,0) ) * s;
00167         } else if (rot(1,1) > rot(2,2)) {
00168           const R r = std::sqrt( 1.0f + rot(1,1) - rot(0,0) - rot(2,2));
00169           quat.y = (R)0.5 * r;
00170           const R s = (R)0.5 / r;
00171           quat.w = (rot(0,2) - rot(2,0) ) * s;
00172           quat.x = (rot(0,1) + rot(1,0) ) * s;
00173           quat.z = (rot(1,2) + rot(2,1) ) * s;
00174         } else {
00175           const R r = std::sqrt( 1.0f + rot(2,2) - rot(0,0) - rot(1,1) );
00176           quat.z = (R)0.5 * r;
00177           const R s = (R)0.5 / r;
00178           quat.w = (rot(1,0) - rot(0,1) ) * s;
00179           quat.x = (rot(0,2) + rot(2,0) ) * s;
00180           quat.y = (rot(1,2) + rot(2,1) ) * s;
00181         }
00182       }
00183       return quat;
00184     }
00185     
00186     //! returns a 3x3 rotation matrix representation
00187     /*! implemented by simplified version of operator*(*this, Matrix<3,3>::identity()) */
00188     Matrix<3,3,R> toMatrix() const {
00189       const R t2 =   w*x;
00190       const R t3 =   w*y;
00191       const R t4 =   w*z;
00192       const R t5 =  -x*x;
00193       const R t6 =   x*y;
00194       const R t7 =   x*z;
00195       const R t8 =  -y*y;
00196       const R t9 =   y*z;
00197       const R t10 = -z*z;
00198       const R values[9] = {
00199         2*(t8 + t10) + 1,
00200         2*(t4 +  t6),
00201         2*(t7 -  t3),
00202         2*(t6 -  t4),
00203         2*(t5 + t10) + 1,
00204         2*(t2 +  t9),
00205         2*(t3 + t7),
00206         2*(t9 - t2),
00207         2*(t5 + t8) + 1
00208       };
00209       return Matrix<3,3,R>(values);
00210     }
00211     //! allows conversion/assignment to Matrix<3,3>
00212     operator Matrix<3,3,R>() const { return toMatrix(); }
00213     
00214     //! return axis of rotation represented by quaternion @a q (i.e. the axis component of axis-angle representation)
00215     Column<3,R> axis() const {
00216       R n2 = (x*x + y*y + z*z);
00217       if(n2 < std::numeric_limits<R>::epsilon())
00218         return fmat::packT<R>(0,0,1); // no rotation, unknown axis
00219       R sc = 1/std::sqrt(n2);
00220       return fmat::packT<R>(x*sc, y*sc, z*sc);
00221     }
00222     
00223     //! Return angle of rotation (range ±π radians) about the quaternion's native axis (i.e. the angle component for axis-angle representation)
00224     /*! Handles some slight denormalization gracefully. */
00225     R angle() const {
00226       R n = std::sqrt(x*x + y*y + z*z);
00227       R ang = 2*std::atan2(n,w); // the atan2 result is always positive because 'n' is always positive
00228       if(ang>static_cast<R>(M_PI)) // so ang is always positive, don't need a '< M_PI' case
00229         ang-=2*static_cast<R>(M_PI);
00230       return ang;
00231       // alternative... faster/better?
00232       /*if(w>1) return 2*std::acos(2-w);
00233        else if(w<-1) return 2*std::acos(2+w);
00234        else return 2*std::acos(w);*/
00235     }
00236     
00237     //! Return angle of rotation represented by the quaternion about an arbitrary axis (assumed to already be normalized)
00238     template<class T>
00239     R axisComponent(const T& v) const {
00240       /*R ang_2 = std::acos(w);
00241        R sc = std::sin(ang_2);*/
00242       R n = std::sqrt(x*x + y*y + z*z);
00243       if(n < std::numeric_limits<R>::epsilon())
00244         return 0;
00245       R ang_2 = std::atan2(n,w);
00246       return 2*ang_2*(x*v[0] + y*v[1] + z*v[2])/n;
00247     }
00248     
00249     //! Returns yaw-pitch-roll aka heading-elevation-bank conversion, where roll-pitch-yaw correspond to compounding rotations about the global x, y, and z axis respectively (in that order)
00250     /*! From the "driver's seat" positive heading is turning to the left (z is up, using right hand rule, not compass heading),
00251      *  positive pitch is looking down, and positive roll is spinning clockwise.  Within this frame-oriented view, we apply
00252      *  rotation axes in the 'reverse' order: first z, then y, then x.
00253      *
00254      *  You can reconstruct a Quaternion from these values by: q · v = aboutZ(yaw) * aboutY(pitch) * aboutX(roll) · v
00255      *  Note we right-multiply v to apply the rotation, so x is applied to an incoming vector first, then y, then z.
00256      *
00257      *  With thanks to http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
00258      *  (note that page uses different axis mapping however!) */
00259     fmat::Column<3,R> ypr() const {
00260       const R sqw=w*w, sqx=x*x, sqy=y*y, sqz=z*z;
00261       const R unit = sqw + sqx + sqy + sqz; // if normalised is one, otherwise is correction factor
00262       const R test = y*w - x*z;
00263       if (test > 0.4999*unit) { // singularity at north pole
00264         return packT<R>(-2 * std::atan2(x,w), (R)M_PI_2, 0);
00265       }
00266       if (test < -0.4999*unit) { // singularity at south pole
00267         return packT<R>(2 * std::atan2(x,w), -(R)M_PI_2, 0);
00268       }
00269       return packT<R>(
00270         std::atan2(2*z*w+2*x*y , sqx - sqz - sqy + sqw),
00271         std::asin(2*test/unit),
00272         std::atan2(2*x*w+2*z*y , -sqx + sqz - sqy + sqw)
00273       );
00274     }
00275     
00276     //! Negates just the quaternion axis
00277     QuaternionT inverse() const ATTR_must_check { return QuaternionT(w,-x,-y,-z); }
00278     
00279     // ! Negates the entire quaternion (not the same as the inverse)
00280     /* ! Although I wonder if it should be the same as inverse(), this is what other quaternion implementations do */
00281     // Since it's not clear what this should do, it's left undefined
00282     //QuaternionT operator-() const ATTR_must_check { return QuaternionT(-w,-x,-y,-z); }
00283     
00284     //! multiply quaternions
00285     template<typename Rb>
00286     QuaternionT<typename fmat_internal::promotion_trait<R,Rb>::type>
00287     operator*(const QuaternionT<Rb>& q) const {
00288       return QuaternionT<typename fmat_internal::promotion_trait<R,Rb>::type>(
00289         w*q.w - x*q.x - y*q.y - z*q.z,
00290         w*q.x + x*q.w + y*q.z - z*q.y,
00291         w*q.y - x*q.z + y*q.w + z*q.x,
00292         w*q.z + x*q.y - y*q.x + z*q.w
00293       );
00294     }
00295     
00296     //! multiply quaternions
00297     QuaternionT operator*=(const QuaternionT& q) { return this->operator=(operator*(q)); }
00298     
00299     //! multiply 3-row matrix by quaternion
00300     template<template<size_t H, size_t W, typename Rt> class T, size_t W>
00301     Matrix<3,W,R> operator*(const T<3,W,R>& m) const {
00302       const R t2 =   w*x;
00303       const R t3 =   w*y;
00304       const R t4 =   w*z;
00305       const R t5 =  -x*x;
00306       const R t6 =   x*y;
00307       const R t7 =   x*z;
00308       const R t8 =  -y*y;
00309       const R t9 =   y*z;
00310       const R t10 = -z*z;
00311       Matrix<3,W,R> o=fmat_internal::NoInit();
00312       for(size_t c = 0; c<W; ++c) {
00313         o(0,c) = 2*( (t8 + t10)*m(0,c) + (t6 -  t4)*m(1,c) + (t3 + t7)*m(2,c) ) + m(0,c);
00314         o(1,c) = 2*( (t4 +  t6)*m(0,c) + (t5 + t10)*m(1,c) + (t9 - t2)*m(2,c) ) + m(1,c);
00315         o(2,c) = 2*( (t7 -  t3)*m(0,c) + (t2 +  t9)*m(1,c) + (t5 + t8)*m(2,c) ) + m(2,c);
00316       }
00317       return o;
00318     }
00319     
00320     //! multiply Transform by quaternion
00321     TransformT<R> operator*(const TransformT<R>& m) const {
00322       const R t2 =   w*x;
00323       const R t3 =   w*y;
00324       const R t4 =   w*z;
00325       const R t5 =  -x*x;
00326       const R t6 =   x*y;
00327       const R t7 =   x*z;
00328       const R t8 =  -y*y;
00329       const R t9 =   y*z;
00330       const R t10 = -z*z;
00331       TransformT<R> o=fmat_internal::NoInit();
00332       for(size_t c = 0; c<4; ++c) {
00333         o(0,c) = 2*( (t8 + t10)*m(0,c) + (t6 -  t4)*m(1,c) + (t3 + t7)*m(2,c) ) + m(0,c);
00334         o(1,c) = 2*( (t4 +  t6)*m(0,c) + (t5 + t10)*m(1,c) + (t9 - t2)*m(2,c) ) + m(1,c);
00335         o(2,c) = 2*( (t7 -  t3)*m(0,c) + (t2 +  t9)*m(1,c) + (t5 + t8)*m(2,c) ) + m(2,c);
00336       }
00337       return o;
00338     }
00339     
00340     //! multiply 3-element vector by quaternion
00341     Column<3,R> operator*(const Column<3,R> v) const {
00342       const R t2 =   w*x;
00343       const R t3 =   w*y;
00344       const R t4 =   w*z;
00345       const R t5 =  -x*x;
00346       const R t6 =   x*y;
00347       const R t7 =   x*z;
00348       const R t8 =  -y*y;
00349       const R t9 =   y*z;
00350       const R t10 = -z*z;
00351       Column<3,R> o=fmat_internal::NoInit();
00352       o[0] = 2*( (t8 + t10)*v[0] + (t6 -  t4)*v[1] + (t3 + t7)*v[2] ) + v[0];
00353       o[1] = 2*( (t4 +  t6)*v[0] + (t5 + t10)*v[1] + (t9 - t2)*v[2] ) + v[1];
00354       o[2] = 2*( (t7 -  t3)*v[0] + (t2 +  t9)*v[1] + (t5 + t8)*v[2] ) + v[2];
00355       return o;
00356     }
00357     
00358     //! returns sum of squares of components; should be "close" to 1, otherwise call normalize()
00359     R sumSq() const { return w*w + x*x + y*y + z*z; }
00360     
00361     //! returns magnitude of quaternion; should be "close" to 1, otherwise call normalize()
00362     R norm() const { return std::sqrt(w*w + x*x + y*y + z*z); }
00363     
00364     //! Re-normalize the quaternion magnitude to 1 (or 0 if norm is already invalid) and positive W, returns the previous magnitude
00365     R normalize() {
00366       R n = sumSq();
00367       if(n < std::numeric_limits<R>::epsilon()) {
00368         w=x=y=z=0; // can't normalize a zero vector, just clean it up
00369         return 0;
00370       }
00371       if(n<=1 && 1-n < std::numeric_limits<R>::epsilon()) {
00372         // already normalized, just fix w if needed
00373         if(w<0) {
00374           w=-w;
00375           x=-x;
00376           y=-y;
00377           z=-z;
00378         }
00379         return 1;
00380       }
00381       R sc = (w<0?-1:1) / std::sqrt(n);
00382       w*=sc;
00383       x*=sc;
00384       y*=sc;
00385       z*=sc;
00386       return n;
00387     }
00388     
00389     friend inline std::ostream& operator<<(std::ostream& os, const QuaternionT<R>& q) { return os << fmat::SubVector<4,const R>((const R*)&q.w); }
00390     
00391   protected:
00392     R w, x, y, z;
00393     
00394     //! no-op constructor for functions which fill in results with additional computation
00395     QuaternionT(const fmat_internal::NoInit&) : w(), x(), y(), z() {}
00396   };
00397   typedef QuaternionT<> Quaternion;
00398   
00399   //! returns the rotation needed to transform @a p into @a q (this is the core of a 'slerp' implementation)
00400   template<class Ra, class Rb>
00401   QuaternionT<typename fmat_internal::promotion_trait<Ra,Rb>::type>
00402   crossProduct(const QuaternionT<Ra>& p, const QuaternionT<Rb>& q) { return q * p.inverse(); }
00403   
00404   // forward to the specialized Quaternion invert
00405   template<typename R> QuaternionT<R> invert(const QuaternionT<R>& q) { return q.inverse(); }
00406   
00407   
00408   
00409   // ************************* Transform ******************** //
00410   
00411   namespace fmat_internal {
00412     //! Expanded calculations to facilitate optimized compilation of these operations for TransformT
00413     template<typename R1, typename R2>
00414     struct transformOps {
00415       //! The output type for these operations, e.g. float * double -> double
00416       typedef typename fmat_internal::promotion_trait<R1,R2>::type out_t;
00417       //! Transform a length-3 column
00418       template<typename V>
00419       inline static Column<3,out_t> multiply3(const TransformT<R1>& tr, const V& x) {
00420         Column<3,out_t> ans=fmat_internal::NoInit();
00421         const R1 *a = &tr(0,0);
00422         const R2 *b = &x[0];
00423         ans[0] = a[0+3*0]*b[0] + a[0+3*1]*b[1] + a[0+3*2]*b[2] + a[0+3*3];
00424         ans[1] = a[1+3*0]*b[0] + a[1+3*1]*b[1] + a[1+3*2]*b[2] + a[1+3*3];
00425         ans[2] = a[2+3*0]*b[0] + a[2+3*1]*b[1] + a[2+3*2]*b[2] + a[2+3*3];
00426         return ans;
00427       }
00428       //! Transform a length-4 column (homogenous coordinate)
00429       template<typename V>
00430       inline static Column<4,out_t> multiply4(const TransformT<R1>& tr, const V& x) {
00431         Column<4,out_t> ans=fmat_internal::NoInit();
00432         const R1 *a = &tr(0,0);
00433         const R2 *b = &x[0];
00434         ans[0] = a[0+3*0]*b[0] + a[0+3*1]*b[1] + a[0+3*2]*b[2] + a[0+3*3]*b[3];
00435         ans[1] = a[1+3*0]*b[0] + a[1+3*1]*b[1] + a[1+3*2]*b[2] + a[1+3*3]*b[3];
00436         ans[2] = a[2+3*0]*b[0] + a[2+3*1]*b[1] + a[2+3*2]*b[2] + a[2+3*3]*b[3];
00437         ans[3] = b[3];
00438         return ans;
00439       }
00440       //! Concatenate transforms
00441       inline static void multiplyT(const R1* a, const R2* b, out_t* ans) {
00442         ans[0+3*0] = a[0+3*0]*b[0+3*0] + a[0+3*1]*b[1+3*0] + a[0+3*2]*b[2+3*0];
00443         ans[1+3*0] = a[1+3*0]*b[0+3*0] + a[1+3*1]*b[1+3*0] + a[1+3*2]*b[2+3*0];
00444         ans[2+3*0] = a[2+3*0]*b[0+3*0] + a[2+3*1]*b[1+3*0] + a[2+3*2]*b[2+3*0];
00445         ans[0+3*1] = a[0+3*0]*b[0+3*1] + a[0+3*1]*b[1+3*1] + a[0+3*2]*b[2+3*1];
00446         ans[1+3*1] = a[1+3*0]*b[0+3*1] + a[1+3*1]*b[1+3*1] + a[1+3*2]*b[2+3*1];
00447         ans[2+3*1] = a[2+3*0]*b[0+3*1] + a[2+3*1]*b[1+3*1] + a[2+3*2]*b[2+3*1];
00448         ans[0+3*2] = a[0+3*0]*b[0+3*2] + a[0+3*1]*b[1+3*2] + a[0+3*2]*b[2+3*2];
00449         ans[1+3*2] = a[1+3*0]*b[0+3*2] + a[1+3*1]*b[1+3*2] + a[1+3*2]*b[2+3*2];
00450         ans[2+3*2] = a[2+3*0]*b[0+3*2] + a[2+3*1]*b[1+3*2] + a[2+3*2]*b[2+3*2];
00451         ans[0+3*3] = a[0+3*0]*b[0+3*3] + a[0+3*1]*b[1+3*3] + a[0+3*2]*b[2+3*3] + a[0+3*3];
00452         ans[1+3*3] = a[1+3*0]*b[0+3*3] + a[1+3*1]*b[1+3*3] + a[1+3*2]*b[2+3*3] + a[1+3*3];
00453         ans[2+3*3] = a[2+3*0]*b[0+3*3] + a[2+3*1]*b[1+3*3] + a[2+3*2]*b[2+3*3] + a[2+3*3];
00454       }
00455     };
00456   }
00457   
00458   
00459   //! Efficient computation of affine transform operations
00460   template<typename R=fmatReal>
00461   class TransformT : public Matrix<3,4,R> {
00462   public:
00463     enum {
00464       HEIGHT=3, H=3,
00465       WIDTH=4, W=4,
00466       CAP=12
00467     };
00468     typedef R storage_t;
00469     typedef typename fmat_internal::unconst<R>::type out_t;
00470     
00471     TransformT(const fmat_internal::NoInit& noinit) : Matrix<H,W,R>(noinit) {}
00472     
00473     TransformT() : Matrix<H,W,R>(Matrix<H,W,R>::identity()) { }
00474     explicit TransformT(const R* x, size_t colStride=H) : Matrix<H,W,R>(x,colStride) {}
00475     TransformT(const SubMatrix<H,W,R>& x) : Matrix<H,W,R>(x) {}
00476     TransformT(const Matrix<H,W,R>& x) : Matrix<H,W,R>(x) {}
00477     template<typename Rot, typename Pt> TransformT(const Rot& r, const Pt& p) : Matrix<H,W,R>(fmat_internal::NoInit()) { rotation()=r; translation()=p; }
00478     template<size_t SH, size_t SW> explicit TransformT(const SubMatrix<SH,SW,R>& x) : Matrix<H,W,R>(x) {}
00479     template<size_t SH, size_t SW> explicit TransformT(const SubMatrix<SH,SW,R>& x, size_t rowOff, size_t colOff) : Matrix<H,W,R>(x,rowOff,colOff) {}
00480     template<size_t SH, size_t SW> explicit TransformT(const Matrix<SH,SW,R>& x) : Matrix<H,W,R>(x) {}
00481     template<size_t SH, size_t SW> explicit TransformT(const Matrix<SH,SW,R>& x, size_t rowOff, size_t colOff) : Matrix<H,W,R>(x,rowOff,colOff) {}
00482     
00483     SubMatrix<3,3,R> rotation() { return SubMatrix<3,3,R>(data); }
00484     SubMatrix<3,3,const R> rotation() const { return SubMatrix<3,3,const R>(data); }
00485     SubVector<3,R> translation() { return Matrix<H,W,R>::column(3); }
00486     SubVector<3,const R> translation() const { return Matrix<H,W,R>::column(3); }
00487     
00488     //! generate transform representing rotation of @a rad radians about X axis
00489     static TransformT<R> aboutX(R rad) {
00490       TransformT<R> ans;
00491       ans(1,1) = ans(2,2) = std::cos(rad);
00492       ans(1,2) = -(ans(2,1) = std::sin(rad));
00493       return ans;
00494     }
00495     //! generate transform representing rotation of @a rad radians about Y axis
00496     static TransformT<R> aboutY(R rad) {
00497       TransformT<R> ans;
00498       ans(0,0) = ans(2,2) = std::cos(rad);
00499       ans(2,0) = -(ans(0,2) = std::sin(rad));
00500       return ans;
00501     }
00502     //! generate transform representing rotation of @a rad radians about Z axis
00503     static TransformT<R> aboutZ(R rad) {
00504       TransformT<R> ans;
00505       ans(0,0) = ans(1,1) = std::cos(rad);
00506       ans(0,1) = -(ans(1,0) = std::sin(rad));
00507       return ans;
00508     }
00509     //! generate transform representing a translation of @a x
00510     template<class V>
00511     static TransformT<R> offset(const V& x) { TransformT<R> ans; ans.translation() = x; return ans; }
00512         
00513     static const TransformT& identity() { return IDENTITY; }
00514     static const TransformT IDENTITY; //!< identity instance
00515     
00516     //! returns the inverse transform, allowing for affine transformations (however, cannot invert 0 or inf scaling!)
00517     inline TransformT inverse() const ATTR_must_check {
00518       TransformT ans=fmat_internal::NoInit();
00519       ans.rotation() = fmat::invert(rotation());
00520       ans.translation() = -ans.rotation() * translation();
00521       return ans;
00522     }
00523 
00524     //! returns the inverse transform, assuming a rigid transform (zero scale and skew) for faster computation
00525     inline TransformT rigidInverse() const ATTR_must_check {
00526       TransformT ans=fmat_internal::NoInit();
00527       R * t2dat = &ans(0,0);
00528       t2dat[0] = data[0];
00529       t2dat[1] = data[3];
00530       t2dat[2] = data[6];
00531       t2dat[3] = data[1];
00532       t2dat[4] = data[4];
00533       t2dat[5] = data[7];
00534       t2dat[6] = data[2];
00535       t2dat[7] = data[5];
00536       t2dat[8] = data[8];
00537       t2dat[9]  = -(data[0]*data[9] + data[1]*data[10] + data[2]*data[11]);
00538       t2dat[10] = -(data[3]*data[9] + data[4]*data[10] + data[5]*data[11]);
00539       t2dat[11] = -(data[6]*data[9] + data[7]*data[10] + data[8]*data[11]);
00540       return ans;
00541     }
00542     
00543     template<typename R2>
00544     inline Column<3,typename fmat_internal::promotion_trait<R,R2>::type>
00545     operator*(const Column<3,R2>& x) const {
00546       return fmat_internal::transformOps<R,R2>::multiply3(*this,x);
00547     }
00548     
00549     template<typename R2>
00550     inline Column<3,typename fmat_internal::promotion_trait<R,R2>::type>
00551     operator*(const SubVector<3,R2>& x) const {
00552       return fmat_internal::transformOps<R,R2>::multiply3(*this,x);
00553     }
00554     
00555     template<size_t N, typename R2>
00556     inline Matrix<3,N,typename fmat_internal::promotion_trait<R,R2>::type>
00557     operator*(const Matrix<3,N,R2>& x) const {
00558       Matrix<3,N,typename fmat_internal::promotion_trait<R,R2>::type> ans = fmat_internal::NoInit();
00559       for(size_t i=0; i<N; ++i)
00560         ans.column(i) = fmat_internal::transformOps<R,R2>::multiply3(*this,x.column(i));
00561       return ans;
00562     }
00563     
00564     template<typename R2>
00565     inline Column<4,typename fmat_internal::promotion_trait<R,R2>::type>
00566     operator*(const Column<4,R2>& x) const {
00567       return fmat_internal::transformOps<R,R2>::multiply4(*this,x);
00568     }
00569     
00570     template<typename R2>
00571     inline Column<4,typename fmat_internal::promotion_trait<R,R2>::type>
00572     operator*(const SubVector<4,R2>& x) const {
00573       return fmat_internal::transformOps<R,R2>::multiply4(*this,x);
00574     }
00575     
00576     template<size_t N, typename R2>
00577     inline Matrix<4,N,typename fmat_internal::promotion_trait<R,R2>::type>
00578     operator*(const Matrix<4,N,R2>& x) const {
00579       Matrix<4,N,typename fmat_internal::promotion_trait<R,R2>::type> ans = fmat_internal::NoInit();
00580       for(size_t i=0; i<N; ++i)
00581         ans.column(i) = fmat_internal::transformOps<R,R2>::multiply4(*this,x.column(i));
00582       return ans;
00583     }
00584     
00585     template<typename R2>
00586     inline TransformT<typename fmat_internal::promotion_trait<R,R2>::type>
00587     operator*(const TransformT<R2>& tb) const {
00588       TransformT<typename fmat_internal::promotion_trait<R,R2>::type> mans=fmat_internal::NoInit();
00589       fmat_internal::transformOps<R,R2>::multiplyT(data,&tb(0,0),&mans(0,0));
00590       return mans;
00591     }
00592     
00593     template<typename R2>
00594     TransformT&
00595     operator*=(const TransformT<R2>& t) {
00596       fmat_internal::transformOps<R,R2>::multiplyT(&TransformT(*this)(0,0),&t(0,0),data);
00597       return *this;
00598     }
00599     using Matrix<H,W,R>::operator=;
00600     using Matrix<H,W,R>::operator*=;
00601     using Matrix<H,W,R>::operator/=;
00602     
00603   protected:
00604     using Matrix<H,W,R>::data;
00605   };
00606   typedef TransformT<> Transform;
00607   
00608   //! invert the matrix, taking advantage of known structure:
00609   template<typename R>
00610   inline Matrix<4,4,R> invertTransform(const Matrix<4,4,R>& t) {
00611     fmat::Column<4> p = -t.column(3);
00612     const R * tdat = &t(0,0);
00613     /*R t2dat[16] = {
00614      tdat[0], tdat[4], tdat[8], 0,
00615      tdat[1], tdat[5], tdat[9], 0,
00616      tdat[2], tdat[6], tdat[10], 0,
00617      fmat::dotProduct(t.column(0),p), fmat::dotProduct(t.column(1),p), fmat::dotProduct(t.column(2),p), tdat[15]
00618      };
00619      return Matrix<4,4,R>(t2dat);*/
00620     Matrix<4,4,R> t2(t);
00621     R * t2dat = &t2(0,0);
00622     t2dat[1] = tdat[4];
00623     t2dat[2] = tdat[8];
00624     t2dat[4] = tdat[1];
00625     t2dat[6] = tdat[9];
00626     t2dat[8] = tdat[2];
00627     t2dat[9] = tdat[6];
00628     t2dat[12] = fmat::dotProduct(t.column(0),p);
00629     t2dat[13] = fmat::dotProduct(t.column(1),p);
00630     t2dat[14] = fmat::dotProduct(t.column(2),p);
00631     return t2;
00632   }
00633   
00634   // forward to the specialized Transform invert
00635   template<typename R>
00636   TransformT<R>
00637   invert(const TransformT<R>& m) { return m.inverse(); }
00638   
00639   //! Returns yaw-pitch-roll aka heading-elevation-bank conversion, where roll-pitch-yaw correspond to compounding rotations about the global x, y, and z axis respectively (in that order)
00640   /*! From the "driver's seat" positive heading is turning to the left (z is up, using right hand rule, not compass heading),
00641    *  positive pitch is looking down, and positive roll is spinning clockwise.  Within this frame-oriented view, we apply
00642    *  rotation axes in the 'reverse' order: first z, then y, then x.
00643    *
00644    *  You can reconstruct a Quaternion from these values by: q · v = aboutZ(yaw) * aboutY(pitch) * aboutX(roll) · v
00645    *  Note we right-multiply v to apply the rotation, so x is applied to an incoming vector first, then y, then z.
00646    *
00647    *  With thanks to http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
00648    *  (note that page uses different axis mapping however!) */
00649   template<typename R>
00650   Column<3,R>
00651   ypr(const QuaternionT<R>& q) { return q.ypr(); }
00652   
00653   //! Returns yaw-pitch-roll aka heading-elevation-bank conversion, where roll-pitch-yaw correspond to compounding rotations about the global x, y, and z axis respectively (in that order)
00654   /*! From the "driver's seat" positive heading is turning to the left (z is up, using right hand rule, not compass heading),
00655    *  positive pitch is looking down, and positive roll is spinning clockwise.  Within this frame-oriented view, we apply
00656    *  rotation axes in the 'reverse' order: first z, then y, then x.
00657    *
00658    *  You can reconstruct a rotation from these values by: q · v = rotationZ(yaw) * rotationY(pitch) * rotationX(roll) · v
00659    *  Note we right-multiply v to apply the rotation, so x is applied to an incoming vector first, then y, then z. */
00660   template<typename R>
00661   Column<3,R>
00662   ypr(const TransformT<R>& m) { return ypr(m.rotation()); }
00663   
00664   //! Returns yaw-pitch-roll aka heading-elevation-bank conversion, where roll-pitch-yaw correspond to compounding rotations about the global x, y, and z axis respectively (in that order)
00665   /*! From the "driver's seat" positive heading is turning to the left (z is up, using right hand rule, not compass heading),
00666    *  positive pitch is looking down, and positive roll is spinning clockwise.  Within this frame-oriented view, we apply
00667    *  rotation axes in the 'reverse' order: first z, then y, then x.
00668    *
00669    *  You can reconstruct a rotation from these values by: q · v = rotationZ(yaw) * rotationY(pitch) * rotationX(roll) · v
00670    *  Note we right-multiply v to apply the rotation, so x is applied to an incoming vector first, then y, then z. */
00671   template<template<size_t,size_t,typename R> class M, typename R>
00672   Column<3,typename fmat_internal::unconst<R>::type>
00673   ypr(const M<3,3,R>& m) {
00674     typedef typename fmat_internal::unconst<R>::type out_t;
00675     if (m(2,0) > 0.9998) { // singularity at north pole
00676       return packT<out_t>(std::atan2(-m(0,1),m(1,1)), -M_PI_2, 0);
00677     }
00678     if (m(2,0) < -0.9998) { // singularity at south pole
00679       return packT<out_t>(std::atan2(-m(0,1),m(1,1)), M_PI_2, 0);
00680     }
00681     return packT<out_t>(
00682       std::atan2(m(1,0),m(0,0)),
00683       std::asin(-m(2,0)),
00684       std::atan2(m(2,1),m(2,2))
00685     );
00686   }
00687   
00688   //! Returns the scaling factors for d1 and d2 through p1 and p2 respectively to reach common intersection
00689   /*! May return infinity if d1 and d2 are parallel (including collinear).  */
00690   template<typename R>
00691   Column<2,R>
00692   segmentIntersection(const Column<2,R>& p1, const Column<2,R>& d1, const Column<2,R>& p2, const Column<2,R>& d2) {
00693     fmat::Matrix<2,2,R> dm = fmat_internal::NoInit();
00694     dm.column(0) = d1;
00695     dm.column(1) = -d2;
00696     try {
00697       dm = invert(dm);
00698     } catch(...) {
00699       // no solution, parallel lines, return infinity
00700       return Column<2,R>(std::numeric_limits<fmat::fmatReal>::infinity());
00701     }
00702     Column<2,R> o = p2-p1;
00703     return dm * o;
00704   }
00705   
00706   //! Returns the scaling factor of d1 from p1 to reach intersection of d2 through p2
00707   /*! May return infinity if d1 and d2 are parallel (including collinear).  */
00708   template<typename R>
00709   R
00710   rayIntersection(const Column<2,R>& p1, const Column<2,R>& d1, const Column<2,R>& p2, const Column<2,R>& d2) {
00711     return segmentIntersection(p1, d1, p2, d2)[0];
00712   }
00713   
00714   //! Returns the point of intersection of d1 through p1 and d2 through p2
00715   /*! May return point at infinity if d1 and d2 are parallel (including collinear),
00716     *  such a point will be in the correct quadrant. */
00717   template<typename R>
00718   Column<2,R>
00719   lineIntersection(const Column<2,R>& p1, const Column<2,R>& d1, const Column<2,R>& p2, const Column<2,R>& d2) {
00720     return d1 * rayIntersection(p1,d1,p2,d2) + p1;
00721   }
00722   
00723   //! Returns the orthogonal left vector (rotate by 90°)
00724   template<template<size_t,typename R> class V, typename R>
00725   Column<2,typename V<2,R>::out_t>
00726   normalLeft(const V<2,R>& v) { return fmat::pack(-v[1],v[0]); }
00727 
00728   //! Returns the orthogonal right vector (rotate by -90°)
00729   template<template<size_t,typename R> class V, typename R>
00730   Column<2,typename V<2,R>::out_t>
00731   normalRight(const V<2,R>& v) { return fmat::pack(v[1],-v[0]); }
00732 }
00733 
00734 /*! @file
00735  * @brief Provides data structures and algorithms for spatial operations
00736  * @author Ethan Tira-Thompson (ejt) (Creator)
00737  */
00738 
00739 #endif

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:40 2012 by Doxygen 1.6.3