quaternion_rotation.h

Go to the documentation of this file.
00001 /* -*- C++ -*- ------------------------------------------------------------
00002  
00003 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/
00004 
00005 The Configurable Math Library (CML) is distributed under the terms of the
00006 Boost Software License, v1.0 (see cml/LICENSE for details).
00007 
00008  *-----------------------------------------------------------------------*/
00013 #ifndef quaternion_rotation_h
00014 #define quaternion_rotation_h
00015 
00016 #include <cml/mathlib/checking.h>
00017 
00018 /* Functions related to quaternion rotations.
00019  *
00020  * Note: A number of these functions simply wrap calls to the corresponding
00021  * matrix functions. Some of them (the 'aim-at' and 'align' functions in
00022  * particular) might be considered a bit superfluous, since the resulting
00023  * quaternion will most likely be converted to a matrix at some point anyway.
00024  * However, they're included here for completeness, and for convenience in
00025  * cases where a quaternion is being used as the primary rotation
00026  * representation.
00027 */
00028 
00029 namespace cml {
00030 
00032 // Rotation about world axes
00034 
00036 template < class E, class A, class O, class C > void
00037 quaternion_rotation_world_axis(quaternion<E,A,O,C>& q, size_t axis, E angle)
00038 {
00039     typedef quaternion<E,A,O,C> quaternion_type;
00040     typedef typename quaternion_type::value_type value_type;
00041     typedef typename quaternion_type::order_type order_type;
00042 
00043     /* Checking */
00044     detail::CheckIndex3(axis);
00045 
00046     q.identity();
00047 
00048     const size_t W = order_type::W;
00049     const size_t I = order_type::X + axis;
00050     
00051     angle *= value_type(.5);
00052     q[I] = std::sin(angle);
00053     q[W] = std::cos(angle);
00054 }
00055 
00057 template < class E, class A, class O, class C > void
00058 quaternion_rotation_world_x(quaternion<E,A,O,C>& q, E angle) {
00059     quaternion_rotation_world_axis(q,0,angle);
00060 }
00061 
00063 template < class E, class A, class O, class C > void
00064 quaternion_rotation_world_y(quaternion<E,A,O,C>& q, E angle) {
00065     quaternion_rotation_world_axis(q,1,angle);
00066 }
00067 
00069 template < class E, class A, class O, class C > void
00070 quaternion_rotation_world_z(quaternion<E,A,O,C>& q, E angle) {
00071     quaternion_rotation_world_axis(q,2,angle);
00072 }
00073 
00075 // Rotation from an axis-angle pair
00077 
00079 template < class E, class A, class O, class C, class VecT > void
00080 quaternion_rotation_axis_angle(
00081     quaternion<E,A,O,C>& q, const VecT& axis, E angle)
00082 {
00083     typedef quaternion<E,A,O,C> quaternion_type;
00084     typedef typename quaternion_type::value_type value_type;
00085     typedef typename quaternion_type::order_type order_type;
00086     
00087     /* Checking */
00088     detail::CheckVec3(axis);
00089 
00090     enum {
00091         W = order_type::W,
00092         X = order_type::X,
00093         Y = order_type::Y,
00094         Z = order_type::Z
00095     };
00096     
00097     angle *= value_type(.5);
00098     
00099     /* @todo: If and when we have a set() function that takes a vector and a
00100      * scalar, this can be written as:
00101      *
00102      * q.set(std::cos(angle), axis * std::sin(angle));
00103      *
00104      * In which case the enum will also not be necessary.
00105      */
00106     
00107     q[W] = std::cos(angle);
00108     value_type s = std::sin(angle);
00109     q[X] = axis[0] * s;
00110     q[Y] = axis[1] * s;
00111     q[Z] = axis[2] * s;
00112 }
00113 
00115 // Rotation from a matrix
00117 
00119 template < class E, class A, class O, class C, class MatT > void
00120 quaternion_rotation_matrix(quaternion<E,A,O,C>& q, const MatT& m)
00121 {
00122     typedef quaternion<E,A,O,C> quaternion_type;
00123     typedef typename quaternion_type::value_type value_type;
00124     typedef typename quaternion_type::order_type order_type;
00125 
00126     /* Checking */
00127     detail::CheckMatLinear3D(m);
00128 
00129     enum {
00130         W = order_type::W,
00131         X = order_type::X,
00132         Y = order_type::Y,
00133         Z = order_type::Z
00134     };
00135 
00136     value_type tr = trace_3x3(m);
00137     if (tr >= value_type(0)) {
00138         q[W] = std::sqrt(tr + value_type(1)) * value_type(.5);
00139         value_type s = value_type(.25) / q[W];
00140         q[X] = (m.basis_element(1,2) - m.basis_element(2,1)) * s;
00141         q[Y] = (m.basis_element(2,0) - m.basis_element(0,2)) * s;
00142         q[Z] = (m.basis_element(0,1) - m.basis_element(1,0)) * s;
00143     } else {
00144         size_t largest_diagonal_element =
00145             index_of_max(
00146                 m.basis_element(0,0),
00147                 m.basis_element(1,1),
00148                 m.basis_element(2,2)
00149             );
00150         size_t i, j, k;
00151         cyclic_permutation(largest_diagonal_element, i, j, k);
00152         const size_t I = X + i;
00153         const size_t J = X + j;
00154         const size_t K = X + k;
00155         q[I] =
00156             std::sqrt(
00157                 m.basis_element(i,i) -
00158                 m.basis_element(j,j) -
00159                 m.basis_element(k,k) +
00160                 value_type(1)
00161             ) * value_type(.5);
00162         value_type s = value_type(.25) / q[I];
00163         q[J] = (m.basis_element(i,j) + m.basis_element(j,i)) * s;
00164         q[K] = (m.basis_element(i,k) + m.basis_element(k,i)) * s;
00165         q[W] = (m.basis_element(j,k) - m.basis_element(k,j)) * s;
00166     }
00167 }
00168 
00170 // Rotation from Euler angles
00172 
00174 template < class E, class A, class O, class C > void
00175 quaternion_rotation_euler(
00176     quaternion<E,A,O,C>& q, E angle_0, E angle_1, E angle_2,
00177     EulerOrder order)
00178 {
00179     typedef quaternion<E,A,O,C> quaternion_type;
00180     typedef typename quaternion_type::value_type value_type;
00181     typedef typename quaternion_type::order_type order_type;
00182 
00183     size_t i, j, k;
00184     bool odd, repeat;
00185     detail::unpack_euler_order(order, i, j, k, odd, repeat);
00186     
00187     const size_t W = order_type::W;
00188     const size_t I = order_type::X + i;
00189     const size_t J = order_type::X + j;
00190     const size_t K = order_type::X + k;
00191 
00192     if (odd) {
00193         angle_1 = -angle_1;
00194     }
00195 
00196     angle_0 *= value_type(.5);
00197     angle_1 *= value_type(.5);
00198     angle_2 *= value_type(.5);
00199     
00200     value_type s0 = std::sin(angle_0);
00201     value_type c0 = std::cos(angle_0);
00202     value_type s1 = std::sin(angle_1);
00203     value_type c1 = std::cos(angle_1);
00204     value_type s2 = std::sin(angle_2);
00205     value_type c2 = std::cos(angle_2);
00206     
00207     value_type s0s2 = s0 * s2;
00208     value_type s0c2 = s0 * c2;
00209     value_type c0s2 = c0 * s2;
00210     value_type c0c2 = c0 * c2;
00211 
00212     if (repeat) {
00213         q[I] = c1 * (c0s2 + s0c2);
00214         q[J] = s1 * (c0c2 + s0s2);
00215         q[K] = s1 * (c0s2 - s0c2);
00216         q[W] = c1 * (c0c2 - s0s2);
00217     } else {
00218         q[I] = c1 * s0c2 - s1 * c0s2;
00219         q[J] = c1 * s0s2 + s1 * c0c2;
00220         q[K] = c1 * c0s2 - s1 * s0c2;
00221         q[W] = c1 * c0c2 + s1 * s0s2;
00222     }
00223     if (odd) {
00224         q[J] = -q[J];
00225     }
00226 }
00227 
00229 // Rotation to align with a vector, multiple vectors, or the view plane
00231 
00233 template < typename E,class A,class O,class C,class VecT_1,class VecT_2 > void
00234 quaternion_rotation_align(
00235     quaternion<E,A,O,C>& q,
00236     const VecT_1& align,
00237     const VecT_2& reference,
00238     bool normalize = true,
00239     AxisOrder order = axis_order_zyx)
00240 {
00241     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00242     
00243     matrix_type m;
00244     matrix_rotation_align(m,align,reference,normalize,order);
00245     quaternion_rotation_matrix(q,m);
00246 }
00247 
00249 template < typename E, class A, class O, class C, class VecT > void
00250 quaternion_rotation_align(quaternion<E,A,O,C>& q, const VecT& align,
00251     bool normalize = true, AxisOrder order = axis_order_zyx)
00252 {
00253     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00254     
00255     matrix_type m;
00256     matrix_rotation_align(m,align,normalize,order);
00257     quaternion_rotation_matrix(q,m);
00258 }
00259 
00261 template < typename E,class A,class O,class C,class VecT_1,class VecT_2 > void
00262 quaternion_rotation_align_axial(quaternion<E,A,O,C>& q, const VecT_1& align,
00263     const VecT_2& axis, bool normalize = true,
00264     AxisOrder order = axis_order_zyx)
00265 {
00266     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00267     
00268     matrix_type m;
00269     matrix_rotation_align_axial(m,align,axis,normalize,order);
00270     quaternion_rotation_matrix(q,m);
00271 }
00272 
00274 template < typename E, class A, class O, class C, class MatT > void
00275 quaternion_rotation_align_viewplane(
00276     quaternion<E,A,O,C>& q,
00277     const MatT& view_matrix,
00278     Handedness handedness,
00279     AxisOrder order = axis_order_zyx)
00280 {
00281     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00282     
00283     matrix_type m;
00284     matrix_rotation_align_viewplane(m,view_matrix,handedness,order);
00285     quaternion_rotation_matrix(q,m);
00286 }
00287 
00289 template < typename E, class A, class O, class C, class MatT > void
00290 quaternion_rotation_align_viewplane_LH(
00291     quaternion<E,A,O,C>& q,
00292     const MatT& view_matrix,
00293     AxisOrder order = axis_order_zyx)
00294 {
00295     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00296     
00297     matrix_type m;
00298     matrix_rotation_align_viewplane_LH(m,view_matrix,order);
00299     quaternion_rotation_matrix(q,m);
00300 }
00301 
00303 template < typename E, class A, class O, class C, class MatT > void
00304 quaternion_rotation_align_viewplane_RH(
00305     quaternion<E,A,O,C>& q,
00306     const MatT& view_matrix,
00307     AxisOrder order = axis_order_zyx)
00308 {
00309     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00310     
00311     matrix_type m;
00312     matrix_rotation_align_viewplane_RH(m,view_matrix,order);
00313     quaternion_rotation_matrix(q,m);
00314 }
00315 
00317 // Rotation to aim at a target
00319 
00321 template < typename E, class A, class O, class C,
00322     class VecT_1, class VecT_2, class VecT_3 > void
00323 quaternion_rotation_aim_at(
00324     quaternion<E,A,O,C>& q,
00325     const VecT_1& pos,
00326     const VecT_2& target,
00327     const VecT_3& reference,
00328     AxisOrder order = axis_order_zyx)
00329 {
00330     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00331     
00332     matrix_type m;
00333     matrix_rotation_aim_at(m,pos,target,reference,order);
00334     quaternion_rotation_matrix(q,m);
00335 }
00336 
00338 template < typename E, class A, class O, class C,
00339     class VecT_1, class VecT_2 > void
00340 quaternion_rotation_aim_at(
00341     quaternion<E,A,O,C>& q,
00342     const VecT_1& pos,
00343     const VecT_2& target,
00344     AxisOrder order = axis_order_zyx)
00345 {
00346     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00347     
00348     matrix_type m;
00349     matrix_rotation_aim_at(m,pos,target,order);
00350     quaternion_rotation_matrix(q,m);
00351 }
00352 
00354 template < typename E, class A, class O, class C,
00355     class VecT_1, class VecT_2, class VecT_3 > void
00356 quaternion_rotation_aim_at_axial(
00357     quaternion<E,A,O,C>& q,
00358     const VecT_1& pos,
00359     const VecT_2& target,
00360     const VecT_3& axis,
00361     AxisOrder order = axis_order_zyx)
00362 {
00363     typedef matrix< E,fixed<3,3>,row_basis,row_major > matrix_type;
00364     
00365     matrix_type m;
00366     matrix_rotation_aim_at_axial(m,pos,target,axis,order);
00367     quaternion_rotation_matrix(q,m);
00368 }
00369 
00371 // Relative rotation about world axes
00373 
00374 /* Rotate a quaternion about the given world axis */
00375 template < class E, class A, class O, class C > void
00376 quaternion_rotate_about_world_axis(quaternion<E,A,O,C>& q,size_t axis,E angle)
00377 {
00378     typedef quaternion<E,A,O,C> quaternion_type;
00379     typedef typename quaternion_type::value_type value_type;
00380     typedef typename quaternion_type::order_type order_type;
00381 
00382     /* Checking */
00383     detail::CheckIndex3(axis);
00384 
00385     size_t i, j, k;
00386     cyclic_permutation(axis, i, j, k);
00387     
00388     const size_t W = order_type::W;
00389     const size_t I = order_type::X + i;
00390     const size_t J = order_type::X + j;
00391     const size_t K = order_type::X + k;
00392     
00393     angle *= value_type(.5);
00394     value_type s = value_type(std::sin(angle));
00395     value_type c = value_type(std::cos(angle));
00396 
00397     quaternion_type result;
00398     result[I] = c * q[I] + s * q[W];
00399     result[J] = c * q[J] - s * q[K];
00400     result[K] = c * q[K] + s * q[J];
00401     result[W] = c * q[W] - s * q[I];
00402     q = result;
00403 }
00404 
00405 /* Rotate a quaternion about the world x axis */
00406 template < class E, class A, class O, class C > void
00407 quaternion_rotate_about_world_x(quaternion<E,A,O,C>& q, E angle) {
00408     quaternion_rotate_about_world_axis(q,0,angle);
00409 }
00410 
00411 /* Rotate a quaternion about the world y axis */
00412 template < class E, class A, class O, class C > void
00413 quaternion_rotate_about_world_y(quaternion<E,A,O,C>& q, E angle) {
00414     quaternion_rotate_about_world_axis(q,1,angle);
00415 }
00416 
00417 /* Rotate a quaternion about the world z axis */
00418 template < class E, class A, class O, class C > void
00419 quaternion_rotate_about_world_z(quaternion<E,A,O,C>& q, E angle) {
00420     quaternion_rotate_about_world_axis(q,2,angle);
00421 }
00422 
00424 // Relative rotation about local axes
00426 
00427 /* Rotate a quaternion about the given local axis */
00428 template < class E, class A, class O, class C > void
00429 quaternion_rotate_about_local_axis(quaternion<E,A,O,C>& q,size_t axis,E angle)
00430 {
00431     typedef quaternion<E,A,O,C> quaternion_type;
00432     typedef typename quaternion_type::value_type value_type;
00433     typedef typename quaternion_type::order_type order_type;
00434 
00435     /* Checking */
00436     detail::CheckIndex3(axis);
00437 
00438     size_t i, j, k;
00439     cyclic_permutation(axis, i, j, k);
00440     
00441     const size_t W = order_type::W;
00442     const size_t I = order_type::X + i;
00443     const size_t J = order_type::X + j;
00444     const size_t K = order_type::X + k;
00445     
00446     angle *= value_type(.5);
00447     value_type s = value_type(std::sin(angle));
00448     value_type c = value_type(std::cos(angle));
00449 
00450     quaternion_type result;
00451     result[I] = c * q[I] + s * q[W];
00452     result[J] = c * q[J] + s * q[K];
00453     result[K] = c * q[K] - s * q[J];
00454     result[W] = c * q[W] - s * q[I];
00455     q = result;
00456 }
00457 
00458 /* Rotate a quaternion about its local x axis */
00459 template < class E, class A, class O, class C > void
00460 quaternion_rotate_about_local_x(quaternion<E,A,O,C>& q, E angle) {
00461     quaternion_rotate_about_local_axis(q,0,angle);
00462 }
00463 
00464 /* Rotate a quaternion about its local y axis */
00465 template < class E, class A, class O, class C > void
00466 quaternion_rotate_about_local_y(quaternion<E,A,O,C>& q, E angle) {
00467     quaternion_rotate_about_local_axis(q,1,angle);
00468 }
00469 
00470 /* Rotate a quaternion about its local z axis */
00471 template < class E, class A, class O, class C > void
00472 quaternion_rotate_about_local_z(quaternion<E,A,O,C>& q, E angle) {
00473     quaternion_rotate_about_local_axis(q,2,angle);
00474 }
00475 
00477 // Rotation from vector to vector
00479 
00480 /* http://www.martinb.com/maths/algebra/vectors/angleBetween/index.htm. */
00481 
00483 template < class E,class A,class O,class C,class VecT_1,class VecT_2 > void
00484 quaternion_rotation_vec_to_vec(
00485     quaternion<E,A,O,C>& q,
00486     const VecT_1& v1,
00487     const VecT_2& v2,
00488     bool unit_length_vectors = false)
00489 {
00490     typedef quaternion<E,A,O,C> quaternion_type;
00491     typedef typename quaternion_type::value_type value_type;
00492     typedef vector< value_type, fixed<3> > vector_type;
00493     
00494     /* Checking handled by cross() */
00495 
00496     /* @todo: If at some point quaternion<> has a set() function that takes a
00497      * vector and a scalar, this can then be written as:
00498      *
00499      * if (...) {
00500      *     q.set(value_type(1)+dot(v1,v2), cross(v1,v2));
00501      * } else {
00502      *     q.set(std::sqrt(...)+dot(v1,v2), cross(v1,v2));
00503      * }
00504      */
00505      
00506     vector_type c = cross(v1,v2);
00507     if (unit_length_vectors) {
00508         q = quaternion_type(value_type(1) + dot(v1,v2), c.data());
00509     } else {
00510         q = quaternion_type(
00511             std::sqrt(v1.length_squared() * v2.length_squared()) + dot(v1,v2),
00512             c/*.data()*/
00513         );
00514     }
00515     q.normalize();
00516 }
00517 
00519 // Scale the angle of a rotation matrix
00521 
00522 template < typename E, class A, class O, class C > void
00523 quaternion_scale_angle(quaternion<E,A,O,C>& q, E t,
00524     E tolerance = epsilon<E>::placeholder())
00525 {
00526     typedef vector< E,fixed<3> > vector_type;
00527     typedef typename vector_type::value_type value_type;
00528     
00529     vector_type axis;
00530     value_type angle;
00531     quaternion_to_axis_angle(q, axis, angle, tolerance);
00532     quaternion_rotation_axis_angle(q, axis, angle * t);
00533 }
00534 
00536 // Support functions for uniform handling of pos- and neg-cross quaternions
00538 
00539 namespace detail {
00540 
00542 template < class QuatT_1, class QuatT_2 >
00543 typename et::QuaternionPromote2<QuatT_1,QuatT_2>::temporary_type
00544 quaternion_rotation_difference(
00545     const QuatT_1& q1, const QuatT_2& q2, positive_cross)
00546 {
00547     return q2 * conjugate(q1);
00548 }
00549 
00551 template < class QuatT_1, class QuatT_2 >
00552 typename et::QuaternionPromote2<QuatT_1,QuatT_2>::temporary_type
00553 quaternion_rotation_difference(
00554     const QuatT_1& q1, const QuatT_2& q2, negative_cross)
00555 {
00556     return conjugate(q1) * q2;
00557 }
00558 
00559 } // namespace detail
00560 
00562 // Quaternions rotation difference
00564 
00566 template < class QuatT_1, class QuatT_2 >
00567 typename et::QuaternionPromote2<QuatT_1,QuatT_2>::temporary_type
00568 quaternion_rotation_difference(const QuatT_1& q1, const QuatT_2& q2) {
00569     return detail::quaternion_rotation_difference(
00570         q1, q2, typename QuatT_1::cross_type());
00571 }
00572 
00574 // Conversions
00576 
00578 template < class QuatT, typename E, class A > void
00579 quaternion_to_axis_angle(
00580     const QuatT& q,
00581     vector<E,A>& axis,
00582     E& angle,
00583     E tolerance = epsilon<E>::placeholder())
00584 {
00585     typedef QuatT quaternion_type;
00586     typedef typename quaternion_type::value_type value_type;
00587     typedef typename quaternion_type::order_type order_type;
00588     
00589     /* Checking */
00590     detail::CheckQuat(q);
00591 
00592     axis = q.imaginary();
00593     value_type l = length(axis);
00594     if (l > tolerance) {
00595         axis /= l;
00596         angle = value_type(2) * std::atan2(l,q.real());
00597     } else {
00598         axis.zero();
00599         angle = value_type(0);
00600     }
00601 }
00602 
00615 template < class QuatT, typename Real > void
00616 quaternion_to_euler(
00617     const QuatT& q,
00618     Real& angle_0,
00619     Real& angle_1,
00620     Real& angle_2,
00621     EulerOrder order,
00622     Real tolerance = epsilon<Real>::placeholder())
00623 {
00624     typedef QuatT quaternion_type;
00625     typedef typename quaternion_type::value_type value_type;
00626     typedef matrix< value_type,fixed<3,3>,row_basis,row_major > matrix_type;
00627 
00628     matrix_type m;
00629     matrix_rotation_quaternion(m, q);
00630     matrix_to_euler(m, angle_0, angle_1, angle_2, order, tolerance);
00631 }
00632 
00633 } // namespace cml
00634 
00635 #endif

Generated on Sat Jul 18 19:35:22 2009 for CML 1.0 by  doxygen 1.5.9