gKit2 light
quaternion.h
1 /****************************************************************************
2 Copyright (C) 2010-2020 Alexandre Meyer
3 
4 This file is part of library.
5 
6 gkit2light is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 gkit2light is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with gkit2light. If not, see <http://www.gnu.org/licenses/>.
18 *****************************************************************************/
19 
20 #ifndef _QUATERNION_H
21 #define _QUATERNION_H
22 
23 
24 #ifndef _USE_MATH_DEFINES
25 #define _USE_MATH_DEFINES
26 #endif
27 #include <cmath>
28 #include <iostream>
29 #include <cstdlib>
30 
31 
35  template<typename Real, typename Vec3Real>
37  {
38  public:
43  { q[0]=q[1]=q[2]=0.0; q[3]=1.0; }
44 
46  TQuaternion(const Vec3Real& axis, const Real angle)
47  {
48  setAxisAngle(axis, angle);
49  }
50 
51  TQuaternion(const Vec3Real& from, const Vec3Real& to)
52  {
53  const Real epsilon = 1E-10f;
54 
55  const Real fromSqNorm = from.squaredNorm();
56  const Real toSqNorm = to.squaredNorm();
57  // Identity TQuaternion when one vector is null
58  if ((fromSqNorm < epsilon) || (toSqNorm < epsilon))
59  {
60  q[0]=q[1]=q[2]=0.0;
61  q[3]=1.0;
62  }
63  else
64  {
65  Vec3Real axis = cross(from, to);
66  const Real axisSqNorm = axis.squaredNorm();
67 
68  // Aligned vectors, pick any axis, not aligned with from or to
69  if (axisSqNorm < epsilon)
70  {
71  axis = Vec3Real(1.0, 0.0, 0.0);
72  if (axis*from < 0.1*sqrt(fromSqNorm))
73  axis = Vec3Real(0.0, 1.0, 0.0);
74  }
75 
76  Real angle = asin(sqrt(axisSqNorm / (fromSqNorm * toSqNorm)));
77 
78  if (from*to < 0.0)
79  angle = M_PI-angle;
80 
81  setAxisAngle(axis, angle);
82  }
83  }
84 
85 
90  TQuaternion(Real q0, Real q1, Real q2, Real q3)
91  { q[0]=q0; q[1]=q1; q[2]=q2; q[3]=q3; }
92 
95  { for (int i=0; i<4; ++i) q[i] = Q.q[i]; }
96 
99  {
100  for (int i=0; i<4; ++i)
101  q[i] = Q.q[i];
102  return (*this);
103  }
104 
105  TQuaternion& operator+=(const TQuaternion& Q)
106  {
107  for (int i = 0; i < 4; ++i)
108  q[i] += Q.q[i];
109  return (*this);
110  }
111 
112 
116  void setAxisAngle(const Vec3Real& axis, const Real angle)
117  {
118  const Real norm = length(axis); // axis.norm();
119  if (norm < 1E-8)
120  {
121  // Null rotation
122  q[0] = 0.0;
123  q[1] = 0.0;
124  q[2] = 0.0;
125  q[3] = 1.0;
126  }
127  else
128  {
129  const Real sin_half_angle = sin(angle / 2.0);
130  q[0] = sin_half_angle*axis.x/norm;
131  q[1] = sin_half_angle*axis.y/norm;
132  q[2] = sin_half_angle*axis.z/norm;
133  q[3] = cos(angle / 2.0);
134  }
135  }
136 
138  void setValue(Real q0, Real q1, Real q2, Real q3)
139  { q[0]=q0; q[1]=q1; q[2]=q2; q[3]=q3; }
140 
141 
142  template<typename MAT>
143  void setFromRotationMatrix(const MAT& m)
144  {
145  // First, find largest diagonal in matrix:
146  int i = 2;
147  if (m[0][0] > m[1][1])
148  {
149  if (m[0][0] > m[2][2])
150  {
151  i = 0;
152  }
153  }
154  else
155  {
156  if (m[1][1] > m[2][2])
157  {
158  i = 1;
159  }
160  }
161 
162  if (m[0][0]+m[1][1]+m[2][2] > m[i][i])
163  {
164  // Compute w first:
165  q[3] = sqrt(m[0][0]+m[1][1]+m[2][2]+1.0)/2.0;
166  // And compute other values:
167  q[0] = (m[2][1]-m[1][2])/(4.0*q[3]);
168  q[1] = (m[0][2]-m[2][0])/(4.0*q[3]);
169  q[2] = (m[1][0]-m[0][1])/(4.0*q[3]);
170  }
171  else
172  {
173  // Compute x, y, or z first:
174  int j = (i+1)%3;
175  int k = (i+2)%3;
176 
177  // Compute first value:
178  q[i] = sqrt(m[i][i]-m[j][j]-m[k][k]+1.0)/2.0;
179 
180  // And the others:
181  q[j] = (m[i][j]+m[j][i])/(4.0*q[i]);
182  q[k] = (m[i][k]+m[k][i])/(4.0*q[i]);
183 
184  q[3] = (m[k][j]-m[j][k])/(4.0*q[i]);
185  }
186  }
187 
188  void setFromRotatedBase(const Vec3Real& X, const Vec3Real& Y, const Vec3Real& Z)
189  {
190  Real m[3][3];
191  Real normX = X.norm();
192  Real normY = Y.norm();
193  Real normZ = Z.norm();
194 
195  for (int i=0; i<3; ++i)
196  {
197  m[i][0] = X[i] / normX;
198  m[i][1] = Y[i] / normY;
199  m[i][2] = Z[i] / normZ;
200  }
201  setFromRotationMatrix(m);
202  }
203 
205 
206 
209  Vec3Real axis() const
210  {
211  Vec3Real res = Vec3Real(q[0], q[1], q[2]);
212  const Real sinus = res.norm();
213  if (sinus > 1E-8)
214  res /= sinus;
215  return (acos(q[3]) <= M_PI/2.0) ? res : -res;
216  }
217 
218 
219  Real angle() const
220  {
221  const Real angle = 2.0 * acos(q[3]);
222  return (angle <= M_PI) ? angle : 2.0*M_PI - angle;
223  }
224 
225 
226  void getAxisAngle(Vec3Real& axis, Real& angle) const
227  {
228  angle = 2.0*acos(q[3]);
229  axis = Vec3Real(q[0], q[1], q[2]);
230  const Real sinus = axis.norm();
231  if (sinus > 1E-8)
232  axis /= sinus;
233 
234  if (angle > M_PI)
235  {
236  angle = 2.0*M_PI - angle;
237  axis = -axis;
238  }
239  }
240 
241 
243  Real operator[](int i) const { return q[i]; }
244 
246  Real& operator[](int i) { return q[i]; }
248 
249 
250 
252  friend TQuaternion operator*(const Real a, const TQuaternion& b)
253  {
254  return TQuaternion<Real,Vec3Real>(a*b[0],a*b[1],a*b[2],a*b[3]);
255  }
256 
257 
268  friend TQuaternion operator*(const TQuaternion& a, const TQuaternion& b)
269  {
270  return TQuaternion(a.q[3]*b.q[0] + b.q[3]*a.q[0] + a.q[1]*b.q[2] - a.q[2]*b.q[1],
271  a.q[3]*b.q[1] + b.q[3]*a.q[1] + a.q[2]*b.q[0] - a.q[0]*b.q[2],
272  a.q[3]*b.q[2] + b.q[3]*a.q[2] + a.q[0]*b.q[1] - a.q[1]*b.q[0],
273  a.q[3]*b.q[3] - b.q[0]*a.q[0] - a.q[1]*b.q[1] - a.q[2]*b.q[2]);
274  }
275 
283  {
284  *this = (*this)*q;
285  return *this;
286  }
287 
291  friend Vec3Real operator*(const TQuaternion& q, const Vec3Real& v)
292  {
293  return q.rotate(v);
294  }
295 
296 
297 
298  Vec3Real rotate(const Vec3Real& v) const
299  {
300  const Real q00 = 2.0l * q[0] * q[0];
301  const Real q11 = 2.0l * q[1] * q[1];
302  const Real q22 = 2.0l * q[2] * q[2];
303 
304  const Real q01 = 2.0l * q[0] * q[1];
305  const Real q02 = 2.0l * q[0] * q[2];
306  const Real q03 = 2.0l * q[0] * q[3];
307 
308  const Real q12 = 2.0l * q[1] * q[2];
309  const Real q13 = 2.0l * q[1] * q[3];
310 
311  const Real q23 = 2.0l * q[2] * q[3];
312 
313  return Vec3Real((1.0 - q11 - q22)*v.x + ( q01 - q23)*v.y + ( q02 + q13)*v.z,
314  ( q01 + q23)*v.x + (1.0 - q22 - q00)*v.y + ( q12 - q03)*v.z,
315  ( q02 - q13)*v.x + ( q12 + q03)*v.y + (1.0 - q11 - q00)*v.z );
316  }
317 
318 
319  Vec3Real inverseRotate(const Vec3Real& v) const
320  {
321  return inverse().rotate(v);
322  }
323 
324 
325 
327 
328 
337  TQuaternion inverse() const { return TQuaternion(-q[0], -q[1], -q[2], q[3]); }
338 
342  void invert() { q[0] = -q[0]; q[1] = -q[1]; q[2] = -q[2]; }
343 
352  void negate() { invert(); q[3] = -q[3]; }
353 
358  Real normalize()
359  {
360  const Real norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
361  for (int i=0; i<4; ++i)
362  q[i] /= norm;
363  return norm;
364  }
366 
367 
381  const Real* matrix() const
382  {
383  static Real m[4][4];
384  getMatrix44(m);
385  return (const Real*)(m);
386  }
387 
388 
393  //void getMatrix(Real m[4][4]) const
394  template<typename MAT>
395  void getMatrix44(MAT& m) const
396  {
397  const Real q00 = 2.0l * q[0] * q[0];
398  const Real q11 = 2.0l * q[1] * q[1];
399  const Real q22 = 2.0l * q[2] * q[2];
400 
401  const Real q01 = 2.0l * q[0] * q[1];
402  const Real q02 = 2.0l * q[0] * q[2];
403  const Real q03 = 2.0l * q[0] * q[3];
404 
405  const Real q12 = 2.0l * q[1] * q[2];
406  const Real q13 = 2.0l * q[1] * q[3];
407 
408  const Real q23 = 2.0l * q[2] * q[3];
409 
410  m[0][0] = 1.0l - q11 - q22;
411  m[1][0] = q01 - q23;
412  m[2][0] = q02 + q13;
413 
414  m[0][1] = q01 + q23;
415  m[1][1] = 1.0l - q22 - q00;
416  m[2][1] = q12 - q03;
417 
418  m[0][2] = q02 - q13;
419  m[1][2] = q12 + q03;
420  m[2][2] = 1.0l - q11 - q00;
421 
422  m[0][3] = 0.0l;
423  m[1][3] = 0.0l;
424  m[2][3] = 0.0l;
425 
426  m[3][0] = 0.0l;
427  m[3][1] = 0.0l;
428  m[3][2] = 0.0l;
429  m[3][3] = 1.0l;
430  }
431 
436  //void getMatrix(Real m[4][4]) const
437  template<typename MAT>
438  void getMatrix33(MAT& m) const
439  {
440  const Real q00 = 2.0l * q[0] * q[0];
441  const Real q11 = 2.0l * q[1] * q[1];
442  const Real q22 = 2.0l * q[2] * q[2];
443 
444  const Real q01 = 2.0l * q[0] * q[1];
445  const Real q02 = 2.0l * q[0] * q[2];
446  const Real q03 = 2.0l * q[0] * q[3];
447 
448  const Real q12 = 2.0l * q[1] * q[2];
449  const Real q13 = 2.0l * q[1] * q[3];
450 
451  const Real q23 = 2.0l * q[2] * q[3];
452 
453  m[0][0] = 1.0l - q11 - q22;
454  m[1][0] = q01 - q23;
455  m[2][0] = q02 + q13;
456 
457  m[0][1] = q01 + q23;
458  m[1][1] = 1.0l - q22 - q00;
459  m[2][1] = q12 - q03;
460 
461  m[0][2] = q02 - q13;
462  m[1][2] = q12 + q03;
463  m[2][2] = 1.0l - q11 - q00;
464  }
465 
466  void getMatrix16(Real m[16]) const
467  {
468  static Real mat[4][4];
469  getMatrix44(mat);
470  int count = 0;
471  for (int i=0; i<4; ++i)
472  for (int j=0; j<4; ++j)
473  m[count++] = mat[i][j];
474  }
475 
476 
481  void getRotationMatrix(Real m[3][3]) const
482  {
483  static Real mat[4][4];
484  getMatrix(mat);
485  for (int i=0; i<3; ++i)
486  for (int j=0; j<3; ++j)
487  // Beware of transposition
488  m[i][j] = mat[j][i];
489  }
490 
497  const Real* inverseMatrix() const
498  {
499  static Real m[4][4];
500  getInverseMatrix(m);
501  return (const Real*)(m);
502  }
503 
508  void getInverseMatrix(Real m[4][4]) const
509  {
510  inverse().getMatrix(m);
511  }
512 
513 
514  void getInverseMatrix(Real m[16]) const
515  {
516  inverse().getMatrix(m);
517  }
518 
519 
524  void getInverseRotationMatrix(Real m[3][3]) const
525  {
526  static Real mat[4][4];
527  getInverseMatrix(mat);
528  for (int i=0; i<3; ++i)
529  for (int j=0; j<3; ++j)
530  // Beware of transposition
531  m[i][j] = mat[j][i];
532  }
534 
535 
538  static TQuaternion slerp(const TQuaternion& a, const TQuaternion& b, Real t, bool allowFlip=true)
539  {
540  Real cosAngle = TQuaternion::dot(a, b);
541 
542  Real c1, c2;
543  // Linear interpolation for close orientations
544  if ((1.0 - fabs(cosAngle)) < 0.01)
545  {
546  c1 = 1.0 - t;
547  c2 = t;
548  }
549  else
550  {
551  // Spherical interpolation
552  Real angle = acos(fabs(cosAngle));
553  Real sinAngle = sin(angle);
554  c1 = sin(angle * (1.0 - t)) / sinAngle;
555  c2 = sin(angle * t) / sinAngle;
556  }
557 
558  // Use the shortest path
559  if (allowFlip && (cosAngle < 0.0))
560  c1 = -c1;
561 
562  return TQuaternion(c1*a[0] + c2*b[0], c1*a[1] + c2*b[1], c1*a[2] + c2*b[2], c1*a[3] + c2*b[3]);
563  }
564 
565 
566  static TQuaternion squad(const TQuaternion& a, const TQuaternion& tgA, const TQuaternion& tgB, const TQuaternion& b, Real t)
567  {
568  TQuaternion ab = TQuaternion::slerp(a, b, t);
569  TQuaternion tg = TQuaternion::slerp(tgA, tgB, t, false);
570  return TQuaternion::slerp(ab, tg, 2.0*t*(1.0-t), false);
571  }
572 
573 
574 
576  static Real dot(const TQuaternion& a, const TQuaternion& b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3]; }
577 
578  TQuaternion log()
579  {
580  Real len = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
581 
582  if (len < 1E-6)
583  return TQuaternion(q[0], q[1], q[2], 0.0);
584  else
585  {
586  Real coef = acos(q[3]) / len;
587  return TQuaternion(q[0]*coef, q[1]*coef, q[2]*coef, 0.0);
588  }
589  }
590 
591 
592  TQuaternion exp()
593  {
594  Real theta = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2]);
595 
596  if (theta < 1E-6)
597  return TQuaternion(q[0], q[1], q[2], cos(theta));
598  else
599  {
600  Real coef = sin(theta) / theta;
601  return TQuaternion(q[0]*coef, q[1]*coef, q[2]*coef, cos(theta));
602  }
603  }
604 
605 
606  static TQuaternion lnDif(const TQuaternion& a, const TQuaternion& b)
607  {
608  TQuaternion dif = a.inverse()*b;
609  dif.normalize();
610  return dif.log();
611  }
612 
613 
614  static TQuaternion squadTangent(const TQuaternion& before, const TQuaternion& center, const TQuaternion& after)
615  {
616  TQuaternion l1 = TQuaternion::lnDif(center,before);
617  TQuaternion l2 = TQuaternion::lnDif(center,after);
618  TQuaternion e;
619  for (int i=0; i<4; ++i)
620  e.q[i] = -0.25 * (l1.q[i] + l2.q[i]);
621  e = center*(e.exp());
622 
623  // if (TQuaternion::dot(e,b) < 0.0)
624  // e.negate();
625 
626  return e;
627  }
629 
632  static TQuaternion randomQuaternion()
633  {
634  // The rand() function is not very portable and may not be available on your system.
635  // Add the appropriate include or replace by an other random function in case of problem.
636  Real seed = rand()/(Real)RAND_MAX;
637  Real r1 = sqrt(1.0 - seed);
638  Real r2 = sqrt(seed);
639  Real t1 = 2.0 * M_PI * (rand()/(Real)RAND_MAX);
640  Real t2 = 2.0 * M_PI * (rand()/(Real)RAND_MAX);
641  return TQuaternion(sin(t1)*r1, cos(t1)*r1, sin(t2)*r2, cos(t2)*r2);
642  }
644 
647  //~ explicit TQuaternion(const QDomElement& element);
648  //~ QDomElement domElement(const QString& name, QDomDocument& document) const;
649  //~ void initFromDOMElement(const QDomElement& element);
651 
652  #ifdef DOXYN
660  inline std::ostream& operator<<(std::ostream& o, const qglviewer::Vec&);
662  #endif
663 
664  //template<typename C>
665  friend inline std::ostream& operator<<(std::ostream& o, const TQuaternion& Q)
666  {
667  o << "(" << Q[0] << ',' << Q[1] << ',' << Q[2] << ',' << Q[3] << ")";
668  return o;
669  }
670 
671 
672  private:
674  Real q[4];
675  };
676 
678  //typedef TQuaternion<double, Vector> Quaterniond;
679 
680 
681 
682 
683 #endif // _QUATERNION_H
684 
A Quaternion class.
Definition: quaternion.h:37
void negate()
Definition: quaternion.h:352
void getInverseMatrix(Real m[4][4]) const
Definition: quaternion.h:508
Real operator[](int i) const
Definition: quaternion.h:243
TQuaternion(const TQuaternion &Q)
Definition: quaternion.h:94
void getMatrix33(MAT &m) const
Definition: quaternion.h:438
void setAxisAngle(const Vec3Real &axis, const Real angle)
Definition: quaternion.h:116
static Real dot(const TQuaternion &a, const TQuaternion &b)
Definition: quaternion.h:576
Real normalize()
Definition: quaternion.h:358
friend Vec3Real operator*(const TQuaternion &q, const Vec3Real &v)
Definition: quaternion.h:291
friend TQuaternion operator*(const TQuaternion &a, const TQuaternion &b)
Definition: quaternion.h:268
void getRotationMatrix(Real m[3][3]) const
Definition: quaternion.h:481
TQuaternion & operator=(const TQuaternion &Q)
Definition: quaternion.h:98
void getMatrix44(MAT &m) const
Definition: quaternion.h:395
TQuaternion & operator*=(const TQuaternion &q)
Definition: quaternion.h:282
TQuaternion inverse() const
Definition: quaternion.h:337
TQuaternion(Real q0, Real q1, Real q2, Real q3)
Definition: quaternion.h:90
void invert()
Definition: quaternion.h:342
const Real * matrix() const
Definition: quaternion.h:381
friend TQuaternion operator*(const Real a, const TQuaternion &b)
Definition: quaternion.h:252
TQuaternion(const Vec3Real &axis, const Real angle)
Definition: quaternion.h:46
void setValue(Real q0, Real q1, Real q2, Real q3)
Definition: quaternion.h:138
Real & operator[](int i)
Definition: quaternion.h:246
void getInverseRotationMatrix(Real m[3][3]) const
Definition: quaternion.h:524
const Real * inverseMatrix() const
Definition: quaternion.h:497
Point center(const Point &a, const Point &b)
renvoie le milieu du segment ab.
Definition: vec.cpp:24
float length(const Vector &v)
renvoie la longueur d'un vecteur.
Definition: vec.cpp:142
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition: vec.cpp:129