def test_equality(self): q1 = Quaternion([0, 0, 0, 1]) q2 = Quaternion([0, 0, 0, 1]) q3 = Quaternion([0, 0, 1, -1]) self.assertEqual(q1, q2) self.assertNotEqual(q1, q3) self.assertNotEqual(q2, q3)
def test_operators_quaternion(self): q1 = Quaternion() q2 = Quaternion.from_x_rotation(0.5) # add self.assertRaises(ValueError, lambda: q1 + q2) # subtract # we had to add this to enable np.array_equal to work # as it uses subtraction #self.assertRaises(ValueError, lambda: q1 - q2) # multiply self.assertTrue( np.array_equal( q1 * q2, quaternion.cross(quaternion.create(), quaternion.create_from_x_rotation(0.5)))) # divide self.assertRaises(ValueError, lambda: q1 / q2) # or self.assertTrue( np.array_equal( q1 | q2, quaternion.dot(quaternion.create(), quaternion.create_from_x_rotation(0.5)))) # inverse self.assertTrue( np.array_equal( ~q2, quaternion.conjugate(quaternion.create_from_x_rotation(0.5))))
def test_create(self): q = Quaternion() self.assertTrue(np.array_equal(q, [0., 0., 0., 1.])) self.assertEqual(q.shape, self._shape) q = Quaternion([1., 2., 3., 4.]) self.assertTrue(np.array_equal(q, [1., 2., 3., 4.])) self.assertEqual(q.shape, self._shape) q = Quaternion(Quaternion([1., 2., 3., 4.])) self.assertTrue(np.array_equal(q, [1., 2., 3., 4.])) self.assertEqual(q.shape, self._shape)
def test_decompose(self): # define expectations for multiple cases testsets = [ (Vector3([1, 1, 2], dtype='f4'), Quaternion.from_y_rotation(np.pi, dtype='f4'), Vector3([10, 0, -5], dtype='f4'), Matrix44([ [-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -2, 0], [10, 0, -5, 1], ], dtype='f4')), (Vector3([-1, 3, .5], dtype='f4'), Quaternion.from_axis_rotation(Vector3([.75, .75, 0], dtype='f4').normalized, np.pi, dtype='f4').normalized, Vector3([1, -1, 1], dtype='f4'), Matrix44([ [0, -1, 0, 0], [3, 0, 0, 0], [0, 0, -.5, 0], [1, -1, 1, 1], ], dtype='f4')), ] for expected_scale, expected_rotation, expected_translation, expected_model in testsets: # compose model matrix using original inputs s = Matrix44.from_scale(expected_scale, dtype='f4') r = Matrix44.from_quaternion(expected_rotation, dtype='f4') t = Matrix44.from_translation(expected_translation, dtype='f4') m = t * r * s # check that it's the same as the expected matrix np.testing.assert_almost_equal(np.array(m), np.array(expected_model)) self.assertTrue(m.dtype == expected_model.dtype) self.assertTrue(isinstance(m, expected_model.__class__)) # decompose this matrix and recompose the model matrix from the decomposition ds, dr, dt = m.decompose() ds = Matrix44.from_scale(ds, dtype='f4') dr = Matrix44.from_quaternion(dr, dtype='f4') dt = Matrix44.from_translation(dt, dtype='f4') dm = dt * dr * ds # check that it's the same as the original matrix np.testing.assert_almost_equal(np.array(m), np.array(dm)) self.assertTrue(m.dtype == dm.dtype) self.assertTrue(isinstance(dm, m.__class__))
def test_decompose(self): # define expectations for multiple cases testsets = [ ( Vector3([1, 1, 2], dtype='f4'), Quaternion.from_y_rotation(np.pi, dtype='f4'), Vector3([10, 0, -5], dtype='f4'), Matrix44([ [-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -2, 0], [10, 0, -5, 1], ], dtype='f4') ), ( Vector3([-1, 3, .5], dtype='f4'), Quaternion.from_axis_rotation(Vector3([.75, .75, 0], dtype='f4').normalized, np.pi, dtype='f4').normalized, Vector3([1, -1, 1], dtype='f4'), Matrix44([ [0, -1, 0, 0], [3, 0, 0, 0], [0, 0, -.5, 0], [1, -1, 1, 1], ], dtype='f4') ), ] for expected_scale, expected_rotation, expected_translation, expected_model in testsets: # compose model matrix using original inputs s = Matrix44.from_scale(expected_scale, dtype='f4') r = Matrix44.from_quaternion(expected_rotation, dtype='f4') t = Matrix44.from_translation(expected_translation, dtype='f4') m = t * r * s # check that it's the same as the expected matrix np.testing.assert_almost_equal(np.array(m), np.array(expected_model)) self.assertTrue(m.dtype == expected_model.dtype) self.assertTrue(isinstance(m, expected_model.__class__)) # decompose this matrix and recompose the model matrix from the decomposition ds, dr, dt = m.decompose() ds = Matrix44.from_scale(ds, dtype='f4') dr = Matrix44.from_quaternion(dr, dtype='f4') dt = Matrix44.from_translation(dt, dtype='f4') dm = dt * dr * ds # check that it's the same as the original matrix np.testing.assert_almost_equal(np.array(m), np.array(dm)) self.assertTrue(m.dtype == dm.dtype) self.assertTrue(isinstance(dm, m.__class__))
def test_from_axis(self): source = np.array([np.pi / 2, 0, 0]) result = Quaternion.from_axis(source) expected = np.array([np.sqrt(0.5), 0, 0, np.sqrt(0.5)]) self.assertTrue(np.allclose(result, expected)) source = np.array([0, np.pi, 0]) result = Quaternion.from_axis(source) expected = np.array([0, 1, 0, 0]) self.assertTrue(np.allclose(result, expected)) source = np.array([0, 0, 2 * np.pi]) result = Quaternion.from_axis(source) expected = np.array([0, 0, 0, -1]) self.assertTrue(np.allclose(result, expected))
def test_operators_quaternion(self): q1 = Quaternion() q2 = Quaternion.from_x_rotation(0.5) # add self.assertRaises(ValueError, lambda: q1 + q2) # subtract # we had to add this to enable np.array_equal to work # as it uses subtraction #self.assertRaises(ValueError, lambda: q1 - q2) # multiply self.assertTrue(np.array_equal(q1 * q2, quaternion.cross(quaternion.create(), quaternion.create_from_x_rotation(0.5)))) # divide self.assertRaises(ValueError, lambda: q1 / q2) # or self.assertTrue(np.array_equal(q1 | q2, quaternion.dot(quaternion.create(), quaternion.create_from_x_rotation(0.5)))) # inverse self.assertTrue(np.array_equal(~q2, quaternion.conjugate(quaternion.create_from_x_rotation(0.5)))) # == self.assertTrue(Quaternion() == Quaternion()) self.assertFalse(Quaternion() == Quaternion([0., 0., 0., 0.])) # != self.assertTrue(Quaternion() != Quaternion([1., 1., 1., 1.])) self.assertFalse(Quaternion() != Quaternion())
def test_normalized(self): q1 = Quaternion([1., 2., 3., 4.]) self.assertFalse(np.allclose(q1.length, 1.)) q2 = q1.normalized self.assertFalse(np.allclose(q1.length, 1.)) self.assertTrue(np.allclose(q2.length, 1.))
def test_apply_to_vector_non_unit(self): q = Quaternion.from_x_rotation(np.pi) # zero length v = Vector3([0., 0., 0.]) self.assertTrue( np.allclose( q * v, quaternion.apply_to_vector( quaternion.create_from_x_rotation(np.pi), [0., 0., 0.]))) # >1 length v = Vector3([2., 0., 0.]) self.assertTrue( np.allclose( q * v, quaternion.apply_to_vector( quaternion.create_from_x_rotation(np.pi), [2., 0., 0.]))) v = Vector3([0., 2., 0.]) self.assertTrue( np.allclose( q * v, quaternion.apply_to_vector( quaternion.create_from_x_rotation(np.pi), [0., 2., 0.]))) v = Vector3([0., 0., 2.]) self.assertTrue( np.allclose( q * v, quaternion.apply_to_vector( quaternion.create_from_x_rotation(np.pi), [0., 0., 2.])))
def test_create_from_quaternion(self): q = Quaternion() m = Matrix44.from_quaternion(q) self.assertTrue(np.array_equal(m, np.eye(4))) self.assertTrue(np.array_equal(m.quaternion, q)) m = Matrix44(q) self.assertTrue(np.array_equal(m, np.eye(4)))
def test_from_z_rotation(self): # 180 degree turn around Z axis q = Quaternion.from_z_rotation(np.pi) self.assertTrue(np.allclose(q, [0., 0., 1., 0.])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [-1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [0., -1., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., 0., 1.])) # 90 degree rotation around Z axis q = Quaternion.from_z_rotation(np.pi / 2.) self.assertTrue(np.allclose(q, [0., 0., np.sqrt(0.5), np.sqrt(0.5)])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [0., 1., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [-1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., 0., 1.])) # -90 degree rotation around Z axis q = Quaternion.from_z_rotation(-np.pi / 2.) self.assertTrue(np.allclose(q, [0., 0., -np.sqrt(0.5), np.sqrt(0.5)])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [0., -1., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., 0., 1.]))
def test_from_z_rotation(self): # 180 degree turn around Z axis q = Quaternion.from_z_rotation(np.pi) self.assertTrue(np.allclose(q, [0., 0., 1., 0.])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [-1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [0.,-1., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., 0., 1.])) # 90 degree rotation around Z axis q = Quaternion.from_z_rotation(np.pi / 2.) self.assertTrue(np.allclose(q, [0., 0., np.sqrt(0.5), np.sqrt(0.5)])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [0., 1., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [-1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., 0., 1.])) # -90 degree rotation around Z axis q = Quaternion.from_z_rotation(-np.pi / 2.) self.assertTrue(np.allclose(q, [0., 0., -np.sqrt(0.5), np.sqrt(0.5)])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [0.,-1., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., 0., 1.]))
def test_accessors(self): q = Quaternion(np.arange(self._size)) self.assertTrue(np.array_equal(q.xy,[0,1])) self.assertTrue(np.array_equal(q.xyz,[0,1,2])) self.assertTrue(np.array_equal(q.xyzw,[0,1,2,3])) self.assertTrue(np.array_equal(q.xz,[0,2])) self.assertTrue(np.array_equal(q.xyz,[0,1,2])) self.assertTrue(np.array_equal(q.xyw,[0,1,3])) self.assertTrue(np.array_equal(q.xw,[0,3])) self.assertEqual(q.x, 0) self.assertEqual(q.y, 1) self.assertEqual(q.z, 2) self.assertEqual(q.w, 3) q.x = 1 self.assertEqual(q.x, 1) self.assertEqual(q[0], 1) q.x += 1 self.assertEqual(q.x, 2) self.assertEqual(q[0], 2)
def test_accessors(self): q = Quaternion(np.arange(self._size)) self.assertTrue(np.array_equal(q.xy, [0, 1])) self.assertTrue(np.array_equal(q.xyz, [0, 1, 2])) self.assertTrue(np.array_equal(q.xyzw, [0, 1, 2, 3])) self.assertTrue(np.array_equal(q.xz, [0, 2])) self.assertTrue(np.array_equal(q.xyz, [0, 1, 2])) self.assertTrue(np.array_equal(q.xyw, [0, 1, 3])) self.assertTrue(np.array_equal(q.xw, [0, 3])) self.assertEqual(q.x, 0) self.assertEqual(q.y, 1) self.assertEqual(q.z, 2) self.assertEqual(q.w, 3) q.x = 1 self.assertEqual(q.x, 1) self.assertEqual(q[0], 1) q.x += 1 self.assertEqual(q.x, 2) self.assertEqual(q[0], 2)
def test_apply_to_vector_non_unit(self): q = Quaternion.from_x_rotation(np.pi) # zero length v = Vector3([0., 0., 0.]) self.assertTrue(np.allclose(q * v, quaternion.apply_to_vector(quaternion.create_from_x_rotation(np.pi), [0., 0., 0.]))) # >1 length v = Vector3([2., 0., 0.]) self.assertTrue(np.allclose(q * v, quaternion.apply_to_vector(quaternion.create_from_x_rotation(np.pi), [2., 0., 0.]))) v = Vector3([0., 2., 0.]) self.assertTrue(np.allclose(q * v, quaternion.apply_to_vector(quaternion.create_from_x_rotation(np.pi), [0., 2., 0.]))) v = Vector3([0., 0., 2.]) self.assertTrue(np.allclose(q * v, quaternion.apply_to_vector(quaternion.create_from_x_rotation(np.pi), [0., 0., 2.])))
def test_operators_vector4(self): q = Quaternion.from_x_rotation(0.5) v = Vector4([1.,0.,0.,1.]) # add self.assertRaises(ValueError, lambda: q + v) # subtract self.assertRaises(ValueError, lambda: q - v) # multiply self.assertTrue(np.array_equal(q * v, quaternion.apply_to_vector(quaternion.create_from_x_rotation(0.5), [1.,0.,0.,1.]))) # divide self.assertRaises(ValueError, lambda: q / v)
def test_operators_matrix44(self): q = Quaternion() m = Matrix44.from_x_rotation(0.5) # add self.assertRaises(ValueError, lambda: q + m) # subtract self.assertRaises(ValueError, lambda: q - m) # multiply self.assertTrue(np.array_equal(q * m, quaternion.cross(quaternion.create(), quaternion.create_from_matrix(matrix44.create_from_x_rotation(0.5))))) # divide self.assertRaises(ValueError, lambda: q / m)
def test_operators_quaternion(self): v = Vector3() q = Quaternion.from_x_rotation(0.5) # add self.assertRaises(ValueError, lambda: v + q) # subtract self.assertRaises(ValueError, lambda: v - q) # multiply self.assertRaises(ValueError, lambda: v * q) # divide self.assertRaises(ValueError, lambda: v / q)
def test_operators_quaternion(self): m = Matrix33.identity() q = Quaternion.from_x_rotation(0.7) # add self.assertRaises(ValueError, lambda: m + q) # subtract self.assertRaises(ValueError, lambda: m - q) # multiply self.assertTrue(np.array_equal(m * q, matrix33.multiply(matrix33.create_identity(), matrix33.create_from_quaternion(quaternion.create_from_x_rotation(0.7))))) # divide self.assertRaises(ValueError, lambda: m / q)
def test_operators_quaternion(self): m = Matrix33.identity() q = Quaternion.from_x_rotation(0.7) # add self.assertRaises(ValueError, lambda: m + q) # subtract self.assertRaises(ValueError, lambda: m - q) # multiply self.assertTrue(np.array_equal(m * q, matrix33.multiply(matrix33.create_from_quaternion(quaternion.create_from_x_rotation(0.7)), matrix33.create_identity()))) # divide self.assertRaises(ValueError, lambda: m / q)
def test_operators_quaternion(self): q1 = Quaternion() q2 = Quaternion.from_x_rotation(0.5) # add self.assertRaises(ValueError, lambda: q1 + q2) # subtract self.assertRaises(ValueError, lambda: q1 - q2) # multiply self.assertTrue(np.array_equal(q1 * q2, quaternion.cross(quaternion.create(), quaternion.create_from_x_rotation(0.5)))) # divide self.assertRaises(ValueError, lambda: q1 / q2) # or self.assertTrue(np.array_equal(q1 | q2, quaternion.dot(quaternion.create(), quaternion.create_from_x_rotation(0.5)))) # inverse self.assertTrue(np.array_equal(~q2, quaternion.conjugate(quaternion.create_from_x_rotation(0.5))))
def test_negative(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q.negative, quaternion.negate(q)))
def test_angle(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertEqual(q.angle, quaternion.rotation_angle(q))
def test_dot(self): q1 = Quaternion.from_x_rotation(np.pi / 2.0) q2 = Quaternion.from_y_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q1.dot(q2), quaternion.dot(q1, q2)))
def test_inverse(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q.inverse, quaternion.inverse(q)))
def test_matrix44(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue( np.allclose(q.matrix44, matrix44.create_from_quaternion(q)))
def test_length(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q.length, quaternion.length(q)))
def test_exp(self): source = Quaternion.from_eulers([0, np.pi / 2, 0]) result = source.exp() expected = np.array([0, 1.31753841, 0, 1.54186346]) self.assertTrue(np.allclose(result, expected))
def test_from_z_rotation(self): q = Quaternion.from_z_rotation(np.pi / 2.) self.assertTrue(np.allclose(q*Vector3([1.,0.,0.]), [0.,-1.,0.])) self.assertTrue(np.allclose(q*Vector3([0.,1.,0.]), [1.,0.,0.])) self.assertTrue(np.allclose(q*Vector3([0.,0.,1.]), [0.,0.,1.]))
def test_from_axis_rotation(self): q = Quaternion.from_axis_rotation([1.,0.,0.], np.pi / 2.) self.assertTrue(np.allclose(q*Vector3([1.,0.,0.]), [1.,0.,0.])) self.assertTrue(np.allclose(q*Vector3([0.,1.,0.]), [0.,0.,-1.])) self.assertTrue(np.allclose(q*Vector3([0.,0.,1.]), [0.,1.,0.]))
def test_axis(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q.axis, quaternion.rotation_axis(q)))
def test_conjugate(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q.conjugate, quaternion.conjugate(q)))
def test_create_from_inverse_quaternion(self): q = Quaternion.from_x_rotation(0.5) m = Matrix33.from_inverse_of_quaternion(q) expected = matrix33.create_from_quaternion(quaternion.inverse(quaternion.create_from_x_rotation(0.5))) np.testing.assert_almost_equal(np.array(m), expected, decimal=5)
def test_power(self): q1 = Quaternion.from_x_rotation(np.pi / 2.0) q2 = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q1.power(2.0), quaternion.power(q2, 2.0)))
def test_matrix44(self): q = Quaternion.from_x_rotation(np.pi / 2.0) self.assertTrue(np.allclose(q.matrix44, matrix44.create_from_quaternion(q)))
def test_is_identity(self): self.assertTrue(quaternion.is_identity(Quaternion())) self.assertTrue(quaternion.is_identity(Quaternion([0., 0., 0., 1.]))) self.assertFalse(quaternion.is_identity(Quaternion([1., 0., 0., 0.])))
def test_create_from_inverse_quaternion(self): q = Quaternion.from_x_rotation(0.5) m = Matrix44.from_inverse_of_quaternion(q) expected = matrix44.create_from_quaternion( quaternion.inverse(quaternion.create_from_x_rotation(0.5))) np.testing.assert_almost_equal(np.array(m), expected, decimal=5)
def test_from_axis_rotation(self): q = Quaternion.from_axis_rotation([1., 0., 0.], np.pi / 2.) self.assertTrue(np.allclose(q, [np.sqrt(0.5), 0., 0., np.sqrt(0.5)])) self.assertTrue(np.allclose(q * Vector3([1., 0., 0.]), [1., 0., 0.])) self.assertTrue(np.allclose(q * Vector3([0., 1., 0.]), [0., 0., 1.])) self.assertTrue(np.allclose(q * Vector3([0., 0., 1.]), [0., -1., 0.]))
def test_normalize(self): q = Quaternion([1., 2., 3., 4.]) self.assertFalse(np.allclose(q.length, 1.)) q.normalize() self.assertTrue(np.allclose(q.length, 1.))
def test_normalise(self): q = Quaternion([1.,2.,3.,4.]) self.assertFalse(np.allclose(q.length, 1.)) q.normalise() self.assertTrue(np.allclose(q.length, 1.))