quaternion.cpp
1 /****************************************************************************
2 
3  Copyright (C) 2002-2013 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.5.2.
6 
7  http://www.libqglviewer.com - contact@libqglviewer.com
8 
9  This file may be used under the terms of the GNU General Public License
10  versions 2.0 or 3.0 as published by the Free Software Foundation and
11  appearing in the LICENSE file included in the packaging of this file.
12  In addition, as a special exception, Gilles Debunne gives you certain
13  additional rights, described in the file GPL_EXCEPTION in this package.
14 
15  libQGLViewer uses dual licensing. Commercial/proprietary software must
16  purchase a libQGLViewer Commercial License.
17 
18  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 
21 *****************************************************************************/
22 
23 #include "domUtils.h"
24 #include "quaternion.h"
25 #include <stdlib.h> // RAND_MAX
26 
27 // All the methods are declared inline in Quaternion.h
28 using namespace qglviewer;
29 using namespace std;
30 
35 Quaternion::Quaternion(const Vec& from, const Vec& to)
36 {
37  const double epsilon = 1E-10f;
38 
39  const double fromSqNorm = from.squaredNorm();
40  const double toSqNorm = to.squaredNorm();
41  // Identity Quaternion when one vector is null
42  if ((fromSqNorm < epsilon) || (toSqNorm < epsilon))
43  {
44  q[0]=q[1]=q[2]=0.0;
45  q[3]=1.0;
46  }
47  else
48  {
49  Vec axis = cross(from, to);
50  const double axisSqNorm = axis.squaredNorm();
51 
52  // Aligned vectors, pick any axis, not aligned with from or to
53  if (axisSqNorm < epsilon)
54  axis = from.orthogonalVec();
55 
56  double angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm)));
57 
58  if (from*to < 0.0)
59  angle = M_PI-angle;
60 
61  setAxisAngle(axis, angle);
62  }
63 }
64 
69 {
70  return inverse().rotate(v);
71 }
72 
76 Vec Quaternion::rotate(const Vec& v) const
77 {
78  const double q00 = 2.0l * q[0] * q[0];
79  const double q11 = 2.0l * q[1] * q[1];
80  const double q22 = 2.0l * q[2] * q[2];
81 
82  const double q01 = 2.0l * q[0] * q[1];
83  const double q02 = 2.0l * q[0] * q[2];
84  const double q03 = 2.0l * q[0] * q[3];
85 
86  const double q12 = 2.0l * q[1] * q[2];
87  const double q13 = 2.0l * q[1] * q[3];
88 
89  const double q23 = 2.0l * q[2] * q[3];
90 
91  return Vec((1.0 - q11 - q22)*v[0] + ( q01 - q23)*v[1] + ( q02 + q13)*v[2],
92  ( q01 + q23)*v[0] + (1.0 - q22 - q00)*v[1] + ( q12 - q03)*v[2],
93  ( q02 - q13)*v[0] + ( q12 + q03)*v[1] + (1.0 - q11 - q00)*v[2] );
94 }
95 
104 void Quaternion::setFromRotationMatrix(const double m[3][3])
105 {
106  // Compute one plus the trace of the matrix
107  const double onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2];
108 
109  if (onePlusTrace > 1E-5)
110  {
111  // Direct computation
112  const double s = sqrt(onePlusTrace) * 2.0;
113  q[0] = (m[2][1] - m[1][2]) / s;
114  q[1] = (m[0][2] - m[2][0]) / s;
115  q[2] = (m[1][0] - m[0][1]) / s;
116  q[3] = 0.25 * s;
117  }
118  else
119  {
120  // Computation depends on major diagonal term
121  if ((m[0][0] > m[1][1])&(m[0][0] > m[2][2]))
122  {
123  const double s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2.0;
124  q[0] = 0.25 * s;
125  q[1] = (m[0][1] + m[1][0]) / s;
126  q[2] = (m[0][2] + m[2][0]) / s;
127  q[3] = (m[1][2] - m[2][1]) / s;
128  }
129  else
130  if (m[1][1] > m[2][2])
131  {
132  const double s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2.0;
133  q[0] = (m[0][1] + m[1][0]) / s;
134  q[1] = 0.25 * s;
135  q[2] = (m[1][2] + m[2][1]) / s;
136  q[3] = (m[0][2] - m[2][0]) / s;
137  }
138  else
139  {
140  const double s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2.0;
141  q[0] = (m[0][2] + m[2][0]) / s;
142  q[1] = (m[1][2] + m[2][1]) / s;
143  q[2] = 0.25 * s;
144  q[3] = (m[0][1] - m[1][0]) / s;
145  }
146  }
147  normalize();
148 }
149 
150 #ifndef DOXYGEN
151 void Quaternion::setFromRotationMatrix(const float m[3][3])
152 {
153  qWarning("setFromRotationMatrix now waits for a double[3][3] parameter");
154 
155  double mat[3][3];
156  for (int i=0; i<3; ++i)
157  for (int j=0; j<3; ++j)
158  mat[i][j] = double(m[i][j]);
159 
160  setFromRotationMatrix(mat);
161 }
162 
163 void Quaternion::setFromRotatedBase(const Vec& X, const Vec& Y, const Vec& Z)
164 {
165  qWarning("setFromRotatedBase is deprecated, use setFromRotatedBasis instead");
166  setFromRotatedBasis(X,Y,Z);
167 }
168 #endif
169 
182 void Quaternion::setFromRotatedBasis(const Vec& X, const Vec& Y, const Vec& Z)
183 {
184  double m[3][3];
185  double normX = X.norm();
186  double normY = Y.norm();
187  double normZ = Z.norm();
188 
189  for (int i=0; i<3; ++i)
190  {
191  m[i][0] = X[i] / normX;
192  m[i][1] = Y[i] / normY;
193  m[i][2] = Z[i] / normZ;
194  }
195 
196  setFromRotationMatrix(m);
197 }
198 
201 void Quaternion::getAxisAngle(Vec& axis, float& angle) const
202 {
203  angle = 2.0*acos(q[3]);
204  axis = Vec(q[0], q[1], q[2]);
205  const double sinus = axis.norm();
206  if (sinus > 1E-8)
207  axis /= sinus;
208 
209  if (angle > M_PI)
210  {
211  angle = 2.0*M_PI - angle;
212  axis = -axis;
213  }
214 }
215 
220 {
221  Vec res = Vec(q[0], q[1], q[2]);
222  const double sinus = res.norm();
223  if (sinus > 1E-8)
224  res /= sinus;
225  return (acos(q[3]) <= M_PI/2.0) ? res : -res;
226 }
227 
234 double Quaternion::angle() const
235 {
236  const double angle = 2.0 * acos(q[3]);
237  return (angle <= M_PI) ? angle : 2.0*M_PI - angle;
238 }
239 
256 QDomElement Quaternion::domElement(const QString& name, QDomDocument& document) const
257 {
258  QDomElement de = document.createElement(name);
259  de.setAttribute("q0", QString::number(q[0]));
260  de.setAttribute("q1", QString::number(q[1]));
261  de.setAttribute("q2", QString::number(q[2]));
262  de.setAttribute("q3", QString::number(q[3]));
263  return de;
264 }
265 
273 void Quaternion::initFromDOMElement(const QDomElement& element)
274 {
275  Quaternion q(element);
276  *this = q;
277 }
278 
286 Quaternion::Quaternion(const QDomElement& element)
287 {
288  QStringList attribute;
289  attribute << "q0" << "q1" << "q2" << "q3";
290  for (int i=0; i<attribute.size(); ++i)
291  q[i] = DomUtils::doubleFromDom(element, attribute[i], ((i<3)?0.0f:1.0f));
292 }
293 
306 const GLdouble* Quaternion::matrix() const
307 {
308  static GLdouble m[4][4];
309  getMatrix(m);
310  return (const GLdouble*)(m);
311 }
312 
317 void Quaternion::getMatrix(GLdouble m[4][4]) const
318 {
319  const double q00 = 2.0l * q[0] * q[0];
320  const double q11 = 2.0l * q[1] * q[1];
321  const double q22 = 2.0l * q[2] * q[2];
322 
323  const double q01 = 2.0l * q[0] * q[1];
324  const double q02 = 2.0l * q[0] * q[2];
325  const double q03 = 2.0l * q[0] * q[3];
326 
327  const double q12 = 2.0l * q[1] * q[2];
328  const double q13 = 2.0l * q[1] * q[3];
329 
330  const double q23 = 2.0l * q[2] * q[3];
331 
332  m[0][0] = 1.0l - q11 - q22;
333  m[1][0] = q01 - q23;
334  m[2][0] = q02 + q13;
335 
336  m[0][1] = q01 + q23;
337  m[1][1] = 1.0l - q22 - q00;
338  m[2][1] = q12 - q03;
339 
340  m[0][2] = q02 - q13;
341  m[1][2] = q12 + q03;
342  m[2][2] = 1.0l - q11 - q00;
343 
344  m[0][3] = 0.0l;
345  m[1][3] = 0.0l;
346  m[2][3] = 0.0l;
347 
348  m[3][0] = 0.0l;
349  m[3][1] = 0.0l;
350  m[3][2] = 0.0l;
351  m[3][3] = 1.0l;
352 }
353 
355 void Quaternion::getMatrix(GLdouble m[16]) const
356 {
357  static GLdouble mat[4][4];
358  getMatrix(mat);
359  int count = 0;
360  for (int i=0; i<4; ++i)
361  for (int j=0; j<4; ++j)
362  m[count++] = mat[i][j];
363 }
364 
371 void Quaternion::getRotationMatrix(float m[3][3]) const
372 {
373  static GLdouble mat[4][4];
374  getMatrix(mat);
375  for (int i=0; i<3; ++i)
376  for (int j=0; j<3; ++j)
377  // Beware of transposition
378  m[i][j] = mat[j][i];
379 }
380 
389 const GLdouble* Quaternion::inverseMatrix() const
390 {
391  static GLdouble m[4][4];
392  getInverseMatrix(m);
393  return (const GLdouble*)(m);
394 }
395 
400 void Quaternion::getInverseMatrix(GLdouble m[4][4]) const
401 {
402  inverse().getMatrix(m);
403 }
404 
406 void Quaternion::getInverseMatrix(GLdouble m[16]) const
407 {
408  inverse().getMatrix(m);
409 }
410 
415 void Quaternion::getInverseRotationMatrix(float m[3][3]) const
416 {
417  static GLdouble mat[4][4];
418  getInverseMatrix(mat);
419  for (int i=0; i<3; ++i)
420  for (int j=0; j<3; ++j)
421  // Beware of transposition
422  m[i][j] = mat[j][i];
423 }
424 
425 
433 Quaternion Quaternion::slerp(const Quaternion& a, const Quaternion& b, float t, bool allowFlip)
434 {
435  double cosAngle = Quaternion::dot(a, b);
436 
437  double c1, c2;
438  // Linear interpolation for close orientations
439  if ((1.0 - fabs(cosAngle)) < 0.01)
440  {
441  c1 = 1.0 - t;
442  c2 = t;
443  }
444  else
445  {
446  // Spherical interpolation
447  double angle = acos(fabs(cosAngle));
448  double sinAngle = sin(angle);
449  c1 = sin(angle * (1.0 - t)) / sinAngle;
450  c2 = sin(angle * t) / sinAngle;
451  }
452 
453  // Use the shortest path
454  if (allowFlip && (cosAngle < 0.0))
455  c1 = -c1;
456 
457  return Quaternion(c1*a[0] + c2*b[0], c1*a[1] + c2*b[1], c1*a[2] + c2*b[2], c1*a[3] + c2*b[3]);
458 }
459 
467 Quaternion Quaternion::squad(const Quaternion& a, const Quaternion& tgA, const Quaternion& tgB, const Quaternion& b, float t)
468 {
469  Quaternion ab = Quaternion::slerp(a, b, t);
470  Quaternion tg = Quaternion::slerp(tgA, tgB, t, false);
471  return Quaternion::slerp(ab, tg, 2.0*t*(1.0-t), false);
472 }
473 
476 {
477  double len = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
478 
479  if (len < 1E-6)
480  return Quaternion(q[0], q[1], q[2], 0.0);
481  else
482  {
483  double coef = acos(q[3]) / len;
484  return Quaternion(q[0]*coef, q[1]*coef, q[2]*coef, 0.0);
485  }
486 }
487 
490 {
491  double theta = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
492 
493  if (theta < 1E-6)
494  return Quaternion(q[0], q[1], q[2], cos(theta));
495  else
496  {
497  double coef = sin(theta) / theta;
498  return Quaternion(q[0]*coef, q[1]*coef, q[2]*coef, cos(theta));
499  }
500 }
501 
504 {
505  Quaternion dif = a.inverse()*b;
506  dif.normalize();
507  return dif.log();
508 }
509 
513 Quaternion Quaternion::squadTangent(const Quaternion& before, const Quaternion& center, const Quaternion& after)
514 {
515  Quaternion l1 = Quaternion::lnDif(center,before);
516  Quaternion l2 = Quaternion::lnDif(center,after);
517  Quaternion e;
518  for (int i=0; i<4; ++i)
519  e.q[i] = -0.25 * (l1.q[i] + l2.q[i]);
520  e = center*(e.exp());
521 
522  // if (Quaternion::dot(e,b) < 0.0)
523  // e.negate();
524 
525  return e;
526 }
527 
528 ostream& operator<<(ostream& o, const Quaternion& Q)
529 {
530  return o << Q[0] << '\t' << Q[1] << '\t' << Q[2] << '\t' << Q[3];
531 }
532 
543 {
544  // The rand() function is not very portable and may not be available on your system.
545  // Add the appropriate include or replace by an other random function in case of problem.
546  double seed = rand()/(double)RAND_MAX;
547  double r1 = sqrt(1.0 - seed);
548  double r2 = sqrt(seed);
549  double t1 = 2.0 * M_PI * (rand()/(double)RAND_MAX);
550  double t2 = 2.0 * M_PI * (rand()/(double)RAND_MAX);
551  return Quaternion(sin(t1)*r1, cos(t1)*r1, sin(t2)*r2, cos(t2)*r2);
552 }
const GLdouble * inverseMatrix() const
Returns the associated 4x4 OpenGL inverse rotation matrix.
Definition: quaternion.cpp:389
Quaternion inverse() const
Returns the inverse Quaternion (inverse rotation).
Definition: quaternion.h:205
double squaredNorm() const
Returns the squared norm of the Vec.
Definition: vec.h:332
static double dot(const Quaternion &a, const Quaternion &b)
Returns the "dot" product of a and b: a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3].
Definition: quaternion.h:270
Quaternion log()
Returns the logarithm of the Quaternion.
Definition: quaternion.cpp:475
double norm() const
Returns the norm of the vector.
Definition: vec.h:335
Vec orthogonalVec() const
Returns a Vec orthogonal to the Vec.
Definition: vec.cpp:59
static Quaternion squadTangent(const Quaternion &before, const Quaternion &center, const Quaternion &after)
Returns a tangent Quaternion for center, defined by before and after Quaternions. ...
Definition: quaternion.cpp:513
QDomElement domElement(const QString &name, QDomDocument &document) const
Returns an XML QDomElement that represents the Quaternion.
Definition: quaternion.cpp:256
static Quaternion lnDif(const Quaternion &a, const Quaternion &b)
Returns log(a.
Definition: quaternion.cpp:503
static Quaternion squad(const Quaternion &a, const Quaternion &tgA, const Quaternion &tgB, const Quaternion &b, float t)
Returns the slerp interpolation of the two Quaternions a and b, at time t, using tangents tgA and tgB...
Definition: quaternion.cpp:467
The Vec class represents 3D positions and 3D vectors.
Definition: vec.h:65
Quaternion exp()
Returns the exponential of the Quaternion.
Definition: quaternion.cpp:489
void getAxisAngle(Vec &axis, float &angle) const
Returns the axis vector and the angle (in radians) of the rotation represented by the Quaternion...
Definition: quaternion.cpp:201
static Quaternion slerp(const Quaternion &a, const Quaternion &b, float t, bool allowFlip=true)
Returns the slerp interpolation of Quaternions a and b, at time t.
Definition: quaternion.cpp:433
void setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z)
Sets the Quaternion from the three rotated vectors of an orthogonal basis.
Definition: quaternion.cpp:182
void getMatrix(GLdouble m[4][4]) const
Fills m with the OpenGL representation of the Quaternion rotation.
Definition: quaternion.cpp:317
Vec inverseRotate(const Vec &v) const
Returns the image of v by the Quaternion inverse() rotation.
Definition: quaternion.cpp:68
Quaternion()
Default constructor, builds an identity rotation.
Definition: quaternion.h:72
double angle() const
Returns the angle (in radians) of the rotation represented by the Quaternion.
Definition: quaternion.cpp:234
The Quaternion class represents 3D rotations and orientations.
Definition: quaternion.h:66
static Quaternion randomQuaternion()
Returns a random unit Quaternion.
Definition: quaternion.cpp:542
Vec axis() const
Returns the normalized axis direction of the rotation represented by the Quaternion.
Definition: quaternion.cpp:219
void getInverseRotationMatrix(float m[3][3]) const
m is set to the 3x3 inverse rotation matrix associated with the Quaternion.
Definition: quaternion.cpp:415
Vec rotate(const Vec &v) const
Returns the image of v by the Quaternion rotation.
Definition: quaternion.cpp:76
double normalize()
Normalizes the Quaternion coefficients.
Definition: quaternion.h:227
void initFromDOMElement(const QDomElement &element)
Restores the Quaternion state from a QDomElement created by domElement().
Definition: quaternion.cpp:273
void getInverseMatrix(GLdouble m[4][4]) const
Fills m with the OpenGL matrix corresponding to the inverse() rotation.
Definition: quaternion.cpp:400
const GLdouble * matrix() const
Returns the Quaternion associated 4x4 OpenGL rotation matrix.
Definition: quaternion.cpp:306
void getRotationMatrix(float m[3][3]) const
Fills m with the 3x3 rotation matrix associated with the Quaternion.
Definition: quaternion.cpp:371