public class Rotation extends Object implements Serializable
Rotations can be represented by several different mathematical
entities (matrices, axe and angle, Cardan or Euler angles,
quaternions). This class presents an higher level abstraction, more
user-oriented and hiding this implementation details. Well, for the
curious, we use quaternions for the internal representation. The
user can build a rotation from any of these representations, and
any of these representations can be retrieved from a
Rotation
instance (see the various constructors and
getters). In addition, a rotation can also be built implicitly
from a set of vectors and their image.
This implies that this class can be used to convert from one representation to another one. For example, converting a rotation matrix into a set of Cardan angles from can be done using the following single line of code:
double[] angles = new Rotation(matrix, 1.0e-10).getAngles(RotationOrder.XYZ);
Focus is oriented on what a rotation do rather than on its
underlying representation. Once it has been built, and regardless of its
internal representation, a rotation is an operator which basically
transforms three dimensional vectors
into other three
dimensional vectors
. Depending on the application, the
meaning of these vectors may vary and the semantics of the rotation also.
For example in an spacecraft attitude simulation tool, users will often consider the vectors are fixed (say the Earth direction for example) and the frames change. The rotation transforms the coordinates of the vector in inertial frame into the coordinates of the same vector in satellite frame. In this case, the rotation implicitly defines the relation between the two frames.
Another example could be a telescope control application, where the rotation would transform the sighting direction at rest into the desired observing direction when the telescope is pointed towards an object of interest. In this case the rotation transforms the direction at rest in a topocentric frame into the sighting direction in the same topocentric frame. This implies in this case the frame is fixed and the vector moves.
In many case, both approaches will be combined. In our telescope example, we will probably also need to transform the observing direction in the topocentric frame into the observing direction in inertial frame taking into account the observatory location and the Earth rotation, which would essentially be an application of the first approach.
These examples show that a rotation is what the user wants it to be. This
class does not push the user towards one specific definition and hence does not
provide methods like projectVectorIntoDestinationFrame
or
computeTransformedDirection
. It provides simpler and more generic
methods: applyTo(Vector3D)
and applyInverseTo(Vector3D)
.
Since a rotation is basically a vectorial operator, several rotations can be
composed together and the composite operation r = r1 o
r2
(which means that for each vector u
,
r(u) = r1(r2(u))
) is also a rotation. Hence
we can consider that in addition to vectors, a rotation can be applied to other
rotations as well (or to itself). With our previous notations, we would say we
can apply r1
to r2
and the result
we get is r = r1 o r2
. For this purpose, the
class provides the methods: applyTo(Rotation)
and
applyInverseTo(Rotation)
.
Rotations are guaranteed to be immutable objects.
Vector3D
,
RotationOrder
,
Serialized FormModifier and Type | Field and Description |
---|---|
static Rotation |
IDENTITY
Identity rotation.
|
Constructor and Description |
---|
Rotation(double[][] m,
double threshold)
Build a rotation from a 3X3 matrix.
|
Rotation(double q0,
double q1,
double q2,
double q3,
boolean needsNormalization)
Build a rotation from the quaternion coordinates.
|
Rotation(RotationOrder order,
double alpha1,
double alpha2,
double alpha3)
Deprecated.
as of 3.6, replaced with
Rotation(RotationOrder, RotationConvention, double, double, double) |
Rotation(RotationOrder order,
RotationConvention convention,
double alpha1,
double alpha2,
double alpha3)
Build a rotation from three Cardan or Euler elementary rotations.
|
Rotation(Vector3D axis,
double angle)
Deprecated.
as of 3.6, replaced with
Rotation(Vector3D, double, RotationConvention) |
Rotation(Vector3D axis,
double angle,
RotationConvention convention)
Build a rotation from an axis and an angle.
|
Rotation(Vector3D u,
Vector3D v)
Build one of the rotations that transform one vector into another one.
|
Rotation(Vector3D u1,
Vector3D u2,
Vector3D v1,
Vector3D v2)
Build the rotation that transforms a pair of vectors into another pair.
|
Modifier and Type | Method and Description |
---|---|
void |
applyInverseTo(double[] in,
double[] out)
Apply the inverse of the rotation to a vector stored in an array.
|
Rotation |
applyInverseTo(Rotation r)
Apply the inverse of the instance to another rotation.
|
Vector3D |
applyInverseTo(Vector3D u)
Apply the inverse of the rotation to a vector.
|
void |
applyTo(double[] in,
double[] out)
Apply the rotation to a vector stored in an array.
|
Rotation |
applyTo(Rotation r)
Apply the instance to another rotation.
|
Vector3D |
applyTo(Vector3D u)
Apply the rotation to a vector.
|
Rotation |
compose(Rotation r,
RotationConvention convention)
Compose the instance with another rotation.
|
Rotation |
composeInverse(Rotation r,
RotationConvention convention)
Compose the inverse of the instance with another rotation.
|
static double |
distance(Rotation r1,
Rotation r2)
Compute the distance between two rotations.
|
double |
getAngle()
Get the angle of the rotation.
|
double[] |
getAngles(RotationOrder order)
Deprecated.
as of 3.6, replaced with
getAngles(RotationOrder, RotationConvention) |
double[] |
getAngles(RotationOrder order,
RotationConvention convention)
Get the Cardan or Euler angles corresponding to the instance.
|
Vector3D |
getAxis()
Deprecated.
as of 3.6, replaced with
getAxis(RotationConvention) |
Vector3D |
getAxis(RotationConvention convention)
Get the normalized axis of the rotation.
|
double[][] |
getMatrix()
Get the 3X3 matrix corresponding to the instance
|
double |
getQ0()
Get the scalar coordinate of the quaternion.
|
double |
getQ1()
Get the first coordinate of the vectorial part of the quaternion.
|
double |
getQ2()
Get the second coordinate of the vectorial part of the quaternion.
|
double |
getQ3()
Get the third coordinate of the vectorial part of the quaternion.
|
Rotation |
revert()
Revert a rotation.
|
public Rotation(double q0, double q1, double q2, double q3, boolean needsNormalization)
A rotation can be built from a normalized quaternion, i.e. a quaternion for which q02 + q12 + q22 + q32 = 1. If the quaternion is not normalized, the constructor can normalize it in a preprocessing step.
Note that some conventions put the scalar part of the quaternion as the 4th component and the vector part as the first three components. This is not our convention. We put the scalar part as the first component.
q0
- scalar part of the quaternionq1
- first coordinate of the vectorial part of the quaternionq2
- second coordinate of the vectorial part of the quaternionq3
- third coordinate of the vectorial part of the quaternionneedsNormalization
- if true, the coordinates are considered
not to be normalized, a normalization preprocessing step is performed
before using them@Deprecated public Rotation(Vector3D axis, double angle) throws MathIllegalArgumentException
Rotation(Vector3D, double, RotationConvention)
Calling this constructor is equivalent to call
new Rotation(axis, angle, RotationConvention.VECTOR_OPERATOR)
axis
- axis around which to rotateangle
- rotation angle.MathIllegalArgumentException
- if the axis norm is zeropublic Rotation(Vector3D axis, double angle, RotationConvention convention) throws MathIllegalArgumentException
axis
- axis around which to rotateangle
- rotation angleconvention
- convention to use for the semantics of the angleMathIllegalArgumentException
- if the axis norm is zeropublic Rotation(double[][] m, double threshold) throws NotARotationMatrixException
Rotation matrices are orthogonal matrices, i.e. unit matrices (which are matrices for which m.mT = I) with real coefficients. The module of the determinant of unit matrices is 1, among the orthogonal 3X3 matrices, only the ones having a positive determinant (+1) are rotation matrices.
When a rotation is defined by a matrix with truncated values (typically when it is extracted from a technical sheet where only four to five significant digits are available), the matrix is not orthogonal anymore. This constructor handles this case transparently by using a copy of the given matrix and applying a correction to the copy in order to perfect its orthogonality. If the Frobenius norm of the correction needed is above the given threshold, then the matrix is considered to be too far from a true rotation matrix and an exception is thrown.
m
- rotation matrixthreshold
- convergence threshold for the iterative
orthogonality correction (convergence is reached when the
difference between two steps of the Frobenius norm of the
correction is below this threshold)NotARotationMatrixException
- if the matrix is not a 3X3
matrix, or if it cannot be transformed into an orthogonal matrix
with the given threshold, or if the determinant of the resulting
orthogonal matrix is negativepublic Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2) throws MathArithmeticException
Except for possible scale factors, if the instance were applied to the pair (u1, u2) it will produce the pair (v1, v2).
If the angular separation between u1 and u2 is not the same as the angular separation between v1 and v2, then a corrected v'2 will be used rather than v2, the corrected vector will be in the (±v1, +v2) half-plane.
u1
- first vector of the origin pairu2
- second vector of the origin pairv1
- desired image of u1 by the rotationv2
- desired image of u2 by the rotationMathArithmeticException
- if the norm of one of the vectors is zero,
or if one of the pair is degenerated (i.e. the vectors of the pair are collinear)public Rotation(Vector3D u, Vector3D v) throws MathArithmeticException
Except for a possible scale factor, if the instance were applied to the vector u it will produce the vector v. There is an infinite number of such rotations, this constructor choose the one with the smallest associated angle (i.e. the one whose axis is orthogonal to the (u, v) plane). If u and v are collinear, an arbitrary rotation axis is chosen.
u
- origin vectorv
- desired image of u by the rotationMathArithmeticException
- if the norm of one of the vectors is zero@Deprecated public Rotation(RotationOrder order, double alpha1, double alpha2, double alpha3)
Rotation(RotationOrder, RotationConvention, double, double, double)
Calling this constructor is equivalent to call
new Rotation(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3)
order
- order of rotations to usealpha1
- angle of the first elementary rotationalpha2
- angle of the second elementary rotationalpha3
- angle of the third elementary rotationpublic Rotation(RotationOrder order, RotationConvention convention, double alpha1, double alpha2, double alpha3)
Cardan rotations are three successive rotations around the canonical axes X, Y and Z, each axis being used once. There are 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler rotations are three successive rotations around the canonical axes X, Y and Z, the first and last rotations being around the same axis. There are 6 such sets of rotations (XYX, XZX, YXY, YZY, ZXZ and ZYZ), the most popular one being ZXZ.
Beware that many people routinely use the term Euler angles even for what really are Cardan angles (this confusion is especially widespread in the aerospace business where Roll, Pitch and Yaw angles are often wrongly tagged as Euler angles).
order
- order of rotations to compose, from left to right
(i.e. we will use r1.compose(r2.compose(r3, convention), convention)
)convention
- convention to use for the semantics of the anglealpha1
- angle of the first elementary rotationalpha2
- angle of the second elementary rotationalpha3
- angle of the third elementary rotationpublic Rotation revert()
public double getQ0()
public double getQ1()
public double getQ2()
public double getQ3()
@Deprecated public Vector3D getAxis()
getAxis(RotationConvention)
Calling this method is equivalent to call
getAxis(RotationConvention.VECTOR_OPERATOR)
Rotation(Vector3D, double, RotationConvention)
public Vector3D getAxis(RotationConvention convention)
Note that as getAngle()
always returns an angle
between 0 and π, changing the convention changes the
direction of the axis, not the sign of the angle.
convention
- convention to use for the semantics of the angleRotation(Vector3D, double, RotationConvention)
public double getAngle()
Rotation(Vector3D, double)
@Deprecated public double[] getAngles(RotationOrder order) throws CardanEulerSingularityException
getAngles(RotationOrder, RotationConvention)
Calling this method is equivalent to call
getAngles(order, RotationConvention.VECTOR_OPERATOR)
order
- rotation order to useCardanEulerSingularityException
- if the rotation is
singular with respect to the angles set specifiedpublic double[] getAngles(RotationOrder order, RotationConvention convention) throws CardanEulerSingularityException
The equations show that each rotation can be defined by two different values of the Cardan or Euler angles set. For example if Cardan angles are used, the rotation defined by the angles a1, a2 and a3 is the same as the rotation defined by the angles π + a1, π - a2 and π + a3. This method implements the following arbitrary choices:
Cardan and Euler angle have a very disappointing drawback: all of them have singularities. This means that if the instance is too close to the singularities corresponding to the given rotation order, it will be impossible to retrieve the angles. For Cardan angles, this is often called gimbal lock. There is nothing to do to prevent this, it is an intrinsic problem with Cardan and Euler representation (but not a problem with the rotation itself, which is perfectly well defined). For Cardan angles, singularities occur when the second angle is close to -π/2 or +π/2, for Euler angle singularities occur when the second angle is close to 0 or π, this implies that the identity rotation is always singular for Euler angles!
order
- rotation order to useconvention
- convention to use for the semantics of the angleCardanEulerSingularityException
- if the rotation is
singular with respect to the angles set specifiedpublic double[][] getMatrix()
public Vector3D applyTo(Vector3D u)
u
- vector to apply the rotation topublic void applyTo(double[] in, double[] out)
in
- an array with three items which stores vector to rotateout
- an array with three items to put result to (it can be the same
array as in)public Vector3D applyInverseTo(Vector3D u)
u
- vector to apply the inverse of the rotation topublic void applyInverseTo(double[] in, double[] out)
in
- an array with three items which stores vector to rotateout
- an array with three items to put result to (it can be the same
array as in)public Rotation applyTo(Rotation r)
Calling this method is equivalent to call
compose(r, RotationConvention.VECTOR_OPERATOR)
.
r
- rotation to apply the rotation topublic Rotation compose(Rotation r, RotationConvention convention)
If the semantics of the rotations composition corresponds to a
vector operator
convention,
applying the instance to a rotation is computing the composition
in an order compliant with the following rule : let u
be any
vector and v
its image by r1
(i.e.
r1.applyTo(u) = v
). Let w
be the image of v
by
rotation r2
(i.e. r2.applyTo(v) = w
). Then
w = comp.applyTo(u)
, where
comp = r2.compose(r1, RotationConvention.VECTOR_OPERATOR)
.
If the semantics of the rotations composition corresponds to a
frame transform
convention,
the application order will be reversed. So keeping the exact same
meaning of all r1
, r2
, u
, v
, w
and comp
as above, comp
could also be computed as
comp = r1.compose(r2, RotationConvention.FRAME_TRANSFORM)
.
r
- rotation to apply the rotation toconvention
- convention to use for the semantics of the anglepublic Rotation applyInverseTo(Rotation r)
Calling this method is equivalent to call
composeInverse(r, RotationConvention.VECTOR_OPERATOR)
.
r
- rotation to apply the rotation topublic Rotation composeInverse(Rotation r, RotationConvention convention)
If the semantics of the rotations composition corresponds to a
vector operator
convention,
applying the inverse of the instance to a rotation is computing
the composition in an order compliant with the following rule :
let u
be any vector and v
its image by r1
(i.e. r1.applyTo(u) = v
). Let w
be the inverse image
of v
by r2
(i.e. r2.applyInverseTo(v) = w
).
Then w = comp.applyTo(u)
, where
comp = r2.composeInverse(r1)
.
If the semantics of the rotations composition corresponds to a
frame transform
convention,
the application order will be reversed, which means it is the
innermost rotation that will be reversed. So keeping the exact same
meaning of all r1
, r2
, u
, v
, w
and comp
as above, comp
could also be computed as
comp = r1.revert().composeInverse(r2.revert(), RotationConvention.FRAME_TRANSFORM)
.
r
- rotation to apply the rotation toconvention
- convention to use for the semantics of the anglepublic static double distance(Rotation r1, Rotation r2)
The distance is intended here as a way to check if two rotations are almost similar (i.e. they transform vectors the same way) or very different. It is mathematically defined as the angle of the rotation r that prepended to one of the rotations gives the other one:
r1(r) = r2
This distance is an angle between 0 and π. Its value is the smallest possible upper bound of the angle in radians between r1(v) and r2(v) for all possible vectors v. This upper bound is reached for some v. The distance is equal to 0 if and only if the two rotations are identical.
Comparing two rotations should always be done using this value rather than for example comparing the components of the quaternions. It is much more stable, and has a geometric meaning. Also comparing quaternions components is error prone since for example quaternions (0.36, 0.48, -0.48, -0.64) and (-0.36, -0.48, 0.48, 0.64) represent exactly the same rotation despite their components are different (they are exact opposites).
r1
- first rotationr2
- second rotationCopyright © 2003–2016 The Apache Software Foundation. All rights reserved.