vmmlib  1.13.0
Templatized C++ vector and matrix math library
quaternion.hpp
1 /*
2  * Copyright (c) 2006-2016, Visualization and Multimedia Lab,
3  * University of Zurich <http://vmml.ifi.uzh.ch>,
4  * Eyescale Software GmbH,
5  * Blue Brain Project, EPFL
6  *
7  * This file is part of VMMLib <https://github.com/VMML/vmmlib/>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  *
12  * Redistributions of source code must retain the above copyright notice, this
13  * list of conditions and the following disclaimer. Redistributions in binary
14  * form must reproduce the above copyright notice, this list of conditions and
15  * the following disclaimer in the documentation and/or other materials provided
16  * with the distribution. Neither the name of the Visualization and Multimedia
17  * Lab, University of Zurich nor the names of its contributors may be used to
18  * endorse or promote products derived from this software without specific prior
19  * written permission.
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #ifndef __VMML__QUATERNION__HPP__
34 #define __VMML__QUATERNION__HPP__
35 
36 #include <vmmlib/types.hpp>
37 #include <vmmlib/vector.hpp> // used inline
38 
39 #include <algorithm>
40 #include <cmath>
41 #include <cstdlib>
42 #include <iomanip>
43 #include <iostream>
44 #include <limits>
45 #include <type_traits>
46 
47 namespace vmml
48 {
49 template <typename T>
51 {
52 public:
55  : array()
56  {
57  array[3] = 1.;
58  }
59  Quaternion(T x, T y, T z, T w);
60 
62  Quaternion(T angle, vector<3, T> axis);
63 
64  // uses the top-left 3x3 part of the supplied matrix as rotation matrix
65  template <size_t M>
66  Quaternion(const Matrix<M, M, T>& rotation_matrix_,
67  typename std::enable_if<M >= 3>::type* = 0);
68 
72  bool equals(const Quaternion& other,
73  T tolerance = std::numeric_limits<T>::epsilon()) const;
74 
75  T x() const { return array[0]; }
76  T y() const { return array[1]; }
77  T z() const { return array[2]; }
78  T w() const { return array[3]; }
80  bool operator==(const Quaternion& a) const;
81 
83  bool operator!=(const Quaternion& a) const;
84 
86  Quaternion operator-() const;
87 
89  Quaternion inverse() const;
90 
91  Quaternion getConjugate() const;
92 
93  T abs() const;
94  T absSquare() const;
95 
99 
100  void normalize();
101 
102  Quaternion& operator=(const Quaternion& other);
103 
106  Quaternion operator+(const Quaternion<T>& a) const;
107  Quaternion operator-(const Quaternion<T>& a) const;
108 
109  // caution: a * q != q * a in general
110  Quaternion operator*(const Quaternion<T>& a) const;
111  void operator+=(const Quaternion<T>& a);
112  void operator-=(const Quaternion<T>& a);
113 
114  // caution: a *= q != q *= a in general
115  void operator*=(const Quaternion<T>& a);
117 
120  Quaternion operator*(T a) const;
121  Quaternion operator/(T a) const;
122 
123  void operator*=(T a);
124  void operator/=(T a);
126 
127  friend std::ostream& operator<<(std::ostream& os, const Quaternion& q)
128  {
129  const std::ios::fmtflags flags = os.flags();
130  const int prec = os.precision();
131 
132  os.setf(std::ios::right, std::ios::adjustfield);
133  os.precision(5);
134 
135  os << "( " << std::setw(10) << q.x() << " " << std::setw(10) << q.y()
136  << " " << std::setw(10) << q.z() << " " << std::setw(10) << q.w()
137  << " "
138  << ")";
139 
140  os.precision(prec);
141  os.setf(flags);
142  return os;
143  }
144 
145  T array[4];
146 };
147 }
148 
149 #include <vmmlib/matrix.hpp>
150 
151 namespace vmml
152 {
155 template <typename T>
156 T dot(const Quaternion<T>& p, const Quaternion<T>& q)
157 {
158  return p.array[3] * q.array[3] + p.array[0] * q.array[0] +
159  p.array[1] * q.array[1] + p.array[2] * q.array[2];
160 }
161 
162 template <typename T>
163 vector<3, T> cross(const Quaternion<T>& p, const Quaternion<T>& q)
164 {
165  return vector<3, T>(p.array[1] * q.array[2] - p.array[2] * q.array[1],
166  p.array[2] * q.array[0] - p.array[0] * q.array[2],
167  p.array[0] * q.array[1] - p.array[1] * q.array[0]);
168 }
170 
171 template <typename T>
172 Quaternion<T>::Quaternion(T x_, T y_, T z_, T w_)
173 {
174  array[0] = x_;
175  array[1] = y_;
176  array[2] = z_;
177  array[3] = w_;
178 }
179 
180 template <typename T>
181 template <size_t M>
183  typename std::enable_if<M >= 3>::type*)
184 {
185  const T trace = rot(0, 0) + rot(1, 1) + rot(2, 2) + T(1);
186  static const T TRACE_EPSILON = 1e-5;
187 
188  // very small traces may introduce a big numerical error
189  if (trace > TRACE_EPSILON)
190  {
191  T s = 0.5 / std::sqrt(trace);
192  array[0] = rot(2, 1) - rot(1, 2);
193  array[0] *= s;
194 
195  array[1] = rot(0, 2) - rot(2, 0);
196  array[1] *= s;
197 
198  array[2] = rot(1, 0) - rot(0, 1);
199  array[2] *= s;
200 
201  array[3] = 0.25 / s;
202  }
203  else
204  {
205  const vector<3, T> diag(rot(0, 0), rot(1, 1), rot(2, 2));
206  const size_t largest = diag.find_max_index();
207 
208  // 0, 0 is largest
209  if (largest == 0)
210  {
211  const T s =
212  0.5 / std::sqrt(T(1) + rot(0, 0) - rot(1, 1) - rot(2, 2));
213  array[0] = 0.25 / s;
214 
215  array[1] = rot(0, 1) + rot(1, 0);
216  array[1] *= s;
217 
218  array[2] = rot(0, 2) + rot(2, 0);
219  array[2] *= s;
220 
221  array[3] = rot(1, 2) - rot(2, 1);
222  array[3] *= s;
223  }
224  else if (largest == 1)
225  {
226  const T s =
227  0.5 / std::sqrt(T(1) + rot(1, 1) - rot(0, 0) - rot(2, 2));
228  array[0] = rot(0, 1) + rot(1, 0);
229  array[0] *= s;
230 
231  array[1] = 0.25 / s;
232 
233  array[2] = rot(1, 2) + rot(2, 1);
234  array[2] *= s;
235 
236  array[3] = rot(0, 2) - rot(2, 0);
237  array[3] *= s;
238  }
239  // 2, 2 is largest
240  else if (largest == 2)
241  {
242  const T s =
243  0.5 / std::sqrt(T(1) + rot(2, 2) - rot(0, 0) - rot(1, 1));
244  array[0] = rot(0, 2) + rot(2, 0);
245  array[0] *= s;
246 
247  array[1] = rot(1, 2) + rot(2, 1);
248  array[1] *= s;
249 
250  array[2] = 0.25 / s;
251 
252  array[3] = rot(0, 1) - rot(1, 0);
253  array[3] *= s;
254  }
255  else
256  {
257  throw std::runtime_error("no clue why, but should not get here");
258  }
259  }
260 }
261 
262 template <typename T>
264 {
265  axis.normalize();
266  const T sinAngle2 = std::sin(angle * 0.5);
267  array[0] = axis.x() * sinAngle2;
268  array[1] = axis.y() * sinAngle2;
269  array[2] = axis.z() * sinAngle2;
270  array[3] = std::cos(angle * 0.5);
271 }
272 
273 template <typename T>
274 bool Quaternion<T>::equals(const Quaternion& other, const T tolerance) const
275 {
276  return std::abs(array[0] - other.array[0]) <= tolerance &&
277  std::abs(array[1] - other.array[1]) <= tolerance &&
278  std::abs(array[2] - other.array[2]) <= tolerance &&
279  std::abs(array[3] - other.array[3]) <= tolerance;
280 }
281 
282 template <typename T>
283 bool Quaternion<T>::operator==(const Quaternion& rhs) const
284 {
285  return array[0] == rhs.array[0] && array[1] == rhs.array[1] &&
286  array[2] == rhs.array[2] && array[3] == rhs.array[3];
287 }
288 
289 template <typename T>
291 {
292  return !this->operator==(a);
293 }
294 
295 template <typename T>
297 {
298  return Quaternion<T>(-array[0], -array[1], -array[2], array[3]);
299 }
300 
301 template <typename T>
302 T Quaternion<T>::abs() const
303 {
304  return std::sqrt(absSquare());
305 }
306 
307 template <typename T>
308 T Quaternion<T>::absSquare() const
309 {
310  return array[0] * array[0] + array[1] * array[1] + array[2] * array[2] +
311  array[3] * array[3];
312 }
313 
314 template <typename T>
316 {
317  const Quaternion<T>& q = getConjugate();
318  const T tmp = T(1) / absSquare();
319  return q * tmp;
320 }
321 
322 template <typename T>
324 {
325  T len = abs();
326  if (len == T(0))
327  return;
328  len = T(1) / len;
329  this->operator*=(len);
330 }
331 
332 //
333 // Quaternion/Quaternion operations
334 //
335 
336 template <typename T>
338 {
339  return Quaternion(array[0] + rhs.array[0], array[1] + rhs.array[1],
340  array[2] + rhs.array[2], array[3] + rhs.array[3]);
341 }
342 
343 template <typename T>
345 {
346  return Quaternion(array[0] - rhs.array[0], array[1] - rhs.array[1],
347  array[2] - rhs.array[2], array[3] - rhs.array[3]);
348 }
349 
350 // returns Grasssmann product
351 template <typename T>
353 {
354  Quaternion<T> ret(*this);
355  ret *= rhs;
356  return ret;
357 }
358 
359 // Grassmann product
360 template <typename T>
362 {
363  // optimized version, 7 less mul, but 15 more add/subs
364  // after Henrik Engstrom, from a gamedev.net article.
365  const T& a_ = array[3];
366  const T& b_ = array[0];
367  const T& c = array[1];
368  const T& d = array[2];
369  const T& _x = q.array[3];
370  const T& _y = q.array[0];
371  const T& _z = q.array[1];
372  const T& _w = q.array[2];
373 
374  const T tmp_00 = (d - c) * (_z - _w);
375  const T tmp_01 = (a_ + b_) * (_x + _y);
376  const T tmp_02 = (a_ - b_) * (_z + _w);
377  const T tmp_03 = (c + d) * (_x - _y);
378  const T tmp_04 = (d - b_) * (_y - _z);
379  const T tmp_05 = (d + b_) * (_y + _z);
380  const T tmp_06 = (a_ + c) * (_x - _w);
381  const T tmp_07 = (a_ - c) * (_x + _w);
382  const T tmp_08 = tmp_05 + tmp_06 + tmp_07;
383  const T tmp_09 = 0.5 * (tmp_04 + tmp_08);
384 
385  array[0] = tmp_01 + tmp_09 - tmp_08;
386  array[1] = tmp_02 + tmp_09 - tmp_07;
387  array[2] = tmp_03 + tmp_09 - tmp_06;
388  array[3] = tmp_00 + tmp_09 - tmp_05;
389 }
390 
391 template <typename T>
393 {
394  return Quaternion(-array[0], -array[1], -array[2], -array[3]);
395 }
396 
397 template <typename T>
399 {
400  array[0] += q.array[0];
401  array[1] += q.array[1];
402  array[2] += q.array[2];
403  array[3] += q.array[3];
404 }
405 
406 template <typename T>
408 {
409  array[0] -= q.array[0];
410  array[1] -= q.array[1];
411  array[2] -= q.array[2];
412  array[3] -= q.array[3];
413 }
414 
415 //
416 // Quaternion/scalar operations
417 //
418 
419 template <typename T>
420 Quaternion<T> Quaternion<T>::operator*(const T a_) const
421 {
422  return Quaternion(array[0] * a_, array[1] * a_, array[2] * a_,
423  array[3] * a_);
424 }
425 
426 template <typename T>
428 {
429  if (a_ == T(0))
430  throw std::runtime_error("Division by zero.");
431 
432  a_ = T(1) / a_;
433  return Quaternion(array[0] * a_, array[1] * a_, array[2] * a_,
434  array[3] * a_);
435 }
436 
437 template <typename T>
439 {
440  array[0] *= q;
441  array[1] *= q;
442  array[2] *= q;
443  array[3] *= q;
444 }
445 
446 template <typename T>
448 {
449  if (q == T(0))
450  throw std::runtime_error("Division by zero");
451 
452  q = T(1) / q;
453  this->operator*=(q);
454 }
455 
456 template <typename T>
458 {
459  const T w2 = array[3] * array[3];
460  const T x2 = array[0] * array[0];
461  const T y2 = array[1] * array[1];
462  const T z2 = array[2] * array[2];
463  const T wx = array[3] * array[0];
464  const T wy = array[3] * array[1];
465  const T wz = array[3] * array[2];
466  const T xy = array[0] * array[1];
467  const T xz = array[0] * array[2];
468  const T yz = array[1] * array[2];
469 
470  Matrix<3, 3, T> matrix;
471  matrix(0, 0) = w2 + x2 - y2 - z2;
472  matrix(0, 1) = 2. * (xy - wz);
473  matrix(0, 2) = 2. * (xz + wy);
474  matrix(1, 0) = 2. * (xy + wz);
475  matrix(1, 1) = w2 - x2 + y2 - z2;
476  matrix(1, 2) = 2. * (yz - wx);
477  matrix(2, 0) = 2. * (xz - wy);
478  matrix(2, 1) = 2. * (yz + wx);
479  matrix(2, 2) = w2 - x2 - y2 + z2;
480  return matrix;
481 }
482 
483 template <typename T>
485 {
486  ::memcpy(array, other.array, 4 * sizeof(T));
487  return *this;
488 }
489 }
490 #endif
bool equals(const Quaternion &other, T tolerance=std::numeric_limits< T >::epsilon()) const
Definition: quaternion.hpp:274
Definition: aabb.hpp:44
bool operator==(const Quaternion &a) const
Definition: quaternion.hpp:283
Matrix< 3, 3, T > getRotationMatrix() const
Definition: quaternion.hpp:457
Quaternion()
Construct an identity quaternion.
Definition: quaternion.hpp:54
Quaternion operator-() const
Definition: quaternion.hpp:392
bool operator!=(const Quaternion &a) const
Definition: quaternion.hpp:290
Matrix with R rows and C columns of element type T.
Definition: matrix.hpp:52
Quaternion inverse() const
Definition: quaternion.hpp:315