4 This file is part of the pyquaternion python module 7 Website: https://github.com/KieranWynn/pyquaternion 8 Documentation: http://kieranwynn.github.io/pyquaternion/ 11 License: The MIT License (MIT) 13 Copyright (c) 2015 Kieran Wynn 15 Permission is hereby granted, free of charge, to any person obtaining a copy 16 of this software and associated documentation files (the "Software"), to deal 17 in the Software without restriction, including without limitation the rights 18 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 copies of the Software, and to permit persons to whom the Software is 20 furnished to do so, subject to the following conditions: 22 The above copyright notice and this permission notice shall be included in all 23 copies or substantial portions of the Software. 25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 test_quaternion.py - Unit test for quaternion module 38 from math
import pi, sin, cos
39 from random
import random
43 from pyquaternion
import Quaternion
46 ALMOST_EQUAL_TOLERANCE = 13
49 return tuple(np.random.uniform(-1, 1, 4))
55 self.assertIsInstance(q, Quaternion)
56 self.assertEqual(q, Quaternion(1., 0., 0., 0.))
59 q1 = Quaternion.random()
61 self.assertIsInstance(q2, Quaternion)
62 self.assertEqual(q2, q1)
63 with self.assertRaises(TypeError):
65 with self.assertRaises(ValueError):
66 q4 = Quaternion(
"String")
69 r1 = Quaternion.random()
70 r2 = Quaternion.random()
71 self.assertAlmostEqual(r1.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
72 self.assertIsInstance(r1, Quaternion)
78 q2 = Quaternion(repr(s))
79 self.assertIsInstance(q1, Quaternion)
80 self.assertIsInstance(q2, Quaternion)
81 self.assertEqual(q1, Quaternion(s, 0.0, 0.0, 0.0))
82 self.assertEqual(q2, Quaternion(s, 0.0, 0.0, 0.0))
83 with self.assertRaises(TypeError):
85 with self.assertRaises(ValueError):
86 q = Quaternion(
"String")
90 q1 = Quaternion(a, b, c, d)
91 q2 = Quaternion(repr(a), repr(b), repr(c), repr(d))
92 q3 = Quaternion(a, repr(b), c, d)
93 self.assertIsInstance(q1, Quaternion)
94 self.assertIsInstance(q2, Quaternion)
95 self.assertIsInstance(q3, Quaternion)
96 self.assertTrue(np.array_equal(q1.q, [a, b, c, d]))
97 self.assertEqual(q1, q2)
98 self.assertEqual(q2, q3)
99 with self.assertRaises(TypeError):
100 q = Quaternion(
None, b, c, d)
101 with self.assertRaises(ValueError):
102 q = Quaternion(a, b,
"String", d)
103 with self.assertRaises(ValueError):
104 q = Quaternion(a, b, c)
105 with self.assertRaises(ValueError):
106 q = Quaternion(a, b, c, d, random())
112 self.assertIsInstance(q, Quaternion)
113 self.assertEqual(q, Quaternion(*r))
114 with self.assertRaises(ValueError):
115 q = Quaternion(a[1:4])
116 with self.assertRaises(ValueError):
117 q = Quaternion(np.hstack((a, a)))
118 with self.assertRaises(ValueError):
119 q = Quaternion(np.array([a, a]))
120 with self.assertRaises(TypeError):
121 q = Quaternion(np.array([
None,
None,
None,
None]))
126 self.assertIsInstance(q, Quaternion)
127 self.assertEqual(q, Quaternion(*t))
128 with self.assertRaises(ValueError):
129 q = Quaternion(t[1:4])
130 with self.assertRaises(ValueError):
131 q = Quaternion(t + t)
132 with self.assertRaises(ValueError):
133 q = Quaternion((t, t))
134 with self.assertRaises(TypeError):
135 q = Quaternion((
None,
None,
None,
None))
141 self.assertIsInstance(q, Quaternion)
142 self.assertEqual(q, Quaternion(*l))
143 with self.assertRaises(ValueError):
144 q = Quaternion(l[1:4])
145 with self.assertRaises(ValueError):
146 q = Quaternion(l + l)
147 with self.assertRaises(ValueError):
148 q = Quaternion((l, l))
149 with self.assertRaises(TypeError):
150 q = Quaternion([
None,
None,
None,
None])
154 q1 = Quaternion(w=e1, x=e2, y=e3, z=e4)
155 q2 = Quaternion(a=e1, b=repr(e2), c=e3, d=e4)
156 q3 = Quaternion(a=e1, i=e2, j=e3, k=e4)
157 q4 = Quaternion(a=e1)
158 self.assertIsInstance(q1, Quaternion)
159 self.assertIsInstance(q2, Quaternion)
160 self.assertIsInstance(q3, Quaternion)
161 self.assertIsInstance(q4, Quaternion)
162 self.assertEqual(q1, Quaternion(e1, e2, e3, e4))
163 self.assertEqual(q1, q2)
164 self.assertEqual(q2, q3)
165 self.assertEqual(q4, Quaternion(e1))
166 with self.assertRaises(TypeError):
167 q = Quaternion(a=
None, b=e2, c=e3, d=e4)
168 with self.assertRaises(ValueError):
169 q = Quaternion(a=e1, b=e2, c=
"String", d=e4)
170 with self.assertRaises(ValueError):
171 q = Quaternion(w=e1, x=e2)
172 with self.assertRaises(ValueError):
173 q = Quaternion(a=e1, b=e2, c=e3, d=e4, e=e1)
179 q1 = Quaternion(real=a, imaginary=(b, c, d))
180 q2 = Quaternion(real=a, imaginary=[b, c, d])
181 q3 = Quaternion(real=a, imaginary=np.array([b, c, d]))
182 q4 = Quaternion(real=a)
183 q5 = Quaternion(imaginary=np.array([b, c, d]))
184 q6 = Quaternion(real=
None, imaginary=np.array([b, c, d]))
185 self.assertIsInstance(q1, Quaternion)
186 self.assertIsInstance(q2, Quaternion)
187 self.assertIsInstance(q3, Quaternion)
188 self.assertIsInstance(q4, Quaternion)
189 self.assertIsInstance(q5, Quaternion)
190 self.assertIsInstance(q6, Quaternion)
191 self.assertEqual(q1, Quaternion(a, b, c, d))
192 self.assertEqual(q1, q2)
193 self.assertEqual(q2, q3)
194 self.assertEqual(q4, Quaternion(a, 0, 0, 0))
195 self.assertEqual(q5, Quaternion(0, b, c, d))
196 self.assertEqual(q5, q6)
197 with self.assertRaises(ValueError):
198 q = Quaternion(real=a, imaginary=[b, c])
199 with self.assertRaises(ValueError):
200 q = Quaternion(real=a, imaginary=(b, c, d, d))
203 q1 = Quaternion(scalar=a, vector=(b, c, d))
204 q2 = Quaternion(scalar=a, vector=[b, c, d])
205 q3 = Quaternion(scalar=a, vector=np.array([b, c, d]))
206 q4 = Quaternion(scalar=a)
207 q5 = Quaternion(vector=np.array([b, c, d]))
208 q6 = Quaternion(scalar=
None, vector=np.array([b, c, d]))
209 self.assertIsInstance(q1, Quaternion)
210 self.assertIsInstance(q2, Quaternion)
211 self.assertIsInstance(q3, Quaternion)
212 self.assertIsInstance(q4, Quaternion)
213 self.assertIsInstance(q5, Quaternion)
214 self.assertIsInstance(q6, Quaternion)
215 self.assertEqual(q1, Quaternion(a, b, c, d))
216 self.assertEqual(q1, q2)
217 self.assertEqual(q2, q3)
218 self.assertEqual(q4, Quaternion(a, 0, 0, 0))
219 self.assertEqual(q5, Quaternion(0, b, c, d))
220 self.assertEqual(q5, q6)
221 with self.assertRaises(ValueError):
222 q = Quaternion(scalar=a, vector=[b, c])
223 with self.assertRaises(ValueError):
224 q = Quaternion(scalar=a, vector=(b, c, d, d))
230 theta = random() * 2.0 * pi
236 q1 = Quaternion(axis=v1, angle=theta)
237 q2 = Quaternion(axis=v2, radians=theta)
238 q3 = Quaternion(axis=v3, degrees=theta / pi * 180)
241 v3 = v3 / np.linalg.norm(v3)
243 q4 = Quaternion(angle=theta, axis=v3)
253 truth = Quaternion(a, b, c, d)
255 self.assertEqual(q1, truth)
256 self.assertEqual(q2, truth)
257 self.assertEqual(q3, truth)
258 self.assertEqual(q4, truth)
259 self.assertEqual(Quaternion(axis=v3, angle=0), Quaternion())
260 self.assertEqual(Quaternion(axis=v3, radians=0), Quaternion())
261 self.assertEqual(Quaternion(axis=v3, degrees=0), Quaternion())
262 self.assertEqual(Quaternion(axis=v3), Quaternion())
265 self.assertAlmostEqual(q1.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
266 self.assertAlmostEqual(q2.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
267 self.assertAlmostEqual(q3.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
268 self.assertAlmostEqual(q4.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
271 with self.assertRaises(ValueError):
272 q = Quaternion(angle=theta)
273 with self.assertRaises(ValueError):
274 q = Quaternion(axis=[b, c], angle=theta)
275 with self.assertRaises(ValueError):
276 q = Quaternion(axis=(b, c, d, d), angle=theta)
277 with self.assertRaises(ZeroDivisionError):
278 q = Quaternion(axis=[0., 0., 0.], angle=theta)
284 Generate a rotation matrix describing a rotation of theta degrees about the z-axis 293 v = np.array([1, 0, 0])
294 for angle
in [0, pi/6, pi/4, pi/2, pi, 4*pi/3, 3*pi/2, 2*pi]:
296 v_prime_r = np.dot(R, v)
298 q1 = Quaternion(axis=[0,0,1], angle=angle)
299 v_prime_q1 = q1.rotate(v)
301 np.testing.assert_almost_equal(v_prime_r, v_prime_q1, decimal=ALMOST_EQUAL_TOLERANCE)
303 q2 = Quaternion(matrix=R)
304 v_prime_q2 = q2.rotate(v)
306 np.testing.assert_almost_equal(v_prime_q2, v_prime_r, decimal=ALMOST_EQUAL_TOLERANCE)
308 R = np.matrix(np.eye(3))
309 q3 = Quaternion(matrix=R)
310 v_prime_q3 = q3.rotate(v)
311 np.testing.assert_almost_equal(v, v_prime_q3, decimal=ALMOST_EQUAL_TOLERANCE)
312 self.assertEqual(q3, Quaternion())
315 with self.assertRaises(ValueError):
316 q4 = Quaternion(matrix=R)
320 The matrix defined in this test is orthogonal was carefully crafted 321 such that it's orthogonal to a precision of 1e-07, but not to a precision 322 of 1e-08. The default value for numpy's atol function is 1e-08, but 323 developers should have the option to use a lower precision if they choose 326 Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html 328 m = [[ 0.73297226, -0.16524626, -0.65988294, -0.07654548],
329 [ 0.13108627, 0.98617666, -0.10135052, -0.04878795],
330 [ 0.66750896, -0.01221443, 0.74450167, -0.05474513],
334 with self.assertRaises(ValueError):
335 Quaternion(matrix=npm)
338 Quaternion(matrix=npm, atol=1e-07)
340 self.fail(
"Quaternion() raised ValueError unexpectedly!")
345 q = Quaternion(array=a)
346 self.assertIsInstance(q, Quaternion)
347 self.assertEqual(q, Quaternion(*r))
348 with self.assertRaises(ValueError):
349 q = Quaternion(array=a[1:4])
350 with self.assertRaises(ValueError):
351 q = Quaternion(array=np.hstack((a, a)))
352 with self.assertRaises(ValueError):
353 q = Quaternion(array=np.array([a, a]))
354 with self.assertRaises(TypeError):
355 q = Quaternion(array=np.array([
None,
None,
None,
None]))
359 q = Quaternion(a, b, c, d)
360 self.assertEqual(q, Quaternion(q))
361 self.assertEqual(q, Quaternion(np.array([a, b, c, d])))
362 self.assertEqual(q, Quaternion((a, b, c, d)))
363 self.assertEqual(q, Quaternion([a, b, c, d]))
364 self.assertEqual(q, Quaternion(w=a, x=b, y=c, z=d))
365 self.assertEqual(q, Quaternion(array=np.array([a, b, c, d])))
371 q = Quaternion(a, b, c, d)
372 string =
"{:.3f} {:+.3f}i {:+.3f}j {:+.3f}k".format(a, b, c, d)
373 self.assertEqual(string, str(q))
377 q = Quaternion(a, b, c, d)
378 for s
in [
'.3f',
'+.14f',
'.6e',
'g']:
379 individual_fmt =
'{:' + s +
'} {:' + s +
'}i {:' + s +
'}j {:' + s +
'}k' 380 quaternion_fmt =
'{:' + s +
'}' 381 self.assertEqual(individual_fmt.format(a, b, c, d), quaternion_fmt.format(q))
385 q = Quaternion(a, b, c, d)
386 string =
"Quaternion(" + repr(a) +
", " + repr(b) +
", " + repr(c) +
", " + repr(d) +
")" 387 self.assertEqual(string, repr(q))
392 self.assertTrue(Quaternion())
393 self.assertFalse(Quaternion(scalar=0.0))
394 self.assertTrue(~Quaternion(scalar=0.0))
395 self.assertFalse(~Quaternion())
399 q = Quaternion(a, b, c, d)
400 self.assertEqual(float(q), a)
404 q = Quaternion(a, b, c, d)
405 self.assertEqual(int(q), int(a))
406 self.assertEqual(int(Quaternion(6.28)), 6)
407 self.assertEqual(int(Quaternion(6.78)), 6)
408 self.assertEqual(int(Quaternion(-4.87)), -4)
409 self.assertEqual(int(round(float(Quaternion(-4.87)))), -5)
413 q = Quaternion(a, b, c, d)
414 self.assertEqual(complex(q), complex(a, b))
421 self.assertEqual(Quaternion(*r), Quaternion(*r))
423 self.assertEqual(q, q)
425 self.assertEqual(q, r)
426 self.assertEqual(Quaternion(1., 0., 0., 0.), 1.0)
427 self.assertEqual(Quaternion(1., 0., 0., 0.),
"1.0")
428 self.assertNotEqual(q, q + Quaternion(0.0, 0.002, 0.0, 0.0))
431 self.assertEqual(Quaternion(1., 0., 0., 0.), Quaternion(1.0 - 1e-14, 0., 0., 0.))
432 self.assertNotEqual(Quaternion(1., 0., 0., 0.), Quaternion(1.0 - 1e-12, 0., 0., 0.))
433 self.assertNotEqual(Quaternion(160., 0., 0., 0.), Quaternion(160.0 - 1e-10, 0., 0., 0.))
434 self.assertNotEqual(Quaternion(1600., 0., 0., 0.), Quaternion(1600.0 - 1e-9, 0., 0., 0.))
436 with self.assertRaises(TypeError):
438 with self.assertRaises(ValueError):
443 q1 = Quaternion(a, b, c, d)
444 q2 = Quaternion(a, b*0.1, c+0.3, d)
445 self.assertNotEqual(q1, q2)
447 self.assertEqual(q1, q2)
451 q = Quaternion(a, b, c, d)
452 self.assertEqual(-q, Quaternion(-a, -b, -c, -d))
462 q3 = Quaternion(array= np.array(r1) + np.array(r2))
463 q4 = Quaternion(array= np.array(r2) + np.array([r, 0.0, 0.0, 0.0]))
464 self.assertEqual(q1 + q2, q3)
466 self.assertEqual(q1, q3)
467 self.assertEqual(q2 + r, q4)
468 self.assertEqual(r + q2, q4)
470 with self.assertRaises(TypeError):
472 with self.assertRaises(TypeError):
483 q3 = Quaternion(array= np.array(r1) - np.array(r2))
484 q4 = Quaternion(array= np.array(r2) - np.array([r, 0.0, 0.0, 0.0]))
485 self.assertEqual(q1 - q2, q3)
487 self.assertEqual(q1, q3)
488 self.assertEqual(q2 - r, q4)
489 self.assertEqual(r - q2, -q4)
491 with self.assertRaises(TypeError):
493 with self.assertRaises(TypeError):
497 one = Quaternion(1.0, 0.0, 0.0, 0.0)
498 i = Quaternion(0.0, 1.0, 0.0, 0.0)
499 j = Quaternion(0.0, 0.0, 1.0, 0.0)
500 k = Quaternion(0.0, 0.0, 0.0, 1.0)
502 self.assertEqual(i * i, j * j)
503 self.assertEqual(j * j, k * k)
504 self.assertEqual(k * k, i * j * k)
505 self.assertEqual(i * j * k, -one)
507 self.assertEqual(i * j, k)
508 self.assertEqual(i * i, -one)
509 self.assertEqual(i * k, -j)
510 self.assertEqual(j * i, -k)
511 self.assertEqual(j * j, -one)
512 self.assertEqual(j * k, i)
513 self.assertEqual(k * i, j)
514 self.assertEqual(k * j, -i)
515 self.assertEqual(k * k, -one)
516 self.assertEqual(i * j * k, -one)
520 q1 = Quaternion(a, b, c, d)
521 for s
in [30.0, 0.3, -2, -4.7, 0]:
522 q2 = Quaternion(s*a, s*b, s*c, s*d)
524 self.assertEqual(q1 * s, q2)
525 self.assertEqual(s * q1, q2)
527 self.assertEqual(q3, q2)
531 with self.assertRaises(TypeError):
533 with self.assertRaises(ValueError):
534 b = q * [1, 1, 1, 1, 1]
535 with self.assertRaises(ValueError):
536 c = q * np.array([[1, 2, 3], [4, 5, 6]])
537 with self.assertRaises(ValueError):
544 self.assertEqual(q / q, Quaternion())
545 self.assertEqual(q / r, Quaternion())
547 with self.assertRaises(ZeroDivisionError):
550 with self.assertRaises(ZeroDivisionError):
552 with self.assertRaises(TypeError):
554 with self.assertRaises(ValueError):
556 with self.assertRaises(ValueError):
557 q / np.array([[1, 2, 3], [4, 5, 6]])
558 with self.assertRaises(ValueError):
562 one = Quaternion(1.0, 0.0, 0.0, 0.0)
563 i = Quaternion(0.0, 1.0, 0.0, 0.0)
564 j = Quaternion(0.0, 0.0, 1.0, 0.0)
565 k = Quaternion(0.0, 0.0, 0.0, 1.0)
567 self.assertEqual(i / i, j / j)
568 self.assertEqual(j / j, k / k)
569 self.assertEqual(k / k, one)
570 self.assertEqual(k / -k, -one)
572 self.assertEqual(i / j, -k)
573 self.assertEqual(i / i, one)
574 self.assertEqual(i / k, j)
575 self.assertEqual(j / i, k)
576 self.assertEqual(j / j, one)
577 self.assertEqual(j / k, -i)
578 self.assertEqual(k / i, -j)
579 self.assertEqual(k / j, i)
580 self.assertEqual(k / k, one)
581 self.assertEqual(i / -j, k)
585 q1 = Quaternion(a, b, c, d)
586 for s
in [30.0, 0.3, -2, -4.7]:
587 q2 = Quaternion(a/s, b/s, c/s, d/s)
589 self.assertEqual(q1 / s, q2)
591 self.assertEqual(s / q1, q2.inverse)
593 with self.assertRaises(ZeroDivisionError):
597 self.assertEqual(q3, q2)
599 with self.assertRaises(ZeroDivisionError):
601 with self.assertRaises(TypeError):
603 with self.assertRaises(ValueError):
607 one = Quaternion(1.0, 0.0, 0.0, 0.0)
608 i = Quaternion(0.0, 1.0, 0.0, 0.0)
609 j = Quaternion(0.0, 0.0, 1.0, 0.0)
610 k = Quaternion(0.0, 0.0, 0.0, 1.0)
612 self.assertEqual(i**2, j**2)
613 self.assertEqual(j**2, k**2)
614 self.assertEqual(k**2, -one)
617 q1 = Quaternion.random()
619 self.assertEqual(q1 ** 0, Quaternion())
620 self.assertEqual(q1 ** 1, q1)
622 self.assertEqual(q2, q1 * q1 * q1 * q1)
623 self.assertEqual((q1 ** 0.5) * (q1 ** 0.5), q1)
624 self.assertEqual(q1 ** -1, q1.inverse)
625 self.assertEqual(4 ** Quaternion(2), Quaternion(16))
626 with self.assertRaises(TypeError):
628 with self.assertRaises(ValueError):
631 self.assertEqual(q3 ** 0.5, q3)
632 self.assertEqual(q3 ** 5, q3)
633 self.assertEqual(q3 ** 3.4, q3)
634 q4 = Quaternion(scalar=5)
635 self.assertEqual(q4 ** 4, Quaternion(scalar=5 ** 4))
638 q1 = Quaternion.random()
639 q2 = Quaternion.random()
640 q3 = Quaternion.random()
641 self.assertEqual(q1 * ( q2 + q3 ), q1 * q2 + q1 * q3)
644 q1 = Quaternion.random()
645 q2 = Quaternion.random()
647 self.assertNotEqual(q1 * q2, q2 * q1)
654 q1 = Quaternion(a, b, c, d)
655 q2 = Quaternion.random()
656 self.assertEqual(q1.conjugate, Quaternion(a, -b, -c, -d))
658 self.assertEqual((q1 * q2).conjugate, q2.conjugate * q1.conjugate)
659 self.assertEqual((q1 + q1.conjugate) / 2, Quaternion(scalar=q1.scalar))
660 self.assertEqual((q1 - q1.conjugate) / 2, Quaternion(vector=q1.vector))
663 q = Quaternion.random()
664 self.assertEqual(q, q.conjugate.conjugate)
669 q2 = Quaternion.random()
670 self.assertEqual(q1.norm, np.linalg.norm(np.array(r)))
671 self.assertEqual(q1.magnitude, np.linalg.norm(np.array(r)))
673 self.assertAlmostEqual((q1 * q2).norm, q1.norm * q2.norm, ALMOST_EQUAL_TOLERANCE)
675 for s
in [30.0, 0.3, -2, -4.7]:
676 self.assertAlmostEqual((q1 * s).norm, q1.norm * abs(s), ALMOST_EQUAL_TOLERANCE)
680 q2 = Quaternion.random()
682 self.assertEqual(q1 * q1.inverse, Quaternion(1.0, 0.0, 0.0, 0.0))
684 with self.assertRaises(ZeroDivisionError):
687 self.assertEqual(q2 * q2.inverse, Quaternion(1.0, 0.0, 0.0, 0.0))
695 if q1 == Quaternion(0):
699 np.testing.assert_almost_equal(v.q, q1.elements / q1.norm, decimal=ALMOST_EQUAL_TOLERANCE)
700 np.testing.assert_almost_equal(n.q, q1.elements / q1.norm, decimal=ALMOST_EQUAL_TOLERANCE)
701 self.assertAlmostEqual(v.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
702 self.assertAlmostEqual(n.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
704 np.testing.assert_almost_equal(q1.axis, v.axis, decimal=ALMOST_EQUAL_TOLERANCE)
705 np.testing.assert_almost_equal(q1.axis, n.axis, decimal=ALMOST_EQUAL_TOLERANCE)
706 self.assertAlmostEqual(q1.angle, v.angle, ALMOST_EQUAL_TOLERANCE)
707 self.assertAlmostEqual(q1.angle, n.angle, ALMOST_EQUAL_TOLERANCE)
710 self.assertEqual(q2, q2.normalised)
714 q2 = Quaternion(1.0, 0, 0, 0.0001)
715 self.assertTrue(q1.is_unit())
716 self.assertFalse(q2.is_unit())
717 self.assertTrue(q2.is_unit(0.001))
721 q = Quaternion(a, b, c, d)
727 self.assertTrue(np.array_equal(q._q_matrix(), M))
731 q = Quaternion(a, b, c, d)
737 self.assertTrue(np.array_equal(q._q_bar_matrix(), M))
741 q = Quaternion(a, b, c, d)
743 self.assertEqual(q.scalar, a)
744 self.assertEqual(q.real, a)
746 self.assertTrue(np.array_equal(q.vector, [b, c, d]))
747 self.assertTrue(np.array_equal(q.imaginary, [b, c, d]))
748 self.assertEqual(tuple(q.vector), (b, c, d))
749 self.assertEqual(list(q.imaginary), [b, c, d])
750 self.assertEqual(q.w, a)
751 self.assertEqual(q.x, b)
752 self.assertEqual(q.y, c)
753 self.assertEqual(q.z, d)
758 self.assertEqual(tuple(q.elements), r)
763 self.assertEqual(q[0], r[0])
764 self.assertEqual(q[1], r[1])
765 self.assertEqual(q[2], r[2])
766 self.assertEqual(q[3], r[3])
767 self.assertEqual(q[-1], r[3])
768 self.assertEqual(q[-4], r[0])
769 with self.assertRaises(TypeError):
771 with self.assertRaises(IndexError):
773 with self.assertRaises(IndexError):
778 self.assertEqual(q[1], 0.0)
780 self.assertEqual(q[1], 10.0)
781 self.assertEqual(q, Quaternion(1.0, 10.0, 0.0, 0.0))
782 with self.assertRaises(TypeError):
784 with self.assertRaises(ValueError):
788 q = Quaternion(axis=[1,1,1], angle=2*pi/3)
789 q2 = Quaternion(axis=[1, 0, 0], angle=-pi)
790 q3 = Quaternion(axis=[1, 0, 0], angle=pi)
791 precision = ALMOST_EQUAL_TOLERANCE
792 for r
in [1, 3.8976, -69.7, -0.000001]:
794 np.testing.assert_almost_equal(q.rotate((r, 0, 0)), (0, r, 0), decimal=ALMOST_EQUAL_TOLERANCE)
795 np.testing.assert_almost_equal(q.rotate([0, r, 0]), [0, 0, r], decimal=ALMOST_EQUAL_TOLERANCE)
796 np.testing.assert_almost_equal(q.rotate(np.array([0, 0, r])), np.array([r, 0, 0]), decimal=ALMOST_EQUAL_TOLERANCE)
797 self.assertEqual(q.rotate(Quaternion(vector=[-r, 0, 0])), Quaternion(vector=[0, -r, 0]))
798 np.testing.assert_almost_equal(q.rotate([0, -r, 0]), [0, 0, -r], decimal=ALMOST_EQUAL_TOLERANCE)
799 self.assertEqual(q.rotate(Quaternion(vector=[0, 0, -r])), Quaternion(vector=[-r, 0, 0]))
801 np.testing.assert_almost_equal(q2.rotate((r, 0, 0)), q3.rotate((r, 0, 0)), decimal=ALMOST_EQUAL_TOLERANCE)
802 np.testing.assert_almost_equal(q2.rotate((0, r, 0)), q3.rotate((0, r, 0)), decimal=ALMOST_EQUAL_TOLERANCE)
803 np.testing.assert_almost_equal(q2.rotate((0, 0, r)), q3.rotate((0, 0, r)), decimal=ALMOST_EQUAL_TOLERANCE)
806 q = Quaternion.random()
807 a, b, c, d = tuple(q.elements)
809 [a**2 + b**2 - c**2 - d**2, 2 * (b * c - a * d), 2 * (a * c + b * d)],
810 [2 * (b * c + a * d), a**2 - b**2 + c**2 - d**2, 2 * (c * d - a * b)],
811 [2 * (b * d - a * c), 2 * (a * b + c * d), a**2 - b**2 - c**2 + d**2]])
812 t = np.array([[0],[0],[0]])
813 T = np.vstack([np.hstack([R,t]), np.array([0,0,0,1])])
814 np.testing.assert_almost_equal(R, q.rotation_matrix, decimal=ALMOST_EQUAL_TOLERANCE)
815 np.testing.assert_almost_equal(T, q.transformation_matrix, decimal=ALMOST_EQUAL_TOLERANCE)
818 v1 = np.array([1, 0, 0])
819 v2 = np.hstack((np.random.uniform(-10, 10, 3), 1.0))
820 v1_ = np.dot(q.rotation_matrix, v1)
821 v2_ = np.dot(q.transformation_matrix, v2)
822 self.assertAlmostEqual(np.linalg.norm(v1_), 1.0, ALMOST_EQUAL_TOLERANCE)
823 self.assertAlmostEqual(np.linalg.norm(v2_), np.linalg.norm(v2), ALMOST_EQUAL_TOLERANCE)
826 np.testing.assert_almost_equal(v1_, q.rotate(v1), decimal=ALMOST_EQUAL_TOLERANCE)
827 np.testing.assert_almost_equal(v2_[0:3], q.rotate(v2[0:3]), decimal=ALMOST_EQUAL_TOLERANCE)
855 p = np.random.randn(3)
856 q = Quaternion.random()
857 yaw, pitch, roll = q.yaw_pitch_roll
860 R_q = q.rotation_matrix
863 R_ypr = np.dot(R_x(roll), np.dot(R_y(pitch), R_z(yaw)))
864 p_ypr = np.dot(R_ypr, p)
866 np.testing.assert_almost_equal(p_q , p_ypr, decimal=ALMOST_EQUAL_TOLERANCE)
867 np.testing.assert_almost_equal(R_q , R_ypr, decimal=ALMOST_EQUAL_TOLERANCE)
870 v = np.random.uniform(-100, 100, 3)
873 q0 = Quaternion.random()
874 R = q0.rotation_matrix
875 q1 = Quaternion(matrix=R)
876 np.testing.assert_almost_equal(q0.rotate(v), np.dot(R, v), decimal=ALMOST_EQUAL_TOLERANCE)
877 np.testing.assert_almost_equal(q0.rotate(v), q1.rotate(v), decimal=ALMOST_EQUAL_TOLERANCE)
878 np.testing.assert_almost_equal(q1.rotate(v), np.dot(R, v), decimal=ALMOST_EQUAL_TOLERANCE)
880 self.assertTrue((q0 == q1)
or (q0 == -q1))
884 def wrap_angle(theta):
885 """ Wrap any angle to lie between -pi and pi 887 Odd multiples of pi are wrapped to +pi (as opposed to -pi) 889 result = ((theta + pi) % (2*pi)) - pi
890 if result == -pi: result = pi
893 theta = wrap_angle(angle)
896 q = Quaternion(angle=theta, axis=v)
902 np.testing.assert_almost_equal(theta_, 0.0, decimal=ALMOST_EQUAL_TOLERANCE)
903 np.testing.assert_almost_equal(v_, np.zeros(3), decimal=ALMOST_EQUAL_TOLERANCE)
905 elif abs(theta) == pi:
907 np.isclose(theta, pi)
or np.isclose(theta, -pi)
909 np.isclose(v, v_).all()
or np.isclose(v, -v_).all()
913 np.isclose(theta, theta_)
and np.isclose(v, v_).all()
915 np.isclose(theta, -theta_)
and np.isclose(v, -v_).all()
918 np.testing.assert_almost_equal(np.linalg.norm(v_), 1.0, decimal=ALMOST_EQUAL_TOLERANCE)
921 random_axis = np.random.uniform(-1, 1, 3)
922 random_axis /= np.linalg.norm(random_axis)
924 angles = np.array([-3, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 3]) * pi
925 axes = [np.array([1, 0, 0]), np.array([0, 1, 0]), np.array([0, 0, 1]), random_axis]
935 v = np.random.uniform(-1, 1, 3)
936 v /= np.linalg.norm(v)
937 theta = float(np.random.uniform(-2,2, 1)) * pi
942 q = Quaternion(axis=[1,0,0], angle=pi)
943 exp_q = Quaternion.exp(q)
944 self.assertEqual(exp_q, exp(0) * Quaternion(scalar=cos(1.0), vector=[sin(1.0), 0,0]))
948 q = Quaternion(axis=[1,0,0], angle=pi)
949 log_q = Quaternion.log(q)
950 self.assertEqual(log_q, Quaternion(scalar=0, vector=[pi/2,0,0]))
953 q = Quaternion(scalar=0, vector=[1,0,0])
954 p = Quaternion(scalar=0, vector=[0,1,0])
955 self.assertEqual(pi/2, Quaternion.distance(q,p))
956 q = Quaternion(angle=pi/2, axis=[1,0,0])
957 p = Quaternion(angle=pi/2, axis=[0,1,0])
958 self.assertEqual(pi/3, Quaternion.distance(q,p))
959 q = Quaternion(scalar=1, vector=[1,1,1])
960 p = Quaternion(scalar=-1, vector=[-1,-1,-1])
963 self.assertAlmostEqual(0, Quaternion.distance(q,p), places=8)
966 q = Quaternion(scalar=0, vector=[1,0,0])
967 p = Quaternion(scalar=0, vector=[0,1,0])
968 self.assertEqual((q-p).norm, Quaternion.absolute_distance(q,p))
969 q = Quaternion(angle=pi/2, axis=[1,0,0])
970 p = Quaternion(angle=pi/2, axis=[0,1,0])
971 self.assertEqual((q-p).norm, Quaternion.absolute_distance(q,p))
972 q = Quaternion(scalar=0, vector=[1,0,0])
973 p = Quaternion(scalar=-1, vector=[0,-1,0])
974 self.assertEqual((q+p).norm, Quaternion.absolute_distance(q,p))
975 q = Quaternion(scalar=1, vector=[1,1,1])
976 p = Quaternion(scalar=-1, vector=[-1,-1,-1])
979 self.assertAlmostEqual(0, Quaternion.absolute_distance(q,p), places=8)
982 q = Quaternion(scalar=0, vector=[1,0,0])
983 p = Quaternion(scalar=0, vector=[0,1,0])
984 self.assertEqual(pi/2, Quaternion.sym_distance(q,p))
985 q = Quaternion(angle=pi/2, axis=[1,0,0])
986 p = Quaternion(angle=pi/2, axis=[0,1,0])
987 self.assertAlmostEqual(pi/3, Quaternion.sym_distance(q,p), places=6)
988 q = Quaternion(scalar=0, vector=[1,0,0])
989 p = Quaternion(scalar=0, vector=[0,-1,0])
990 self.assertEqual(pi/2, Quaternion.sym_distance(q,p))
991 q = Quaternion(scalar=1, vector=[1,1,1])
992 p = Quaternion(scalar=-1, vector=[-1,-1,-1])
995 self.assertAlmostEqual(pi, Quaternion.sym_distance(q,p), places=8)
998 q1 = Quaternion(axis=[1, 0, 0], angle=0.0)
999 q2 = Quaternion(axis=[1, 0, 0], angle=pi/2)
1000 q3 = Quaternion.slerp(q1, q2, 0.5)
1001 self.assertEqual(q3, Quaternion(axis=[1,0,0], angle=pi/4))
1004 for axis
in [[1, 0, 0], [0, 1, 0], [0, 0, 1]]:
1005 q1 = Quaternion(axis=axis, angle=0.0)
1006 q2 = Quaternion(axis=axis, angle=pi/2.0)
1007 q3 = Quaternion(axis=axis, angle=pi*3.0/2.0)
1008 for t
in np.arange(0.1, 1, 0.1):
1009 q4 = Quaternion.slerp(q1, q2, t)
1010 q5 = Quaternion.slerp(q1, q3, t)
1011 q6 = Quaternion(axis=axis, angle=t*pi/2)
1012 q7 = Quaternion(axis=axis, angle=-t*pi/2)
1013 assert q4 == q6
or q4 == -q6
1014 assert q5 == q7
or q5 == -q7
1017 q1 = Quaternion(axis=[1, 0, 0], angle=0.0)
1018 q2 = Quaternion(axis=[1, 0, 0], angle=2*pi/3)
1019 num_intermediates = 3
1021 list1 = list(Quaternion.intermediates(q1, q2, num_intermediates, include_endpoints=
False))
1022 list2 = list(Quaternion.intermediates(q1, q2, num_intermediates, include_endpoints=
True))
1023 self.assertEqual(len(list1), num_intermediates)
1024 self.assertEqual(len(list2), num_intermediates+2)
1025 self.assertEqual(list1[0], list2[1])
1026 self.assertEqual(list1[1], list2[2])
1027 self.assertEqual(list1[2], list2[3])
1029 self.assertEqual(list2[0], q1)
1030 self.assertEqual(list2[1], Quaternion(axis=[1, 0, 0], angle=base))
1031 self.assertEqual(list2[2], Quaternion(axis=[1, 0, 0], angle=2*base))
1032 self.assertEqual(list2[3], Quaternion(axis=[1, 0, 0], angle=3*base))
1033 self.assertEqual(list2[4], q2)
1036 q = Quaternion.random()
1037 omega = np.random.uniform(-1, 1, 3)
1039 q_dash = 0.5 * q * Quaternion(vector=omega)
1041 self.assertEqual(q_dash, q.derivative(omega))
1044 rotation_rate = [0, 0, 2*pi]
1046 for dt
in [0, 0.25, 0.5, 0.75, 1, 2, 10, 1e-10, random()*10]:
1048 qt.integrate(rotation_rate, dt)
1049 q_truth = Quaternion(axis=[0,0,1], angle=dt*2*pi)
1051 b = q_truth.rotate(v)
1052 np.testing.assert_almost_equal(a, b, decimal=ALMOST_EQUAL_TOLERANCE)
1053 self.assertTrue(qt.is_unit())
1056 for i
in range(1000):
1057 q.integrate([pi, 0, 0], 0.001)
1058 self.assertTrue(q.is_unit())
1063 from copy
import copy
1064 q = Quaternion.random()
1066 self.assertEqual(q, q2)
1067 self.assertFalse(q
is q2)
1068 self.assertTrue(all(q.q == q2.q))
1071 from copy
import deepcopy
1072 q = Quaternion.random()
1074 self.assertEqual(q, q2)
1075 self.assertFalse(q
is q2)
1076 self.assertFalse(q.q
is q2.q)
1080 q1 = Quaternion(1, 0, 0, 0)
1081 q2 = Quaternion(1, 0, 0, 0)
1083 self.assertEqual(hash(q1), hash(q2))
1086 q1 = Quaternion(1, 0, 0, 0)
1087 q2 = Quaternion(0, 1, 0, 0)
1089 self.assertNotEqual(hash(q1), hash(q2))
1092 if __name__ ==
'__main__':
def test_division_of_bases(self)
def test_init_from_explicit_elements(self)
def test_axis_angle_io(self)
def test_divide_by_scalar(self)
def test_init_default(self)
def test_conversion_to_ypr(self)
def test_noncommutative(self)
def test_init_from_explicit_arrray(self)
def test_init_from_explicit_component(self)
def test_init_from_explicit_matrix(self)
def test_interpolate(self)
def test_init_from_explicit_rotation_params(self)
def test_sym_distance(self)
def test_conversion_to_matrix(self)
def test_init_from_scalar(self)
def test_differentiation(self)
def test_init_from_explicit_matrix_with_optional_tolerance_arguments(self)
def test_distributive(self)
def validate_axis_angle(self, axis, angle)
def test_absolute_distance(self)
def test_init_from_array(self)
def test_element_assignment(self)
def test_equal_quaternions(self)
def test_conversion_to_axis_angle(self)
def test_q_bar_matrix(self)
def test_unary_minus(self)
def test_init_from_tuple(self)
def test_init_random(self)
def test_multiply_by_scalar(self)
def test_assignment(self)
def test_slerp_extensive(self)
def test_output_of_elements(self)
def test_init_from_list(self)
def test_double_conjugate(self)
def test_integration(self)
def test_element_access(self)
def test_init_from_elements(self)
def test_unequal_quaternions(self)
def test_equivalent_initialisations(self)
def test_output_of_components(self)
def test_multiplication_of_bases(self)
def test_multiply_incorrect_type(self)
def test_normalisation(self)