Quaternion-Impl.hpp
Go to the documentation of this file.
1 // This code is based on Jet framework.
2 // Copyright (c) 2018 Doyub Kim
3 // CubbyFlow is voxel-based fluid simulation engine for computer games.
4 // Copyright (c) 2020 CubbyFlow Team
5 // Core Part: Chris Ohk, Junwoo Hwang, Jihong Sin, Seungwoo Yoo
6 // AI Part: Dongheon Cho, Minseo Kim
7 // We are making my contributions/submissions to this project solely in our
8 // personal capacity and are not conveying any rights to any intellectual
9 // property of any third parties.
10 
11 #ifndef CUBBYFLOW_QUATERNION_IMPL_HPP
12 #define CUBBYFLOW_QUATERNION_IMPL_HPP
13 
14 namespace CubbyFlow
15 {
16 template <typename T>
18 {
19  SetIdentity();
20 }
21 
22 template <typename T>
23 Quaternion<T>::Quaternion(T newW, T newX, T newY, T newZ)
24 {
25  Set(newW, newX, newY, newZ);
26 }
27 
28 template <typename T>
29 Quaternion<T>::Quaternion(const std::initializer_list<T>& list)
30 {
31  Set(list);
32 }
33 
34 template <typename T>
36 {
37  Set(axis, angle);
38 }
39 
40 template <typename T>
42 {
43  Set(from, to);
44 }
45 
46 template <typename T>
47 Quaternion<T>::Quaternion(const Vector3<T>& axis0, const Vector3<T>& axis1,
48  const Vector3<T>& axis2)
49 {
50  Set(axis0, axis1, axis2);
51 }
52 
53 template <typename T>
55 {
56  Set(m33);
57 }
58 
59 template <typename T>
61 {
62  Set(other);
63 }
64 
65 template <typename T>
67 {
68  Set(other);
69 }
70 
71 template <typename T>
73 {
74  Set(other);
75  return *this;
76 }
77 
78 template <typename T>
80 {
81  Set(other);
82  return *this;
83 }
84 
85 template <typename T>
86 void Quaternion<T>::Set(const Quaternion& other)
87 {
88  Set(other.w, other.x, other.y, other.z);
89 }
90 
91 template <typename T>
92 void Quaternion<T>::Set(T newW, T newX, T newY, T newZ)
93 {
94  w = newW;
95  x = newX;
96  y = newY;
97  z = newZ;
98 }
99 
100 template <typename T>
101 void Quaternion<T>::Set(const std::initializer_list<T>& list)
102 {
103  assert(list.size() == 4);
104 
105  auto inputElem = list.begin();
106  w = *inputElem;
107  x = *(++inputElem);
108  y = *(++inputElem);
109  z = *(++inputElem);
110 }
111 
112 template <typename T>
113 void Quaternion<T>::Set(const Vector3<T>& axis, T angle)
114 {
115  static const T eps = std::numeric_limits<T>::epsilon();
116 
117  T axisLengthSquared = axis.LengthSquared();
118 
119  if (axisLengthSquared < eps)
120  {
121  SetIdentity();
122  }
123  else
124  {
125  Vector3<T> normalizedAxis = axis.Normalized();
126  T s = std::sin(angle / 2);
127 
128  x = normalizedAxis.x * s;
129  y = normalizedAxis.y * s;
130  z = normalizedAxis.z * s;
131  w = std::cos(angle / 2);
132  }
133 }
134 
135 template <typename T>
136 void Quaternion<T>::Set(const Vector3<T>& from, const Vector3<T>& to)
137 {
138  static const T eps = std::numeric_limits<T>::epsilon();
139 
140  Vector3<T> axis = from.Cross(to);
141 
142  T fromLengthSquared = from.LengthSquared();
143  T toLengthSquared = to.LengthSquared();
144 
145  if (fromLengthSquared < eps || toLengthSquared < eps)
146  {
147  SetIdentity();
148  }
149  else
150  {
151  T axisLengthSquared = axis.LengthSquared();
152 
153  // In case two vectors are exactly the opposite, pick orthogonal vector
154  // for axis.
155  if (axisLengthSquared < eps)
156  {
157  axis = std::get<0>(from.Tangentials());
158  }
159 
160  Set(from.Dot(to), axis.x, axis.y, axis.z);
161  w += L2Norm();
162 
163  Normalize();
164  }
165 }
166 
167 template <typename T>
168 void Quaternion<T>::Set(const Vector3<T>& rotationBasis0,
169  const Vector3<T>& rotationBasis1,
170  const Vector3<T>& rotationBasis2)
171 {
172  Matrix3x3<T> matrix3;
173 
174  matrix3.SetColumn(0, rotationBasis0.Normalized());
175  matrix3.SetColumn(1, rotationBasis1.Normalized());
176  matrix3.SetColumn(2, rotationBasis2.Normalized());
177 
178  Set(matrix3);
179 }
180 
181 template <typename T>
183 {
184  static const T eps = std::numeric_limits<T>::epsilon();
185  static const T quarter = static_cast<T>(0.25);
186 
187  T onePlusTrace = m.Trace() + 1;
188 
189  if (onePlusTrace > eps)
190  {
191  T S = std::sqrt(onePlusTrace) * 2;
192  w = quarter * S;
193  x = (m(2, 1) - m(1, 2)) / S;
194  y = (m(0, 2) - m(2, 0)) / S;
195  z = (m(1, 0) - m(0, 1)) / S;
196  }
197  else if (m(0, 0) > m(1, 1) && m(0, 0) > m(2, 2))
198  {
199  T S = std::sqrt(1 + m(0, 0) - m(1, 1) - m(2, 2)) * 2;
200  w = (m(2, 1) - m(1, 2)) / S;
201  x = quarter * S;
202  y = (m(0, 1) + m(1, 0)) / S;
203  z = (m(0, 2) + m(2, 0)) / S;
204  }
205  else if (m(1, 1) > m(2, 2))
206  {
207  T S = std::sqrt(1 + m(1, 1) - m(0, 0) - m(2, 2)) * 2;
208  w = (m(0, 2) - m(2, 0)) / S;
209  x = (m(0, 1) + m(1, 0)) / S;
210  y = quarter * S;
211  z = (m(1, 2) + m(2, 1)) / S;
212  }
213  else
214  {
215  T S = std::sqrt(1 + m(2, 2) - m(0, 0) - m(1, 1)) * 2;
216  w = (m(1, 0) - m(0, 1)) / S;
217  x = (m(0, 2) + m(2, 0)) / S;
218  y = (m(1, 2) + m(2, 1)) / S;
219  z = quarter * S;
220  }
221 }
222 
223 template <typename T>
224 template <typename U>
226 {
227  return Quaternion<U>{ static_cast<U>(w), static_cast<U>(x),
228  static_cast<U>(y), static_cast<U>(z) };
229 }
230 
231 template <typename T>
233 {
234  Quaternion q{ *this };
235  q.Normalize();
236  return q;
237 }
238 
239 template <typename T>
241 {
242  T _2xx = 2 * x * x;
243  T _2yy = 2 * y * y;
244  T _2zz = 2 * z * z;
245  T _2xy = 2 * x * y;
246  T _2xz = 2 * x * z;
247  T _2xw = 2 * x * w;
248  T _2yz = 2 * y * z;
249  T _2yw = 2 * y * w;
250  T _2zw = 2 * z * w;
251 
252  return Vector3<T>{
253  (1 - _2yy - _2zz) * v.x + (_2xy - _2zw) * v.y + (_2xz + _2yw) * v.z,
254  (_2xy + _2zw) * v.x + (1 - _2zz - _2xx) * v.y + (_2yz - _2xw) * v.z,
255  (_2xz - _2yw) * v.x + (_2yz + _2xw) * v.y + (1 - _2yy - _2xx) * v.z
256  };
257 }
258 
259 template <typename T>
261 {
262  return Quaternion{ w * other.w - x * other.x - y * other.y - z * other.z,
263  w * other.x + x * other.w + y * other.z - z * other.y,
264  w * other.y - x * other.z + y * other.w + z * other.x,
265  w * other.z + x * other.y - y * other.x + z * other.w };
266 }
267 
268 template <typename T>
269 T Quaternion<T>::Dot(const Quaternion<T>& other) const
270 {
271  return w * other.w + x * other.x + y * other.y + z * other.z;
272 }
273 
274 template <typename T>
276 {
277  return Quaternion{ other.w * w - other.x * x - other.y * y - other.z * z,
278  other.w * x + other.x * w + other.y * z - other.z * y,
279  other.w * y - other.x * z + other.y * w + other.z * x,
280  other.w * z + other.x * y - other.y * x + other.z * w };
281 }
282 
283 template <typename T>
284 void Quaternion<T>::IMul(const Quaternion& other)
285 {
286  *this = Mul(other);
287 }
288 
289 template <typename T>
291 {
292  Set(1, 0, 0, 0);
293 }
294 
295 template <typename T>
296 void Quaternion<T>::Rotate(T angleInRadians)
297 {
298  Vector3<T> axis;
299  T currentAngle;
300 
301  GetAxisAngle(&axis, &currentAngle);
302 
303  currentAngle += angleInRadians;
304 
305  Set(axis, currentAngle);
306 }
307 
308 template <typename T>
310 {
311  T norm = L2Norm();
312 
313  if (norm > 0)
314  {
315  w /= norm;
316  x /= norm;
317  y /= norm;
318  z /= norm;
319  }
320 }
321 
322 template <typename T>
324 {
325  Vector3<T> result{ x, y, z };
326  result.Normalize();
327 
328  if (2 * std::acos(w) < PI<T>())
329  {
330  return result;
331  }
332 
333  return -result;
334 }
335 
336 template <typename T>
338 {
339  T result = 2 * std::acos(w);
340 
341  if (result < PI<T>())
342  {
343  return result;
344  }
345 
346  // Wrap around
347  return 2 * PI<T>() - result;
348 }
349 
350 template <typename T>
351 void Quaternion<T>::GetAxisAngle(Vector3<T>* axis, T* angle) const
352 {
353  *axis = Vector3<T>(x, y, z);
354  axis->Normalize();
355  *angle = 2 * std::acos(w);
356 
357  if (*angle > PI<T>())
358  {
359  // Wrap around
360  (*axis) = -(*axis);
361  *angle = 2 * PI<T>() - (*angle);
362  }
363 }
364 
365 template <typename T>
367 {
368  const T denom = w * w + x * x + y * y + z * z;
369  return Quaternion{ w / denom, -x / denom, -y / denom, -z / denom };
370 }
371 
372 template <typename T>
374 {
375  T _2xx = 2 * x * x;
376  T _2yy = 2 * y * y;
377  T _2zz = 2 * z * z;
378  T _2xy = 2 * x * y;
379  T _2xz = 2 * x * z;
380  T _2xw = 2 * x * w;
381  T _2yz = 2 * y * z;
382  T _2yw = 2 * y * w;
383  T _2zw = 2 * z * w;
384 
385  Matrix3x3<T> m{ 1 - _2yy - _2zz, _2xy - _2zw, _2xz + _2yw,
386  _2xy + _2zw, 1 - _2zz - _2xx, _2yz - _2xw,
387  _2xz - _2yw, _2yz + _2xw, 1 - _2yy - _2xx };
388 
389  return m;
390 }
391 
392 template <typename T>
394 {
395  T _2xx = 2 * x * x;
396  T _2yy = 2 * y * y;
397  T _2zz = 2 * z * z;
398  T _2xy = 2 * x * y;
399  T _2xz = 2 * x * z;
400  T _2xw = 2 * x * w;
401  T _2yz = 2 * y * z;
402  T _2yw = 2 * y * w;
403  T _2zw = 2 * z * w;
404 
405  Matrix4x4<T> m{ 1 - _2yy - _2zz,
406  _2xy - _2zw,
407  _2xz + _2yw,
408  0,
409  _2xy + _2zw,
410  1 - _2zz - _2xx,
411  _2yz - _2xw,
412  0,
413  _2xz - _2yw,
414  _2yz + _2xw,
415  1 - _2yy - _2xx,
416  0,
417  0,
418  0,
419  0,
420  1 };
421 
422  return m;
423 }
424 
425 template <typename T>
427 {
428  return std::sqrt(w * w + x * x + y * y + z * z);
429 }
430 
431 template <typename T>
433 {
434  IMul(other);
435  return *this;
436 }
437 
438 template <typename T>
440 {
441  assert(i >= 0 && i < 4);
442 
443  if (i == 0)
444  {
445  return w;
446  }
447 
448  if (i == 1)
449  {
450  return x;
451  }
452 
453  if (i == 2)
454  {
455  return y;
456  }
457 
458  return z;
459 }
460 
461 template <typename T>
462 const T& Quaternion<T>::operator[](size_t i) const
463 {
464  assert(i >= 0 && i < 4);
465 
466  if (i == 0)
467  {
468  return w;
469  }
470 
471  if (i == 1)
472  {
473  return x;
474  }
475 
476  if (i == 2)
477  {
478  return y;
479  }
480 
481  return z;
482 }
483 
484 template <typename T>
485 bool Quaternion<T>::operator==(const Quaternion& other) const
486 {
487  return (w == other.w && x == other.x && y == other.y && z == other.z);
488 }
489 
490 template <typename T>
491 bool Quaternion<T>::operator!=(const Quaternion& other) const
492 {
493  return (w != other.w || x != other.x || y != other.y || z != other.z);
494 }
495 
496 template <typename T>
498 {
499  return Quaternion{};
500 }
501 
502 template <typename T>
504 {
505  static const double threshold = 0.01;
506  static const T eps = std::numeric_limits<T>::epsilon();
507 
508  T cosHalfAngle = a.Dot(b);
509  T weightA, weightB;
510 
511  // For better accuracy, return lerp result when a and b are close enough.
512  if (1.0 - std::fabs(cosHalfAngle) < threshold)
513  {
514  weightA = 1.0 - t;
515  weightB = t;
516  }
517  else
518  {
519  T halfAngle = std::acos(cosHalfAngle);
520  T sinHalfAngle = std::sqrt(1 - cosHalfAngle * cosHalfAngle);
521 
522  // In case of angle ~ 180, pick middle value.
523  // If not, perform slerp.
524  if (std::fabs(sinHalfAngle) < eps)
525  {
526  weightA = static_cast<T>(0.5);
527  weightB = static_cast<T>(0.5);
528  }
529  else
530  {
531  weightA = std::sin((1 - t) * halfAngle) / sinHalfAngle;
532  weightB = std::sin(t * halfAngle) / sinHalfAngle;
533  }
534  }
535 
536  return Quaternion<T>{ weightA * a.w + weightB * b.w,
537  weightA * a.x + weightB * b.x,
538  weightA * a.y + weightB * b.y,
539  weightA * a.z + weightB * b.z };
540 }
541 
542 template <typename T>
544 {
545  return q.Mul(v);
546 }
547 
548 template <typename T>
550 {
551  return a.Mul(b);
552 }
553 } // namespace CubbyFlow
554 
555 #endif
T z
Definition: Quaternion.hpp:168
T L2Norm() const
Returns L2 norm of this quaternion.
Definition: Quaternion-Impl.hpp:426
Quaternion RMul(const Quaternion &other) const
Returns other quaternion * this quaternion.
Definition: Quaternion-Impl.hpp:275
ValueType LengthSquared() const
Definition: MatrixExpression-Impl.hpp:286
static Quaternion MakeIdentity()
Returns identity matrix.
Definition: Quaternion-Impl.hpp:497
Quaternion< T > Slerp(const Quaternion< T > &a, const Quaternion< T > &b, T t)
Computes spherical linear interpolation.
Definition: Quaternion-Impl.hpp:503
T w
Real part.
Definition: Quaternion.hpp:159
Matrix4x4< T > Matrix4() const
Converts to the 4x4 rotation matrix.
Definition: Quaternion-Impl.hpp:393
Quaternion Normalized() const
Returns normalized quaternion.
Definition: Quaternion-Impl.hpp:232
Matrix3x3< T > Matrix3() const
Converts to the 3x3 rotation matrix.
Definition: Quaternion-Impl.hpp:373
T Angle() const
Returns the rotational angle.
Definition: Quaternion-Impl.hpp:337
void Rotate(T angleInRadians)
Rotate this quaternion with given angle in radians.
Definition: Quaternion-Impl.hpp:296
Quaternion()
Make an identity quaternion.
Definition: Quaternion-Impl.hpp:17
void SetColumn(size_t j, const MatrixExpression< T, R, C, E > &col)
Sets j-th column with input vector.
Definition: MatrixDenseBase-Impl.hpp:74
Definition: Matrix.hpp:27
std::enable_if_t<(IsMatrixSizeDynamic< Rows, Cols >)||Cols==1) &&(IsMatrixSizeDynamic< R, C >)||C==1), U > Dot(const MatrixExpression< T, R, C, E > &expression) const
Definition: MatrixExpression-Impl.hpp:391
bool operator==(const Quaternion &other) const
Returns true if equal.
Definition: Quaternion-Impl.hpp:485
Quaternion Inverse() const
Returns the inverse quaternion.
Definition: Quaternion-Impl.hpp:366
Definition: pybind11Utils.hpp:20
void Normalize()
Normalizes the quaternion.
Definition: Quaternion-Impl.hpp:309
Quaternion & operator*=(const Quaternion &other)
Returns this quaternion *= other quaternion.
Definition: Quaternion-Impl.hpp:432
Quaternion< U > CastTo() const
Returns quaternion with other base type.
Definition: Quaternion-Impl.hpp:225
ValueType Trace() const
Definition: MatrixExpression-Impl.hpp:183
T y
Imaginary part (k).
Definition: Quaternion.hpp:165
T & operator[](size_t i)
Returns the reference to the i-th element.
Definition: Quaternion-Impl.hpp:439
std::enable_if_t<(IsMatrixSizeDynamic< Rows, Cols >)||(Rows==2 &&Cols==1)) &&(IsMatrixSizeDynamic< R, C >)||(R==2 &&C==1)), U > Cross(const MatrixExpression< T, R, C, E > &expression) const
Definition: MatrixExpression-Impl.hpp:412
Vector3< T > Mul(const Vector3< T > &v) const
Returns this quaternion * vector.
Definition: Quaternion-Impl.hpp:240
void IMul(const Quaternion &other)
Returns this quaternion *= other quaternion.
Definition: Quaternion-Impl.hpp:284
void Normalize()
Definition: MatrixDenseBase-Impl.hpp:86
T Dot(const Quaternion< T > &other) const
Computes the dot product with other quaternion.
Definition: Quaternion-Impl.hpp:269
Vector3< T > Axis() const
Returns the rotational axis.
Definition: Quaternion-Impl.hpp:323
void Set(const Quaternion &other)
Sets the quaternion with other quaternion.
Definition: Quaternion-Impl.hpp:86
bool operator!=(const Quaternion &other) const
Returns true if not equal.
Definition: Quaternion-Impl.hpp:491
Vector< T, 3 > operator*(const Quaternion< T > &q, const Vector< T, 3 > &v)
Returns quaternion q * vector v.
Definition: Quaternion-Impl.hpp:543
std::enable_if_t<(IsMatrixSizeDynamic< Rows, Cols >)||(Rows==3 &&Cols==1)), std::tuple< Matrix< U, 3, 1 >, Matrix< U, 3, 1 > > > Tangentials() const
Returns the tangential vectors for this vector.
Definition: MatrixExpression-Impl.hpp:492
MatrixScalarElemWiseBinaryOp< T, Rows, Cols, const Matrix< T, Rows, Cols > &, std::divides< T > > Normalized() const
Definition: MatrixExpression-Impl.hpp:315
void GetAxisAngle(Vector3< T > *axis, T *angle) const
Returns the axis and angle.
Definition: Quaternion-Impl.hpp:351
Quaternion & operator=(const Quaternion &other)
Copy assignment operator.
Definition: Quaternion-Impl.hpp:72
void SetIdentity()
Makes this quaternion identity.
Definition: Quaternion-Impl.hpp:290
Quaternion class defined as q = w + xi + yj + zk.
Definition: Quaternion.hpp:22
T x
Imaginary part (j).
Definition: Quaternion.hpp:162