29
29
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
30
//-------------------------------------------------------------------------
31
31
use std:: ops:: { Mul , Add , Sub , Neg , Div } ;
32
- use num:: { Num , Float , Signed } ;
32
+ use num:: { Num , Float , Signed , Zero , One } ;
33
+ use num:: traits:: FloatConst ;
33
34
34
35
use crate :: vector3:: * ;
35
36
use crate :: matrix3x3:: M33 ;
36
37
37
- // NOTE(elsuizo:2021-04-13): vamos a tomar la idea de poner un flag para saber
38
- // si el Quaternion es un unit quaternion
38
+ // TODO(elsuizo:2021-04-14): missing methods:
39
+ // - [ ] quaternion -> euler_angles
40
+
39
41
/// Quaternion type
40
- #[ derive( Copy , Debug , Clone ) ]
42
+ #[ derive( Copy , Debug , Clone , PartialEq ) ]
41
43
pub struct Quaternion < T > {
42
44
/// Scalar part
43
45
pub q0 : T ,
@@ -49,15 +51,15 @@ pub struct Quaternion<T> {
49
51
50
52
impl < T > Quaternion < T > {
51
53
54
+ /// construct a new Quaternion from a number(real part) and a vector(imag part)
52
55
pub const fn new ( q0 : T , q : V3 < T > ) -> Self {
53
56
Self { q0, q, normalized : false }
54
57
}
55
58
59
+ /// construct a new Quaternion from four numbers
56
60
pub fn new_from ( q0 : T , q1 : T , q2 : T , q3 : T ) -> Self {
57
61
Self { q0, q : V3 :: new ( [ q1, q2, q3] ) , normalized : false }
58
62
}
59
-
60
-
61
63
}
62
64
63
65
impl < T : Num + Copy > Quaternion < T > {
@@ -76,20 +78,54 @@ impl<T: Num + Copy> Quaternion<T> {
76
78
self . q
77
79
}
78
80
81
+ /// construct a unit Quaternion
82
+ pub fn one ( ) -> Quaternion < T > {
83
+ <Quaternion < T > as One >:: one ( )
84
+ }
85
+
86
+ /// construct a zero Quaternion
79
87
pub fn zero ( ) -> Self {
80
- Self :: new ( T :: zero ( ) , V3 :: zeros ( ) )
88
+ < Quaternion < T > as Zero > :: zero ( )
81
89
}
82
90
91
+ /// construct a pure "real" Quaternion
83
92
pub fn new_real ( q0 : T ) -> Self {
84
93
Self { q0, q : V3 :: zeros ( ) , normalized : true }
85
94
}
95
+
96
+ /// construct a pure "imaginary" Quaternion
97
+ pub fn new_imag ( q : V3 < T > ) -> Self {
98
+ Self { q0 : T :: zero ( ) , q, normalized : false }
99
+ }
100
+
101
+ /// calculate the abs2 of the Quaternion
102
+ pub fn abs2 ( & self ) -> T {
103
+ self . q0 * self . q0 + self . q [ 0 ] * self . q [ 0 ] + self . q [ 1 ] * self . q [ 1 ] + self . q [ 2 ] * self . q [ 2 ]
104
+ }
105
+ }
106
+
107
+ impl < T : Num + Copy > Zero for Quaternion < T > {
108
+ fn zero ( ) -> Self {
109
+ Self :: new ( T :: zero ( ) , V3 :: zeros ( ) )
110
+ }
111
+
112
+ fn is_zero ( & self ) -> bool {
113
+ * self == Quaternion :: zero ( )
114
+ }
115
+ }
116
+
117
+ impl < T : Num + Copy > One for Quaternion < T > {
118
+ /// Create an identity Quaternion
119
+ fn one ( ) -> Self {
120
+ let one = T :: one ( ) ;
121
+ Self :: new ( one, V3 :: zeros ( ) )
122
+ }
86
123
}
87
124
88
125
// q + q
89
126
impl < T : Num + Copy > Add for Quaternion < T > {
90
127
type Output = Self ;
91
128
fn add ( self , rhs : Self ) -> Self {
92
- // Self{q0: self.q0 + rhs.q0, q: self.q + rhs.q}
93
129
Self :: new ( self . q0 + rhs. q0 , self . q + rhs. q )
94
130
}
95
131
}
@@ -98,7 +134,6 @@ impl<T: Num + Copy> Add for Quaternion<T> {
98
134
impl < T : Num + Copy > Sub for Quaternion < T > {
99
135
type Output = Self ;
100
136
fn sub ( self , rhs : Self ) -> Self {
101
- // Self{q0: self.q0 - rhs.q0, q: self.q - rhs.q}
102
137
Self :: new ( self . q0 + rhs. q0 , self . q + rhs. q )
103
138
}
104
139
}
@@ -110,7 +145,6 @@ impl<T: Num + Copy> Div<T> for Quaternion<T> {
110
145
fn div ( self , rhs : T ) -> Self :: Output {
111
146
let q0 = self . q0 / rhs;
112
147
let q = self . q / rhs;
113
- // Self{q0, q}
114
148
Self :: new ( q0, q)
115
149
}
116
150
}
@@ -122,7 +156,6 @@ impl<T: Num + Copy> Mul for Quaternion<T> {
122
156
fn mul ( self , rhs : Self ) -> Self :: Output {
123
157
let q0 = self . q0 * rhs. q0 - self . q * rhs. q ;
124
158
let q = rhs. q * self . q0 + self . q * rhs. q0 + self . q . cross ( rhs. q ) ;
125
- // Self{q0, q}
126
159
Self :: new ( q0, q)
127
160
}
128
161
}
@@ -144,27 +177,41 @@ impl<T: Num + Copy + Signed> Neg for Quaternion<T> {
144
177
type Output = Self ;
145
178
#[ inline]
146
179
fn neg ( self ) -> Self {
147
- // Self{q0: -self.q0, q: -self.q}
148
180
Self :: new ( -self . q0 , -self . q )
149
181
}
150
182
}
151
183
152
184
impl < T : Num + Copy + Signed > Quaternion < T > {
153
185
pub fn conj ( & self ) -> Self {
154
- // Self{q0: self.q0, q: -self.q}
155
186
Self :: new ( self . q0 , -self . q )
156
187
}
157
188
}
158
189
159
- impl < T : Float > Quaternion < T > {
190
+ impl < T : Float + Signed > Quaternion < T > {
191
+ pub fn inv ( & self ) -> Option < Self > {
192
+ if !self . normalized {
193
+ let norm_sqr = self . dot ( * self ) ;
194
+ if norm_sqr > T :: epsilon ( ) {
195
+ Some ( self . conj ( ) / self . abs2 ( ) )
196
+ } else {
197
+ None
198
+ }
199
+ } else {
200
+ Some ( self . conj ( ) )
201
+ }
202
+ }
203
+ }
204
+
205
+ // TODO(elsuizo:2021-04-14): a warning from here but i dont known why???
206
+ impl < T : Float + FloatConst > Quaternion < T > {
160
207
/// normalize the Quaternion only if necesary
161
208
pub fn normalize ( & self ) -> Option < Self > {
162
209
if self . normalized {
163
210
Some ( * self )
164
211
} else {
165
212
let norm_sqr = self . dot ( * self ) ;
166
- let mut result = Self :: zero ( ) ;
167
213
if norm_sqr > T :: epsilon ( ) {
214
+ let mut result = Self :: zero ( ) ;
168
215
result = * self / self . dot ( * self ) . sqrt ( ) ;
169
216
result. normalized = true ;
170
217
Some ( result)
@@ -174,13 +221,14 @@ impl<T: Float> Quaternion<T> {
174
221
}
175
222
}
176
223
224
+
177
225
/// generate a Quaternion that represents a rotation of a angle `theta`
178
226
/// around the axis(normalized) `v`
179
227
pub fn rotation ( theta : T , v : V3 < T > ) -> Self {
180
228
let two = T :: from ( 2u8 ) . unwrap ( ) ;
181
229
let n = v. normalize ( ) . expect ( "the input has to be a non zero vector" ) ;
182
- let q0 = ( theta. to_radians ( ) / two) . cos ( ) ;
183
- let q = n * ( theta. to_radians ( ) / two) . sin ( ) ;
230
+ let q0 = ( theta / two) . cos ( ) ;
231
+ let q = n * ( theta / two) . sin ( ) ;
184
232
Self { q0, q, normalized : true }
185
233
}
186
234
@@ -192,8 +240,8 @@ impl<T: Float> Quaternion<T> {
192
240
let two = T :: from ( 2.0 ) . unwrap ( ) ;
193
241
let theta = v. norm2 ( ) ;
194
242
if theta > T :: epsilon ( ) {
195
- let s = T :: sin ( theta. to_radians ( ) / two) / theta;
196
- Self :: new ( T :: cos ( theta. to_radians ( ) / two) , v * s)
243
+ let s = T :: sin ( theta / two) / theta;
244
+ Self :: new ( T :: cos ( theta / two) , v * s)
197
245
} else {
198
246
Self :: new ( one, V3 :: zeros ( ) )
199
247
}
@@ -213,21 +261,26 @@ impl<T: Float> Quaternion<T> {
213
261
two* q1* q3 - two* q0* q2, two* q2* q3 + two* q0* q1, q0_s - q1_s - q2_s + q3_s)
214
262
}
215
263
264
+ // NOTE(elsuizo:2021-04-14): this implementation avoid the use of sqrt(which is expensive
265
+ // computationally)
266
+ // TODO(elsuizo:2021-04-14): maybe here we could acept a tuple (T, T, T)
216
267
/// create a quaternion that represents the rotation from a Euler angles
217
268
/// with the roll-pitch-yay convention
218
269
pub fn from_euler_angles ( yay : T , pitch : T , roll : T ) -> Self {
219
270
let two = T :: one ( ) + T :: one ( ) ;
220
- let cy = T :: cos ( yay. to_radians ( ) / two) ;
221
- let sy = T :: sin ( yay. to_radians ( ) / two) ;
222
- let cp = T :: cos ( pitch. to_radians ( ) / two) ;
223
- let sp = T :: sin ( pitch. to_radians ( ) / two) ;
224
- let cr = T :: cos ( roll. to_radians ( ) / two) ;
225
- let sr = T :: sin ( roll. to_radians ( ) / two) ;
226
-
227
- let q0 = cr * cp * cy + sr * sp * sy;
228
- let q1 = sr * cp * cy - cr * sp * sy;
229
- let q2 = cr * sp * cy + sr * cp * sy;
230
- let q3 = cr * cp * sy - sr * sp * cy;
271
+ let c1 = T :: cos ( yay / two) ;
272
+ let s1 = T :: sin ( yay / two) ;
273
+ let c2 = T :: cos ( pitch / two) ;
274
+ let s2 = T :: sin ( pitch / two) ;
275
+ let c3 = T :: cos ( roll / two) ;
276
+ let s3 = T :: sin ( roll / two) ;
277
+ let c1c2 = c1 * c2;
278
+ let s1s2 = s1 * s2;
279
+
280
+ let q0 = c1c2 * c3 - s1s2 * s3;
281
+ let q1 = c1c2 * s3 + s1s2 * c3;
282
+ let q2 = s1 * c2 * c3 + c1 * s2 * s3;
283
+ let q3 = c1 * s2 * c3 - s1 * c2 * s3;
231
284
232
285
Self :: new_from ( q0, q1, q2, q3)
233
286
}
@@ -248,6 +301,43 @@ impl<T: Float> Quaternion<T> {
248
301
None
249
302
}
250
303
}
304
+
305
+ // NOTE(elsuizo:2021-04-14): this implementation is a translation from this
306
+ // webpage from Martin John Baker.
307
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm
308
+ //
309
+ pub fn to_euler_angles ( & self ) -> ( T , T , T ) {
310
+ let one = T :: one ( ) ;
311
+ let two = one + one;
312
+ let q0_2 = self . q0 * self . q0 ;
313
+ let q1_2 = self . q [ 0 ] * self . q [ 0 ] ;
314
+ let q2_2 = self . q [ 1 ] * self . q [ 1 ] ;
315
+ let q3_2 = self . q [ 2 ] * self . q [ 2 ] ;
316
+ // if the Quaternion is normalized this is one, otherwise is a correction factor
317
+ let unit = q0_2 + q1_2 + q2_2 + q3_2;
318
+ let test = self . q [ 0 ] * self . q [ 1 ] + self . q [ 2 ] * self . q0 ;
319
+ let sing_number = one / two;
320
+ if test - sing_number * unit > T :: epsilon ( ) {
321
+ let yay = two * T :: atan2 ( self . q [ 0 ] , self . q0 ) ;
322
+ let pitch = T :: FRAC_PI_2 ( ) ;
323
+ let roll = T :: zero ( ) ;
324
+ return ( yay, pitch, roll) ;
325
+ }
326
+ if test + sing_number * unit < T :: epsilon ( ) {
327
+ let yay = -two * T :: atan2 ( self . q [ 0 ] , self . q0 ) ;
328
+ let pitch = -one * T :: FRAC_PI_2 ( ) ;
329
+ let roll = T :: zero ( ) ;
330
+ return ( yay, pitch, roll) ;
331
+ }
332
+ let aux1 = two * self . q [ 1 ] * self . q0 - two * self . q [ 0 ] * self . q [ 2 ] ;
333
+ let aux2 = q1_2 - q2_2 - q3_2 + q0_2;
334
+ let yay = T :: atan2 ( aux1, aux2) ;
335
+ let pitch = T :: asin ( two * test / unit) ;
336
+ let aux3 = two * self . q [ 0 ] * self . q0 - two * self . q [ 1 ] * self . q [ 2 ] ;
337
+ let aux4 = -q1_2 + q2_2 - q3_2 + q0_2;
338
+ let roll = T :: atan2 ( aux3, aux4) ;
339
+ ( yay, pitch, roll)
340
+ }
251
341
}
252
342
253
343
//-------------------------------------------------------------------------
@@ -317,9 +407,10 @@ mod test_quaternion {
317
407
assert_eq ! ( result_float. q[ 2 ] , -1.0 ) ;
318
408
}
319
409
410
+ // NOTE(elsuizo:2021-04-14): we assume all the values of the angles in radians!!!
320
411
#[ test]
321
412
fn rotate_vec ( ) {
322
- let q1 = Quaternion :: rotation ( 90.0 , V3 :: new_from ( 0.0 , 0.0 , 1.0 ) ) ;
413
+ let q1 = Quaternion :: rotation ( 90.0f32 . to_radians ( ) , V3 :: new_from ( 0.0 , 0.0 , 1.0 ) ) ;
323
414
let x = V3 :: new_from ( 1.0 , 0.0 , 0.0 ) ;
324
415
// rotate x around z 90 degrees
325
416
let result = q1 * x;
@@ -331,7 +422,7 @@ mod test_quaternion {
331
422
332
423
#[ test]
333
424
fn rotate_vec_composition_360 ( ) {
334
- let q1 = Quaternion :: rotation ( 90.0 , V3 :: new_from ( 0.0 , 0.0 , 1.0 ) ) ;
425
+ let q1 = Quaternion :: rotation ( 90.0f32 . to_radians ( ) , V3 :: new_from ( 0.0 , 0.0 , 1.0 ) ) ;
335
426
let x = V3 :: new_from ( 1.0 , 0.0 , 0.0 ) ;
336
427
// rotate x around z (90 * 4 = 360) degrees
337
428
let result = q1 * q1 * q1 * q1 * x;
@@ -342,7 +433,7 @@ mod test_quaternion {
342
433
343
434
#[ test]
344
435
fn rotate_vec_angle_encode ( ) {
345
- let q = Quaternion :: rotation_norm_encoded ( V3 :: new_from ( 0.0 , 0.0 , 90.0 ) ) ;
436
+ let q = Quaternion :: rotation_norm_encoded ( V3 :: new_from ( 0.0 , 0.0 , 90.0f32 . to_radians ( ) ) ) ;
346
437
let x = V3 :: x_axis ( ) ;
347
438
let result = q * x;
348
439
let expected = V3 :: new_from ( 0.0 , -1.0 , 0.0 ) ;
@@ -353,7 +444,7 @@ mod test_quaternion {
353
444
354
445
#[ test]
355
446
fn convert_dcm_test ( ) {
356
- let q = Quaternion :: rotation_norm_encoded ( V3 :: new_from ( 0.0 , 0.0 , 90.0 ) ) ;
447
+ let q = Quaternion :: rotation_norm_encoded ( V3 :: new_from ( 0.0 , 0.0 , 90.0f32 . to_radians ( ) ) ) ;
357
448
let x = V3 :: x_axis ( ) ;
358
449
// rotate the x around z axis 360 degrees
359
450
let expected = q * q * q * q * x;
@@ -366,4 +457,27 @@ mod test_quaternion {
366
457
assert ! ( compare_floats( result[ 1 ] , expected[ 1 ] , EPS ) ) ;
367
458
assert ! ( compare_floats( result[ 2 ] , expected[ 2 ] , EPS ) ) ;
368
459
}
460
+
461
+ #[ test]
462
+ fn inverse_test ( ) {
463
+ let q = Quaternion :: new_from ( 1.0 , 1.0 , 1.0 , 10.0 ) ;
464
+ if let Some ( inv) = q. inv ( ) {
465
+ let result = q * inv;
466
+ let expected = Quaternion :: one ( ) ;
467
+ assert ! ( compare_floats( result. q0, expected. q0, EPS ) ) ;
468
+ assert ! ( compare_floats( result. q[ 0 ] , expected. q[ 0 ] , EPS ) ) ;
469
+ assert ! ( compare_floats( result. q[ 1 ] , expected. q[ 1 ] , EPS ) ) ;
470
+ assert ! ( compare_floats( result. q[ 2 ] , expected. q[ 2 ] , EPS ) ) ;
471
+ }
472
+ }
473
+
474
+ #[ test]
475
+ fn euler_and_quaternions ( ) {
476
+ let expected = ( 0.1 , 0.2 , 0.3 ) ;
477
+ let q = Quaternion :: from_euler_angles ( expected. 0 , expected. 1 , expected. 2 ) ;
478
+ let result = q. to_euler_angles ( ) ;
479
+ assert ! ( compare_floats( result. 0 , expected. 0 , EPS ) ) ;
480
+ assert ! ( compare_floats( result. 1 , expected. 1 , EPS ) ) ;
481
+ assert ! ( compare_floats( result. 2 , expected. 2 , EPS ) ) ;
482
+ }
369
483
}
0 commit comments