-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQuaternion.cpp
More file actions
317 lines (279 loc) · 10.5 KB
/
Quaternion.cpp
File metadata and controls
317 lines (279 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
//////////////////////////////////////////////////////////////////////
//
// University of Leeds
// COMP 5812M Foundations of Modelling & Rendering
// User Interface for Coursework
//
// September, 2020
//
// ------------------------
// Quaternion.cpp
// ------------------------
//
// A class representing a quaternion
//
// Note: the emphasis here is on clarity, not efficiency
// A number of the routines could be implemented more
// efficiently but aren't
//
///////////////////////////////////////////////////
#include <math.h>
#include "Quaternion.h"
// constructor
Quaternion::Quaternion()
{ // constructor
coords[0] = coords[1] = coords[2] = 0.0;
coords[3] = 1.0;
} // constructor
// constructor: sets the quaternion to (x, y, z, w)
Quaternion::Quaternion(float x, float y, float z, float w)
{ // constructor
coords[0] = x;
coords[1] = y;
coords[2] = z;
coords[3] = w;
} // constructor
// Set to a pure scalar value
Quaternion::Quaternion(float scalar)
{ // copy scalar
// set first three coords to 0.0
for (int i = 0; i < 3; i++)
coords[i] = 0.0;
// and the real part to the scalar
coords[3] = scalar;
} // copy scalar
// Set to a pure vector value
Quaternion::Quaternion(const Cartesian3 &vector)
{ // copy vector
// copy vector part
for (int i = 0; i < 3; i++)
coords[i] = vector[i];
// set the real part to 0.0
coords[3] = 0.0;
} // copy vector
// Set to a homogeneous point
Quaternion::Quaternion(const Homogeneous4 &point)
{ // copy point
// just copy the coordinates
for (int i = 0; i < 4; i++)
coords[i] = point[i];
} // copy point
// Set to a rotation defined by a rotation matrix
// WARNING: MATRIX MUST BE A VALID ROTATION MATRIX
Quaternion::Quaternion(const Matrix4 &matrix)
{ // copy rotation matrix
// first, compute the trace of the matrix: the sum of the
// diagonal elements (see Convert() for coefficients)
float trace = matrix.coordinates[0][0] + matrix.coordinates[1][1]
+ matrix.coordinates[2][2] + matrix.coordinates[3][3];
// the trace should now contain 4 (1 - x^2 - y^2 - z^2)
// and IF it is a pure rotation with no scaling, then
// this is just 4 (w^2) since we will have a unit quaternion
float w = sqrt(trace * 0.25);
// now we can compute the vector component from symmetric
// pairs of entries
// (2yz + 2xw) - (2yz - 2xw) = 4 xw
float x = 0.25 * (matrix.coordinates[1][2] - matrix.coordinates[2][1]) / w;
// (2xz + 2yw) - (2xz - 2yw) = 4 yw
float y = 0.25 * (matrix.coordinates[2][0] - matrix.coordinates[0][2]) / w;
// (2xy + 2zw) - (2xy - 2zw) = 4 zw
float z = 0.25 * (matrix.coordinates[0][1] - matrix.coordinates[1][0]) / w;
// now store them in the appropriate locations
coords[0] = x;
coords[1] = y;
coords[2] = z;
coords[3] = w;
} // copy rotation matrix
// Set to a rotation defined by an axis and angle
Quaternion::Quaternion(const Cartesian3 &axis, float theta)
{ // Quaternion()
// convert the axis to a unit vector and multiply by sin theta
// then add cos theta as a scalar
(*this) = Quaternion(axis.unit() * sin(theta)) + Quaternion(cos(theta));
} // Quaternion()
// Computes the norm (sum of squares)
float Quaternion::Norm() const
{ // Norm()
return (coords[0]*coords[0]+coords[1]*coords[1]+
coords[2]*coords[2]+coords[3]*coords[3]);
} // Norm()
// Reduce to unit quaternion
Quaternion Quaternion::Unit() const
{ // Unit()
Quaternion result;
// get the square root of the norm
float sqrtNorm = Norm();
// now divide by it
for (int i = 0; i < 4; i++)
result.coords[i] = coords[i] / sqrtNorm;
return result;
} // Unit()
// Conjugate the quaternion
Quaternion Quaternion::Conjugate() const
{ // Conjugate()
Quaternion result;
for (int i = 0; i < 3; i++)
result.coords[i] = coords[i] * -1;
result.coords[3] = coords[3];
return result;
} // Conjugate()
// Invert the quaternion
Quaternion Quaternion::Inverse() const
{ // Invert()
Quaternion result = Conjugate() / Norm();
return result;
} // Invert()
// Scalar left-multiplication
Quaternion operator *(float scalar, const Quaternion &quat)
{ // scalar left-multiplication
Quaternion result;
for (int i = 0; i < 4; i++)
result.coords[i] = scalar * quat.coords[i];
return result;
} // scalar left-multiplication
// Scalar right-multiplication
Quaternion Quaternion::operator *(float scalar) const
{ // scalar right-multiplication
Quaternion result;
for (int i = 0; i < 4; i++)
result.coords[i] = coords[i] * scalar;
return result;
} // scalar right-multiplication
// Scalar right-division
Quaternion Quaternion::operator /(float scalar) const
{ // scalar right-division
Quaternion result;
for (int i = 0; i < 4; i++)
result.coords[i] = coords[i] / scalar;
return result;
} // scalar right-division
// Adds two quaternions together
Quaternion Quaternion::operator +(const Quaternion &other) const
{ // addition
Quaternion result;
for (int i = 0; i < 4; i++)
result.coords[i] = coords[i] + other.coords[i];
return result;
} // addition
// Subtracts one quaternion from another
Quaternion Quaternion::operator -(const Quaternion &other) const
{ // subtraction
Quaternion result;
for (int i = 0; i < 4; i++)
result.coords[i] = coords[i] - other.coords[i];
return result;
} // subtraction
// Multiplies two quaternions together
Quaternion Quaternion::operator *(const Quaternion &other) const
{ // multiplication
Quaternion result;
// and compute each set of coords
result.coords[0] = + coords[0] * other.coords[3] // i 1
+ coords[1] * other.coords[2] // j k
- coords[2] * other.coords[1] // k j
+ coords[3] * other.coords[0]; // 1 i
result.coords[1] = - coords[0] * other.coords[2] // i k
+ coords[1] * other.coords[3] // j 1
+ coords[2] * other.coords[0] // k i
+ coords[3] * other.coords[1]; // 1 j
result.coords[2] = + coords[0] * other.coords[1] // i j
- coords[1] * other.coords[0] // j i
+ coords[2] * other.coords[3] // k 1
+ coords[3] * other.coords[2]; // 1 k
result.coords[3] = - coords[0] * other.coords[0] // i i
- coords[1] * other.coords[1] // j j
- coords[2] * other.coords[2] // k k
+ coords[3] * other.coords[3]; // 1 1
return result;
} // multiplication
// Acts on a vector
Cartesian3 Quaternion::Act(const Cartesian3 &vector) const
{ // Act()
// compute the result
Quaternion resultQuat = Inverse() * Quaternion(vector) * (*this);
Cartesian3 resultVector(resultQuat.coords[0], resultQuat.coords[1],
resultQuat.coords[2]);
// and return the vector
return resultVector;
} // Act()
// Acts on a homogeneous point
Homogeneous4 Quaternion::Act(const Homogeneous4 &point) const
{ // Act()
Quaternion resultQuat = Inverse() * Quaternion(point) * (*this);
Homogeneous4 resultPoint(resultQuat.coords[0], resultQuat.coords[1],
resultQuat.coords[2], resultQuat.coords[3]);
// and return the point
return resultPoint;
} // Act()
// Returns the angle 2*theta of the action in degrees
float Quaternion::AngleOfAction() const
{ // AngleOfAction()
float sqrtNorm = sqrt(Norm());
// normalize, compute arc cosine & return twice the angle
return (2.0 * acos(coords[3] / sqrtNorm));
} // AngleOfAction()
// Returns the axis of rotation
Cartesian3 Quaternion::AxisOfRotation() const
{ // AxisOfRotation()
Cartesian3 axis;
// retrieve the angle of action
float thetaDeg = AngleOfAction();
float theta = thetaDeg * 2.0 * M_PI / 360.0;
// and set the axis by dividing by sin theta
for (int i = 0; i < 3; i++)
axis[i] = coords[i] / sin(theta);
if (theta == 0.0)
{ // no rotation at all - axis unknown
axis[0] = 1.0f;
axis[1] = axis[2] = 0.0f;
} // no rotation at all - axis unknown
return axis;
} // AxisOfRotation()
// Converts a quaternion to a rotation matrix
Matrix4 Quaternion::GetMatrix() const
{ // GetMatrix()
Matrix4 result;
// a quaternion (x y z w) is equivalent to the following matrix
// | 1 - 2(y^2+z^2) 2(xy-wz) 2(xz+wy) 0 |
// | 2(xy+wz) 1 - 2(x^2+z^2) 2(yz-wx) 0 |
// | 2(xz-wy) 2(yz+wx) 1 - 2(x^2+y^2) 0 |
// | 0 0 0 1 |
float xx = coords[0] * coords[0];
float xy = coords[0] * coords[1];
float xz = coords[0] * coords[2];
float xw = coords[0] * coords[3];
float yy = coords[1] * coords[1];
float yz = coords[1] * coords[2];
float yw = coords[1] * coords[3];
float zz = coords[2] * coords[2];
float zw = coords[2] * coords[3];
result.coordinates[0][0] = 1 - 2 * ( yy + zz );
result.coordinates[0][1] = 2 * ( xy - zw );
result.coordinates[0][2] = 2 * ( xz + yw );
result.coordinates[0][3] = 0.0;
result.coordinates[1][0] = 2 * ( xy + zw );
result.coordinates[1][1] = 1 - 2 * ( xx + zz );
result.coordinates[1][2] = 2 * ( yz - xw );
result.coordinates[1][3] = 0.0;
result.coordinates[2][0] = 2 * ( xz - yw );
result.coordinates[2][1] = 2 * ( yz + xw );
result.coordinates[2][2] = 1 - 2 * ( xx + yy );
result.coordinates[2][3] = 0.0;
result.coordinates[3][0] = 0.0;
result.coordinates[3][1] = 0.0;
result.coordinates[3][2] = 0.0;
result.coordinates[3][3] = 1.0;
return result;
} // GetMatrix()
// stream input
std::istream & operator >> (std::istream &inStream, Quaternion &quat)
{ // stream input
inStream >> quat.coords[0] >> quat.coords[1] >> quat.coords[2] >> quat.coords[3];
return inStream;
} // stream input
// stream output
std::ostream & operator << (std::ostream &outStream, const Quaternion &quat)
{ // stream output
outStream << quat.coords[0] << " " << quat.coords[1] << " " << quat.coords[2] << " " << quat.coords[3] << std::endl;
return outStream;
} // stream output