Welcome
Welcome! pyquaternion is a full-featured Python module for representing and using quaternions.
The following should get you up and running with pyquaternion in no time.
Getting started
The following aims to familiarize you with the basic functionality of quaternions in pyquaternion. It provides an entry point and a quick orientation (no pun intended) for those who want get stuck straight in. More comprehensive feature summaries can be found in the features and operations documentation.
If you want to learn more about quaternions and how they apply to certain problems, you can read about quaternions here, and their application to rotations here.
Installation
To start, you will need to install pyquaternion into your environment.
[Optional] If you are using virtual environments, switch to or create your environment of choice now:
$ workon <my_environment>
Now use pip to install pyquaternion and its dependencies
$ pip install pyquaternion
Note: pyquaternion requires Numpy for the representation of arrays and matrices. Chances are if you're needing quaternions, you've been dealing with numerical computation and you're already familiar with numpy. If not, don't worry, it will be installed into your environment automatically.
Great, you now have pyquaternion installed and you're ready to roll. Or pitch. Or yaw. No judging here :)
Basic Usage
In your code, simply import the Quaternion object from the pyquaternion module:
>>> from pyquaternion import Quaternion
Next, create a Quaternion object to describe your desired rotation:
>>> my_quaternion = Quaternion(axis=[1, 0, 0], angle=3.14159265)
Note: There are many ways to create a Quaternion object. See the initialisation section for a complete guide.
Now you can do a bunch of useful things with your new quaternion object. Let's try rotating a vector:
>>> import numpy
>>> numpy.set_printoptions(suppress=True) # Suppress insignificant values for clarity
>>> v = numpy.array([0., 0., 1.]) # Unit vector in the +z direction
>>> v_prime = my_quaternion.rotate(v)
>>> v_prime
array([ 0., 0., -1.])
>>>
A cool feature of quaternions is that they can be intuitively chained together to form a composite rotation from a sequence of discrete rotations:
>>> q1 = Quaternion(axis=[1, 0, 0], angle=3.14159265) # Rotate 180 about X
>>> q2 = Quaternion(axis=[0, 1, 0], angle=3.14159265 / 2) # Rotate 90 about Y
>>> q3 = q1 * q2 # Composite rotation of q1 then q2 expressed as standard multiplication
>>> v_prime = q3.rotate(v)
>>> v_prime
array([ 1., 0., 0.])
>>>
Quaternions are used extensively in animation to describe smooth transitions between known orientations. This is known as interpolation. This is an example of an area where quaternions are preferred to rotation matrices as smooth interpolation is not possible with the latter. Here's quaternion interpolation in action:
>>> import numpy
>>> numpy.set_printoptions(suppress=True) # Suppress insignificant values for clarity
>>> v = numpy.array([0., 0., 1.]) # Unit vector in the +z direction
>>> q0 = Quaternion(axis=[1, 1, 1], angle=0.0) # Rotate 0 about x=y=z
>>> q1 = Quaternion(axis=[1, 1, 1], angle=2 * 3.14159265 / 3) # Rotate 120 about x=y=z
>>> for q in Quaternion.intermediates(q0, q1, 8, include_endpoints=True):
... v_prime = q.rotate(v)
... print(v_prime)
...
[ 0. 0. 1.]
[ 0.14213118 -0.12416109 0.98202991]
[ 0.29457011 -0.22365854 0.92908843]
[ 0.44909878 -0.29312841 0.84402963]
[ 0.59738651 -0.32882557 0.73143906]
[ 0.73143906 -0.32882557 0.59738651]
[ 0.84402963 -0.29312841 0.44909879]
[ 0.92908843 -0.22365854 0.29457012]
[ 0.98202991 -0.12416109 0.14213118]
[ 1. 0. 0.]
In the code above, the expression Quaternion.intermediates(q0, q1, 8, include_endpoints=True)
returns an iterator over a sequence of Quaternion objects describing a set of 10 (8 + 2) rotations between q0
and q1
. The printed output is then the path of the point originally at [0, 0, 1] as it is rotated through 120 degrees about x=y=z to end up at [1, 0, 0].
This could easily be plugged into a visualisation framework to show smooth animated rotation sequences. Read the full documentation on interpolation features here.
For a full demonstration of 3D interpolation and animation, run the demo2.py
script included in the pyquaternion package. This will require some elements of the full SciPy package that are not required for pyquaternion itself.
Object Initialisation
A Quaternion object can be created in the following ways:
Default
Quaternion()
Creates a unit quaternion 1 + 0i + 0j + 0k
: the quaternion representation of the real number 1.0,
and the representation of a null rotation.
q1 = Quaternion()
For the purposes of rotation, this is a null quaternion (has no effect on the rotated vector). For the purposes of quaternion multiplication, this is a unit quaternion (has no effect when multiplying)
Copy
Quaternion(other)
Clone another quaternion object
Params:
other
must be another Quaternion instance.
q2 = Quaternion(q1)
Raises: TypeError
if the provided object is not an instance of Quaternion, or any valid positional argument as outlined below.
Random
Quaternion.random()
Create a random quaternion that describes a rotation randomly chosen from a uniform distribution across the rotation space. Source.
This is a class method and is called as a method of the class itself rather than on a particular instance.
q3 = Quaternion.random() # called as a class method
From scalar
Quaternion(scalar)
Create the quaternion representation of a scalar (single real number) value.
Params:
scalar
can be a real number, or a string representing a real number.
The imaginary part of the resulting quaternion will always be 0i + 0j + 0k
.
q4 = Quaternion(4.7349)
q4 = Quaternion(-3)
q4 = Quaternion("4.7349")
q4 = Quaternion("98")
Raises:
TypeError
if the provided value cannot be converted to a real number.ValueError
if a provided string cannot be interpreted as a real number.
From elements
Quaternion(w, x, y, z)
Create a quaternion by specifying 4 real-numbered scalar elements.
Params:
w, x, y, z
can be real numbers, strings representing real numbers, or a mixture of both.
q5 = Quaternion(1, 1, 0, 0)
q5 = Quaternion("1.0", "0", ""0.347"", "0.0")
q5 = Quaternion("1.76", 0, 0, 0)
Raises:
TypeError
if any of the provided values cannot be converted to a real number.ValueError
if any of the provided strings cannot be interpreted as a real number.
From a numpy array
Quaternion(array)
Create a quaternion from the elements of a 4-element Numpy array
Params:
array
must be a 4-element numpy array containing real valued elements.
The elements [a, b, c, d]
of the array correspond the the real, and each imaginary component respectively in the order a + bi + cj + dk
.
q6 = Quaternion(numpy.array([a, b, c, d]))
Raises:
TypeError
if any of the array contents cannot be converted to a real number.ValueError
if the array contains less/more than 4 elements
From a sequence
Quaternion(seq)
Create a quaternion object from an ordered sequence containing 4 real valued scalar elements
Params:
seq
can be a list, a tuple, a generator or any iterable sequence containing 4 values, each convertible to a real number.
The sequential elements a, b, c, d
of the sequence correspond the the real, and each imaginary component respectively in the order a + bi + cj + dk
.
q7 = Quaternion((a, b, c, d)) // from 4-tuple
q7 = Quaternion([a, b, c, d]) // from list of 4
Raises:
TypeError
if any of the sequence contents cannot be converted to a real number.ValueError
if the sequence contains less/more than 4 elements
Explicitly by element
Quaternion(a=w, b=x, c=y, d=z)
Specify each element, using any sequence of ordered labels
Params:
a=w, b=x, c=y, d=z
can be real numbers, strings representing real numbers, or a mixture of both.
q8a = Quaternion(a=1.0, b=0.0, c=0.0, d=0.0)
q8a = Quaternion(w=1.0, x=0.0, y=0.0, z=0.0)
q8a = Quaternion(a=1.0, i=0.0, j=0.0, k=0.0)
q8a = Quaternion(q1=1.0, q2=0.0, q3=0.0, q4=0.0)
Rasises: Exception behaviour is the same as initialisation by element as described above.
Explicitly by component
Quaternion(scalar=s, vector=v)
orQuaternion(real=r, imaginary=i)
Specify the scalar (real) and vector (imaginary) parts of the desired quaternion.
Params:
scalar=s
orreal=r
can be a real number, or a string representing a real number.vector=v
orimaginary=i
can be a sequence or numpy array containing 3 real numbers.
Either component (but not both) may be absent, None
or empty, and will be assumed to be zero in that case.
q8b = Quaternion(scalar=1.0, vector=(0.0, 0.0, 0.0)) // Using 3-tuple
q8b = Quaternion(scalar=None, vector=[1.0, 0.0, 0.0]) // Using list
q8b = Quaternion(vector=numpy.array([1.0, 0.0, 0.0])) // Using Numpy 3-array
q8b = Quaternion(real=1.0, imaginary=(0.0, 0.0, 0.0)) // Using 3-tuple
q8b = Quaternion(real=None, imaginary=[1.0, 0.0, 0.0]) // Using list
q8b = Quaternion(imaginary=numpy.array([1.0, 0.0, 0.0])) // Using Numpy 3-array
Raises: ValueError
if the vector
or imaginary
component contains less/more than 3 elements
Explicitly by rotation parameters
Quaternion(axis=ax, radians=rad)
orQuaternion(axis=ax, degrees=deg)
orQuaternion(axis=ax, angle=theta)
Specify the angle (qualified as radians or degrees) for a rotation about an axis vector [x, y, z] to be described by the quaternion object.
Params
axis=ax
can be a sequence or numpy array containing 3 real numbers. It can have any magnitude except 0
.
radians=rad
[optional] a real number, or a string representing a real number in radians.
degrees=deg
[optional] a real number, or a string representing a real number in degrees.
angle=theta
[optional] a real number, or a string representing a real number in radians.
The angle
(radians/degrees/angle) keyword may be absent, None
or empty, and will be assumed to be zero in that case,
but the axis
keyword must be provided to describe a meaningful rotation.
q8c = Quaternion(axis=(1.0, 0.0, 0.0), radians=math.pi/2) // Using radians asnd a 3-tuple
q8c = Quaternion(axis=[1.0, 0.0, 0.0], degrees=90) // Using degrees and a list
q8c = Quaternion(axis=numpy.array([1.0, 0.0, 0.0]), angle=math.pi/2) // Using radians and a Numpy 3-array
Raises:
ValueError
ifaxis
is missingValueError
ifaxis
contains less/more than 3 elementsTypeError
ifradians/degrees/angle
cannot be interpreted as a real numberZeroDivisionError
ifaxis
has 0 length.
Explicitly by rotation or transformation matrix
Quaternion(matrix=R)
orQuaternion(matrix=T)
Specify the 3x3 rotation matrix (R
) or 4x4 transformation matrix (T
) from which the quaternion's rotation should be created.
Params:
matrix=R
can be a 3x3 numpy array or matrixmatrix=T
can be a 4x4 numpy array or matrix. In this case, the translation part will be ignored, and only the rotational component of the matrix will be encoded within the quaternion.
Important: The rotation component of the provided matrix must be a pure rotation i.e. special orthogonal.
rotation = numpy.eye(3)
transformation = numpy.eye(4)
q8d = Quaternion(matrix=rotation) // Using 3x3 rotation matrix
q8d = Quaternion(matrix=transformation) // Using 4x4 transformation matrix
This code uses a modification of the algorithm described in Converting a Rotation Matrix to a Quaternion, which is itself based on the method described here.
Note: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence, the output may jump between these equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.
Raises:
ValueError
if the matrix is not 3x3 or 4x4 or if the matrix is not special orthogonal.TypeError
if the matrix is of the wrong type
Explicitly by a numpy array
Quaternion(array=a)
Specify a numpy 4-array of quaternion elements to be assigned directly to the internal vector representation of the quaternion object.
This is more direct, and may be faster than feeding a numpy array as a positional argument to the initialiser.
Params:
array=a
must be a 4-element numpy array containing real valued elements.
The elements [a, b, c, d]
of the array correspond the the real, and each imaginary component respectively in the order a + bi + cj + dk
.
q8e = Quaternion(array=numpy.array([1.0, 0.0, 0.0, 0.0]))
Raises: ValueError
if the array vector contains less/more than 4 elements
Quaternion Features
This section defines features available for pyquaternion's Quaternion objects
The code examples below assume the existence of a Quaternion object. You can recreate this by running the following in your Python interpreter of choice:
my_quaternion = Quaternion.random()
Norm
norm
ormagnitude
L2 norm of the quaternion 4-vector
This should be 1.0 for a unit quaternion (versor)
Returns: a scalar real number representing the square root of the sum of the squares of the elements of the quaternion.
my_quaternion.norm
my_quaternion.magnitude
is_unit(tolerance=1e-14)
Params:
tolerance
- [optional] - maximum absolute value by which the norm can differ from 1.0 for the object to be considered a unit quaternion. Defaults to1e-14
.
Returns: True
if the Quaternion object is of unit length to within the specified tolerance value. False
otherwise.
Inversion
inverse
Inverse of the quaternion object
For a unit quaternion, this is the inverse rotation, i.e. when combined with the original rotation, will result in the null rotation.
Returns: a new Quaternion object representing the inverse of this object
inv_quaternion = my_quaternion.inverse
Conjugation
conjugate
Quaternion conjugate
For a unit quaternion, this is the same as the inverse.
Returns: a new Quaternion object clone with its vector part negated
conj_quaternion = my_quaternion.conjugate
Normalisation
normalised
orunit
Get a unit quaternion (versor) copy of this Quaternion object.
A unit quaternion has a norm
of 1.0
Note: A Quaternion representing zero i.e. Quaternion(0, 0, 0, 0)
cannot be normalised. In this case, the returned object will remain zero.
Returns: a new Quaternion object clone that is guaranteed to be a unit quaternion unless the original object was zero, in which case the norm will remain zero.
unit_quaternion = my_quaternion.normalised
unit_quaternion = my_quaternion.unit
Rotation
rotate(vector)
Rotate a 3D vector by the rotation stored in the Quaternion object
Params:
vector
- a 3-vector specified as any ordered sequence of 3 real numbers corresponding to x, y, and z values. Some types that are recognised are: numpy arrays, lists and tuples. A 3-vector can also be represented by a Quaternion object who's scalar part is 0 and vector part is the required 3-vector. Thus it is possible to callQuaternion.rotate(q)
with another quaternion object as an input.
Returns: the rotated vector returned as the same type it was specified at input.
rotated_tuple = my_quaternion.rotate((1, 0, 0)) # Returns a tuple
rotated_list = my_quaternion.rotate([1.0, 0.0, 0.0]) # Returns a list
rotated_array = my_quaternion.rotate(numpy.array([1.0, 0.0, 0.0])) # Returns a Numpy 3-array
rotated_quaternion = my_quaternion.rotate(Quaternion(vector=[1, 0, 0])) # Returns a Quaternion object
Raises:
TypeError
if any of the vector elements cannot be converted to a real number.ValueError
ifvector
cannot be interpreted as a 3-vector or a Quaternion object.
Exp and Log Maps
Quaternion.exp(q)
- class method
Quaternion Exponential.
Params:
q
- the input quaternion/argument as a Quaternion object.
Returns: A quaternion amount representing the exp(q). See Source.
Note: The method can compute the exponential of any quaternion.
Quaternion.log(q)
- class method
Quaternion Logarithm.
Params:
q
- the input quaternion/argument as a Quaternion object.
Returns: A quaternion amount representing log(q) := (log(|q|), v/|v|acos(w/|q|))
.
Note: The method computes the logarithm of general quaternions. See Source for more details.
Quaternion.exp_map(q, eta)
- class method
Quaternion exponential map.
Find the exponential map on the Riemannian manifold described by the quaternion space.
Params:
q
- the base point of the exponential map, i.e. a Quaternion objecteta
- the argument of the exponential map, a tangent vector, i.e. a Quaternion object
Returns: A quaternion p such that p is the endpoint of the geodesic starting at q in the direction of eta, having the length equal to the magnitude of eta.
Note: The exponential map plays an important role in integrating orientation variations (e.g. angular velocities). This is done by projecting quaternion tangent vectors onto the quaternion manifold.
Quaternion.sym_exp_map(q, eta)
- class method
Quaternion symmetrized exponential map.
Find the symmetrized exponential map on the quaternion Riemannian manifold.
Params:
q
- the base point as a Quaternion objecteta
- the tangent vector argument of the exponential map as a Quaternion object
Returns: A quaternion p.
Note: The symmetrized exponential formulation is akin to the exponential formulation for symmetric positive definite tensors Source
Quaternion.log_map(q, p)
- class method
Quaternion logarithm map.
Find the logarithm map on the quaternion Riemannian manifold.
Params:
q
- the base point at which the logarithm is computed, i.e. a Quaternion objectp
- the argument of the quaternion map, a Quaternion object
Returns: A tangent vector having the length and direction given by the geodesic joining q and p.
Quaternion.sym_log_map(q, p)
- class method
Quaternion symmetrized logarithm map.
Find the symmetrized logarithm map on the quaternion Riemannian manifold.
Params:
q
- the base point at which the logarithm is computed, i.e. a Quaternion objectp
- the argument of the quaternion map, a Quaternion object
Returns: A tangent vector corresponding to the symmetrized geodesic curve formulation.
Note: Information on the symmetrized formulations given in Source.
Distance computation
Quaternion.absolute_distance(q0, q1)
- class method
Quaternion absolute distance.
Find the distance between two quaternions accounting for the sign ambiguity.
Params:
q0
- the first quaternionq1
- the second quaternion
Returns: A positive scalar corresponding to the chord of the shortest path/arc that connects q0 to q1.
Note: This function does not measure the distance on the hypersphere, but it takes into account the fact that q and -q encode the same rotation. It is thus a good indicator for rotation similarities.
Quaternion.distance(q0, q1)
- class method
Quaternion intrinsic distance.
Find the intrinsic geodesic distance between q0 and q1.
Params:
q0
- the first quaternionq1
- the second quaternion
Returns: A positive amount corresponding to the length of the geodesic arc connecting q0 to q1.
Note: Although q0^(-1)*q1 != q1^(-1)*q0
, the length of the path joining them is given by the logarithm of those product quaternions, the norm of which is the same.
Quaternion.sym_distance(q0, q1)
- class method
Quaternion symmetrized distance.
Find the intrinsic symmetrized geodesic distance between q0 and q1.
Params:
q0
- the first quaternionq1
- the second quaternion
Returns: A positive amount corresponding to the length of the symmetrized geodesic curve connecting q0 to q1.
Note: This formulation is more numerically stable when performing iterative gradient descent on the Riemannian quaternion manifold. However, the distance between q and -q is equal to pi, rendering this formulation not useful for measuring rotation similarities when the samples are spread over a "solid" angle of more than pi/2 radians (the spread refers to quaternions as point samples on the unit hypersphere).
Interpolation
Quaternion.slerp(q0, q1, amount=0.5)
- class method
Find a valid quaternion rotation at a specified distance along the minor arc of a great circle passing through any two existing quaternion endpoints lying on the unit radius hypersphere. Source
This is a class method and is called as a method of the class itself rather than on a particular instance.
Params:
q0
- first endpoint rotation as a Quaternion objectq1
- second endpoint rotation as a Quaternion objectamount
- interpolation parameter between 0 and 1. This describes the linear placement position of the result along the arc between endpoints; 0 being atq0
and 1 being atq1
. Defaults to the midpoint (0.5).
Returns: a new Quaternion object representing the interpolated rotation. This is guaranteed to be a unit quaternion.
Note: This feature only makes sense when interpolating between unit quaternions (those lying on the unit radius hypersphere). Calling this method will implicitly normalise the endpoints to unit quaternions if they are not already unit length.
q0 = Quaternion(axis=[1, 1, 1], angle=0.0)
q1 = Quaternion(axis=[1, 1, 1], angle=3.141592)
q = Quaternion.slerp(q0, q1, 2.0/3.0) # Rotate 120 degrees (2 * pi / 3)
Quaternion.intermediates(q_start, q_end, n, include_endpoints=False)
- class method
Generator method to get an iterable sequence of n
evenly spaced quaternion rotations between any two existing quaternion endpoints lying on the unit radius hypersphere. This is a convenience function that is based on Quaternion.slerp()
as defined above.
This is a class method and is called as a method of the class itself rather than on a particular instance.
Params:
q_start
- initial endpoint rotation as a Quaternion objectq_end
- final endpoint rotation as a Quaternion objectn
- number of intermediate quaternion objects to include within the intervalinclude_endpoints
- [optional] - If set toTrue
, the sequence of intermediates will be 'bookended' byq_start
andq_end
, resulting in a sequence length ofn + 2
. If set toFalse
, endpoints are not included. Defaults toFalse
.
Yields: a generator object iterating over a sequence of intermediate quaternion objects.
Note: This feature only makes sense when interpolating between unit quaternions (those lying on the unit radius hypersphere). Calling this method will implicitly normalise the endpoints to unit quaternions if they are not already unit length.
q0 = Quaternion(axis=[1, 1, 1], angle=0.0)
q1 = Quaternion(axis=[1, 1, 1], angle=2 * 3.141592 / 3)
for q in Quaternion.intermediates(q0, q1, 8, include_endpoints=True):
v = q.rotate([1, 0, 0])
print(v)
Differentiation
derivative(rate)
Get the instantaneous quaternion derivative representing a quaternion rotating at a 3D rate vector rate
Params:
rate
- numpy 3-array (or array-like) describing rotation rates about the global x, y and z axes respectively.
Returns: A unit quaternion describing the rotation rate
q_dot = my_quaternion.derivative([0, 0, 3.14159]) # Rotate about z at 0.5 rotation per second
Raises:
TypeError
if any ofrate
contents cannot be converted to a real number.ValueError
ifrate
contains less/more than 3 elements
Integration
integrate(rate, timestep)
Advance a time varying quaternion to its value at a time timestep
in the future.
The Quaternion object will be modified to its future value. It is guaranteed to remain a unit quaternion.
Params:
rate
- numpy 3-array (or array-like) describing rotation rates about the global x, y and z axes respectively.timestep
- interval over which to integrate into the future. Assuming now isT=0
, the integration occurs over the intervalT=0
toT=timestep
. Smaller intervals are more accurate whenrate
changes over time.
Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one. Many quaternion integration algorithms will have unwanted scaling effects leading a quaternion object to become non-unit over time, thus the object is re-normalised with each call to integrate()
. Because this method is often called very frequently (every timestep
for realtime simulation) an optimised re-normalisation is performed. See _fast_normalise()
for more info.
Note 2: The solution is in closed form given the assumption that rate
is constant over the interval of length timestep
. This algorithm is not an exact solution to the differential equation over any interval where the angular rates are not constant. It is a second order approximation, meaning the integral error contains terms proportional to timestep ** 3
and higher powers.
>>> q = Quaternion() # null rotation
>>> q.integrate([2*pi, 0, 0], 0.25) # Rotate about x at 1 rotation per second
>>> q == Quaternion(axis=[1, 0, 0], angle=(pi/2))
True
>>>
Raises:
TypeError
if any ofrate
contents cannot be converted to a real number.ValueError
ifrate
contains less/more than 3 elements
Accessing matrix form
rotation_matrix
&transformation_matrix
Get the 3x3 rotation or 4x4 homogeneous transformation matrix equivalent of the quaternion rotation.
Returns:
Quaternion.rotation_matrix
: a 3x3 orthogonal rotation matrix as a 3x3 Numpy arrayQuaternion.transformation_matrix
: a 4x4 homogeneous transformation matrix as a 4x4 Numpy array
Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one.
Note 2: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence, the output may jump between different but equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.
R = my_quaternion.rotation_matrix # 3x3 rotation matrix
T = my_quaternion.transformation_matrix # 4x4 transformation matrix
Accessing rotation axis
axis
orget_axis(undefined=[0,0,0])
Get the axis or vector about which the quaternion rotation occurs
For a null rotation (a purely real quaternion), the rotation angle will always be 0
, but the rotation axis is undefined. It is by default assumed to be [0, 0, 0]
.
Note: In the case of a null rotation, retrieving the axis is geometrically meaningless, as it could be any of an infinite set of vectors.
By default, ([0, 0, 0]
) is returned in this instance, but should this causes undesired behaviour, please use the
alternative get_axis()
form, specifying the undefined
keyword to return a vector of your choice.
Params:
undefined
- [optional] - specify the axis vector that should define a null rotation.
Returns: a Numpy unit 3-vector describing the Quaternion object's axis of rotation.
Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one.
Note 2: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence to axis/angle representation, the output may jump between different but equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.
u = my_quaternion.axis # Unit vector about which rotation occurs #or
u = my_quaternion.get_axis(undefined=[1, 0, 0]) # Prefers a custom axis vector in the case of undefined result
Accessing rotation angle
angle
,degrees
orradians
Get the angle (in radians) describing the magnitude of the quaternion rotation about its rotation axis. This is guaranteed to be within the range (-pi:pi) with the direction of rotation indicated by the sign.
When a particular rotation describes a 180 degree rotation about an arbitrary axis vector v
, the conversion to axis / angle representation may jump discontinuously between all permutations of (-pi, pi)
and (-v, v)
, each being geometrically equivalent (see Note 2 below).
Returns: a real number in the range (-pi:pi) describing the angle of rotation in radians about a Quaternion object's axis of rotation.
Note 1: This feature only makes sense when referring to a unit quaternion. Calling this method will implicitly normalise the Quaternion object to a unit quaternion if it is not already one.
Note 2: Both matrices and quaternions avoid the singularities and discontinuities involved with rotation in 3 dimensions by adding extra dimensions. This has the effect that different values could represent the same rotation, for example quaternion q and -q represent the same rotation. It is therefore possible that, when converting a rotation sequence to axis/angle representation, the output may jump between different but equivalent forms. This could cause problems where subsequent operations such as differentiation are done on this data. Programmers should be aware of this issue.
theta = my_quaternion.angle # Magnitude of rotation about the prescribed axis, in radians
theta = my_quaternion.radians # Equivalent, but explicit
theta = my_quaternion.degrees # The same, but in degrees
Accessing real components
scalar
orreal
Get the real or scalar component of the Quaternion object
A quaternion can be described in terms of a scalar and vector part, q = [r, v] where:
- r is the scalar coefficient of the real part of the quaternion i.e. a in a + bi + cj + dk
- v is the 3-vector of coefficients to the imaginary parts of the quaternion i.e. [b, c, d] in a + bi + cj + dk
This property returns r
Returns the scalar, real valued element of the Quaternion object
r = my_quaternion.scalar
r = my_quaternion.real
Accessing imaginary components
vector
orimaginary
Get the imaginary or vector component of the Quaternion object. This can be used, for example, to extract the stored vector when a pure-imaginary quaternion object is used to describe a vector within the three-dimensional vector space.
A quaternion can be described in terms of a scalar and vector part, q = [r, v] where:
- r is the scalar coefficient of the real part of the quaternion i.e. a in a + bi + cj + dk
- v is the 3-vector of coefficients to the imaginary parts of the quaternion i.e. [b, c, d] in a + bi + cj + dk
This property returns v
Returns Numpy 3-array of the 3 imaginary elements of the Quaternion object
v = my_quaternion.vector
v = my_quaternion.imaginary
Accessing individual elements
elements
Return all four elements of the quaternion object. Result is not guaranteed to be a unit 4-vector.
Returns: a numpy 4-array of real numbered coefficients.
>>> a = my_quaternion.elements
>>> print("{} + {}i + {}j + {}k".format(a[0], a[1], a[2], a[3]))
-0.6753741977725701 + 0.4624451782281068i + -0.059197245808339134j + 0.5714103921047806k
__getitem__(index)
my_quaternion[i]
returns the real numbered element at the specified index i
in the quaternion 4-array
Params:
index
- integer in the range [-4:3] inclusive
>>> print("{} + {}i + {}j + {}k".format(my_quaternion[0], my_quaternion[1], my_quaternion[2], my_quaternion[3]))
-0.6753741977725701 + 0.4624451782281068i + -0.059197245808339134j + 0.5714103921047806k
>>> print("{} + {}i + {}j + {}k".format(my_quaternion[-4], my_quaternion[-3], my_quaternion[-2], my_quaternion[-1]))
-0.6753741977725701 + 0.4624451782281068i + -0.059197245808339134j + 0.5714103921047806k
>>>
Raises:
IndexError
if the index provided is invalidTypeError
orValueError
if the index cannot be interpreted as an integer
Modifying individual elements
__setitem__(index, value)
my_quaternion[i] = x
sets the element at the specified index i
in the quaternion 4-array to the specified value x
.
Params:
index
- integer in the range [-4:3] inclusivevalue
- real value to be inserted into the quaternion array atindex
>>> str(my_quaternion)
'-0.653 -0.127i -0.220j +0.714k'
>>> my_quaternion[2] = 9
>>> str(my_quaternion)
'-0.653 -0.127i +9.000j +0.714k'
>>>
Raises:
IndexError
if the index provided is invalidTypeError
orValueError
if the value cannot be interpreted as a real number
Quaternion Operations
This section defines operations applicable to pyquaternion's Quaternion objects.
The code examples below assume the existence of a Quaternion object. You can recreate this by running the following in your Python interpreter of choice:
my_quaternion = Quaternion.random()
String Representation
__str__()
str(my_quaternion)
returns an informal, nicely printable string representation of the Quaternion object. Source
>>> str(my_quaternion)
'-0.810 +0.022i -0.563j -0.166k'
>>> print(my_quaternion)
-0.810 +0.022i -0.563j -0.166k
>>>
__repr__()
repr(my_quaternion)
returns the 'official' string representation of the Quaternion object. This is a string representation of a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). Source
>>> repr(my_quaternion)
'Quaternion(-0.80951530224438595, 0.022231097065902788, -0.56268832802625091,-0.16604999023923223)'
>>> my_quaternion
Quaternion(-0.80951530224438595, 0.022231097065902788, -0.56268832802625091,-0.16604999023923223)
>>>
__format__(format_spec)
a_string_containing_{format_spec}_placeholders.format(my_quaternion)
inserts a customisable, nicely printable string representation of the Quaternion object into the respective places in the provided string. Source
The syntax for format_spec
mirrors that of the built in format specifiers for floating point types. Check out the official Python format specification mini-language for details.
An empty format_spec
string will result in the same behaviour as the Quaternion.__str__()
.
>>> "My quaternion is: {}".format(my_quaternion)
'My quaternion is: -0.810 +0.022i -0.563j -0.166k'
>>> "My quaternion is: {:+.6}".format(my_quaternion)
'My quaternion is: -0.809515 +0.0222311i -0.562688j -0.16605k'
Bool
__bool__()
or__nonzero__()
Returns: False
within a logical context if the Quaternion object is zero, i.e. Quaternion(0.0, 0.0, 0.0, 0.0)
or True
otherwise.
The bitwise not operator ~
can be used to invert the boolean value, however the keyword not
(logical) is preferred.
Note: This does not evaluate the booleanity of a quaternion rotation. A non-zero Quaternion object such as Quaternion(1.0, 0.0, 0.0, 0.0)
will have a boolean value of True
even though it represents a null rotation.
>>> Quaternion() == True
True
>>> not Quaternion() == False
True
>>> Quaternion(scalar=0.0) == False
True
Equality
__eq__(other)
q1 == q2
returns True
if all corresponding elements are equal between two Quaternion objects q1
and q2
, or False
otherwise.
The inequality operator !=
can also be used to verify inequality in a similar way.
Because comparisons are carried out on floating point elements, equality is considered True
when the absolute difference between elements falls below a threshold error. This is determined by numpy.allclose() with an absolute tolerance of 1.0e-14
and a relative tolerance of 1.0e-13
. As a result, objects differing by very small individual element differences may be considered equal.
Note: This does not directly evaluate the equality of a quaternion rotation. For example, unit Quaternions q and -q will have an equality of False
even though they represent the equivalent rotation.
>>> Quaternion(1, 0, 1, 1) == Quaternion(scalar=1.0, vector=[0.0, 1.0, 1.0])
True
>>> Quaternion(1, 0, 1, 1) == Quaternion(scalar=1.0, vector=[0.1, 1.0, 1.0])
False
>>> Quaternion() != Quaternion(scalar=2)
True
Negation
__neg__()
-q
is the quaternion formed by the element wise negation of the elements of q
.
Returns: a new Quaternion object representing the negation of the single operand. If the operand is a unit quaternion, the result is guaranteed to be a unit quaternion.
>>> my_elements = my_quaternion.elements() # Numpy array of individual elements
>>> -my_quaternion == Quaternion(-my_elements)
True
Addition
__add__(other)
q1 + q2
is the quaternion formed by element-wise sum of q1
and q2
. Source
Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError
or ValueError
will be raised if this conversion fails.
Returns: a new Quaternion object representing the sum of the inputs. The sum is not guaranteed to be a unit quaternion.
>>> q1 = Quaternion.random()
>>> q2 = Quaternion.random()
>>> q1 + q2 == Quaternion(q1.elements() + q2.elements())
True
Subtraction
__sub__(other)
q1 - q2
is the quaternion formed by element-wise difference between q1
and q2
. Source
Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError
or ValueError
will be raised if this conversion fails.
Returns: a new Quaternion object representing the difference of the inputs. The difference is not guaranteed to be a unit quaternion.
>>> q1 = Quaternion.random()
>>> q2 = Quaternion.random()
>>> q1 - q2 == Quaternion(q1.elements() - q2.elements())
True
Multiplication
__mul__(other)
q1 * q2
is the quaternion formed by Hamilton product of q1
and q2
. Source
The Hamiltonian product is not commutative. Ensure your operands are correctly placed.
Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError
or ValueError
will be raised if this conversion fails. As a result this operation holds true for scalar multiplication as scalars are converted to pure real Quaternion objects.
Returns: a new Quaternion object representing the Hamilton product of the inputs. If the two multiplicands are unit quaternions, the product is guaranteed to be a unit quaternion.
>>> one = Quaternion(1, 0, 0, 0)
>>> i = Quaternion(0, 1, 0, 0)
>>> j = Quaternion(0, 0, 1, 0)
>>> k = Quaternion(0, 0, 0, 1)
>>> (i * i) == (j * j) == (k * k) == (i * j * k) == -1
True
Division
__truediv__(other)
or__div__(other)
q1 / q2
is the quaternion formed by Hamilton product of q1
and q2.inverse()
. Source
The Hamiltonian product is not commutative. Ensure your operands are correctly placed.
Note: If 'other' is not a Quaternion object, it will be converted to one, using the behaviour described in the [object initialisation][initialisation] section. As described therein, a TypeError
or ValueError
will be raised if this conversion fails. As a result this operation holds true for scalar division as scalars are converted to pure real Quaternion objects.
Returns: a new Quaternion object representing the Hamilton quotient of the inputs. If the dividend and divisor are unit quaternions, the quotient is guaranteed to be a unit quaternion.
>>> my_quaternion / my_quaternion == Quaternion(1.0)
True
Exponentiation
__pow__(other)
q ** p
is the quaternion formed by raising the Quaternion q1
to the power of p
for any real p
. Source
Returns: a new Quaternion object representing the the object raised to the power of the input. If the base object is a unit quaternion, the result is guaranteed to be a unit quaternion.
>>> one = Quaternion(1, 0, 0, 0)
>>> i = Quaternion(0, 1, 0, 0)
>>> j = Quaternion(0, 0, 1, 0)
>>> k = Quaternion(0, 0, 0, 1)
>>> (i ** 2) == (j ** 2) == (k ** 2) == -1
True
Raises: TypeError
if other
cannot be interpreted as a real number.