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):
69 r1 = Quaternion.random()
70 r2 = Quaternion.random()
71 self.assertAlmostEqual(r1.norm, 1.0, ALMOST_EQUAL_TOLERANCE)
72 self.assertIsInstance(r1, Quaternion)
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):
91 q2 =
Quaternion(repr(a), repr(b), repr(c), repr(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):
101 with self.assertRaises(ValueError):
103 with self.assertRaises(ValueError):
105 with self.assertRaises(ValueError):
112 self.assertIsInstance(q, Quaternion)
114 with self.assertRaises(ValueError):
116 with self.assertRaises(ValueError):
118 with self.assertRaises(ValueError):
120 with self.assertRaises(TypeError):
121 q =
Quaternion(np.array([
None,
None,
None,
None]))
126 self.assertIsInstance(q, Quaternion)
128 with self.assertRaises(ValueError):
130 with self.assertRaises(ValueError):
132 with self.assertRaises(ValueError):
134 with self.assertRaises(TypeError):
141 self.assertIsInstance(q, Quaternion)
143 with self.assertRaises(ValueError):
145 with self.assertRaises(ValueError):
147 with self.assertRaises(ValueError):
149 with self.assertRaises(TypeError):
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)
166 with self.assertRaises(TypeError):
168 with self.assertRaises(ValueError):
170 with self.assertRaises(ValueError):
172 with self.assertRaises(ValueError):
181 q3 =
Quaternion(real=a, imaginary=np.array([b, c, d]))
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)
192 self.assertEqual(q1, q2)
193 self.assertEqual(q2, q3)
196 self.assertEqual(q5, q6)
197 with self.assertRaises(ValueError):
199 with self.assertRaises(ValueError):
200 q =
Quaternion(real=a, imaginary=(b, c, d, d))
205 q3 =
Quaternion(scalar=a, 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)
216 self.assertEqual(q1, q2)
217 self.assertEqual(q2, q3)
220 self.assertEqual(q5, q6)
221 with self.assertRaises(ValueError):
223 with self.assertRaises(ValueError):
230 theta = random() * 2.0 * pi
238 q3 =
Quaternion(axis=v3, degrees=theta / pi * 180)
241 v3 = v3 / np.linalg.norm(v3)
255 self.assertEqual(q1, truth)
256 self.assertEqual(q2, truth)
257 self.assertEqual(q3, truth)
258 self.assertEqual(q4, truth)
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):
273 with self.assertRaises(ValueError):
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)
299 v_prime_q1 = q1.rotate(v)
301 np.testing.assert_almost_equal(v_prime_r, v_prime_q1, decimal=ALMOST_EQUAL_TOLERANCE)
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))
310 v_prime_q3 = q3.rotate(v)
311 np.testing.assert_almost_equal(v, v_prime_q3, decimal=ALMOST_EQUAL_TOLERANCE)
315 with self.assertRaises(ValueError):
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):
340 self.fail(
"Quaternion() raised ValueError unexpectedly!")
346 self.assertIsInstance(q, Quaternion)
348 with self.assertRaises(ValueError):
350 with self.assertRaises(ValueError):
352 with self.assertRaises(ValueError):
354 with self.assertRaises(TypeError):
355 q =
Quaternion(array=np.array([
None,
None,
None,
None]))
361 self.assertEqual(q,
Quaternion(np.array([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])))
372 string =
"{:.3f} {:+.3f}i {:+.3f}j {:+.3f}k".format(a, b, c, d)
373 self.assertEqual(string, str(q))
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))
386 string =
"Quaternion(" + repr(a) +
", " + repr(b) +
", " + repr(c) +
", " + repr(d) +
")"
387 self.assertEqual(string, repr(q))
400 self.assertEqual(float(q), a)
405 self.assertEqual(int(q), int(a))
409 self.assertEqual(int(round(float(
Quaternion(-4.87)))), -5)
414 self.assertEqual(complex(q), complex(a, b))
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))
436 with self.assertRaises(TypeError):
438 with self.assertRaises(ValueError):
445 self.assertNotEqual(q1, q2)
447 self.assertEqual(q1, q2)
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):
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)
521 for s
in [30.0, 0.3, -2, -4.7, 0]:
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):
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):
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)
586 for s
in [30.0, 0.3, -2, -4.7]:
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):
612 self.assertEqual(i**2, j**2)
613 self.assertEqual(j**2, k**2)
614 self.assertEqual(k**2, -one)
617 q1 = Quaternion.random()
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)
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)
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)
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))
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)
715 self.assertTrue(q1.is_unit())
716 self.assertFalse(q2.is_unit())
717 self.assertTrue(q2.is_unit(0.001))
727 self.assertTrue(np.array_equal(q._q_matrix(), M))
737 self.assertTrue(np.array_equal(q._q_bar_matrix(), M))
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):
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)
798 np.testing.assert_almost_equal(q.rotate([0, -r, 0]), [0, 0, -r], decimal=ALMOST_EQUAL_TOLERANCE)
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
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)
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
943 exp_q = Quaternion.exp(q)
944 self.assertEqual(exp_q, exp(0) *
Quaternion(scalar=cos(1.0), vector=[sin(1.0), 0,0]))
949 log_q = Quaternion.log(q)
950 self.assertEqual(log_q,
Quaternion(scalar=0, vector=[pi/2,0,0]))
955 self.assertEqual(pi/2, Quaternion.distance(q,p))
958 self.assertEqual(pi/3, Quaternion.distance(q,p))
963 self.assertAlmostEqual(0, Quaternion.distance(q,p), places=8)
968 self.assertEqual((q-p).norm, Quaternion.absolute_distance(q,p))
971 self.assertEqual((q-p).norm, Quaternion.absolute_distance(q,p))
974 self.assertEqual((q+p).norm, Quaternion.absolute_distance(q,p))
979 self.assertAlmostEqual(0, Quaternion.absolute_distance(q,p), places=8)
984 self.assertEqual(pi/2, Quaternion.sym_distance(q,p))
987 self.assertAlmostEqual(pi/3, Quaternion.sym_distance(q,p), places=6)
990 self.assertEqual(pi/2, Quaternion.sym_distance(q,p))
995 self.assertAlmostEqual(pi, Quaternion.sym_distance(q,p), places=8)
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]]:
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)
1013 assert q4 == q6
or q4 == -q6
1014 assert q5 == q7
or q5 == -q7
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)
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)
1083 self.assertEqual(hash(q1), hash(q2))
1089 self.assertNotEqual(hash(q1), hash(q2))
1092 if __name__ ==
'__main__':