QuaternionRotation.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.geometry.euclidean.threed.rotation;

  18. import java.util.Objects;
  19. import java.util.function.DoubleFunction;

  20. import org.apache.commons.geometry.core.internal.GeometryInternalError;
  21. import org.apache.commons.geometry.euclidean.internal.Vectors;
  22. import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D;
  23. import org.apache.commons.geometry.euclidean.threed.Vector3D;
  24. import org.apache.commons.numbers.angle.Angle;
  25. import org.apache.commons.numbers.quaternion.Quaternion;
  26. import org.apache.commons.numbers.quaternion.Slerp;

  27. /**
  28.  * Class using a unit-length quaternion to represent
  29.  * <a href="https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation">rotations</a>
  30.  * in 3-dimensional Euclidean space.
  31.  * The underlying quaternion is in <em>positive polar form</em>: It is normalized and has a
  32.  * non-negative scalar component ({@code w}).
  33.  *
  34.  * @see Quaternion
  35.  */
  36. public final class QuaternionRotation implements Rotation3D {
  37.     /** Threshold value for the dot product of antiparallel vectors. If the dot product of two vectors is
  38.      * less than this value, (adjusted for the lengths of the vectors), then the vectors are considered to be
  39.      * antiparallel (ie, negations of each other).
  40.      */
  41.     private static final double ANTIPARALLEL_DOT_THRESHOLD = 2.0e-15 - 1.0;

  42.     /** Threshold value used to identify singularities when converting from quaternions to
  43.      * axis angle sequences.
  44.      */
  45.     private static final double AXIS_ANGLE_SINGULARITY_THRESHOLD = 0.9999999999;

  46.     /** Instance used to represent the identity rotation, ie a rotation with
  47.      * an angle of zero.
  48.      */
  49.     private static final QuaternionRotation IDENTITY_INSTANCE = of(Quaternion.ONE);

  50.     /** Unit-length quaternion instance in positive polar form. */
  51.     private final Quaternion quat;

  52.     /** Simple constructor. The given quaternion is converted to positive polar form.
  53.      * @param quat quaternion instance
  54.      * @throws IllegalStateException if the the norm of the given components is zero,
  55.      *                              NaN, or infinite
  56.      */
  57.     private QuaternionRotation(final Quaternion quat) {
  58.         this.quat = quat.positivePolarForm();
  59.     }

  60.     /** Get the underlying quaternion instance.
  61.      * @return the quaternion instance
  62.      */
  63.     public Quaternion getQuaternion() {
  64.         return quat;
  65.     }

  66.     /**
  67.      * Get the axis of rotation as a normalized {@link Vector3D}. The rotation axis
  68.      * is not well defined when the rotation is the identity rotation, ie it has a
  69.      * rotation angle of zero. In this case, the vector representing the positive
  70.      * x-axis is returned.
  71.      *
  72.      * @return the axis of rotation
  73.      */
  74.     @Override
  75.     public Vector3D getAxis() {
  76.         final Vector3D axis = Vector3D.of(quat.getX(), quat.getY(), quat.getZ())
  77.                 .normalizeOrNull();
  78.         return axis != null ?
  79.                 axis :
  80.                 Vector3D.Unit.PLUS_X;
  81.     }

  82.     /**
  83.      * Get the angle of rotation in radians. The returned value is in the range 0
  84.      * through {@code pi}.
  85.      *
  86.      * @return The rotation angle in the range {@code [0, pi]}.
  87.      */
  88.     @Override
  89.     public double getAngle() {
  90.         return 2 * Math.acos(quat.getW());
  91.     }

  92.     /**
  93.      * Get the inverse of this rotation. The returned rotation has the same
  94.      * rotation angle but the opposite rotation axis. If {@code r.apply(u)}
  95.      * is equal to {@code v}, then {@code r.negate().apply(v)} is equal
  96.      * to {@code u}.
  97.      *
  98.      * @return the negation (inverse) of the rotation
  99.      */
  100.     @Override
  101.     public QuaternionRotation inverse() {
  102.         return new QuaternionRotation(quat.conjugate());
  103.     }

  104.     /**
  105.      * Apply this rotation to the given vector.
  106.      *
  107.      * @param v vector to rotate
  108.      * @return the rotated vector
  109.      */
  110.     @Override
  111.     public Vector3D apply(final Vector3D v) {
  112.         final double qw = quat.getW();
  113.         final double qx = quat.getX();
  114.         final double qy = quat.getY();
  115.         final double qz = quat.getZ();

  116.         final double x = v.getX();
  117.         final double y = v.getY();
  118.         final double z = v.getZ();

  119.         // calculate the Hamilton product of the quaternion and vector
  120.         final double iw = -(qx * x) - (qy * y) - (qz * z);
  121.         final double ix = (qw * x) + (qy * z) - (qz * y);
  122.         final double iy = (qw * y) + (qz * x) - (qx * z);
  123.         final double iz = (qw * z) + (qx * y) - (qy * x);

  124.         // calculate the Hamilton product of the intermediate vector and
  125.         // the inverse quaternion

  126.         return Vector3D.of(
  127.                     (iw * -qx) + (ix * qw) + (iy * -qz) - (iz * -qy),
  128.                     (iw * -qy) - (ix * -qz) + (iy * qw) + (iz * -qx),
  129.                     (iw * -qz) + (ix * -qy) - (iy * -qx) + (iz * qw)
  130.                 );
  131.     }

  132.     /** {@inheritDoc}
  133.      *
  134.      * <p>This method simply calls {@code apply(vec)} since rotations treat
  135.      * points and vectors similarly.</p>
  136.      */
  137.     @Override
  138.     public Vector3D applyVector(final Vector3D vec) {
  139.         return apply(vec);
  140.     }

  141.     /** {@inheritDoc}
  142.      *
  143.      * <p>This method simply returns true since rotations always preserve the orientation
  144.      * of the space.</p>
  145.      */
  146.     @Override
  147.     public boolean preservesOrientation() {
  148.         return true;
  149.     }

  150.     /** Return an {@link AffineTransformMatrix3D} representing the same rotation as this
  151.      * instance.
  152.      * @return a transform matrix representing the same rotation as this instance
  153.      */
  154.     public AffineTransformMatrix3D toMatrix() {

  155.         final double qw = quat.getW();
  156.         final double qx = quat.getX();
  157.         final double qy = quat.getY();
  158.         final double qz = quat.getZ();

  159.         // pre-calculate products that we'll need
  160.         final double xx = qx * qx;
  161.         final double xy = qx * qy;
  162.         final double xz = qx * qz;
  163.         final double xw = qx * qw;

  164.         final double yy = qy * qy;
  165.         final double yz = qy * qz;
  166.         final double yw = qy * qw;

  167.         final double zz = qz * qz;
  168.         final double zw = qz * qw;

  169.         final double m00 = 1.0 - (2.0 * (yy + zz));
  170.         final double m01 = 2.0 * (xy - zw);
  171.         final double m02 = 2.0 * (xz + yw);
  172.         final double m03 = 0.0;

  173.         final double m10 = 2.0 * (xy + zw);
  174.         final double m11 = 1.0 - (2.0 * (xx + zz));
  175.         final double m12 = 2.0 * (yz - xw);
  176.         final double m13 = 0.0;

  177.         final double m20 = 2.0 * (xz - yw);
  178.         final double m21 = 2.0 * (yz + xw);
  179.         final double m22 = 1.0 - (2.0 * (xx + yy));
  180.         final double m23 = 0.0;

  181.         return AffineTransformMatrix3D.of(
  182.                     m00, m01, m02, m03,
  183.                     m10, m11, m12, m13,
  184.                     m20, m21, m22, m23
  185.                 );
  186.     }

  187.     /**
  188.      * Multiply this instance by the given argument, returning the result as
  189.      * a new instance. This is equivalent to the expression {@code t * q} where
  190.      * {@code q} is the argument and {@code t} is this instance.
  191.      *
  192.      * <p>
  193.      * Multiplication of quaternions behaves similarly to transformation
  194.      * matrices in regard to the order that operations are performed.
  195.      * For example, if <code>q<sub>1</sub></code> and <code>q<sub>2</sub></code> are unit
  196.      * quaternions, then the quaternion <code>q<sub>r</sub> = q<sub>1</sub>*q<sub>2</sub></code>
  197.      * will give the effect of applying the rotation in <code>q<sub>2</sub></code> followed
  198.      * by the rotation in <code>q<sub>1</sub></code>. In other words, the rightmost element
  199.      * in the multiplication is applied first.
  200.      * </p>
  201.      *
  202.      * @param q quaternion to multiply with the current instance
  203.      * @return the result of multiplying this quaternion by the argument
  204.      */
  205.     public QuaternionRotation multiply(final QuaternionRotation q) {
  206.         final Quaternion product = quat.multiply(q.quat);
  207.         return new QuaternionRotation(product);
  208.     }

  209.     /** Multiply the argument by this instance, returning the result as
  210.      * a new instance. This is equivalent to the expression {@code q * t} where
  211.      * {@code q} is the argument and {@code t} is this instance.
  212.      *
  213.      * <p>
  214.      * Multiplication of quaternions behaves similarly to transformation
  215.      * matrices in regard to the order that operations are performed.
  216.      * For example, if <code>q<sub>1</sub></code> and <code>q<sub>2</sub></code> are unit
  217.      * quaternions, then the quaternion <code>q<sub>r</sub> = q<sub>1</sub>*q<sub>2</sub></code>
  218.      * will give the effect of applying the rotation in <code>q<sub>2</sub></code> followed
  219.      * by the rotation in <code>q<sub>1</sub></code>. In other words, the rightmost element
  220.      * in the multiplication is applied first.
  221.      * </p>
  222.      *
  223.      * @param q quaternion to multiply by the current instance
  224.      * @return the result of multiplying the argument by the current instance
  225.      */
  226.     public QuaternionRotation premultiply(final QuaternionRotation q) {
  227.         return q.multiply(this);
  228.     }

  229.     /**
  230.      * Creates a function that performs a
  231.      * <a href="https://en.wikipedia.org/wiki/Slerp">spherical
  232.      * linear interpolation</a> between this instance and the argument.
  233.      * <p>
  234.      * The argument to the function returned by this method is the
  235.      * interpolation parameter {@code t}.
  236.      * If {@code t = 0}, the rotation is equal to this instance.
  237.      * If {@code t = 1}, the rotation is equal to the {@code end} instance.
  238.      * All other values are interpolated (or extrapolated if {@code t} is
  239.      * outside of the {@code [0, 1]} range).
  240.      *
  241.      * @param end end value of the interpolation
  242.      * @return a function that interpolates between this instance and the
  243.      * argument.
  244.      *
  245.      * @see org.apache.commons.numbers.quaternion.Slerp
  246.      */
  247.     public DoubleFunction<QuaternionRotation> slerp(final QuaternionRotation end) {
  248.         final Slerp s = new Slerp(getQuaternion(), end.getQuaternion());
  249.         return t -> QuaternionRotation.of(s.apply(t));
  250.     }

  251.     /** Get a sequence of axis-angle rotations that produce an overall rotation equivalent to this instance.
  252.      *
  253.      * <p>
  254.      * In most cases, the returned rotation sequence will be unique. However, at points of singularity
  255.      * (second angle equal to {@code 0} or {@code -pi} for Euler angles and {@code +pi/2} or {@code -pi/2}
  256.      * for Tait-Bryan angles), there are an infinite number of possible sequences that produce the same result.
  257.      * In these cases, the result is returned that leaves the last rotation equal to 0 (in the case of a relative
  258.      * reference frame) or the first rotation equal to 0 (in the case of an absolute reference frame).
  259.      * </p>
  260.      *
  261.      * @param frame the reference frame used to interpret the positions of the rotation axes
  262.      * @param axes the sequence of rotation axes
  263.      * @return a sequence of axis-angle rotations equivalent to this rotation
  264.      */
  265.     public AxisAngleSequence toAxisAngleSequence(final AxisReferenceFrame frame, final AxisSequence axes) {
  266.         if (frame == null) {
  267.             throw new IllegalArgumentException("Axis reference frame cannot be null");
  268.         }
  269.         if (axes == null) {
  270.             throw new IllegalArgumentException("Axis sequence cannot be null");
  271.         }

  272.         final double[] angles = getAngles(frame, axes);

  273.         return new AxisAngleSequence(frame, axes, angles[0], angles[1], angles[2]);
  274.     }

  275.     /** Get a sequence of axis-angle rotations that produce an overall rotation equivalent to this instance.
  276.      * Each rotation axis is interpreted relative to the rotated coordinate frame (ie, intrinsic rotation).
  277.      * @param axes the sequence of rotation axes
  278.      * @return a sequence of relative axis-angle rotations equivalent to this rotation
  279.      * @see #toAxisAngleSequence(AxisReferenceFrame, AxisSequence)
  280.      */
  281.     public AxisAngleSequence toRelativeAxisAngleSequence(final AxisSequence axes) {
  282.         return toAxisAngleSequence(AxisReferenceFrame.RELATIVE, axes);
  283.     }

  284.     /** Get a sequence of axis-angle rotations that produce an overall rotation equivalent to this instance.
  285.      * Each rotation axis is interpreted as part of an absolute, unmoving coordinate frame (ie, extrinsic rotation).
  286.      * @param axes the sequence of rotation axes
  287.      * @return a sequence of absolute axis-angle rotations equivalent to this rotation
  288.      * @see #toAxisAngleSequence(AxisReferenceFrame, AxisSequence)
  289.      */
  290.     public AxisAngleSequence toAbsoluteAxisAngleSequence(final AxisSequence axes) {
  291.         return toAxisAngleSequence(AxisReferenceFrame.ABSOLUTE, axes);
  292.     }

  293.     /** {@inheritDoc} */
  294.     @Override
  295.     public int hashCode() {
  296.         return quat.hashCode();
  297.     }

  298.     /** {@inheritDoc} */
  299.     @Override
  300.     public boolean equals(final Object obj) {
  301.         if (this == obj) {
  302.             return true;
  303.         }
  304.         if (!(obj instanceof QuaternionRotation)) {
  305.             return false;
  306.         }

  307.         final QuaternionRotation other = (QuaternionRotation) obj;
  308.         return Objects.equals(this.quat, other.quat);
  309.     }

  310.     /** {@inheritDoc} */
  311.     @Override
  312.     public String toString() {
  313.         return quat.toString();
  314.     }

  315.     /** Get a sequence of angles around the given axes that produce a rotation equivalent
  316.      * to this instance.
  317.      * @param frame the reference frame used to define the positions of the axes
  318.      * @param axes the axis sequence
  319.      * @return a sequence of angles around the given axes that produce a rotation equivalent
  320.      *      to this instance
  321.      */
  322.     private double[] getAngles(final AxisReferenceFrame frame, final AxisSequence axes) {

  323.         final AxisSequenceType sequenceType = axes.getType();

  324.         final Vector3D axis1 = axes.getAxis1();
  325.         final Vector3D axis2 = axes.getAxis2();
  326.         final Vector3D axis3 = axes.getAxis3();

  327.         if (frame == AxisReferenceFrame.RELATIVE) {
  328.             if (sequenceType == AxisSequenceType.TAIT_BRYAN) {
  329.                 return getRelativeTaitBryanAngles(axis1, axis2, axis3);
  330.             } else if (sequenceType == AxisSequenceType.EULER) {
  331.                 return getRelativeEulerAngles(axis1, axis2);
  332.             }
  333.         } else if (frame == AxisReferenceFrame.ABSOLUTE) {
  334.             if (sequenceType == AxisSequenceType.TAIT_BRYAN) {
  335.                 return getAbsoluteTaitBryanAngles(axis1, axis2, axis3);
  336.             } else if (sequenceType == AxisSequenceType.EULER) {
  337.                 return getAbsoluteEulerAngles(axis1, axis2);
  338.             }
  339.         }

  340.         // all possibilities should have been covered above
  341.         throw new GeometryInternalError();
  342.     }

  343.     /** Get a sequence of angles around the given Tait-Bryan axes that produce a rotation equivalent
  344.      * to this instance. The axes are interpreted as being relative to the rotated coordinate frame.
  345.      * @param axis1 first Tait-Bryan axis
  346.      * @param axis2 second Tait-Bryan axis
  347.      * @param axis3 third Tait-Bryan axis
  348.      * @return a sequence of rotation angles around the relative input axes that produce a rotation equivalent
  349.      *      to this instance
  350.      */
  351.     private double[] getRelativeTaitBryanAngles(final Vector3D axis1, final Vector3D axis2, final Vector3D axis3) {

  352.         // We can use geometry to get the first and second angles pretty easily here by analyzing the positions
  353.         // of the transformed rotation axes. The third angle is trickier but we can get it by treating it as
  354.         // if it were the first rotation in the inverse (which it would be).

  355.         final Vector3D vec3 = apply(axis3);
  356.         final Vector3D invVec1 = inverse().apply(axis1);

  357.         final double angle2Sin = vec3.dot(axis2.cross(axis3));

  358.         if (angle2Sin < -AXIS_ANGLE_SINGULARITY_THRESHOLD ||
  359.                 angle2Sin > AXIS_ANGLE_SINGULARITY_THRESHOLD) {

  360.             final Vector3D vec2 = apply(axis2);

  361.             final double angle1TanY = vec2.dot(axis1.cross(axis2));
  362.             final double angle1TanX = vec2.dot(axis2);

  363.             final double angle2 = angle2Sin > AXIS_ANGLE_SINGULARITY_THRESHOLD ?
  364.                     Angle.PI_OVER_TWO :
  365.                     -Angle.PI_OVER_TWO;

  366.             return new double[] {
  367.                 Math.atan2(angle1TanY, angle1TanX),
  368.                 angle2,
  369.                 0.0
  370.             };
  371.         }

  372.         final Vector3D  crossAxis13 = axis1.cross(axis3);

  373.         final double angle1TanY = vec3.dot(crossAxis13);
  374.         final double angle1TanX = vec3.dot(axis3);

  375.         final double angle3TanY = invVec1.dot(crossAxis13);
  376.         final double angle3TanX = invVec1.dot(axis1);

  377.         return new double[] {
  378.             Math.atan2(angle1TanY, angle1TanX),
  379.             Math.asin(angle2Sin),
  380.             Math.atan2(angle3TanY, angle3TanX)
  381.         };
  382.     }

  383.     /** Get a sequence of angles around the given Tait-Bryan axes that produce a rotation equivalent
  384.      * to this instance. The axes are interpreted as being part of an absolute (unmoving) coordinate frame.
  385.      * @param axis1 first Tait-Bryan axis
  386.      * @param axis2 second Tait-Bryan axis
  387.      * @param axis3 third Tait-Bryan axis
  388.      * @return a sequence of rotation angles around the absolute input axes that produce a rotation equivalent
  389.      *      to this instance
  390.      */
  391.     private double[] getAbsoluteTaitBryanAngles(final Vector3D axis1, final Vector3D axis2, final Vector3D axis3) {
  392.         // A relative axis-angle rotation sequence is equivalent to an absolute one with the rotation
  393.         // sequence reversed, meaning we can reuse our relative logic here.
  394.         return reverseArray(getRelativeTaitBryanAngles(axis3, axis2, axis1));
  395.     }

  396.     /** Get a sequence of angles around the given Euler axes that produce a rotation equivalent
  397.      * to this instance. The axes are interpreted as being relative to the rotated coordinate frame. Only
  398.      * the first two axes are needed since, by definition, the first Euler angle axis is repeated as the
  399.      * third axis.
  400.      * @param axis1 first Euler axis
  401.      * @param axis2 second Euler axis
  402.      * @return a sequence of rotation angles around the relative input axes that produce a rotation equivalent
  403.      *      to this instance
  404.      */
  405.     private double[] getRelativeEulerAngles(final Vector3D axis1, final Vector3D axis2) {

  406.         // Use the same overall approach as with the Tait-Bryan angles: get the first two angles by looking
  407.         // at the transformed rotation axes and the third by using the inverse.

  408.         final Vector3D crossAxis = axis1.cross(axis2);

  409.         final Vector3D vec1 = apply(axis1);
  410.         final Vector3D invVec1 = inverse().apply(axis1);

  411.         final double angle2Cos = vec1.dot(axis1);

  412.         if (angle2Cos < -AXIS_ANGLE_SINGULARITY_THRESHOLD ||
  413.                 angle2Cos > AXIS_ANGLE_SINGULARITY_THRESHOLD) {

  414.             final Vector3D vec2 = apply(axis2);

  415.             final double angle1TanY = vec2.dot(crossAxis);
  416.             final double angle1TanX = vec2.dot(axis2);

  417.             final double angle2 = angle2Cos > AXIS_ANGLE_SINGULARITY_THRESHOLD ? 0.0 : Math.PI;

  418.             return new double[] {
  419.                 Math.atan2(angle1TanY, angle1TanX),
  420.                 angle2,
  421.                 0.0
  422.             };
  423.         }

  424.         final double angle1TanY = vec1.dot(axis2);
  425.         final double angle1TanX = -vec1.dot(crossAxis);

  426.         final double angle3TanY = invVec1.dot(axis2);
  427.         final double angle3TanX = invVec1.dot(crossAxis);

  428.         return new double[] {
  429.             Math.atan2(angle1TanY, angle1TanX),
  430.             Math.acos(angle2Cos),
  431.             Math.atan2(angle3TanY, angle3TanX)
  432.         };
  433.     }

  434.     /** Get a sequence of angles around the given Euler axes that produce a rotation equivalent
  435.      * to this instance. The axes are interpreted as being part of an absolute (unmoving) coordinate frame.
  436.      * Only the first two axes are needed since, by definition, the first Euler angle axis is repeated as
  437.      * the third axis.
  438.      * @param axis1 first Euler axis
  439.      * @param axis2 second Euler axis
  440.      * @return a sequence of rotation angles around the absolute input axes that produce a rotation equivalent
  441.      *      to this instance
  442.      */
  443.     private double[] getAbsoluteEulerAngles(final Vector3D axis1, final Vector3D axis2) {
  444.         // A relative axis-angle rotation sequence is equivalent to an absolute one with the rotation
  445.         // sequence reversed, meaning we can reuse our relative logic here.
  446.         return reverseArray(getRelativeEulerAngles(axis1, axis2));
  447.     }

  448.     /** Create a new instance from the given quaternion. The quaternion is normalized and
  449.      * converted to positive polar form (ie, with w &gt;= 0).
  450.      *
  451.      * @param quat the quaternion to use for the rotation
  452.      * @return a new instance built from the given quaternion.
  453.      * @throws IllegalStateException if the the norm of the given components is zero,
  454.      *                              NaN, or infinite
  455.      * @see Quaternion#normalize()
  456.      * @see Quaternion#positivePolarForm()
  457.      */
  458.     public static QuaternionRotation of(final Quaternion quat) {
  459.         return new QuaternionRotation(quat);
  460.     }

  461.     /**
  462.      * Create a new instance from the given quaternion values. The inputs are
  463.      * normalized and converted to positive polar form (ie, with w &gt;= 0).
  464.      *
  465.      * @param w quaternion scalar component
  466.      * @param x first quaternion vectorial component
  467.      * @param y second quaternion vectorial component
  468.      * @param z third quaternion vectorial component
  469.      * @return a new instance containing the normalized quaterion components
  470.      * @throws IllegalStateException if the the norm of the given components is zero,
  471.      *                              NaN, or infinite
  472.      * @see Quaternion#normalize()
  473.      * @see Quaternion#positivePolarForm()
  474.      */
  475.     public static QuaternionRotation of(final double w,
  476.                                         final double x,
  477.                                         final double y,
  478.                                         final double z) {
  479.         return of(Quaternion.of(w, x, y, z));
  480.     }

  481.     /** Return an instance representing a rotation of zero.
  482.      * @return instance representing a rotation of zero.
  483.      */
  484.     public static QuaternionRotation identity() {
  485.         return IDENTITY_INSTANCE;
  486.     }

  487.     /** Create a new instance representing a rotation of {@code angle} radians around
  488.      * {@code axis}.
  489.      *
  490.      * <p>
  491.      * Rotation direction follows the right-hand rule, meaning that if one
  492.      * places their right hand such that the thumb points in the direction of the vector,
  493.      * the curl of the fingers indicates the direction of rotation.
  494.      * </p>
  495.      *
  496.      * <p>
  497.      * Note that the returned quaternion will represent the defined rotation but the values
  498.      * returned by {@link #getAxis()} and {@link #getAngle()} may not match the ones given here.
  499.      * This is because the axis and angle are normalized such that the axis has unit length,
  500.      * and the angle lies in the range {@code [0, pi]}. Depending on the inputs, the axis may
  501.      * need to be inverted in order for the angle to lie in this range.
  502.      * </p>
  503.      *
  504.      * @param axis the axis of rotation
  505.      * @param angle angle of rotation in radians
  506.      * @return a new instance representing the defined rotation
  507.      *
  508.      * @throws IllegalArgumentException if the given axis cannot be normalized or the angle is NaN or infinite
  509.      */
  510.     public static QuaternionRotation fromAxisAngle(final Vector3D axis, final double angle) {
  511.         // reference formula:
  512.         // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
  513.         final Vector3D normAxis = axis.normalize();

  514.         if (!Double.isFinite(angle)) {
  515.             throw new IllegalArgumentException("Invalid angle: " + angle);
  516.         }

  517.         final double halfAngle = 0.5 * angle;
  518.         final double sinHalfAngle = Math.sin(halfAngle);

  519.         final double w = Math.cos(halfAngle);
  520.         final double x = sinHalfAngle * normAxis.getX();
  521.         final double y = sinHalfAngle * normAxis.getY();
  522.         final double z = sinHalfAngle * normAxis.getZ();

  523.         return of(w, x, y, z);
  524.     }

  525.     /** Return an instance that rotates the first vector to the second.
  526.      *
  527.      * <p>Except for a possible scale factor, if the returned instance is
  528.      * applied to vector {@code u}, it will produce the vector {@code v}. There are an
  529.      * infinite number of such rotations; this method chooses the one with the smallest
  530.      * associated angle, meaning the one whose axis is orthogonal to the {@code (u, v)}
  531.      * plane. If {@code u} and {@code v} are collinear, an arbitrary rotation axis is
  532.      * chosen.</p>
  533.      *
  534.      * @param u origin vector
  535.      * @param v target vector
  536.      * @return a new instance that rotates {@code u} to point in the direction of {@code v}
  537.      * @throws IllegalArgumentException if either vector has a norm of zero, NaN, or infinity
  538.      */
  539.     public static QuaternionRotation createVectorRotation(final Vector3D u, final Vector3D v) {

  540.         final double normProduct  = Vectors.checkedNorm(u) * Vectors.checkedNorm(v);
  541.         final double dot = u.dot(v);

  542.         if (dot < ANTIPARALLEL_DOT_THRESHOLD * normProduct) {
  543.             // Special case where u1 = -u2:
  544.             // create a pi angle rotation around
  545.             // an arbitrary unit vector orthogonal to u1
  546.             final Vector3D axis = u.orthogonal();

  547.             return of(0,
  548.                       axis.getX(),
  549.                       axis.getY(),
  550.                       axis.getZ());
  551.         }

  552.         // General case:
  553.         // (u1, u2) defines a plane so rotate around the normal of the plane

  554.         // w must equal cos(theta/2); we can calculate this directly using values
  555.         // we already have with the identity cos(theta/2) = sqrt((1 + cos(theta)) / 2)
  556.         // and the fact that dot = norm(u1) * norm(u2) * cos(theta).
  557.         final double w = Math.sqrt(0.5 * (1.0 + (dot / normProduct)));

  558.         // The cross product u1 x u2 must be normalized and then multiplied by
  559.         // sin(theta/2) in order to set the vectorial part of the quaternion. To
  560.         // accomplish this, we'll use the following:
  561.         //
  562.         // 1) norm(a x b) = norm(a) * norm(b) * sin(theta)
  563.         // 2) sin(theta/2) = sqrt((1 - cos(theta)) / 2)
  564.         //
  565.         // Our full, combined normalization and sine half angle term factor then becomes:
  566.         //
  567.         // sqrt((1 - cos(theta)) / 2) / (norm(u1) * norm(u2) * sin(theta))
  568.         //
  569.         // This can be simplified to the expression below.
  570.         final double vectorialScaleFactor = 1.0 / (2.0 * w * normProduct);
  571.         final Vector3D axis = u.cross(v);

  572.         return of(w,
  573.                   vectorialScaleFactor * axis.getX(),
  574.                   vectorialScaleFactor * axis.getY(),
  575.                   vectorialScaleFactor * axis.getZ());
  576.     }

  577.     /** Return an instance that rotates the basis defined by the first two vectors into the basis
  578.      * defined by the second two.
  579.      *
  580.      * <p>
  581.      * The given basis vectors do not have to be directly orthogonal. A right-handed orthonormal
  582.      * basis is created from each pair by normalizing the first vector, making the second vector
  583.      * orthogonal to the first, and then taking the cross product. A rotation is then calculated
  584.      * that rotates the first to the second.
  585.      * </p>
  586.      *
  587.      * @param u1 first vector of the source basis
  588.      * @param u2 second vector of the source basis
  589.      * @param v1 first vector of the target basis
  590.      * @param v2 second vector of the target basis
  591.      * @return an instance that rotates the source basis to the target basis
  592.      * @throws IllegalArgumentException if any of the input vectors cannot be normalized
  593.      *      or the vectors defining either basis are collinear
  594.      */
  595.     public static QuaternionRotation createBasisRotation(final Vector3D u1, final Vector3D u2,
  596.             final Vector3D v1, final Vector3D v2) {

  597.         // calculate orthonormalized bases
  598.         final Vector3D a = u1.normalize();
  599.         final Vector3D b = a.orthogonal(u2);
  600.         final Vector3D c = a.cross(b);

  601.         final Vector3D d = v1.normalize();
  602.         final Vector3D e = d.orthogonal(v2);
  603.         final Vector3D f = d.cross(e);

  604.         // create an orthogonal rotation matrix representing the change of basis; this matrix will
  605.         // be the multiplication of the matrix composed of the column vectors d, e, f and the
  606.         // inverse of the matrix composed of the column vectors a, b, c (which is simply the transpose since
  607.         // it's orthogonal).
  608.         final double m00 = Vectors.linearCombination(d.getX(), a.getX(), e.getX(), b.getX(), f.getX(), c.getX());
  609.         final double m01 = Vectors.linearCombination(d.getX(), a.getY(), e.getX(), b.getY(), f.getX(), c.getY());
  610.         final double m02 = Vectors.linearCombination(d.getX(), a.getZ(), e.getX(), b.getZ(), f.getX(), c.getZ());

  611.         final double m10 = Vectors.linearCombination(d.getY(), a.getX(), e.getY(), b.getX(), f.getY(), c.getX());
  612.         final double m11 = Vectors.linearCombination(d.getY(), a.getY(), e.getY(), b.getY(), f.getY(), c.getY());
  613.         final double m12 = Vectors.linearCombination(d.getY(), a.getZ(), e.getY(), b.getZ(), f.getY(), c.getZ());

  614.         final double m20 = Vectors.linearCombination(d.getZ(), a.getX(), e.getZ(), b.getX(), f.getZ(), c.getX());
  615.         final double m21 = Vectors.linearCombination(d.getZ(), a.getY(), e.getZ(), b.getY(), f.getZ(), c.getY());
  616.         final double m22 = Vectors.linearCombination(d.getZ(), a.getZ(), e.getZ(), b.getZ(), f.getZ(), c.getZ());


  617.         return orthogonalRotationMatrixToQuaternion(
  618.                     m00, m01, m02,
  619.                     m10, m11, m12,
  620.                     m20, m21, m22
  621.                 );
  622.     }

  623.     /** Create a new instance equivalent to the given sequence of axis-angle rotations.
  624.      * @param sequence the axis-angle rotation sequence to convert to a quaternion rotation
  625.      * @return instance representing a rotation equivalent to the given axis-angle sequence
  626.      */
  627.     public static QuaternionRotation fromAxisAngleSequence(final AxisAngleSequence sequence) {
  628.         final AxisSequence axes = sequence.getAxisSequence();

  629.         final QuaternionRotation q1 = fromAxisAngle(axes.getAxis1(), sequence.getAngle1());
  630.         final QuaternionRotation q2 = fromAxisAngle(axes.getAxis2(), sequence.getAngle2());
  631.         final QuaternionRotation q3 = fromAxisAngle(axes.getAxis3(), sequence.getAngle3());

  632.         if (sequence.getReferenceFrame() == AxisReferenceFrame.ABSOLUTE) {
  633.             return q3.multiply(q2).multiply(q1);
  634.         }

  635.         return q1.multiply(q2).multiply(q3);
  636.     }

  637.     /** Create an instance from an orthogonal rotation matrix.
  638.      *
  639.      * @param m00 matrix entry <code>m<sub>0,0</sub></code>
  640.      * @param m01 matrix entry <code>m<sub>0,1</sub></code>
  641.      * @param m02 matrix entry <code>m<sub>0,2</sub></code>
  642.      * @param m10 matrix entry <code>m<sub>1,0</sub></code>
  643.      * @param m11 matrix entry <code>m<sub>1,1</sub></code>
  644.      * @param m12 matrix entry <code>m<sub>1,2</sub></code>
  645.      * @param m20 matrix entry <code>m<sub>2,0</sub></code>
  646.      * @param m21 matrix entry <code>m<sub>2,1</sub></code>
  647.      * @param m22 matrix entry <code>m<sub>2,2</sub></code>
  648.      * @return an instance representing the same 3D rotation as the given matrix
  649.      */
  650.     private static QuaternionRotation orthogonalRotationMatrixToQuaternion(
  651.             final double m00, final double m01, final double m02,
  652.             final double m10, final double m11, final double m12,
  653.             final double m20, final double m21, final double m22) {

  654.         // reference formula:
  655.         // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/

  656.         // The overall approach here is to take the equations for converting a quaternion to
  657.         // a matrix along with the fact that 1 = x^2 + y^2 + z^2 + w^2 for a normalized quaternion
  658.         // and solve for the various terms. This can theoretically be done using just the diagonal
  659.         // terms from the matrix. However, there are a few issues with this:
  660.         // 1) The term that we end up taking the square root of may be negative.
  661.         // 2) It's ambiguous as to whether we should use a plus or minus for the value of the
  662.         //    square root.
  663.         // We'll address these concerns by only calculating a single term from one of the diagonal
  664.         // elements and then calculate the rest from the non-diagonals, which do not involve
  665.         // a square root. This solves the first issue since we can make sure to choose a diagonal
  666.         // element that will not cause us to take a square root of a negative number. The second
  667.         // issue is solved since only the relative signs between the quaternion terms are important
  668.         // (q and -q represent the same 3D rotation). It therefore doesn't matter whether we choose
  669.         // a plus or minus for our initial square root solution.

  670.         final double trace = m00 + m11 + m22;

  671.         final double w;
  672.         final double x;
  673.         final double y;
  674.         final double z;

  675.         if (trace > 0) {
  676.             // let s = 4*w
  677.             final double s = 2.0 * Math.sqrt(1.0 + trace);
  678.             final double sinv = 1.0 / s;

  679.             x = (m21 - m12) * sinv;
  680.             y = (m02 - m20) * sinv;
  681.             z = (m10 - m01) * sinv;
  682.             w = 0.25 * s;
  683.         } else if ((m00 > m11) && (m00 > m22)) {
  684.             // let s = 4*x
  685.             final double s = 2.0 * Math.sqrt(1.0 + m00 - m11 - m22);
  686.             final double sinv = 1.0 / s;

  687.             x = 0.25 * s;
  688.             y = (m01 + m10) * sinv;
  689.             z = (m02 + m20) * sinv;
  690.             w = (m21 - m12) * sinv;
  691.         } else if (m11 > m22) {
  692.             // let s = 4*y
  693.             final double s = 2.0 * Math.sqrt(1.0 + m11 - m00 - m22);
  694.             final double sinv = 1.0 / s;

  695.             x = (m01 + m10) * sinv;
  696.             y = 0.25 * s;
  697.             z = (m21 + m12) * sinv;
  698.             w = (m02 - m20) * sinv;
  699.         } else {
  700.             // let s = 4*z
  701.             final double s = 2.0 * Math.sqrt(1.0 + m22 - m00 - m11);
  702.             final double sinv = 1.0 / s;

  703.             x = (m02 + m20) * sinv;
  704.             y = (m21 + m12) * sinv;
  705.             z = 0.25 * s;
  706.             w = (m10 - m01) * sinv;
  707.         }

  708.         return of(w, x, y, z);
  709.     }

  710.     /** Reverse the elements in {@code arr}. The array is returned.
  711.      * @param arr the array to reverse
  712.      * @return the input array with the elements reversed
  713.      */
  714.     private static double[] reverseArray(final double[] arr) {

  715.         final int len = arr.length;
  716.         double temp;

  717.         int i;
  718.         int j;

  719.         for (i = 0, j = len - 1; i < len / 2; ++i, --j) {
  720.             temp = arr[i];
  721.             arr[i] = arr[j];
  722.             arr[j] = temp;
  723.         }

  724.         return arr;
  725.     }
  726. }