wfmath  1.0.3
A math library for the Worldforge system.
quaternion.cpp
1 // quaternion.cpp (Quaternion implementation)
2 //
3 // The WorldForge Project
4 // Copyright (C) 2002 The WorldForge Project
5 //
6 // This program 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 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program 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 this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // For information about WorldForge and its authors, please contact
21 // the Worldforge Web Site at http://www.worldforge.org.
22 //
23 
24 // Author: Ron Steinke
25 
26 // Some code here was taken from the quaternion implementation is
27 // eris. Some of the other algorithms are based on information
28 // found here <http://www.cs.ualberta.ca/~andreas/math/matrfaq_latest.html>
29 // and here <http://www.cs.berkeley.edu/~laura/cs184/quat/quaternion.html>.
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include "quaternion.h"
36 #include "error.h"
37 #include "rotmatrix.h"
38 
39 #include <cmath>
40 
41 #include <cassert>
42 
43 namespace WFMath {
44 
46  CoordType x_in,
47  CoordType y_in,
48  CoordType z_in)
49  : m_w(0), m_vec(), m_valid(true), m_age(1)
50 {
51  CoordType norm = std::sqrt(w_in*w_in + x_in*x_in + y_in*y_in + z_in*z_in);
52 
53  m_w = w_in / norm;
54  m_vec[0] = x_in / norm;
55  m_vec[1] = y_in / norm;
56  m_vec[2] = z_in / norm;
57  m_vec.setValid();
58 }
59 
61 {
62  static Quaternion ident = Quaternion(Identity());
63  return ident;
64 }
65 
66 
67 // The equality functions regard q and -q as equal, since they
68 // correspond to the same rotation matrix. We consider the form
69 // of the quaternion with w > 0 canonical.
70 
71 bool Quaternion::isEqualTo(const Quaternion &q, CoordType epsilon) const
72 {
73  // Since the sum of squares is 1, the magnitude of the largest
74  // element must be between 1 and 0.5, so we don't need to scale epsilon.
75 
76  assert(epsilon > 0);
77 
78  //If anyone is invalid they are never equal
79  if (!q.m_valid || !m_valid) {
80  return false;
81  }
82 
83  if(std::fabs(m_w - q.m_w) <= epsilon) {
84  int i;
85  for(i = 0; i < 3; ++i)
86  if(std::fabs(m_vec[i] - q.m_vec[i]) > epsilon)
87  break; // try again with swapped signs
88  if(i == 3) // got through the loop
89  return true;
90  }
91 
92  // This makes q == -q true
93  if(std::fabs(m_w + q.m_w) <= epsilon) {
94  for(int i = 0; i < 3; ++i)
95  if(std::fabs(m_vec[i] + q.m_vec[i]) > epsilon)
96  return false;
97  return true;
98  }
99 
100  return false;
101 }
102 
103 // order of multiplication vs sense of rotation
104 //
105 // v.rotate(q1).rotate(q2) is the same as v.rotate(q1 * q2),
106 // the same as with matrices
107 
108 Quaternion& Quaternion::operator*= (const Quaternion& rhs)
109 {
110  m_valid = m_valid && rhs.m_valid;
111  m_age = m_age + rhs.m_age;
112  checkNormalization();
113 
114  CoordType old_w = m_w;
115  m_w = m_w * rhs.m_w - Dot(m_vec, rhs.m_vec);
116  m_vec = old_w * rhs.m_vec + rhs.m_w * m_vec - Cross(m_vec, rhs.m_vec);
117 
118  return *this;
119 }
120 
121 Quaternion& Quaternion::operator/= (const Quaternion& rhs)
122 {
123  m_valid = m_valid && rhs.m_valid;
124  m_age = m_age + rhs.m_age;
125  checkNormalization();
126 
127  CoordType old_w = m_w;
128  m_w = m_w * rhs.m_w + Dot(m_vec, rhs.m_vec);
129  m_vec = rhs.m_w * m_vec - old_w * rhs.m_vec + Cross(m_vec, rhs.m_vec);
130 
131  return *this;
132 }
133 
135 {
136  RotMatrix<3> m_tmp;
137  bool not_flip = !m.parity();
138 
139  m_valid = m.isValid();
140  m_vec.setValid(m.isValid());
141 
142  if(!not_flip)
143  m_tmp = Prod(m, RotMatrix<3>().mirrorX());
144 
145  const RotMatrix<3> &m_ref = not_flip ? m : m_tmp;
146 
147  CoordType s;
148  const int nxt[3] = {1, 2, 0};
149  CoordType tr = m_ref.trace();
150 
151  // check the diagonal
152  if (tr > 0.0) {
153  s = std::sqrt(tr + 1.0f);
154  m_w = (s / 2.0f);
155  s = (0.5f / s);
156 
157  m_vec[0] = (m_ref.elem(2, 1) - m_ref.elem(1, 2)) * s;
158  m_vec[1] = (m_ref.elem(0, 2) - m_ref.elem(2, 0)) * s;
159  m_vec[2] = (m_ref.elem(1, 0) - m_ref.elem(0, 1)) * s;
160  } else {
161  // diagonal is negative
162  int i = 0;
163 
164  if (m_ref.elem(1, 1) > m_ref.elem(0, 0)) i = 1;
165  if (m_ref.elem(2, 2) > m_ref.elem(i, i)) i = 2;
166 
167  int j = nxt[i], k = nxt[j];
168 
169  s = std::sqrt (1.0f + m_ref.elem(i, i) - m_ref.elem(j, j) - m_ref.elem(k, k));
170  m_vec[i] = -(s * 0.5f);
171 
172  assert("sqrt() returns positive" && s > 0.0);
173  s = (0.5f / s);
174 
175  m_w = (m_ref.elem(k, j) - m_ref.elem(j, k)) * s;
176  m_vec[j] = (m_ref.elem(i, j) + m_ref.elem(j, i)) * s;
177  m_vec[k] = (m_ref.elem(i, k) + m_ref.elem(k, i)) * s;
178  }
179 
180  m_age = m.age();
181 
182  return not_flip;
183 }
184 
186 {
187  Quaternion q(m_valid);
188  q.m_w = m_w;
189  q.m_vec = -m_vec;
190  q.m_age = m_age; // no multiplication was done, so roundoff error does not increase
191  return q;
192 }
193 
195 {
196  // FIXME find a more efficient way to do this
197  Quaternion tmp;
198  tmp.fromRotMatrix(m);
199  *this *= tmp;
200  return *this;
201 }
202 
204 {
205  if (axis < 0 || axis > 2) {
206  m_valid = false;
207  return *this;
208  }
209 
210  CoordType half_angle = angle / 2;
211 
212  m_w = std::cos(half_angle);
213  for(int i = 0; i < 3; ++i)
214  // Note sin() only called once
215  m_vec[i] = (i == axis) ? std::sin(half_angle) : 0;
216  m_vec.setValid();
217 
218  m_valid = true;
219  m_age = 1;
220 
221  return *this;
222 }
223 
225 {
226  CoordType axis_mag = axis.mag();
227  CoordType half_angle = angle / 2;
228 
229  if (axis_mag < numeric_constants<CoordType>::epsilon()) {
230  m_valid = false;
231  return *this;
232  }
233 
234  m_w = std::cos(half_angle);
235  m_vec = axis * (std::sin(half_angle) / axis_mag);
236 
237  m_valid = axis.isValid();
238  m_age = 1;
239 
240  return *this;
241 }
242 
244 {
245  CoordType axis_mag = axis.mag();
246  CoordType half_angle = axis_mag / 2;
247 
248  if (axis_mag < numeric_constants<CoordType>::epsilon()) {
249  m_valid = false;
250  return *this;
251  }
252 
253  m_w = std::cos(half_angle);
254  m_vec = axis * (std::sin(half_angle) / axis_mag);
255 
256  m_valid = axis.isValid();
257  m_age = 1;
258 
259  return *this;
260 }
261 
263 {
264  CoordType mag_prod = std::sqrt(from.sqrMag() * to.sqrMag());
265  CoordType ctheta_plus_1 = Dot(from, to) / mag_prod + 1;
266 
267  if (mag_prod < numeric_constants<CoordType>::epsilon()) {
268  m_valid = false;
269  return *this;
270  }
271 
272  // antiparallel vectors
273  if(ctheta_plus_1 < numeric_constants<CoordType>::epsilon()) // same check as used in the RotMatrix function
274  throw ColinearVectors<3>(from, to);
275 
276  // cosine of half the angle
277  m_w = std::sqrt(ctheta_plus_1 / 2.f);
278 
279  // vector in direction of axis, magnitude of cross product is proportional to
280  // the sin of the angle, divide to make the magnitude the sin of half the angle,
281  // sin(x) = 2sin(x/2)cos(x/2), so sin(x/2) = sin(x)/(2cos(x/2))
282  m_vec = Cross(from, to) / (2 * mag_prod * m_w);
283 
284  m_valid = from.isValid() && to.isValid();
285  m_age = 1;
286 
287  return *this;
288 }
289 
290 Quaternion& Quaternion::rotation(const Vector<3>& from, const Vector<3>& to, const Vector<3>& fallbackAxis)
291 {
292  CoordType mag_prod = std::sqrt(from.sqrMag() * to.sqrMag());
293  CoordType ctheta_plus_1 = Dot(from, to) / mag_prod + 1;
294 
295  if (mag_prod < numeric_constants<CoordType>::epsilon()) {
296  m_valid = false;
297  return *this;
298  }
299 
300  // antiparallel vectors
301  if(ctheta_plus_1 < numeric_constants<CoordType>::epsilon()) { // same check as used in the RotMatrix function
303  } else {
304  // cosine of half the angle
305  m_w = std::sqrt(ctheta_plus_1 / 2.f);
306 
307  // vector in direction of axis, magnitude of cross product is proportional to
308  // the sin of the angle, divide to make the magnitude the sin of half the angle,
309  // sin(x) = 2sin(x/2)cos(x/2), so sin(x/2) = sin(x)/(2cos(x/2))
310  m_vec = Cross(from, to) / (2 * mag_prod * m_w);
311  m_valid = from.isValid() && to.isValid();
312  m_age = 1;
313  }
314 
315  return *this;
316 }
317 
319 {
320  // Assume that we're not too far off, and compute the norm
321  // only to linear order in the difference from 1.
322  // If q.sqrMag() = 1 + x, q.mag() = 1 + x/2 = (q.SqrMag() + 1)/2
323  // to linear order.
324  CoordType norm = (m_w * m_w + m_vec.sqrMag() + 1)/2;
325  m_w /= norm;
326  m_vec /= norm;
327  m_age = 1;
328 }
329 
330 }
WFMath::Cross
CoordType Cross(const Vector< 2 > &v1, const Vector< 2 > &v2)
2D only: get the z component of the cross product of two vectors
Definition: vector.cpp:102
WFMath::Quaternion::Identity
Definition: quaternion.h:46
WFMath::Quaternion::rotation
Quaternion & rotation(int axis, CoordType angle)
sets the Quaternion to a rotation by angle around axis
Definition: quaternion.cpp:203
WFMath::Quaternion::rotate
Quaternion & rotate(const RotMatrix< 3 > &)
Rotate quaternion using the matrix.
Definition: quaternion.cpp:194
WFMath::RotMatrix::elem
CoordType elem(const int i, const int j) const
get the (i, j) element of the matrix
Definition: rotmatrix.h:112
WFMath::Vector::setValid
void setValid(bool valid=true)
make isValid() return true if you've initialized the vector by hand
Definition: vector.h:154
WFMath::Quaternion::Quaternion
Quaternion()
Construct a Quaternion.
Definition: quaternion.h:52
WFMath::Quaternion::inverse
Quaternion inverse() const
returns the inverse of the Quaternion
Definition: quaternion.cpp:185
WFMath::RotMatrix
A dim dimensional rotation matrix. Technically, a member of the group O(dim).
Definition: const.h:53
WFMath::Quaternion::fromRotMatrix
bool fromRotMatrix(const RotMatrix< 3 > &m)
set a Quaternion's value from a RotMatrix
Definition: quaternion.cpp:134
WFMath::ColinearVectors
An error thrown by certain functions when passed parallel vectors.
Definition: error.h:37
WFMath::Vector::sqrMag
CoordType sqrMag() const
The squared magnitude of a vector.
Definition: vector_funcs.h:241
WFMath::Quaternion
A normalized quaternion.
Definition: quaternion.h:35
WFMath::Prod
RotMatrix< dim > Prod(const RotMatrix< dim > &m1, const RotMatrix< dim > &m2)
returns m1 * m2
Definition: rotmatrix_funcs.h:95
WFMath::RotMatrix::age
unsigned age() const
current round-off age
Definition: rotmatrix.h:201
WFMath
Generic library namespace.
Definition: shape.h:41
WFMath::Quaternion::IDENTITY
static const Quaternion & IDENTITY()
Definition: quaternion.cpp:60
WFMath::Vector< 3 >
WFMath::Quaternion::normalize
void normalize()
normalize to remove accumulated round-off error
Definition: quaternion.cpp:318
WFMath::CoordType
double CoordType
Basic floating point type.
Definition: const.h:140
WFMath::numeric_constants
Definition: const.h:64
WFMath::RotMatrix::trace
CoordType trace() const
Get the trace of the matrix.
Definition: rotmatrix_funcs.h:355
WFMath::Vector::mag
CoordType mag() const
The magnitude of a vector.
Definition: vector.h:217
WFMath::RotMatrix::parity
bool parity() const
Get the parity of the matrix.
Definition: rotmatrix.h:154