Rotation.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2006 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <cmath>
00027 # include <climits>
00028 #endif
00029 
00030 #include "Rotation.h"
00031 #include "Matrix.h"
00032 
00033 using namespace Base;
00034 
00035 Rotation::Rotation()
00036 {
00037     quat[0]=quat[1]=quat[2]=0.0f;quat[3]=1.0f;
00038 }
00039 
00041 Rotation::Rotation(const Vector3d& axis, const double fAngle)
00042 {
00043     this->setValue(axis, fAngle);
00044 }
00045 
00046 Rotation::Rotation(const Matrix4D& matrix)
00047 {
00048     this->setValue(matrix);
00049 }
00050 
00055 Rotation::Rotation(const double q[4])
00056 {
00057     this->setValue(q);
00058 }
00059 
00064 Rotation::Rotation(const double q0, const double q1, const double q2, const double q3)
00065 {
00066     this->setValue(q0, q1, q2, q3);
00067 }
00068 
00069 Rotation::Rotation(const Vector3d & rotateFrom, const Vector3d & rotateTo)
00070 {
00071     this->setValue(rotateFrom, rotateTo);
00072 }
00073 
00074 Rotation::Rotation(const Rotation& rot)
00075 {
00076     this->quat[0] = rot.quat[0];
00077     this->quat[1] = rot.quat[1];
00078     this->quat[2] = rot.quat[2];
00079     this->quat[3] = rot.quat[3];
00080 }
00081 
00082 const double * Rotation::getValue(void) const
00083 {
00084     return &this->quat[0];
00085 }
00086 
00087 void Rotation::getValue(double & q0, double & q1, double & q2, double & q3) const
00088 {
00089     q0 = this->quat[0];
00090     q1 = this->quat[1];
00091     q2 = this->quat[2];
00092     q3 = this->quat[3];
00093 }
00094 
00095 void Rotation::setValue(const double q0, const double q1, const double q2, const double q3)
00096 {
00097     this->quat[0] = q0;
00098     this->quat[1] = q1;
00099     this->quat[2] = q2;
00100     this->quat[3] = q3;
00101     this->normalize();
00102 }
00103 
00104 void Rotation::getValue(Vector3d & axis, double & rfAngle) const
00105 {
00106     // Taken from <http://de.wikipedia.org/wiki/Quaternionen>
00107     //
00108     // Note: -1 < w < +1 (|w| == 1 not allowed, with w:=quat[3]) 
00109     if((this->quat[3] > -1.0f) && (this->quat[3] < 1.0f)) {
00110         rfAngle = double(acos(this->quat[3])) * 2.0f;
00111         double scale = (double)sin(rfAngle / 2.0f);
00112         // Get a normalized vector 
00113         axis.x = this->quat[0] / scale;
00114         axis.y = this->quat[1] / scale;
00115         axis.z = this->quat[2] / scale;
00116     }
00117     else {
00118         // The quaternion doesn't describe a rotation, so we can setup any value we want 
00119         axis.Set(0.0f, 0.0f, 1.0f);
00120         rfAngle = 0.0f;
00121     }
00122 }
00123 
00127 void Rotation::getValue(Matrix4D & matrix) const
00128 {
00129     // Taken from <http://de.wikipedia.org/wiki/Quaternionen>
00130     //
00131     const double x = this->quat[0];
00132     const double y = this->quat[1];
00133     const double z = this->quat[2];
00134     const double w = this->quat[3];
00135 
00136     matrix[0][0] = 1.0f-2.0f*(y*y+z*z);
00137     matrix[0][1] = 2.0f*(x*y-z*w);
00138     matrix[0][2] = 2.0f*(x*z+y*w);
00139     matrix[0][3] = 0.0f;
00140 
00141     matrix[1][0] = 2.0f*(x*y+z*w);
00142     matrix[1][1] = 1.0f-2.0f*(x*x+z*z);
00143     matrix[1][2] = 2.0f*(y*z-x*w);
00144     matrix[1][3] = 0.0f;
00145 
00146     matrix[2][0] = 2.0f*(x*z-y*w);
00147     matrix[2][1] = 2.0f*(y*z+x*w);
00148     matrix[2][2] = 1.0f-2.0f*(x*x+y*y);
00149     matrix[2][3] = 0.0f;
00150 
00151     matrix[3][0] = 0.0f;
00152     matrix[3][1] = 0.0f;
00153     matrix[3][2] = 0.0f;
00154     matrix[3][3] = 1.0f;
00155 }
00156 
00157 void Rotation::setValue(const double q[4])
00158 {
00159     this->quat[0] = q[0];
00160     this->quat[1] = q[1];
00161     this->quat[2] = q[2];
00162     this->quat[3] = q[3];
00163     this->normalize();
00164 }
00165 
00166 void Rotation::setValue(const Matrix4D & m)
00167 {
00168     double trace = (double)(m[0][0] + m[1][1] + m[2][2]);
00169     if (trace > 0.0f) {
00170         double s = (double)sqrt(1.0f+trace);
00171         this->quat[3] = 0.5f * s;
00172         s = 0.5f / s;
00173         this->quat[0] = (double)((m[2][1] - m[1][2]) * s);
00174         this->quat[1] = (double)((m[0][2] - m[2][0]) * s);
00175         this->quat[2] = (double)((m[1][0] - m[0][1]) * s);
00176     }
00177     else {
00178         // Described in RotationIssues.pdf from <http://www.geometrictools.com>
00179         //
00180         // Get the max. element of the trace
00181         int i = 0;
00182         if (m[1][1] > m[0][0]) i = 1;
00183         if (m[2][2] > m[i][i]) i = 2;
00184 
00185         int j = (i+1)%3;
00186         int k = (i+2)%3;
00187 
00188         double s = (double)sqrt((m[i][i] - (m[j][j] + m[k][k])) + 1.0f);
00189         this->quat[i] = s * 0.5f;
00190         s = 0.5f / s;
00191         this->quat[3] = (double)((m[k][j] - m[j][k]) * s);
00192         this->quat[j] = (double)((m[j][i] + m[i][j]) * s);
00193         this->quat[k] = (double)((m[k][i] + m[i][k]) * s);
00194     }
00195 }
00196 
00197 void Rotation::setValue(const Vector3d & axis, const double fAngle)
00198 {
00199     // Taken from <http://de.wikipedia.org/wiki/Quaternionen>
00200     //
00201     this->quat[3] = (double)cos(fAngle/2.0);
00202     Vector3d norm = axis;
00203     norm.Normalize();
00204     double scale = (double)sin(fAngle/2.0);
00205     this->quat[0] = norm.x * scale;
00206     this->quat[1] = norm.y * scale;
00207     this->quat[2] = norm.z * scale;
00208 }
00209 
00210 void Rotation::setValue(const Vector3d & rotateFrom, const Vector3d & rotateTo)
00211 {
00212     Vector3d u(rotateFrom); u.Normalize();
00213     Vector3d v(rotateTo); v.Normalize();
00214 
00215     // The vector from x to is the roatation axis because it's the normal of the plane defined by (0,u,v) 
00216     const double dot = u * v;
00217     Vector3d w = u % v;
00218     const double wlen = w.Length();
00219 
00220     if (wlen == 0.0f) { // Parallel vectors
00221         // Check if they are pointing in the same direction.
00222         if (dot > 0.0f) {
00223             this->setValue(0.0f, 0.0f, 0.0f, 1.0f);
00224         }
00225         else {
00226             // We can use any axis perpendicular to u (and v)
00227             Vector3d t = u % Vector3d(1.0f, 0.0f, 0.0f);
00228             if(t.Length() < FLT_EPSILON) 
00229                 t = u % Vector3d(0.0f, 1.0f, 0.0f);
00230             this->setValue(t.x, t.y, t.z, 0.0f);
00231         }
00232     }
00233     else { // Vectors are not parallel
00234         // Note: A quaternion is not well-defined by specifying a point and its transformed point.
00235         // Every quaternion with a rotation axis having the same angle to the vectors of both points is okay.
00236         double angle = (double)acos(dot);
00237         this->setValue(w, angle);
00238     }
00239 }
00240 
00241 void Rotation::normalize()
00242 {
00243     double len = (double)sqrt(this->quat[0]*this->quat[0]+
00244                               this->quat[1]*this->quat[1]+
00245                               this->quat[2]*this->quat[2]+
00246                               this->quat[3]*this->quat[3]);
00247     if (len != 0) {
00248         this->quat[0] /= len;
00249         this->quat[1] /= len;
00250         this->quat[2] /= len;
00251         this->quat[3] /= len;
00252     }
00253 }
00254 
00255 Rotation & Rotation::invert(void)
00256 {
00257     this->quat[0] = -this->quat[0];
00258     this->quat[1] = -this->quat[1];
00259     this->quat[2] = -this->quat[2];
00260     this->quat[3] =  this->quat[3];
00261     return *this;
00262 }
00263 
00264 Rotation Rotation::inverse(void) const
00265 {
00266     Rotation rot;
00267     rot.quat[0] = -this->quat[0];
00268     rot.quat[1] = -this->quat[1];
00269     rot.quat[2] = -this->quat[2];
00270     rot.quat[3] =  this->quat[3];
00271     return rot;
00272 }
00273 
00274 Rotation & Rotation::operator*=(const Rotation & q)
00275 {
00276     // Taken from <http://de.wikipedia.org/wiki/Quaternionen>
00277     double x0, y0, z0, w0;
00278     this->getValue(x0, y0, z0, w0);
00279     double x1, y1, z1, w1;
00280     q.getValue(x1, y1, z1, w1);
00281 
00282     this->setValue(w0*x1 + x0*w1 + y0*z1 - z0*y1,
00283                    w0*y1 - x0*z1 + y0*w1 + z0*x1,
00284                    w0*z1 + x0*y1 - y0*x1 + z0*w1,
00285                    w0*w1 - x0*x1 - y0*y1 - z0*z1);
00286     return *this;
00287 }
00288 
00289 Rotation Rotation::operator*(const Rotation & q) const
00290 {
00291     Rotation quat(*this);
00292     quat *= q;
00293     return quat;
00294 }
00295 
00296 bool Rotation::operator==(const Rotation & q) const
00297 {
00298     bool equal = true;
00299     for (int i=0; i<4;i++)
00300         equal &= (fabs(this->quat[i] - q.quat[i]) < 0.005 );
00301     return equal;
00302 }
00303 
00304 bool Rotation::operator!=(const Rotation & q) const
00305 {
00306     return !(*this == q);
00307 }
00308 
00309 void Rotation::multVec(const Vector3d & src, Vector3d & dst) const
00310 {
00311     double x = this->quat[0];
00312     double y = this->quat[1];
00313     double z = this->quat[2];
00314     double w = this->quat[3];
00315     double x2 = x * x;
00316     double y2 = y * y;
00317     double z2 = z * z;
00318     double w2 = w * w;
00319 
00320     double dx = (x2+w2-y2-z2)*src.x + 2.0f*(x*y-z*w)*src.y + 2.0f*(x*z+y*w)*src.z;
00321     double dy = 2.0f*(x*y+z*w)*src.x + (w2-x2+y2-z2)*src.y + 2.0f*(y*z-x*w)*src.z;
00322     double dz = 2.0f*(x*z-y*w)*src.x + 2.0f*(x*w+y*z)*src.y + (w2-x2-y2+z2)*src.z;
00323     dst.x = dx;
00324     dst.y = dy;
00325     dst.z = dz;
00326 }
00327 
00328 void Rotation::scaleAngle(const double scaleFactor)
00329 {
00330     Vector3d axis;
00331     double fAngle;
00332     this->getValue(axis, fAngle);
00333     this->setValue(axis, fAngle * scaleFactor);
00334 }
00335 
00336 Rotation Rotation::slerp(const Rotation & q0, const Rotation & q1, double t)
00337 {
00338     // Taken from <http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/>
00339     // q = [q0*sin((1-t)*theta)+q1*sin(t*theta)]/sin(theta), 0<=t<=1
00340     if (t<0.0f) t=0.0f;
00341     else if (t>1.0f) t=1.0f;
00342     //return q0;
00343 
00344     double scale0 = 1.0f - t;
00345     double scale1 = t;
00346     double dot = q0.quat[0]*q1.quat[0]+q0.quat[1]*q1.quat[1]+q0.quat[2]*q1.quat[2]+q0.quat[3]*q1.quat[3];
00347     bool neg=false;
00348     if(dot < 0.0f) {
00349         dot = -dot;
00350         neg = true;
00351     }
00352 
00353     if ((1.0f - dot) > FLT_EPSILON) {
00354         double angle = (double)acos(dot);
00355         double sinangle = (double)sin(angle);
00356         // If possible calculate spherical interpolation, otherwise use linear interpolation
00357         if (sinangle > FLT_EPSILON) {
00358             scale0 = double(sin((1.0 - t) * angle)) / sinangle;
00359             scale1 = double(sin(t * angle)) / sinangle;
00360         }
00361     }
00362 
00363     if (neg)
00364         scale1 = -scale1;
00365 
00366     double x = scale0 * q0.quat[0] + scale1 * q1.quat[0];
00367     double y = scale0 * q0.quat[1] + scale1 * q1.quat[1];
00368     double z = scale0 * q0.quat[2] + scale1 * q1.quat[2];
00369     double w = scale0 * q0.quat[3] + scale1 * q1.quat[3];
00370     return Rotation(x, y, z, w);
00371 }
00372 
00373 Rotation Rotation::identity(void)
00374 {
00375     return Rotation(0.0f, 0.0f, 0.0f, 1.0f);
00376 }
00377 
00378 void Rotation::setYawPitchRoll(double y, double p, double r)
00379 {
00380     // taken from http://www.resonancepub.com/quaterni.htm
00381     // The Euler angles (yaw,pitch,roll) are in ZY'X''-notation
00382     double c1 = cos(((y/180.0)*D_PI)/2.0);
00383     double s1 = sin(((y/180.0)*D_PI)/2.0);
00384     double c2 = cos(((p/180.0)*D_PI)/2.0);
00385     double s2 = sin(((p/180.0)*D_PI)/2.0);
00386     double c3 = cos(((r/180.0)*D_PI)/2.0);
00387     double s3 = sin(((r/180.0)*D_PI)/2.0);
00388 
00389     quat[0] = c1*c2*s3 - s1*s2*c3;
00390     quat[1] = c1*s2*c3 + s1*c2*s3;
00391     quat[2] = s1*c2*c3 - c1*s2*s3;
00392     quat[3] = c1*c2*c3 + s1*s2*s3;
00393 }
00394 
00395 void Rotation::getYawPitchRoll(double& y, double& p, double& r) const
00396 {
00397     // taken from http://www.resonancepub.com/quaterni.htm
00398     // see also http://willperone.net/Code/quaternion.php
00399     double q00 = quat[0]*quat[0];
00400     double q11 = quat[1]*quat[1];
00401     double q22 = quat[2]*quat[2];
00402     double q33 = quat[3]*quat[3];
00403     double q01 = quat[0]*quat[1];
00404     double q02 = quat[0]*quat[2];
00405     double q03 = quat[0]*quat[3];
00406     double q12 = quat[1]*quat[2];
00407     double q13 = quat[1]*quat[3];
00408     double q23 = quat[2]*quat[3];
00409     double qd2 = 2.0*(q13-q02);
00410 
00411     y = atan2(2.0*(q01+q23),(q00+q33)-(q11+q22));
00412     p = qd2 > 1.0 ? D_PI/2.0 : (qd2 < -1.0 ? -D_PI/2.0 : asin (qd2));
00413     r = atan2(2.0*(q12+q03),(q22+q33)-(q00+q11));
00414 
00415     // convert to degree
00416     y = (y/D_PI)*180;
00417     p = (p/D_PI)*180;
00418     r = (r/D_PI)*180;
00419 }

Generated on Wed Nov 23 19:00:36 2011 for FreeCAD by  doxygen 1.6.1