Pioneer
Quaternion.h
Go to the documentation of this file.
1 // Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #ifndef _QUATERNION_H
5 #define _QUATERNION_H
6 
7 #include "matrix4x4.h"
8 #include "vector3.h"
9 #include <math.h>
10 #include <type_traits>
11 
12 template <typename T>
13 class Quaternion {
14  using other_float_t = typename std::conditional<std::is_same<T, float>::value, double, float>::type;
15 
16 public:
17  T w, x, y, z;
18 
19  // Constructor definitions are outside class declaration to enforce that
20  // only float and double versions are possible.
22  Quaternion(T w, T x, T y, T z);
23  // from angle and axis
24  Quaternion(T ang, vector3<T> axis)
25  {
26  const T halfAng = ang * T(0.5);
27  const T sinHalfAng = sin(halfAng);
28  w = cos(halfAng);
29  x = axis.x * sinHalfAng;
30  y = axis.y * sinHalfAng;
31  z = axis.z * sinHalfAng;
32  }
33  // from axis, assume angle == PI
34  // optimized fast path using sin(PI/2) = 1
36  {
37  w = 0;
38  x = axis.x;
39  y = axis.y;
40  z = axis.z;
41  }
43  w(o.w),
44  x(o.x),
45  y(o.y),
46  z(o.z) {}
47 
48  void GetAxisAngle(T &angle, vector3<T> &axis)
49  {
50  if (w > 1.0) *this = Normalized(); // if w>1 acos and sqrt will produce errors, this can't happen if quaternion is normalised
51  angle = 2.0 * acos(w);
52  double s = sqrt(1.0 - w * w); // assuming quaternion normalised then w is less than 1, so term always positive.
53  if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt
54  // if s close to zero then direction of axis not important
55  axis.x = x; // if it is important that axis is normalised then replace with x=1; y=z=0;
56  axis.y = y;
57  axis.z = z;
58  } else {
59  axis.x = x / s; // normalise axis
60  axis.y = y / s;
61  axis.z = z / s;
62  }
63  }
64  bool operator==(const Quaternion &a) const
65  {
66  return is_equal_exact(a.w, w) && is_equal_exact(a.x, x) && is_equal_exact(a.y, y) && is_equal_exact(a.z, z);
67  }
68  bool ExactlyEqual(const Quaternion &a) const
69  {
70  return is_equal_exact(a.w, w) && is_equal_exact(a.x, x) && is_equal_exact(a.y, y) && is_equal_exact(a.z, z);
71  }
72 
73  // conjugate (inverse)
74  friend Quaternion operator~(const Quaternion &a)
75  {
76  Quaternion r;
77  r.w = a.w;
78  r.x = -a.x;
79  r.y = -a.y;
80  r.z = -a.z;
81  return r;
82  }
83  friend Quaternion operator*(const Quaternion &a, const Quaternion &b)
84  {
85  Quaternion r;
86  r.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z;
87  r.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y;
88  r.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x;
89  r.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w;
90  return r;
91  }
92  friend Quaternion operator*(const T s, const Quaternion &a) { return a * s; }
93  friend Quaternion operator*(const Quaternion &a, const T s)
94  {
95  Quaternion r;
96  r.w = a.w * s;
97  r.x = a.x * s;
98  r.y = a.y * s;
99  r.z = a.z * s;
100  return r;
101  }
102  // vector transform with quaternion
103  // (see https://community.khronos.org/t/quaternion-functions-for-glsl/50140/3)
104  friend vector3<T> operator*(const Quaternion &a, const vector3<T> &vec)
105  {
106  vector3<T> xyz = vector3<T>(a.x, a.y, a.z);
107  return vec + 2.0 * (vec.Cross(xyz) + a.w * vec).Cross(xyz);
108  }
109  friend Quaternion operator+(const Quaternion &a, const Quaternion &b)
110  {
111  Quaternion r;
112  r.w = a.w + b.w;
113  r.x = a.x + b.x;
114  r.y = a.y + b.y;
115  r.z = a.z + b.z;
116  return r;
117  }
118  friend Quaternion operator-(const Quaternion &a, const Quaternion &b)
119  {
120  Quaternion r;
121  r.w = a.w - b.w;
122  r.x = a.x - b.x;
123  r.y = a.y - b.y;
124  r.z = a.z - b.z;
125  return r;
126  }
127 
129  {
130  T l = 1.0 / sqrt(w * w + x * x + y * y + z * z);
131  return Quaternion(w * l, x * l, y * l, z * l);
132  }
133  static T Dot(const Quaternion &a, const Quaternion &b) { return a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z; }
134 
135  template <typename U>
137  {
138  Quaternion r;
139  if (m[0] + m[4] + m[8] > 0.0f) {
140  U t = m[0] + m[4] + m[8] + 1.0;
141  U s = 0.5 / sqrt(t);
142  r.w = s * t;
143  r.z = (m[3] - m[1]) * s;
144  r.y = (m[2] - m[6]) * s;
145  r.x = (m[7] - m[5]) * s;
146  } else if ((m[0] > m[4]) && (m[0] > m[8])) {
147  U t = m[0] - m[4] - m[8] + 1.0;
148  U s = 0.5 / sqrt(t);
149  r.x = s * t;
150  r.y = (m[1] + m[3]) * s;
151  r.z = (m[2] + m[6]) * s;
152  r.w = (m[7] - m[5]) * s;
153  } else if (m[4] > m[8]) {
154  U t = -m[0] + m[4] - m[8] + 1.0;
155  U s = 0.5 / sqrt(t);
156  r.w = (m[2] - m[6]) * s;
157  r.x = (m[1] + m[3]) * s;
158  r.y = s * t;
159  r.z = (m[5] + m[7]) * s;
160  } else {
161  U t = -m[0] - m[4] + m[8] + 1.0;
162  U s = 0.5 / sqrt(t);
163  r.w = (m[3] - m[1]) * s;
164  r.x = (m[2] + m[6]) * s;
165  r.y = (m[7] + m[5]) * s;
166  r.z = s * t;
167  }
168  return r;
169  }
170 
171  template <typename U>
173  {
174  matrix3x3<U> m;
175  U xx = x * x;
176  U xy = x * y;
177  U xz = x * z;
178  U xw = x * w;
179 
180  U yy = y * y;
181  U yz = y * z;
182  U yw = y * w;
183 
184  U zz = z * z;
185  U zw = z * w;
186 
187  m[0] = 1.0 - 2.0 * (yy + zz);
188  m[1] = 2.0 * (xy - zw);
189  m[2] = 2.0 * (xz + yw);
190 
191  m[3] = 2.0 * (xy + zw);
192  m[4] = 1.0 - 2.0 * (xx + zz);
193  m[5] = 2.0 * (yz - xw);
194 
195  m[6] = 2.0 * (xz - yw);
196  m[7] = 2.0 * (yz + xw);
197  m[8] = 1.0 - 2.0 * (xx + yy);
198  return m;
199  }
200  /* normalized linear interpolation between 2 quaternions */
201  static Quaternion Nlerp(const Quaternion &a, const Quaternion &b, T t)
202  {
203  //printf("a: %f,%f,%f,%f\n", a.x, a.y, a.z, a.w);
204  //printf("b: %f,%f,%f,%f\n", b.x, b.y, b.z, b.w);
205  return (a + t * (b - a)).Normalized();
206  }
207 
208  // spherical linear interpolation between two quaternions
209  // taken from assimp via #2514
210  static Quaternion Slerp(const Quaternion &a, const Quaternion &b, T t)
211  {
212  // calc cosine theta
213  T cosom = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
214 
215  // adjust signs (if necessary)
216  Quaternion end = b;
217  if (cosom < T(0.0)) {
218  cosom = -cosom;
219  end.x = -end.x; // Reverse all signs
220  end.y = -end.y;
221  end.z = -end.z;
222  end.w = -end.w;
223  }
224 
225  // Calculate coefficients
226  T sclp, sclq;
227  if ((T(1.0) - cosom) > T(0.0001)) { // 0.0001 -> some epsillon
228  // Standard case (slerp)
229  T omega, sinom;
230  omega = acos(cosom); // extract theta from dot product's cos theta
231  sinom = sin(omega);
232  sclp = sin((T(1.0) - t) * omega) / sinom;
233  sclq = sin(t * omega) / sinom;
234  } else {
235  // Very close, do linear interp (because it's faster)
236  sclp = T(1.0) - t;
237  sclq = t;
238  }
239 
240  return Quaternion(
241  sclp * a.w + sclq * end.w,
242  sclp * a.x + sclq * end.x,
243  sclp * a.y + sclq * end.y,
244  sclp * a.z + sclq * end.z);
245  }
246 
247  //void Print() const {
248  // printf("%f,%f,%f,%f\n", w, x, y, z);
249  //}
250 };
251 
252 template <>
254  w(1.f),
255  x(0.f),
256  y(0.f),
257  z(0.f) {}
258 template <>
260  w(1.),
261  x(0.),
262  y(0.),
263  z(0.) {}
264 template <>
265 inline Quaternion<float>::Quaternion(float w_, float x_, float y_, float z_) :
266  w(w_),
267  x(x_),
268  y(y_),
269  z(z_) {}
270 template <>
271 inline Quaternion<double>::Quaternion(double w_, double x_, double y_, double z_) :
272  w(w_),
273  x(x_),
274  y(y_),
275  z(z_) {}
276 
279 
280 #endif /* _QUATERNION_H */
bool is_equal_exact(float a, float b)
Definition: FloatComparison.h:112
Quaternion< double > Quaterniond
Definition: Quaternion.h:278
Quaternion< float > Quaternionf
Definition: Quaternion.h:277
Definition: Quaternion.h:13
friend Quaternion operator-(const Quaternion &a, const Quaternion &b)
Definition: Quaternion.h:118
Quaternion Normalized() const
Definition: Quaternion.h:128
static Quaternion Slerp(const Quaternion &a, const Quaternion &b, T t)
Definition: Quaternion.h:210
T y
Definition: Quaternion.h:17
Quaternion(T w, T x, T y, T z)
friend Quaternion operator~(const Quaternion &a)
Definition: Quaternion.h:74
static Quaternion Nlerp(const Quaternion &a, const Quaternion &b, T t)
Definition: Quaternion.h:201
friend Quaternion operator*(const T s, const Quaternion &a)
Definition: Quaternion.h:92
Quaternion(T ang, vector3< T > axis)
Definition: Quaternion.h:24
friend Quaternion operator*(const Quaternion &a, const Quaternion &b)
Definition: Quaternion.h:83
void GetAxisAngle(T &angle, vector3< T > &axis)
Definition: Quaternion.h:48
static Quaternion FromMatrix3x3(const matrix3x3< U > &m)
Definition: Quaternion.h:136
matrix3x3< U > ToMatrix3x3() const
Definition: Quaternion.h:172
T x
Definition: Quaternion.h:17
friend Quaternion operator*(const Quaternion &a, const T s)
Definition: Quaternion.h:93
T w
Definition: Quaternion.h:17
Quaternion(const Quaternion< other_float_t > &o)
Definition: Quaternion.h:42
friend Quaternion operator+(const Quaternion &a, const Quaternion &b)
Definition: Quaternion.h:109
friend vector3< T > operator*(const Quaternion &a, const vector3< T > &vec)
Definition: Quaternion.h:104
Quaternion(vector3< T > axis)
Definition: Quaternion.h:35
bool ExactlyEqual(const Quaternion &a) const
Definition: Quaternion.h:68
bool operator==(const Quaternion &a) const
Definition: Quaternion.h:64
static T Dot(const Quaternion &a, const Quaternion &b)
Definition: Quaternion.h:133
T z
Definition: Quaternion.h:17
Definition: matrix3x3.h:13
Definition: vector3.h:16
T y
Definition: vector3.h:18
T x
Definition: vector3.h:18
T z
Definition: vector3.h:18
vector3 Cross(const vector3 &b) const
Definition: vector3.h:117