CMU462 Library  1.0
15-462/15-662: Computer Graphics (Fall 2015)
quaternion.h
1 #ifndef CMU462_QUATERNION_H
2 #define CMU462_QUATERNION_H
3 
4 #include "CMU462.h"
5 #include "matrix3x3.h"
6 #include "matrix4x4.h"
7 
8 #include <iosfwd>
9 
10 namespace CMU462 {
11 
12 class Quaternion : public Vector4D {
13  public:
14 
19  Quaternion( ) : Vector4D( 0.0, 0.0, 0.0, 1.0 ) { }
20 
24  Quaternion(const Vector3D& v, double w) : Vector4D(v.x, v.y, v.z, w) { }
25 
26  Quaternion(const Vector4D& v) : Vector4D(v.x, v.y, v.z, v.w) { }
27 
28  Quaternion(double x, double y, double z, double w) : Vector4D(x, y, z, w) { }
29 
34  void from_axis_angle(const Vector3D& axis, double radians) {
35  radians /= 2;
36  const Vector3D& nAxis = axis.unit();
37  double sinTheta = sin(radians);
38  x = sinTheta * nAxis.x;
39  y = sinTheta * nAxis.y;
40  z = sinTheta * nAxis.z;
41  w = cos(radians);
42  this->normalize();
43  }
44 
45  Vector3D complex() const { return Vector3D(x, y, z); }
46  void setComplex(const Vector3D& c)
47  {
48  x = c.x;
49  y = c.y;
50  z = c.z;
51  }
52 
53  double real() const { return w; }
54  void setReal(double r) { w = r; }
55 
56  Quaternion conjugate(void) const
57  {
58  return Quaternion(-complex(), real());
59  }
60 
71  Quaternion inverse(void) const
72  {
73  return conjugate() / norm();
74  }
75 
76 
85  Quaternion product(const Quaternion& rhs) const {
86  return Quaternion(y*rhs.z - z*rhs.y + x*rhs.w + w*rhs.x,
87  z*rhs.x - x*rhs.z + y*rhs.w + w*rhs.y,
88  x*rhs.y - y*rhs.x + z*rhs.w + w*rhs.z,
89  w*rhs.w - x*rhs.x - y*rhs.y - z*rhs.z);
90  }
91 
108  Quaternion operator*(const Quaternion& rhs) const {
109  return product(rhs);
110  }
111 
123  Matrix4x4 matrix() const {
124 
125  double m[16] = {
126  w, -z, y, x,
127  z, w, -x, y,
128  -y, x, w, z,
129  -x, -y, -z, w
130  };
131 
132  return Matrix4x4(m);
133  }
134 
147  Matrix4x4 rightMatrix() const {
148  double m[16] = {
149  +w, -z, y, -x,
150  +z, w, -x, -y,
151  -y, x, w, -z,
152  +x, y, z, w
153  };
154 
155  return Matrix4x4(m);
156  }
157 
163  Vector4D vector() const { return Vector4D(x, y, z, w); }
164 
173  Matrix3x3 rotationMatrix() const {
174  double m[9] = {
175  1-2*y*y-2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w,
176  2*x*y + 2*z*w, 1-2*x*x-2*z*z, 2*y*z - 2*x*w,
177  2*x*z - 2*y*w, 2*y*z + 2*x*w, 1-2*x*x-2*y*y
178  };
179 
180  return Matrix3x3(m);
181  }
182 
183 
188  Vector3D scaledAxis(void) const{
189 
190  Quaternion q1 = (Quaternion)unit();
191 
192  // Algorithm from http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/
193 
194  double angle = 2 * acos(q1.w);
195 
196  // s must be positive, because q1 <= 1, due to normalization.
197  double s = sqrt(1-q1.w*q1.w);
198 
199  // Avoid dividing by 0.
200  if (s < 0.001)
201  {
202  // if s close to zero then direction of axis not important
203  // if it is important that axis is normalised then replace with x=1; y=z=0;
204 
205  return Vector3D(q1.x, q1.y, q1.z);
206  }
207  else
208  {
209  // normalise axis
210  return Vector3D(q1.x / s, q1.y / s, q1.z / s);
211  }
212 
213  // NEVER getsgg HERE.
214 
215  }
216 
221  void scaledAxis(const Vector3D& vec_in)
222  {
223  double theta = vec_in.norm();
224 
225  // Small magnitudes are handled via the default vector.
226  if (theta > 0.0001)
227  {
228  double s = sin(theta / 2.0);
229  Vector3D W(vec_in / theta * s);
230  x = W.x;
231  y = W.y;
232  z = W.z;
233  w = cos(theta / 2.0);
234  }
235  else
236  {
237  x = y = z = 0;
238  w = 1.0;
239  }
240  }
241 
251  Vector3D rotatedVector(const Vector3D& v) const {
252  return (((*this) * Quaternion(v, 0)) * conjugate()).complex();
253  }
254 
255 
256 
262  void euler(const Vector3D& euler) {
263  double c1 = cos(euler[2] * 0.5);
264  double c2 = cos(euler[1] * 0.5);
265  double c3 = cos(euler[0] * 0.5);
266  double s1 = sin(euler[2] * 0.5);
267  double s2 = sin(euler[1] * 0.5);
268  double s3 = sin(euler[0] * 0.5);
269 
270  x = c1*c2*s3 - s1*s2*c3;
271  y = c1*s2*c3 + s1*c2*s3;
272  z = s1*c2*c3 - c1*s2*s3;
273  w = c1*c2*c3 + s1*s2*s3;
274  }
275 
280  Vector3D euler(void) const
281  {
282  Vector3D euler;
283  const static double PI_OVER_2 = M_PI * 0.5;
284  double sqw, sqx, sqy, sqz;
285 
286  // quick conversion to Euler angles to give tilt to user
287  sqw = w*w;
288  sqx = x*x;
289  sqy = y*y;
290  sqz = z*z;
291 
292  euler[1] = asin(2.0 * (w*y - x*z));
293  if (PI_OVER_2 - fabs(euler[1]) > EPS_D) {
294  euler[2] = atan2(2.0 * (x*y + w*z),
295  sqx - sqy - sqz + sqw);
296  euler[0] = atan2(2.0 * (w*x + y*z),
297  sqw - sqx - sqy + sqz);
298  }
299  else
300  {
301  // compute heading from local 'down' vector
302  euler[2] = atan2(2*y*z - 2*x*w,
303  2*x*z + 2*y*w);
304  euler[0] = 0.0;
305 
306  // If facing down, reverse yaw
307  if (euler[1] < 0)
308  {
309  euler[2] = M_PI - euler[2];
310  }
311  }
312 
313  return euler;
314  }
315 
323  void decoupleZ(Quaternion* Qxy, Quaternion* Qz) const {
324  Vector3D ztt(0,0,1);
325  Vector3D zbt = this->rotatedVector(ztt);
326  Vector3D axis_xy = cross(ztt, zbt);
327  double axis_norm = axis_xy.norm();
328 
329  double axis_theta = acos(clamp(zbt.z, -1.0, 1.0));
330  if (axis_norm > 0.00001)
331  {
332  axis_xy = axis_xy * (axis_theta/axis_norm); // limit is *1
333  }
334 
335  Qxy->scaledAxis(axis_xy);
336  *Qz = (Qxy->conjugate() * (*this));
337  }
338 
343  Quaternion slerp(const Quaternion& q1, double t)
344  {
345  return slerp(*this, q1, t);
346  }
347 
349  static Quaternion slerp(const Quaternion& q0, const Quaternion& q1, double t) {
350 
351  double omega = acos(clamp(q0.x*q1.x +
352  q0.y*q1.y +
353  q0.z*q1.z +
354  q0.w*q1.w, -1.0, 1.0));
355  if (fabs(omega) < 1e-10)
356  {
357  omega = 1e-10;
358  }
359  double som = sin(omega);
360  double st0 = sin((1-t) * omega) / som;
361  double st1 = sin(t * omega) / som;
362 
363  return Quaternion(q0.x*st0 + q1.x*st1,
364  q0.y*st0 + q1.y*st1,
365  q0.z*st0 + q1.z*st1,
366  q0.w*st0 + q1.w*st1);
367  }
368 
369 
370 };
371 
375 Quaternion operator*(double s, const Quaternion& q);
376 
377 } // namespace CMU462
378 
379 #endif /* CMU462_QUATERNION_H */
Vector4D unit(void) const
Returns unit vector.
Definition: vector4D.h:137
Definition: CMU462.h:8
double norm(void) const
Returns Euclidean distance metric extended to 4 dimensions.
Definition: vector4D.h:123
Vector4D()
Constructor.
Definition: vector4D.h:25
void normalize(void)
Divides by Euclidean length.
Definition: vector4D.h:146