quaternion.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 
3  Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.6.3.
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 qreal epsilon = 1E-10;
38 
39  const qreal fromSqNorm = from.squaredNorm();
40  const qreal 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 qreal 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  qreal 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 qreal q00 = 2.0 * q[0] * q[0];
79  const qreal q11 = 2.0 * q[1] * q[1];
80  const qreal q22 = 2.0 * q[2] * q[2];
81 
82  const qreal q01 = 2.0 * q[0] * q[1];
83  const qreal q02 = 2.0 * q[0] * q[2];
84  const qreal q03 = 2.0 * q[0] * q[3];
85 
86  const qreal q12 = 2.0 * q[1] * q[2];
87  const qreal q13 = 2.0 * q[1] * q[3];
88 
89  const qreal q23 = 2.0 * 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 qreal m[3][3])
105 {
106  // Compute one plus the trace of the matrix
107  const qreal onePlusTrace = 1.0 + m[0][0] + m[1][1] + m[2][2];
108 
109  if (onePlusTrace > 1E-5)
110  {
111  // Direct computation
112  const qreal 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 qreal 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 qreal 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 qreal 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 expects a double[3][3] parameter");
154 
155  qreal mat[3][3];
156  for (int i=0; i<3; ++i)
157  for (int j=0; j<3; ++j)
158  mat[i][j] = qreal(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  qreal m[3][3];
185  qreal normX = X.norm();
186  qreal normY = Y.norm();
187  qreal 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, qreal& angle) const
202 {
203  angle = 2.0 * acos(q[3]);
204  axis = Vec(q[0], q[1], q[2]);
205  const qreal sinus = axis.norm();
206  if (sinus > 1E-8)
207  axis /= sinus;
208 
209  if (angle > M_PI)
210  {
211  angle = 2.0 * qreal(M_PI) - angle;
212  axis = -axis;
213  }
214 }
215 
220 {
221  Vec res = Vec(q[0], q[1], q[2]);
222  const qreal 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 qreal Quaternion::angle() const
235 {
236  const qreal 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::qrealFromDom(element, attribute[i], ((i<3)?0.0:1.0));
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 qreal q00 = 2.0 * q[0] * q[0];
320  const qreal q11 = 2.0 * q[1] * q[1];
321  const qreal q22 = 2.0 * q[2] * q[2];
322 
323  const qreal q01 = 2.0 * q[0] * q[1];
324  const qreal q02 = 2.0 * q[0] * q[2];
325  const qreal q03 = 2.0 * q[0] * q[3];
326 
327  const qreal q12 = 2.0 * q[1] * q[2];
328  const qreal q13 = 2.0 * q[1] * q[3];
329 
330  const qreal q23 = 2.0 * q[2] * q[3];
331 
332  m[0][0] = 1.0 - 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.0 - 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.0 - q11 - q00;
343 
344  m[0][3] = 0.0;
345  m[1][3] = 0.0;
346  m[2][3] = 0.0;
347 
348  m[3][0] = 0.0;
349  m[3][1] = 0.0;
350  m[3][2] = 0.0;
351  m[3][3] = 1.0;
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(qreal 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] = qreal(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(qreal 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] = qreal(mat[j][i]);
423 }
424 
425 
433 Quaternion Quaternion::slerp(const Quaternion& a, const Quaternion& b, qreal t, bool allowFlip)
434 {
435  qreal cosAngle = Quaternion::dot(a, b);
436 
437  qreal 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  qreal angle = acos(fabs(cosAngle));
448  qreal 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, qreal 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  qreal 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  qreal coef = acos(q[3]) / len;
484  return Quaternion(q[0]*coef, q[1]*coef, q[2]*coef, 0.0);
485  }
486 }
487 
490 {
491  qreal 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  qreal 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  qreal seed = rand()/(qreal)RAND_MAX;
547  qreal r1 = sqrt(1.0 - seed);
548  qreal r2 = sqrt(seed);
549  qreal t1 = 2.0 * M_PI * (rand()/(qreal)RAND_MAX);
550  qreal t2 = 2.0 * M_PI * (rand()/(qreal)RAND_MAX);
551  return Quaternion(sin(t1)*r1, cos(t1)*r1, sin(t2)*r2, cos(t2)*r2);
552 }
Quaternion inverse() const
Definition: quaternion.h:205
void getAxisAngle(Vec &axis, qreal &angle) const
Definition: quaternion.cpp:201
static Quaternion randomQuaternion()
Definition: quaternion.cpp:542
void setFromRotatedBase(const Vec &X, const Vec &Y, const Vec &Z)
Definition: quaternion.cpp:163
qreal norm() const
Definition: vec.h:335
qreal angle() const
Definition: quaternion.cpp:234
static Quaternion squad(const Quaternion &a, const Quaternion &tgA, const Quaternion &tgB, const Quaternion &b, qreal t)
Definition: quaternion.cpp:467
Vec orthogonalVec() const
Definition: vec.cpp:59
qreal squaredNorm() const
Definition: vec.h:332
Vec rotate(const Vec &v) const
Definition: quaternion.cpp:76
#define M_PI
static Quaternion squadTangent(const Quaternion &before, const Quaternion &center, const Quaternion &after)
Definition: quaternion.cpp:513
Vec inverseRotate(const Vec &v) const
Definition: quaternion.cpp:68
void setFromRotationMatrix(const float m[3][3])
Definition: quaternion.cpp:151
const GLdouble * inverseMatrix() const
Definition: quaternion.cpp:389
ostream & operator<<(ostream &o, const Quaternion &Q)
Definition: quaternion.cpp:528
The Vec class represents 3D positions and 3D vectors.
Definition: vec.h:65
void initFromDOMElement(const QDomElement &element)
Definition: quaternion.cpp:273
static qreal dot(const Quaternion &a, const Quaternion &b)
Definition: quaternion.h:270
void getInverseRotationMatrix(qreal m[3][3]) const
Definition: quaternion.cpp:415
void getRotationMatrix(qreal m[3][3]) const
Definition: quaternion.cpp:371
static qreal qrealFromDom(const QDomElement &e, const QString &attribute, qreal defValue)
Definition: domUtils.h:44
static Quaternion lnDif(const Quaternion &a, const Quaternion &b)
Definition: quaternion.cpp:503
const GLdouble * matrix() const
Definition: quaternion.cpp:306
The Quaternion class represents 3D rotations and orientations.
Definition: quaternion.h:66
void setFromRotatedBasis(const Vec &X, const Vec &Y, const Vec &Z)
Definition: quaternion.cpp:182
static Quaternion slerp(const Quaternion &a, const Quaternion &b, qreal t, bool allowFlip=true)
Definition: quaternion.cpp:433
QDomElement domElement(const QString &name, QDomDocument &document) const
Definition: quaternion.cpp:256
void getMatrix(GLdouble m[4][4]) const
Definition: quaternion.cpp:317
void getInverseMatrix(GLdouble m[4][4]) const
Definition: quaternion.cpp:400


octovis
Author(s): Kai M. Wurm , Armin Hornung
autogenerated on Wed Jun 5 2019 19:26:39