qmath.c

Go to the documentation of this file.
00001 /*                         Q M A T H . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2004-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** \addtogroup mat */
00023 /*@{*/
00024 
00025 /** @file qmath.c
00026  * @brief
00027  *  Quaternion math routines.
00028  *
00029  *  Unit Quaternions:
00030  *      Q = [ r, a ]    where r = cos(theta/2) = rotation amount
00031  *                          |a| = sin(theta/2) = rotation axis
00032  *
00033  *      If a = 0 we have the reals; if one coord is zero we have
00034  *       complex numbers (2D rotations).
00035  *
00036  *  [r,a][s,b] = [rs - a.b, rb + sa + axb]
00037  *
00038  *       -1
00039  *  [r,a]   = (r - a) / (r^2 + a.a)
00040  *
00041  *  Powers of quaternions yield incremental rotations,
00042  *   e.g. Q^3 is rotated three times as far as Q.
00043  *
00044  *  Some operations on quaternions:
00045  *            -1
00046  *   [0,P'] = Q  [0,P]Q         Rotate a point P by quaternion Q
00047  *                     -1  a
00048  *   slerp(Q,R,a) = Q(Q  R)     Spherical linear interp: 0 < a < 1
00049  *
00050  *   bisect(P,Q) = (P + Q) / |P + Q|    Great circle bisector
00051  *
00052  *
00053  *  @author
00054  *      Phil Dykstra, 25 Sep 1985
00055  *
00056  *  Additions inspired by "Quaternion Calculus For Animation" by Ken Shoemake,
00057  *  SIGGRAPH '89 course notes for "Math for SIGGRAPH", May 1989.
00058  *
00059  *  @par Source -
00060  *      SECAD/VLD Computing Consortium, Bldg 394
00061  *@n    The U. S. Army Ballistic Research Laboratory
00062  *@n    Aberdeen Proving Ground, Maryland  21005-5066
00063  *
00064  */
00065 
00066 
00067 #ifndef lint
00068 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/libbn/qmath.c,v 14.11 2006/09/07 01:19:17 lbutler Exp $ (BRL)";
00069 #endif
00070 
00071 #include "common.h"
00072 
00073 
00074 
00075 #include <stdio.h>              /* DEBUG need stderr for now... */
00076 #include <math.h>
00077 #include "machine.h"
00078 #include "bu.h"
00079 #include "vmath.h"
00080 #include "bn.h"
00081 
00082 #ifdef M_PI
00083 #define PI M_PI
00084 #else
00085 #define PI      3.14159265358979323264
00086 #endif
00087 #define RTODEG  (180.0/PI)
00088 
00089 /**
00090  *                      Q U A T _ M A T 2 Q U A T
00091  *@brief
00092  *  Convert Matrix to Quaternion.
00093  */
00094 void
00095 quat_mat2quat(register fastf_t *quat, register const fastf_t *mat)
00096 {
00097         fastf_t         tr;
00098         FAST fastf_t    s;
00099 
00100 #define XX      0
00101 #define YY      5
00102 #define ZZ      10
00103 #define MMM(a,b)                mat[4*(a)+(b)]
00104 
00105         tr = mat[XX] + mat[YY] + mat[ZZ];
00106         if( tr > 0.0 )  {
00107                 s = sqrt( tr + 1.0 );
00108                 quat[W] = s * 0.5;
00109                 s = 0.5 / s;
00110                 quat[X] = ( mat[6] - mat[9] ) * s;
00111                 quat[Y] = ( mat[8] - mat[2] ) * s;
00112                 quat[Z] = ( mat[1] - mat[4] ) * s;
00113                 return;
00114         }
00115 
00116         /* Find dominant element of primary diagonal */
00117         if( mat[YY] > mat[XX] )  {
00118                 if( mat[ZZ] > mat[YY] )  {
00119                         s = sqrt( MMM(Z,Z) - (MMM(X,X)+MMM(Y,Y)) + 1.0 );
00120                         quat[Z] = s * 0.5;
00121                         s = 0.5 / s;
00122                         quat[W] = (MMM(X,Y) - MMM(Y,X)) * s;
00123                         quat[X] = (MMM(Z,X) + MMM(X,Z)) * s;
00124                         quat[Y] = (MMM(Z,Y) + MMM(Y,Z)) * s;
00125                 } else {
00126                         s = sqrt( MMM(Y,Y) - (MMM(Z,Z)+MMM(X,X)) + 1.0 );
00127                         quat[Y] = s * 0.5;
00128                         s = 0.5 / s;
00129                         quat[W] = (MMM(Z,X) - MMM(X,Z)) * s;
00130                         quat[Z] = (MMM(Y,Z) + MMM(Z,Y)) * s;
00131                         quat[X] = (MMM(Y,X) + MMM(X,Y)) * s;
00132                 }
00133         } else {
00134                 if( mat[ZZ] > mat[XX] )  {
00135                         s = sqrt( MMM(Z,Z) - (MMM(X,X)+MMM(Y,Y)) + 1.0 );
00136                         quat[Z] = s * 0.5;
00137                         s = 0.5 / s;
00138                         quat[W] = (MMM(X,Y) - MMM(Y,X)) * s;
00139                         quat[X] = (MMM(Z,X) + MMM(X,Z)) * s;
00140                         quat[Y] = (MMM(Z,Y) + MMM(Y,Z)) * s;
00141                 } else {
00142                         s = sqrt( MMM(X,X) - (MMM(Y,Y)+MMM(Z,Z)) + 1.0 );
00143                         quat[X] = s * 0.5;
00144                         s = 0.5 / s;
00145                         quat[W] = (MMM(Y,Z) - MMM(Z,Y)) * s;
00146                         quat[Y] = (MMM(X,Y) + MMM(Y,X)) * s;
00147                         quat[Z] = (MMM(X,Z) + MMM(Z,X)) * s;
00148                 }
00149         }
00150 #undef MMM
00151 }
00152 
00153 /**
00154  *                      Q U A T _ Q U A T 2 M A T
00155  *@brief
00156  *  Convert Quaternion to Matrix.
00157  *
00158  * NB: This only works for UNIT quaternions.  We may get imaginary results
00159  *   otherwise.  We should normalize first (still yields same rotation).
00160  */
00161 void
00162 quat_quat2mat(register fastf_t *mat, register const fastf_t *quat)
00163 {
00164         quat_t  q;
00165 
00166         QMOVE( q, quat );       /* private copy */
00167         QUNITIZE( q );
00168 
00169         mat[0] = 1.0 - 2.0*q[Y]*q[Y] - 2.0*q[Z]*q[Z];
00170         mat[1] = 2.0*q[X]*q[Y] + 2.0*q[W]*q[Z];
00171         mat[2] = 2.0*q[X]*q[Z] - 2.0*q[W]*q[Y];
00172         mat[3] = 0.0;
00173         mat[4] = 2.0*q[X]*q[Y] - 2.0*q[W]*q[Z];
00174         mat[5] = 1.0 - 2.0*q[X]*q[X] - 2.0*q[Z]*q[Z];
00175         mat[6] = 2.0*q[Y]*q[Z] + 2.0*q[W]*q[X];
00176         mat[7] = 0.0;
00177         mat[8] = 2.0*q[X]*q[Z] + 2.0*q[W]*q[Y];
00178         mat[9] = 2.0*q[Y]*q[Z] - 2.0*q[W]*q[X];
00179         mat[10] = 1.0 - 2.0*q[X]*q[X] - 2.0*q[Y]*q[Y];
00180         mat[11] = 0.0;
00181         mat[12] = 0.0;
00182         mat[13] = 0.0;
00183         mat[14] = 0.0;
00184         mat[15] = 1.0;
00185 }
00186 
00187 /**
00188  *                      Q U A T _ D I S T A N C E
00189  *@brief
00190  * Gives the euclidean distance between two quaternions.
00191  */
00192 double
00193 quat_distance(const fastf_t *q1, const fastf_t *q2)
00194 {
00195         quat_t  qtemp;
00196 
00197         QSUB2( qtemp, q1, q2 );
00198         return( QMAGNITUDE( qtemp ) );
00199 }
00200 
00201 /**
00202  *                      Q U A T _ D O U B L E
00203  *@brief
00204  * Gives the quaternion point representing twice the rotation
00205  *   from q1 to q2.
00206  *   Needed for patching Bezier curves together.
00207  *   A rather poor name admittedly.
00208  */
00209 void
00210 quat_double(fastf_t *qout, const fastf_t *q1, const fastf_t *q2)
00211 {
00212         quat_t  qtemp;
00213         double  scale;
00214 
00215         scale = 2.0 * QDOT( q1, q2 );
00216         QSCALE( qtemp, q2, scale );
00217         QSUB2( qout, qtemp, q1 );
00218         QUNITIZE( qout );
00219 }
00220 
00221 /**
00222  *                      Q U A T _ B I S E C T
00223  *@brief
00224  * Gives the bisector of quaternions q1 and q2.
00225  * (Could be done with quat_slerp and factor 0.5)
00226  * [I believe they must be unit quaternions this to work]
00227  */
00228 void
00229 quat_bisect(fastf_t *qout, const fastf_t *q1, const fastf_t *q2)
00230 {
00231         QADD2( qout, q1, q2 );
00232         QUNITIZE( qout );
00233 }
00234 
00235 /**
00236  *                      Q U A T _ S L E R P
00237  *@brief
00238  * Do Spherical Linear Interpolation between two unit quaternions
00239  *  by the given factor.
00240  *
00241  * As f goes from 0 to 1, qout goes from q1 to q2.
00242  * Code based on code by Ken Shoemake
00243  */
00244 void
00245 quat_slerp(fastf_t *qout, const fastf_t *q1, const fastf_t *q2, double f)
00246 {
00247         double          omega;
00248         double          cos_omega;
00249         double          invsin;
00250         register double s1, s2;
00251 
00252         cos_omega = QDOT( q1, q2 );
00253         if( (1.0 + cos_omega) > 1.0e-5 )  {
00254                 /* cos_omega > -0.99999 */
00255                 if( (1.0 - cos_omega) > 1.0e-5 )  {
00256                         /* usual case */
00257                         omega = acos(cos_omega);        /* XXX atan2? */
00258                         invsin = 1.0 / sin(omega);
00259                         s1 = sin( (1.0-f)*omega ) * invsin;
00260                         s2 = sin( f*omega ) * invsin;
00261                 } else {
00262                         /*
00263                          *  cos_omega > 0.99999
00264                          * The ends are very close to each other,
00265                          * use linear interpolation, to avoid divide-by-zero
00266                          */
00267                         s1 = 1.0 - f;
00268                         s2 = f;
00269                 }
00270                 QBLEND2( qout, s1, q1, s2, q2 );
00271         } else {
00272                 /*
00273                  *  cos_omega == -1, omega = PI.
00274                  *  The ends are nearly opposite, 180 degrees (PI) apart.
00275                  */
00276                 /* (I have no idea what permuting the elements accomplishes,
00277                  * perhaps it creates a perpendicular? */
00278                 qout[X] = -q1[Y];
00279                 qout[Y] =  q1[X];
00280                 qout[Z] = -q1[W];
00281                 s1 = sin( (0.5-f) * PI );
00282                 s2 = sin( f * PI );
00283                 VBLEND2( qout, s1, q1, s2, qout );
00284                 qout[W] =  q1[Z];
00285         }
00286 }
00287 
00288 /**
00289  *                      Q U A T _ S B E R P
00290  *@brief
00291  * Spherical Bezier Interpolate between four quaternions by amount f.
00292  * These are intended to be used as start and stop quaternions along
00293  *   with two control quaternions chosen to match spline segments with
00294  *   first order continuity.
00295  *
00296  *  Uses the method of successive bisection.
00297  */
00298 void
00299 quat_sberp(fastf_t *qout, const fastf_t *q1, const fastf_t *qa, const fastf_t *qb, const fastf_t *q2, double f)
00300 {
00301         quat_t  p1, p2, p3, p4, p5;
00302 
00303         /* Interp down the three segments */
00304         quat_slerp( p1, q1, qa, f );
00305         quat_slerp( p2, qa, qb, f );
00306         quat_slerp( p3, qb, q2, f );
00307 
00308         /* Interp down the resulting two */
00309         quat_slerp( p4, p1, p2, f );
00310         quat_slerp( p5, p2, p3, f );
00311 
00312         /* Interp this segment for final quaternion */
00313         quat_slerp( qout, p4, p5, f );
00314 }
00315 
00316 /**
00317  *                      Q U A T _ M A K E _ N E A R E S T
00318  *@brief
00319  *  Set the quaternion q1 to the quaternion which yields the
00320  *   smallest rotation from q2 (of the two versions of q1 which
00321  *   produce the same orientation).
00322  *
00323  * Note that smallest euclidian distance implies smallest great
00324  *   circle distance as well (since surface is convex).
00325  */
00326 void
00327 quat_make_nearest(fastf_t *q1, const fastf_t *q2)
00328 {
00329         quat_t  qtemp;
00330         double  d1, d2;
00331 
00332         QSCALE( qtemp, q1, -1.0 );
00333         d1 = quat_distance( q1, q2 );
00334         d2 = quat_distance( qtemp, q2 );
00335 
00336         /* Choose smallest distance */
00337         if( d2 < d1 ) {
00338                 QMOVE( q1, qtemp );
00339         }
00340 }
00341 
00342 /**
00343  *                      Q U A T _ P R I N T
00344  */
00345 /* DEBUG ROUTINE */
00346 void
00347 quat_print(const char *title, const fastf_t *quat)
00348 {
00349         int     i;
00350         vect_t  axis;
00351 
00352         fprintf( stderr, "QUATERNION: %s\n", title );
00353         for( i = 0; i < 4; i++ )
00354                 fprintf( stderr, "%8f  ", quat[i] );
00355         fprintf( stderr, "\n" );
00356 
00357         fprintf( stderr, "rot_angle = %8f deg", RTODEG * 2.0 * acos( quat[W] ) );
00358         VMOVE( axis, quat );
00359         VUNITIZE( axis );
00360         fprintf( stderr, ", Axis = (%f, %f, %f)\n",
00361                 axis[X], axis[Y], axis[Z] );
00362 }
00363 
00364 /**
00365  *                      Q U A T _ E X P
00366  *@brief
00367  *  Exponentiate a quaternion, assuming that the scalar part is 0.
00368  *  Code by Ken Shoemake.
00369  */
00370 void
00371 quat_exp(fastf_t *out, const fastf_t *in)
00372 {
00373         FAST fastf_t    theta;
00374         FAST fastf_t    scale;
00375 
00376         if( (theta = MAGNITUDE( in )) > VDIVIDE_TOL )
00377                 scale = sin(theta)/theta;
00378         else
00379                 scale = 1.0;
00380 
00381         VSCALE( out, in, scale );
00382         out[W] = cos(theta);
00383 }
00384 
00385 /**
00386  *                      Q U A T _ L O G
00387  *@brief
00388  *  Take the natural logarithm of a unit quaternion.
00389  *  Code by Ken Shoemake.
00390  */
00391 void
00392 quat_log(fastf_t *out, const fastf_t *in)
00393 {
00394         FAST fastf_t    theta;
00395         FAST fastf_t    scale;
00396 
00397         if( (scale = MAGNITUDE(in)) > VDIVIDE_TOL )  {
00398                 theta = atan2( scale, in[W] );
00399                 scale = theta/scale;
00400         }
00401 
00402         VSCALE( out, in, scale );
00403         out[W] = 0.0;
00404 }
00405 
00406 /*@}*/
00407 /*
00408  * Local Variables:
00409  * mode: C
00410  * tab-width: 8
00411  * c-basic-offset: 4
00412  * indent-tabs-mode: t
00413  * End:
00414  * ex: shiftwidth=4 tabstop=8
00415  */

Generated on Mon Sep 18 01:24:47 2006 for BRL-CAD by  doxygen 1.4.6