public class Rotation extends Object implements Serializable
Rotations can be represented by several different mathematical entities (matrices, axis 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 normalized rotation 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 can be done using the following single line of code:
double[] angles = new Rotation(matrix, 1.0e-10).getAngles(RotationOrder.XYZ);
A rotation is an operator which basically rotates three dimensional vectors
into other three dimensional vectors
using applyTo(Vector3D)
.
For example, the image of vector A using the rotation r could be obtained by : B = r.applyTo(A).
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). We can apply r1
to r2
and the result we get is r = r1 o r2
. This rotation
could be applied to vector A like this : B = r.applyTo(A).
The rotation can be used to change the basis of a vector using applyInverseTo(Vector3D)
. For example, r12 represents the orientation of frame R2 with respect to frame R1: The
image of a vector A expressed in frame R2 is : B = r12.applyInverseTo(A).
A rotation can be considered as a change of basis from R1 to R2 as follow : r_r2 = r12.applyTo(r_R1.revert()).
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(boolean needsNormalization,
double[] q)
Build a rotation from the quaternion coordinates.
|
Rotation(boolean needsNormalization,
double q0In,
double q1In,
double q2In,
double q3In)
Build a rotation from the quaternion coordinates.
|
Rotation(boolean needsNormalization,
Quaternion quaternion)
Build a rotation from the quaternion.
|
Rotation(double[][] m,
double threshold)
Build a rotation from a 3X3 matrix.
|
Rotation(RotationOrder order,
double alpha1,
double alpha2,
double alpha3)
Build a rotation from three Cardan or Euler elementary rotations.
|
Rotation(Vector3D axis,
double angle)
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 u1In,
Vector3D u2In,
Vector3D v1In,
Vector3D v2In)
Build the rotation that transforms a pair of vector into another pair.
|
Modifier and Type | Method and Description |
---|---|
void |
applyInverseTo(double[] vIn,
double[] vOut)
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[] vIn,
double[] vOut)
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.
|
static double |
distance(Rotation r1,
Rotation r2)
Compute the distance between two rotations.
|
double |
getAngle()
Get the angle of the rotation. inverse trigo function are protected because for a normalize
quaternion it is not possible to have asin(x > 1) when q0 is different from 0 else acos is
called with a value between -0.1 and 0.1.
|
double[] |
getAngles(RotationOrder order)
Get the Cardan or Euler angles corresponding to the instance.
|
Vector3D |
getAxis()
Get the normalized axis of the rotation.
|
double[][] |
getMatrix()
Get the 3X3 rotation matrix corresponding to the instance.
|
double[] |
getQi()
Get the normalized quaternion in double[] type : {q0, q1,
q2, q3}.
|
Quaternion |
getQuaternion()
Get the normalized quaternion.
|
boolean |
isEqualTo(Rotation rotation)
Compare two rotations with respect to the distance between them (see
distance(Rotation, Rotation) |
boolean |
isEqualTo(Rotation rotation,
double angleThreshold,
double axisThreshold)
Compare two rotations with respect to their axis and angle
|
static Rotation |
lerp(Rotation r0,
Rotation r1,
double h)
Returns linear interpolated rotation.
|
Rotation |
revert()
Revert a rotation.
|
static Rotation |
slerp(Rotation r0,
Rotation r1,
double h)
Returns spherical linear interpolated rotation.
|
String |
toString()
Get a string representation for the rotation.
|
public static final Rotation IDENTITY
public Rotation(boolean needsNormalization, double q0In, double q1In, double q2In, double q3In)
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. As a consequence, the normalized quaternion coordinates are defined as :
q0 = cos(θ/2)
q1 = x * sin(θ/2)
q2 = y * sin(θ/2)
q3 = z * sin(θ/2) }
θ beeing the oriented rotation angle around the axis (x, y, z).
needsNormalization
- if true, the coordinates are considered not to be normalized, a
normalization preprocessing step is performed before using themq0In
- scalar part of the quaternionq1In
- first coordinate of the vectorial part of the quaternionq2In
- second coordinate of the vectorial part of the quaternionq3In
- third coordinate of the vectorial part of the quaternionMathIllegalArgumentException
- thrown if norm of the quaternion is zeropublic Rotation(boolean needsNormalization, Quaternion quaternion)
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.
The normalized coordinates of this quaternion are defined as :q0 = cos(θ/2)
q1 = x * sin(θ/2)
q2 = y * sin(θ/2)
q3 = z * sin(θ/2) }
θ beeing the oriented rotation angle around the axis (x, y, z).
needsNormalization
- if true, the coordinates are considered not to be normalized, a
normalization preprocessing step is performed before using themquaternion
- the quaternion of rotationMathIllegalArgumentException
- thrown if norm of the quaternion is zeroQuaternion
public Rotation(boolean needsNormalization, double[] q)
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. As a consequence, the normalized coordinates {q0, q1, q2, q3} are defined as :
q0 = cos(θ/2)
q1 = x * sin(θ/2)
q2 = y * sin(θ/2)
q3 = z * sin(θ/2) }
θ beeing the oriented rotation angle around the axis (x, y, z).
needsNormalization
- if true, the coordinates are considered not to be normalized, a
normalization preprocessing step is performed before using themq
- the quaternion of rotationMathIllegalArgumentException
- thrown if norm of the quaternion is zeropublic Rotation(Vector3D axis, double angle)
We use the convention that angles are oriented according to the effect of the rotation on vectors around the
axis. That means that if (i, j, k) is a direct frame and if we first provide +k as the axis and π/2 as the
angle to this constructor, and then apply
the instance to +i, we will get +j.
Another way to represent our convention is to say that a rotation of angle θ about the unit vector (x, y, z) is the same as the rotation build from quaternion components { cos(θ/2), x * sin(θ/2), y * sin(θ/2), z * sin(θ/2) }. No minus sign on the angle!
axis
- axis around which to rotateangle
- rotation angle.MathIllegalArgumentException
- if the axis norm is zeropublic Rotation(double[][] m, double threshold)
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 u1In, Vector3D u2In, Vector3D v1In, Vector3D v2In)
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) plane.
u1In
- first vector of the origin pairu2In
- second vector of the origin pairv1In
- desired image of u1 by the rotationv2In
- 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 colinear)public Rotation(Vector3D u, Vector3D v)
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 colinear, 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 zeropublic Rotation(RotationOrder order, 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 usealpha1
- angle of the first elementary rotationalpha2
- angle of the second elementary rotationalpha3
- angle of the third elementary rotationpublic Rotation revert()
public Vector3D getAxis()
Rotation(Vector3D, double)
public Quaternion getQuaternion()
The coordinates of this quaternion are defined as :
q0 = cos(θ/2)
q1 = x * sin(θ/2)
q2 = y * sin(θ/2)
q3 = z * sin(θ/2) }
θ beeing the oriented rotation angle around the axis (x, y, z).
public double[] getQi()
Those coordinates are defined as :
q0 = cos(θ/2)
q1 = x * sin(θ/2)
q2 = y * sin(θ/2)
q3 = z * sin(θ/2) }
θ beeing the oriented rotation angle around the axis (x, y, z).
public double getAngle()
Rotation(Vector3D, double)
public double[] getAngles(RotationOrder order)
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. 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 usepublic double[][] getMatrix()
public Vector3D applyTo(Vector3D u)
u
- vector to apply the rotation topublic void applyTo(double[] vIn, double[] vOut)
vIn
- an array with three items which stores vector to rotatevOut
- 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[] vIn, double[] vOut)
vIn
- an array with three items which stores vector to rotatevOut
- an array with three items to put result to (it can be the same array as in)public Rotation applyTo(Rotation r)
Applying the instance to a rotation is creating a composed rotation in the following order :
R3 = R2.applyTo(R1) is equivalent to R3 = R2 o R1
Example :
The vector u being transformed into v by R1 : v = R1(u)
The vector v being transformed into w by R2 : w = R2(v)
w = R2(v) = R2(R1(u)) = R2 o R1(u) = R3(u)
r
- rotation to apply the rotation topublic Rotation applyInverseTo(Rotation r)
Applying the inverse of the instance to a rotation is creating a composed rotation in the following order :
R3 = R2.applyInverseTo(R1) is equivalent to R3 = R2-1 o R1
Example :
The vector u being transformed into v by R1 : v = R1(u)
The vector v being transformed into w by R2-1 : w = R2-1(v)
w = R2-1(v) = R2-1(R1(u)) = R2-1 o R1(u) = R3(u)
r
- rotation to apply the rotation topublic 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 rotationpublic boolean isEqualTo(Rotation rotation, double angleThreshold, double axisThreshold)
rotation
- : the rotation with which one want s to compare this rotationangleThreshold
- : threshold below which one the angle between the rotations is
neglectableaxisThreshold
- : threshold below which one the angle between the axis of the rotations
is neglectablepublic boolean isEqualTo(Rotation rotation)
distance(Rotation, Rotation)
rotation
- : the rotation with which one want s to compare this rotationpublic static Rotation lerp(Rotation r0, Rotation r1, double h)
r0
- rotation at interpolation parameter h=0r1
- rotation at interpolation parameter h=1h
- interpolation parameter, must be in [0;1] rangepublic static Rotation slerp(Rotation r0, Rotation r1, double h)
r0
- rotation at interpolation parameter h=0r1
- rotation at interpolation parameter h=1h
- interpolation parameter, must be in [0;1] rangeCopyright © 2019 CNES. All rights reserved.