Free Electron
Quaternion.h
Go to the documentation of this file.
1 /* Copyright (C) 2003-2021 Free Electron Organization
2  Any use of this software requires a license. If a valid license
3  was not distributed with this file, visit freeelectron.org. */
4 
5 /** @file */
6 
7 #ifndef __math_Quaternion_h__
8 #define __math_Quaternion_h__
9 
10 namespace fe
11 {
12 
13 #define FE_SLERP_DELTA (1e-6f)
14 #define FE_QUAT_DELTA (1e-6f)
15 #define FE_ALMOST1 (1.0f-FE_SLERP_DELTA)
16 
17 /**************************************************************************//**
18  @brief Four-dimensional complex number
19 
20  @ingroup math
21 
22  Laid out as <x,y,z,w> using right-handed logic.
23 
24  Unit quaternions are commonly used to represent rotations in 3D space.
25  So, many of the operation presume that the quaternion is normalized
26  and may behave poorly otherwise.
27 
28  Reference: Some of the quaternion math draws from Nick Bobic's
29  GLquat source code from his Feb 98 Game Developer article.
30 *//***************************************************************************/
31 template<class T>
32 class Quaternion: protected Vector<4,T>
33 {
34  public:
35  Quaternion(void);
36  Quaternion(const Quaternion<T>& other);
37  Quaternion(T radians,const Vector<3,T>& axis);
38  Quaternion(T radians,Axis axis);
39  Quaternion(const T* pArray);
40  Quaternion(T x,T y,T z,T w);
41  Quaternion(const Matrix<3,4,T>& matrix);
42 
43  Quaternion<T>& operator=(const Quaternion<T>& other);
44  Quaternion<T>& operator=(const Matrix<3,4,T>& matrix);
45 
46  void computeAngleAxis(T &radians,Vector<3,T>& axis) const;
47 
48 using Vector<4,T>::operator[];
50 };
51 
52 template<class T>
53 inline Quaternion<T>::Quaternion(void)
54 {
55  set(*this,0.0f,0.0f,0.0f,1.0f);
56 }
57 
58 template<class T>
59 inline Quaternion<T>::Quaternion(const Quaternion<T>& other):
60  Vector<4,T>()
61 {
62  operator=(other);
63 }
64 
65 template<class T>
66 inline Quaternion<T>::Quaternion(T radians,const Vector<3,T>& axis)
67 {
68  set(*this,radians,axis);
69 }
70 
71 template<class T>
72 inline Quaternion<T>::Quaternion(T radians,const Axis axis)
73 {
74  set(*this,radians,axis);
75 }
76 
77 template<class T>
78 inline Quaternion<T>::Quaternion(const T* pArray)
79 {
80  operator=(pArray);
81 }
82 
83 template<class T>
84 inline Quaternion<T>::Quaternion(T x, T y, T z, T w)
85 {
86  set(*this,x,y,z,w);
87 }
88 
89 template<class T>
91 {
92  set(*this,other[0],other[1],other[2],other[3]);
93  return *this;
94 }
95 
96 template<class T>
97 inline void Quaternion<T>::computeAngleAxis(T &radians,Vector<3,T>& axis) const
98 {
99  T len=(*this)[0]*(*this)[0]+(*this)[1]*(*this)[1]+(*this)[2]*(*this)[2];
100  if(len==0.0f)
101  {
102  set(axis,0.0f,0.0f,1.0f);
103  radians=0.0f;
104  return;
105  }
106 
107  T inv=1.0f/sqrt(len);
108  if((*this)[3]<0.0f)
109  inv= -inv;
110 
111  set(axis,(*this)[0]*inv,(*this)[1]*inv,(*this)[2]*inv);
112 
113  radians=2.0f*acos(fabs((*this)[3]));
114  if(FE_INVALID_SCALAR(radians))
115  {
116  radians=0.0f;
117  }
118 }
119 
120 /** @brief Set the quaternion to identity
121 
122  @relates Quaternion
123 
124  The default for the components are the elements of the identity quaternion.
125  The state is not automatically normalized.
126  */
127 template<class T>
128 inline Quaternion<T>& set(Quaternion<T>& result)
129 {
130  Vector<4,T>* pVector=reinterpret_cast< Vector<4,T>* >(&result);
131  set(*pVector,T(0),T(0),T(0),T(1));
132  return result;
133 }
134 
135 /** @brief Set the components explicitly
136 
137  @relates Quaternion
138 
139  The state is not automatically normalized.
140  */
141 template<class T,class X,class Y,class Z,class W>
142 inline Quaternion<T>& set(Quaternion<T>& result,X x,Y y,Z z,W w)
143 {
144  Vector<4,T>* pVector=reinterpret_cast< Vector<4,T>* >(&result);
145  set(*pVector,x,y,z,w);
146  return result;
147 }
148 
149 /** @brief Normalize all four components
150 
151  @relates Quaternion
152  */
153 template<class T>
155 {
156  Vector<4,T>* pVector=reinterpret_cast< Vector<4,T>* >(&result);
157  normalize(*pVector);
158  return result;
159 }
160 
161 /** @brief Set the value at the index
162 
163  @relates Quaternion
164  */
165 template<class T,class U>
166 inline Quaternion<T>& setAt(Quaternion<T> &lhs, U32 index, U value)
167 {
168  Vector<4,T>* pVector=reinterpret_cast< Vector<4,T>* >(&lhs);
169  setAt(*pVector,index,value);
170  return lhs;
171 }
172 
173 /** @brief Set using arbitrary angle/axis format
174 
175  @relates Quaternion
176 
177  Axis should already be normalized.
178  */
179 template<class T,class U>
180 inline Quaternion<T>& set(Quaternion<T> &result,U radians,
181  const Vector<3,T>& axis)
182 {
183  if(fabs(axis[0])+fabs(axis[1])+fabs(axis[2])<FE_QUAT_DELTA)
184  {
185  set(result);
186  return result;
187  }
188 
189  T halfAngle=radians*0.5f;
190  const Vector<3,T> temp=axis*sin(halfAngle);
191 
192  set(result,temp[0],temp[1],temp[2],cos(halfAngle));
193  return result;
194 }
195 
196 /** @brief Set using angle about a specific axis
197 
198  @relates Quaternion
199  */
200 template<class T,class U>
201 inline Quaternion<T>& set(Quaternion<T> &result,U radians,const Axis axis)
202 {
203  T halfAngle=radians*0.5f;
204 
205  set(result,0.0f,0.0f,0.0f,cos(halfAngle));
206  setAt(result,axis,sin(halfAngle));
207  return result;
208 }
209 
210 /** @brief Set as the angle between two vectors
211 
212  @relates Quaternion
213 
214  Operands should be pre-normalized.
215 
216  Twist should be considered arbitrary, but it is fairly consistant.
217  */
218 template<class T>
219 inline Quaternion<T>& set(Quaternion<T> &result,const Vector<3,T>& from,
220  const Vector<3,T>& to)
221 {
222  //* NOTE requires normalized operands
223  FEASSERT(fabs(magnitudeSquared(from)-1.0)<1e-3);
224  FEASSERT(fabs(magnitudeSquared(to)-1.0)<1e-3);
225 
226  T tx,ty,tz,temp,dist;
227 
228  // get dot product of two vectors
229 // cost=from[0]*to[0] + from[1]*to[1] + from[2]*to[2];
230  T cost=dot(from,to);
231 
232  // check if parallel
233  if (cost > FE_ALMOST1)
234  {
235  set(result);
236  return result;
237  }
238  if (cost < -FE_ALMOST1)
239  {
240  // check if opposite
241 
242  // check if we can use cross product of from vector with [1, 0, 0]
243  tx=0.0f;
244  ty=from[0];
245  tz= -from[1];
246 
247  temp=ty*ty + tz*tz;
248  FEASSERT(temp>=0.0);
249  T len=sqrt(ty*ty + tz*tz);
250 
251  if (len < FE_SLERP_DELTA)
252  {
253  // no, we need cross product of from vector with [0, 1, 0]
254  tx= -from[2];
255  ty=0.0f;
256  tz=from[0];
257  }
258 
259  // normalize
260  temp=tx*tx + ty*ty + tz*tz;
261  FEASSERT(temp>0.0);
262  dist=1.0f/sqrt(temp);
263 
264  tx*=dist;
265  ty*=dist;
266  tz*=dist;
267 
268  set(result,tx,ty,tz,0.0f);
269  return result;
270  }
271 
272  // else just cross two vectors
273  tx=from[1]*to[2] - from[2]*to[1];
274  ty=from[2]*to[0] - from[0]*to[2];
275  tz=from[0]*to[1] - from[1]*to[0];
276 
277  temp=tx*tx + ty*ty + tz*tz;
278  FEASSERT(temp>0.0);
279  dist=1.0f/sqrt(temp);
280 
281  tx*=dist;
282  ty*=dist;
283  tz*=dist;
284 
285  // use half-angle formula (sin^2 t=( 1 - cos(2t) )/2)
286  temp=0.5f * (1.0f - cost);
287  FEASSERT(temp>=0.0);
288  T ss=sqrt(temp);
289 
290  tx*=ss;
291  ty*=ss;
292  tz*=ss;
293 
294  // cos^2 t=( 1 + cos (2t) ) / 2
295  // w part is cosine of half the rotation angle
296  set(result,tx,ty,tz,sqrt(0.5f * (1.0f + cost)));
297  return result;
298 }
299 
300 
301 //* UNARY OPERATORS
302 /** @brief Multiply in place
303 
304  @relates Quaternion
305  */
306 template<class T>
308 {
309  Quaternion<T> temp=lhs; // deep copy
310 
311  return lhs=temp*rhs;
312 }
313 
314 /** @brief Scale in place
315 
316  @relates Quaternion
317  */
318 template<class T,class U>
319 inline Quaternion<T>& operator*=(Quaternion<T>& lhs, U scale)
320 {
321  Quaternion<T> temp=lhs; // deep copy
322 
323  return lhs=temp*scale;
324 }
325 
326 /** @brief Negate all elements if W is negative
327 
328  @relates Quaternion
329 
330  Every quaternion has an equivalent quaternion with all the elements
331  reversed. This forces the quaternion to the positive-W form.
332  */
333 template<class T>
335 {
336  if(lhs[3]<0)
337  {
338  set(lhs,-lhs[0],-lhs[1],-lhs[2],-lhs[3]);
339  }
340  return lhs;
341 }
342 
343 /** @brief Reverse the direction of rotation (and retain the magnitude)
344 
345  @relates Quaternion
346  */
347 template<class T>
349 {
350  const Quaternion<T>& clhs=lhs;
351  set(lhs,-clhs[0],-clhs[1],-clhs[2],clhs[3]);
352  return lhs;
353 }
354 
355 /** @brief Return the inverse rotation
356 
357  @relates Quaternion
358  */
359 template<class T>
361 {
362  Quaternion<T> inverse;
363  set(inverse,-lhs[0],-lhs[1],-lhs[2],lhs[3]);
364  return inverse;
365 }
366 
367 /** @brief Print to a string
368 
369  @relates Quaternion
370  */
371 template<class T>
372 inline String print(const Quaternion<T>& lhs)
373 {
374  String s;
375  s.sPrintf("%g %g %g %g",lhs[0],lhs[1],lhs[2],lhs[3]);
376  return s;
377 }
378 
379 //* BINARY OPERATORS
380 /** @brief Equality test
381 
382  @relates Quaternion
383  */
384 template<class T>
385 inline bool operator==(const Quaternion<T>& lhs, const Quaternion<T>& rhs)
386 {
387  for(U32 i=0; i<4; i++)
388  if(rhs[i] != lhs[i])
389  return false;
390  return true;
391 }
392 
393 /** @brief Equivalence test within the given tolerance @em margin
394 
395  @relates Quaternion
396  */
397 template<class T>
398 inline bool equivalent(const Quaternion<T> &lhs,const Quaternion<T> &rhs,
399  T margin)
400 {
401  return( !FE_INVALID_SCALAR(lhs[0]) &&
402  !FE_INVALID_SCALAR(lhs[1]) &&
403  !FE_INVALID_SCALAR(lhs[2]) &&
404  !FE_INVALID_SCALAR(lhs[3]) &&
405  !FE_INVALID_SCALAR(rhs[0]) &&
406  !FE_INVALID_SCALAR(rhs[1]) &&
407  !FE_INVALID_SCALAR(rhs[2]) &&
408  !FE_INVALID_SCALAR(rhs[3]) &&
409  fabs(rhs[0]-lhs[0]) < margin &&
410  fabs(rhs[1]-lhs[1]) < margin &&
411  fabs(rhs[2]-lhs[2]) < margin &&
412  fabs(rhs[3]-lhs[3]) < margin);
413 }
414 
415 /** @brief Multiply two quaternions
416 
417  @relates Quaternion
418  */
419 template<class T>
421  const Quaternion<T>& rhs)
422 {
423  Quaternion<T> sum;
424  set(sum, lhs[3]*rhs[0]+lhs[0]*rhs[3]+lhs[1]*rhs[2]-lhs[2]*rhs[1],
425  lhs[3]*rhs[1]+lhs[1]*rhs[3]+lhs[2]*rhs[0]-lhs[0]*rhs[2],
426  lhs[3]*rhs[2]+lhs[2]*rhs[3]+lhs[0]*rhs[1]-lhs[1]*rhs[0],
427  lhs[3]*rhs[3]-lhs[0]*rhs[0]-lhs[1]*rhs[1]-lhs[2]*rhs[2]);
428  return sum;
429 }
430 
431 /** @brief Scale the rotation angle
432 
433  @relates Quaternion
434  */
435 template<class T,class U>
436 inline Quaternion<T> operator*(const U& lhs, const Quaternion<T>& rhs)
437 {
438  return operator*(rhs,lhs);
439 }
440 
441 /** @brief Scale the rotation angle
442 
443  @relates Quaternion
444  */
445 template<class T,class U>
446 inline Quaternion<T> operator*(const Quaternion<T>& lhs, const U& rhs)
447 {
448  Quaternion<T> sum;
449 
450  T angle;
451  Vector<3,T> axis;
452 
453  lhs.computeAngleAxis(angle,axis);
454  set(sum,angle*rhs,axis);
455  return sum;
456 }
457 
458 template <typename T>
459 inline void rotateVector(const Quaternion<T>& lhs,const Vector<3,T>& in,
460  Vector<3,T>& out)
461 {
462  T mid[4];
463 
464  mid[0]= lhs[3]*in[0]+lhs[1]*in[2]-lhs[2]*in[1];
465  mid[1]= lhs[3]*in[1]+lhs[2]*in[0]-lhs[0]*in[2];
466  mid[2]= lhs[3]*in[2]+lhs[0]*in[1]-lhs[1]*in[0];
467  mid[3]= -lhs[0]*in[0]-lhs[1]*in[1]-lhs[2]*in[2];
468 
469  fe::set(out,
470  -mid[3]*lhs[0]+mid[0]*lhs[3]-mid[1]*lhs[2]+mid[2]*lhs[1],
471  -mid[3]*lhs[1]+mid[1]*lhs[3]-mid[2]*lhs[0]+mid[0]*lhs[2],
472  -mid[3]*lhs[2]+mid[2]*lhs[3]-mid[0]*lhs[1]+mid[1]*lhs[0]);
473 }
474 
475 template <typename T>
476 inline void rotateXVector(const Quaternion<T>& lhs,Real x,
477  Vector<3,T>& out)
478 {
479  fe::set(out,
480  x*(lhs[0]*lhs[0]+lhs[3]*lhs[3]-lhs[2]*lhs[2]-lhs[1]*lhs[1]),
481  2.0*x*(lhs[0]*lhs[1]+lhs[2]*lhs[3]),
482  2.0*x*(lhs[0]*lhs[2]-lhs[1]*lhs[3]));
483 }
484 
485 template <typename T>
486 inline void rotateYVector(const Quaternion<T>& lhs,Real y,
487  Vector<3,T>& out)
488 {
489  fe::set(out,
490  2.0*y*(lhs[0]*lhs[1]-lhs[2]*lhs[3]),
491  y*(lhs[1]*lhs[1]+lhs[3]*lhs[3]-lhs[0]*lhs[0]-lhs[2]*lhs[2]),
492  2.0*y*(lhs[0]*lhs[3]+lhs[1]*lhs[2]));
493 }
494 
495 template <typename T>
496 inline void rotateZVector(const Quaternion<T>& lhs,Real z,
497  Vector<3,T>& out)
498 {
499  fe::set(out,
500  2.0*z*(lhs[0]*lhs[2]+lhs[3]*lhs[1]),
501  2.0*z*(lhs[2]*lhs[1]-lhs[0]*lhs[3]),
502  z*(lhs[2]*lhs[2]+lhs[3]*lhs[3]-lhs[1]*lhs[1]-lhs[0]*lhs[0]));
503 }
504 
505 /** @brief Generate a rotation that in between two other rotations
506 
507  Uses basic linear interpolation with renormalization.
508  Commutative and torque-minimal.
509 
510  @relates Quaternion
511  */
512 template<class T>
514  const Quaternion<T> &from,const Quaternion<T> &to,T fraction)
515 {
516  result=from+fraction*(to-from);
517  normalize(result);
518  return result;
519 }
520 
521 /** @brief Generate a rotation that in between two other rotations
522 
523  Uses spherically linear interpolation.
524  Constant velocity and torque-minimal.
525 
526  (See Grassia log-quaternion lerp for commutative and constant velocity.)
527 
528  @relates Quaternion
529  */
530 template<class T>
532  const Quaternion<T> &from,const Quaternion<T> &to,T fraction)
533 {
534  T copy[4];
535  T cosom;
536  T scale0, scale1;
537 
538  // calc cosine
539  cosom = from[0]*to[0]+
540  from[1]*to[1]+
541  from[2]*to[2]+
542  from[3]*to[3];
543 
544  // adjust signs (if necessary)
545  if(cosom < 0.0f)
546  {
547  cosom= -cosom;
548 
549  copy[0]= -to[0];
550  copy[1]= -to[1];
551  copy[2]= -to[2];
552  copy[3]= -to[3];
553  }
554  else
555  {
556  copy[0]=to[0];
557  copy[1]=to[1];
558  copy[2]=to[2];
559  copy[3]=to[3];
560  }
561 
562  // calculate coefficients
563 
564  if ( (1.0f - cosom) > FE_SLERP_DELTA )
565  {
566  // standard case (slerp)
567  T omega = acos(cosom);
568  T sinom = sin(omega);
569  scale0 = sin((1.0f-fraction) * omega) / sinom;
570  scale1 = sin(fraction * omega) / sinom;
571  }
572  else
573  {
574  // "from" and "to" quaternions are very close
575  // ... so we can do a linear interpolation
576 
577  scale0 = 1.0f-fraction;
578  scale1 = fraction;
579  }
580 
581  // calculate final values
582  set(result, scale0*from[0] + scale1*copy[0],
583  scale0*from[1] + scale1*copy[1],
584  scale0*from[2] + scale1*copy[2],
585  scale0*from[3] + scale1*copy[3]);
586  return result;
587 }
588 
589 template<class T>
590 Real removeTwistAboutX(Quaternion<T>& rRotation)
591 {
592  Vector<3,T> yAxis(0.0f,1.0f,0.0f);
593 
594  forcePositiveW(rRotation);
595 
596  Vector<3,T> rotated;
597  rotateVector(rRotation,yAxis,rotated);
598 
599  FEASSERT(rotated[0]!=0.0f);
600  Real angle=atan2(rotated[2],rotated[1]);
601 
602  Quaternion<T> qinv;
603  set(qinv,-angle,e_xAxis);
604 
605  Quaternion<T> result=qinv*rRotation;
606 
607  rRotation=result;
608  return angle;
609 }
610 
611 template<class T>
612 Real removeTwistAboutY(Quaternion<T>& rRotation)
613 {
614  Vector<3,T> zAxis(0.0f,0.0f,1.0f);
615 
616  forcePositiveW(rRotation);
617 
618  Vector<3,T> rotated;
619  rotateVector(rRotation,zAxis,rotated);
620 
621  FEASSERT(rotated[0]!=0.0f);
622  Real angle=atan2(rotated[0],rotated[2]);
623 
624  Quaternion<T> qinv;
625  set(qinv,-angle,e_yAxis);
626 
627  Quaternion<T> result=qinv*rRotation;
628 
629  rRotation=result;
630  return angle;
631 }
632 
633 template<class T>
634 Real removeTwistAboutZ(Quaternion<T>& rRotation)
635 {
636  Vector<3,T> xAxis(1.0f,0.0f,0.0f);
637 
638  forcePositiveW(rRotation);
639 
640  Vector<3,T> rotated;
641  rotateVector(rRotation,xAxis,rotated);
642 
643  FEASSERT(rotated[0]!=0.0f);
644  Real angle=atan2(rotated[1],rotated[0]);
645 
646  Quaternion<T> qinv;
647  set(qinv,-angle,e_zAxis);
648 
649  Quaternion<T> result=qinv*rRotation;
650 
651  rRotation=result;
652  return angle;
653 }
654 
658 
659 /** @brief Return a new velocity based on desired change and constraints
660 
661  Over the given delta time, tries to return @em desired change,
662  but contrains the result using the @em previous velocity,
663  maximum velocity and maximum acceleration.
664 
665  The @em desired change is how far the velocity would take the angle
666  in this frame if acceleration was infinite.
667  The result may try to predict deceleration if the remaining change is
668  relatively small with respect to the @em maxAcceleration.
669 
670  Note that the use of quaternions limits angular values to 180 degrees,
671  including velocity and acceleration (per second and second^2).
672 */
673 FE_DL_PUBLIC SpatialQuaternion stepVelocity(const SpatialQuaternion& previous,
674  const SpatialQuaternion& desired,
675  Real deltaT,Real maxVelocity, Real maxAcceleration);
676 
677 } /* namespace */
678 
679 #endif /* __math_Quaternion_h__ */
680 
Quaternion< T > & invert(Quaternion< T > &lhs)
Reverse the direction of rotation (and retain the magnitude)
Definition: Quaternion.h:348
bool operator==(const Quaternion< T > &lhs, const Quaternion< T > &rhs)
Equality test.
Definition: Quaternion.h:385
Matrix for 3D transformations.
Definition: Matrix3x4.h:47
SpatialQuaternion stepVelocity(const SpatialQuaternion &previous, const SpatialQuaternion &desired, Real deltaT, Real maxVelocity, Real maxAcceleration)
Return a new velocity based on desired change and constraints.
Definition: Quaternion.cc:14
kernel
Definition: namespace.dox:3
Quaternion< T > & forcePositiveW(Quaternion< T > &lhs)
Negate all elements if W is negative.
Definition: Quaternion.h:334
Quaternion< T > & normalize(Quaternion< T > &result)
Normalize all four components.
Definition: Quaternion.h:154
Quaternion< T > & nlerp(Quaternion< T > &result, const Quaternion< T > &from, const Quaternion< T > &to, T fraction)
Generate a rotation that in between two other rotations.
Definition: Quaternion.h:513
Dense vector - size fixed by template.
Definition: Vector.h:19
String print(const Quaternion< T > &lhs)
Print to a string.
Definition: Quaternion.h:372
Quaternion< T > & operator*=(Quaternion< T > &lhs, U scale)
Scale in place.
Definition: Quaternion.h:319
String & sPrintf(const char *fmt,...)
Populate the string in the manner of sprintf().
Definition: String.cc:529
Automatically reference-counted string container.
Definition: String.h:128
Quaternion< T > operator*(const Quaternion< T > &lhs, const U &rhs)
Scale the rotation angle.
Definition: Quaternion.h:446
Quaternion< T > & slerp(Quaternion< T > &result, const Quaternion< T > &from, const Quaternion< T > &to, T fraction)
Generate a rotation that in between two other rotations.
Definition: Quaternion.h:531
Quaternion< T > & setAt(Quaternion< T > &lhs, U32 index, U value)
Set the value at the index.
Definition: Quaternion.h:166
Quaternion< T > operator-(const Quaternion< T > &lhs)
Return the inverse rotation.
Definition: Quaternion.h:360
Quaternion< T > operator*(const Quaternion< T > &lhs, const Quaternion< T > &rhs)
Multiply two quaternions.
Definition: Quaternion.h:420
Partially specialized 4-component vector intended for spatial use.
Definition: Vector4.h:21
Four-dimensional complex number.
Definition: Quaternion.h:32
bool equivalent(const Quaternion< T > &lhs, const Quaternion< T > &rhs, T margin)
Equivalence test within the given tolerance margin.
Definition: Quaternion.h:398
Quaternion< T > & operator*=(Quaternion< T > &lhs, const Quaternion< T > &rhs)
Multiply in place.
Definition: Quaternion.h:307
Quaternion< T > operator*(const U &lhs, const Quaternion< T > &rhs)
Scale the rotation angle.
Definition: Quaternion.h:436