Source code for pyrr.objects.quaternion

# -*- coding: utf-8 -*-
"""Represents a Quaternion rotation.

The Quaternion class provides a number of convenient functions and
conversions.
::

    import numpy as np
    from pyrr import Quaternion, Matrix33, Matrix44, Vector3, Vector4

    q = Quaternion()

    # explicit creation
    q = Quaternion.from_x_rotation(np.pi / 2.0)
    q = Quaternion.from_matrix(Matrix33.identity())
    q = Quaternion.from_matrix(Matrix44.identity())

    # inferred conversions
    q = Quaternion(Quaternion())
    q = Quaternion(Matrix33.identity())
    q = Quaternion(Matrix44.identity())

    # apply one quaternion to another
    q1 = Quaternion.from_y_rotation(np.pi / 2.0)
    q2 = Quaternion.from_x_rotation(np.pi / 2.0)
    q3 = q1 * q2

    # extract a matrix from the quaternion
    m33 = q3.matrix33
    m44 = q3.matrix44

    # convert from matrix back to quaternion
    q4 = Quaternion(m44)

    # rotate a quaternion by a matrix
    q = Quaternion() * Matrix33.identity()
    q = Quaternion() * Matrix44.identity()

    # apply quaternion to a vector
    v3 = Quaternion() * Vector3()
    v4 = Quaternion() * Vector4()

    # undo a rotation
    q = Quaternion.from_x_rotation(np.pi / 2.0)
    v = q * Vector3([1.,1.,1.])
    # ~q is the same as q.conjugate
    original = ~q * v
    assert np.allclose(original, v)

    # get the dot product of 2 Quaternions
    dot = Quaternion() | Quaternion.from_x_rotation(np.pi / 2.0)
"""

from __future__ import absolute_import
import numpy as np
from multipledispatch import dispatch
from .base import BaseObject, BaseQuaternion, BaseMatrix, BaseVector, NpProxy
from .. import quaternion

[docs]class Quaternion(BaseQuaternion): _module = quaternion _shape = (4,) #: The X value of this Quaternion. x = NpProxy(0) #: The Y value of this Quaternion. y = NpProxy(1) #: The Z value of this Quaternion. z = NpProxy(2) #: The W value of this Quaternion. w = NpProxy(3) #: The X,Y value of this Quaternion as a numpy.ndarray. xy = NpProxy([0,1]) #: The X,Y,Z value of this Quaternion as a numpy.ndarray. xyz = NpProxy([0,1,2]) #: The X,Y,Z,W value of this Quaternion as a numpy.ndarray. xyzw = NpProxy([0,1,2,3]) #: The X,Z value of this Quaternion as a numpy.ndarray. xz = NpProxy([0,2]) #: The X,Z,W value of this Quaternion as a numpy.ndarray. xzw = NpProxy([0,2,3]) #: The X,Y,W value of this Quaternion as a numpy.ndarray. xyw = NpProxy([0,1,3]) #: The X,W value of this Quaternion as a numpy.ndarray. xw = NpProxy([0,3]) ######################## # Creation
[docs] @classmethod def from_x_rotation(cls, theta, dtype=None): """Creates a new Quaternion with a rotation around the X-axis. """ return cls(quaternion.create_from_x_rotation(theta, dtype))
[docs] @classmethod def from_y_rotation(cls, theta, dtype=None): """Creates a new Quaternion with a rotation around the Y-axis. """ return cls(quaternion.create_from_y_rotation(theta, dtype))
[docs] @classmethod def from_z_rotation(cls, theta, dtype=None): """Creates a new Quaternion with a rotation around the Z-axis. """ return cls(quaternion.create_from_z_rotation(theta, dtype))
[docs] @classmethod def from_axis_rotation(cls, axis, theta, dtype=None): """Creates a new Quaternion with a rotation around the specified axis. """ return cls(quaternion.create_from_axis_rotation(axis, theta, dtype))
[docs] @classmethod def from_axis(cls, axis, dtype=None): """Creates a new Quaternion from an axis with angle magnitude. """ return cls(quaternion.create_from_axis(axis, dtype))
[docs] @classmethod def from_matrix(cls, matrix, dtype=None): """Creates a Quaternion from the specified Matrix (Matrix33 or Matrix44). """ return cls(quaternion.create_from_matrix(matrix, dtype))
[docs] @classmethod def from_eulers(cls, eulers, dtype=None): """Creates a Quaternion from the specified Euler angles. """ return cls(quaternion.create_from_eulers(eulers, dtype))
[docs] @classmethod def from_inverse_of_eulers(cls, eulers, dtype=None): """Creates a Quaternion from the inverse of the specified Euler angles. """ return cls(quaternion.create_from_inverse_of_eulers(eulers, dtype))
def __new__(cls, value=None, dtype=None): if value is not None: obj = value if not isinstance(value, np.ndarray): obj = np.array(value, dtype=dtype) # matrix33, matrix44 if obj.shape in ((4,4,), (3,3,)) or isinstance(obj, (Matrix33, Matrix44)): obj = quaternion.create_from_matrix(obj, dtype=dtype) else: obj = quaternion.create(dtype=dtype) obj = obj.view(cls) return super(Quaternion, cls).__new__(cls, obj) ######################## # Basic Operators @dispatch(BaseObject) def __add__(self, other): self._unsupported_type('add', other) @dispatch(BaseObject) def __sub__(self, other): self._unsupported_type('subtract', other) @dispatch(BaseObject) def __mul__(self, other): self._unsupported_type('multiply', other) @dispatch(BaseObject) def __truediv__(self, other): self._unsupported_type('divide', other) @dispatch(BaseObject) def __div__(self, other): self._unsupported_type('divide', other) ######################## # Quaternions @dispatch((BaseQuaternion, np.ndarray, list)) def __sub__(self, other): return Quaternion(super(Quaternion, self).__sub__(other)) @dispatch((BaseQuaternion, list)) def __mul__(self, other): return self.cross(other) @dispatch((BaseQuaternion, list)) def __or__(self, other): return self.dot(other) def __invert__(self): return self.conjugate @dispatch((BaseQuaternion, np.ndarray, list)) def __ne__(self, other): # For quaternions q and -q represent the same rotation return bool(np.any(super(Quaternion, self).__ne__(other)))\ or bool(np.all(super(Quaternion, self).__eq__(-other))) @dispatch((BaseQuaternion, np.ndarray, list)) def __eq__(self, other): # For quaternions q and -q represent the same rotation return bool(np.all(super(Quaternion, self).__eq__(other))) \ or bool(np.all(super(Quaternion, self).__eq__(-other))) ######################## # Matrices @dispatch(BaseMatrix) def __mul__(self, other): return self * Quaternion(other) ######################## # Vectors @dispatch(BaseVector) def __mul__(self, other): return type(other)(quaternion.apply_to_vector(self, other)) ######################## # Methods and Properties @property def length(self): """Returns the length of this Quaternion. """ return quaternion.length(self)
[docs] def normalize(self): """normalizes this Quaternion in-place. """ self[:] = quaternion.normalize(self)
@property def normalized(self): """Returns a normalized version of this Quaternion as a new Quaternion. """ return Quaternion(quaternion.normalize(self))
[docs] def normalise(self): # TODO: mark as deprecated """normalizes this Quaternion in-place. """ self[:] = quaternion.normalize(self)
@property def normalised(self): # TODO: mark as deprecated """Returns a normalized version of this Quaternion as a new Quaternion. """ return Quaternion(quaternion.normalize(self)) @property def angle(self): """Returns the angle around the axis of rotation of this Quaternion as a float. """ return quaternion.rotation_angle(self) @property def axis(self): """Returns the axis of rotation of this Quaternion as a Vector3. """ return Vector3(quaternion.rotation_axis(self))
[docs] def cross(self, other): """Returns the cross of this Quaternion and another. This is the equivalent of combining Quaternion rotations (like Matrix multiplication). """ return Quaternion(quaternion.cross(self, other))
[docs] def lerp(self, other, t): """Interpolates between quat1 and quat2 by t. The parameter t is clamped to the range [0, 1] """ return Quaternion(quaternion.lerp(self, other, t))
[docs] def slerp(self, other, t): """Spherically interpolates between quat1 and quat2 by t. The parameter t is clamped to the range [0, 1] """ return Quaternion(quaternion.slerp(self, other, t))
[docs] def dot(self, other): """Returns the dot of this Quaternion and another. """ return quaternion.dot(self, other)
@property def conjugate(self): """Returns the conjugate of this Quaternion. This is a Quaternion with the opposite rotation. """ return Quaternion(quaternion.conjugate(self)) @property def inverse(self): """Returns the inverse of this quaternion. """ return Quaternion(quaternion.inverse(self))
[docs] def exp(self): """Returns a new Quaternion representing the exponentional of this Quaternion """ return Quaternion(quaternion.exp(self))
[docs] def power(self, exponent): """Returns a new Quaternion representing this Quaternion to the power of the exponent. """ return Quaternion(quaternion.power(self, exponent))
@property def negative(self): """Returns the negative of the Quaternion. """ return Quaternion(quaternion.negate(self)) @property def is_identity(self): """Returns True if the Quaternion has no rotation (0.,0.,0.,1.). """ return quaternion.is_identity(self) @property def matrix44(self): """Returns a Matrix44 representation of this Quaternion. """ return Matrix44.from_quaternion(self) @property def matrix33(self): """Returns a Matrix33 representation of this Quaternion. """ return Matrix33.from_quaternion(self)
from .vector3 import Vector3 from .matrix33 import Matrix33 from .matrix44 import Matrix44