FieldRotation.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.math3.geometry.euclidean.threed;

  18. import java.io.Serializable;

  19. import org.apache.commons.math3.RealFieldElement;
  20. import org.apache.commons.math3.Field;
  21. import org.apache.commons.math3.exception.MathArithmeticException;
  22. import org.apache.commons.math3.exception.MathIllegalArgumentException;
  23. import org.apache.commons.math3.exception.util.LocalizedFormats;
  24. import org.apache.commons.math3.util.FastMath;
  25. import org.apache.commons.math3.util.MathArrays;

  26. /**
  27.  * This class is a re-implementation of {@link Rotation} using {@link RealFieldElement}.
  28.  * <p>Instance of this class are guaranteed to be immutable.</p>
  29.  *
  30.  * @param <T> the type of the field elements
  31.  * @see FieldVector3D
  32.  * @see RotationOrder
  33.  * @since 3.2
  34.  */

  35. public class FieldRotation<T extends RealFieldElement<T>> implements Serializable {

  36.     /** Serializable version identifier */
  37.     private static final long serialVersionUID = 20130224l;

  38.     /** Scalar coordinate of the quaternion. */
  39.     private final T q0;

  40.     /** First coordinate of the vectorial part of the quaternion. */
  41.     private final T q1;

  42.     /** Second coordinate of the vectorial part of the quaternion. */
  43.     private final T q2;

  44.     /** Third coordinate of the vectorial part of the quaternion. */
  45.     private final T q3;

  46.     /** Build a rotation from the quaternion coordinates.
  47.      * <p>A rotation can be built from a <em>normalized</em> quaternion,
  48.      * i.e. a quaternion for which q<sub>0</sub><sup>2</sup> +
  49.      * q<sub>1</sub><sup>2</sup> + q<sub>2</sub><sup>2</sup> +
  50.      * q<sub>3</sub><sup>2</sup> = 1. If the quaternion is not normalized,
  51.      * the constructor can normalize it in a preprocessing step.</p>
  52.      * <p>Note that some conventions put the scalar part of the quaternion
  53.      * as the 4<sup>th</sup> component and the vector part as the first three
  54.      * components. This is <em>not</em> our convention. We put the scalar part
  55.      * as the first component.</p>
  56.      * @param q0 scalar part of the quaternion
  57.      * @param q1 first coordinate of the vectorial part of the quaternion
  58.      * @param q2 second coordinate of the vectorial part of the quaternion
  59.      * @param q3 third coordinate of the vectorial part of the quaternion
  60.      * @param needsNormalization if true, the coordinates are considered
  61.      * not to be normalized, a normalization preprocessing step is performed
  62.      * before using them
  63.      */
  64.     public FieldRotation(final T q0, final T q1, final T q2, final T q3, final boolean needsNormalization) {

  65.         if (needsNormalization) {
  66.             // normalization preprocessing
  67.             final T inv =
  68.                     q0.multiply(q0).add(q1.multiply(q1)).add(q2.multiply(q2)).add(q3.multiply(q3)).sqrt().reciprocal();
  69.             this.q0 = inv.multiply(q0);
  70.             this.q1 = inv.multiply(q1);
  71.             this.q2 = inv.multiply(q2);
  72.             this.q3 = inv.multiply(q3);
  73.         } else {
  74.             this.q0 = q0;
  75.             this.q1 = q1;
  76.             this.q2 = q2;
  77.             this.q3 = q3;
  78.         }

  79.     }

  80.     /** Build a rotation from an axis and an angle.
  81.      * <p>We use the convention that angles are oriented according to
  82.      * the effect of the rotation on vectors around the axis. That means
  83.      * that if (i, j, k) is a direct frame and if we first provide +k as
  84.      * the axis and &pi;/2 as the angle to this constructor, and then
  85.      * {@link #applyTo(FieldVector3D) apply} the instance to +i, we will get
  86.      * +j.</p>
  87.      * <p>Another way to represent our convention is to say that a rotation
  88.      * of angle &theta; about the unit vector (x, y, z) is the same as the
  89.      * rotation build from quaternion components { cos(-&theta;/2),
  90.      * x * sin(-&theta;/2), y * sin(-&theta;/2), z * sin(-&theta;/2) }.
  91.      * Note the minus sign on the angle!</p>
  92.      * <p>On the one hand this convention is consistent with a vectorial
  93.      * perspective (moving vectors in fixed frames), on the other hand it
  94.      * is different from conventions with a frame perspective (fixed vectors
  95.      * viewed from different frames) like the ones used for example in spacecraft
  96.      * attitude community or in the graphics community.</p>
  97.      * @param axis axis around which to rotate
  98.      * @param angle rotation angle.
  99.      * @exception MathIllegalArgumentException if the axis norm is zero
  100.      */
  101.     public FieldRotation(final FieldVector3D<T> axis, final T angle)
  102.         throws MathIllegalArgumentException {

  103.         final T norm = axis.getNorm();
  104.         if (norm.getReal() == 0) {
  105.             throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_AXIS);
  106.         }

  107.         final T halfAngle = angle.multiply(-0.5);
  108.         final T coeff = halfAngle.sin().divide(norm);

  109.         q0 = halfAngle.cos();
  110.         q1 = coeff.multiply(axis.getX());
  111.         q2 = coeff.multiply(axis.getY());
  112.         q3 = coeff.multiply(axis.getZ());

  113.     }

  114.     /** Build a rotation from a 3X3 matrix.

  115.      * <p>Rotation matrices are orthogonal matrices, i.e. unit matrices
  116.      * (which are matrices for which m.m<sup>T</sup> = I) with real
  117.      * coefficients. The module of the determinant of unit matrices is
  118.      * 1, among the orthogonal 3X3 matrices, only the ones having a
  119.      * positive determinant (+1) are rotation matrices.</p>

  120.      * <p>When a rotation is defined by a matrix with truncated values
  121.      * (typically when it is extracted from a technical sheet where only
  122.      * four to five significant digits are available), the matrix is not
  123.      * orthogonal anymore. This constructor handles this case
  124.      * transparently by using a copy of the given matrix and applying a
  125.      * correction to the copy in order to perfect its orthogonality. If
  126.      * the Frobenius norm of the correction needed is above the given
  127.      * threshold, then the matrix is considered to be too far from a
  128.      * true rotation matrix and an exception is thrown.<p>

  129.      * @param m rotation matrix
  130.      * @param threshold convergence threshold for the iterative
  131.      * orthogonality correction (convergence is reached when the
  132.      * difference between two steps of the Frobenius norm of the
  133.      * correction is below this threshold)

  134.      * @exception NotARotationMatrixException if the matrix is not a 3X3
  135.      * matrix, or if it cannot be transformed into an orthogonal matrix
  136.      * with the given threshold, or if the determinant of the resulting
  137.      * orthogonal matrix is negative

  138.      */
  139.     public FieldRotation(final T[][] m, final double threshold)
  140.         throws NotARotationMatrixException {

  141.         // dimension check
  142.         if ((m.length != 3) || (m[0].length != 3) ||
  143.                 (m[1].length != 3) || (m[2].length != 3)) {
  144.             throw new NotARotationMatrixException(
  145.                                                   LocalizedFormats.ROTATION_MATRIX_DIMENSIONS,
  146.                                                   m.length, m[0].length);
  147.         }

  148.         // compute a "close" orthogonal matrix
  149.         final T[][] ort = orthogonalizeMatrix(m, threshold);

  150.         // check the sign of the determinant
  151.         final T d0 = ort[1][1].multiply(ort[2][2]).subtract(ort[2][1].multiply(ort[1][2]));
  152.         final T d1 = ort[0][1].multiply(ort[2][2]).subtract(ort[2][1].multiply(ort[0][2]));
  153.         final T d2 = ort[0][1].multiply(ort[1][2]).subtract(ort[1][1].multiply(ort[0][2]));
  154.         final T det =
  155.                 ort[0][0].multiply(d0).subtract(ort[1][0].multiply(d1)).add(ort[2][0].multiply(d2));
  156.         if (det.getReal() < 0.0) {
  157.             throw new NotARotationMatrixException(
  158.                                                   LocalizedFormats.CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT,
  159.                                                   det);
  160.         }

  161.         final T[] quat = mat2quat(ort);
  162.         q0 = quat[0];
  163.         q1 = quat[1];
  164.         q2 = quat[2];
  165.         q3 = quat[3];

  166.     }

  167.     /** Build the rotation that transforms a pair of vector into another pair.

  168.      * <p>Except for possible scale factors, if the instance were applied to
  169.      * the pair (u<sub>1</sub>, u<sub>2</sub>) it will produce the pair
  170.      * (v<sub>1</sub>, v<sub>2</sub>).</p>

  171.      * <p>If the angular separation between u<sub>1</sub> and u<sub>2</sub> is
  172.      * not the same as the angular separation between v<sub>1</sub> and
  173.      * v<sub>2</sub>, then a corrected v'<sub>2</sub> will be used rather than
  174.      * v<sub>2</sub>, the corrected vector will be in the (v<sub>1</sub>,
  175.      * v<sub>2</sub>) plane.</p>

  176.      * @param u1 first vector of the origin pair
  177.      * @param u2 second vector of the origin pair
  178.      * @param v1 desired image of u1 by the rotation
  179.      * @param v2 desired image of u2 by the rotation
  180.      * @exception MathArithmeticException if the norm of one of the vectors is zero,
  181.      * or if one of the pair is degenerated (i.e. the vectors of the pair are colinear)
  182.      */
  183.     public FieldRotation(FieldVector3D<T> u1, FieldVector3D<T> u2, FieldVector3D<T> v1, FieldVector3D<T> v2)
  184.         throws MathArithmeticException {

  185.         // build orthonormalized base from u1, u2
  186.         // this fails when vectors are null or colinear, which is forbidden to define a rotation
  187.         final FieldVector3D<T> u3 = FieldVector3D.crossProduct(u1, u2).normalize();
  188.         u2 = FieldVector3D.crossProduct(u3, u1).normalize();
  189.         u1 = u1.normalize();

  190.         // build an orthonormalized base from v1, v2
  191.         // this fails when vectors are null or colinear, which is forbidden to define a rotation
  192.         final FieldVector3D<T> v3 = FieldVector3D.crossProduct(v1, v2).normalize();
  193.         v2 = FieldVector3D.crossProduct(v3, v1).normalize();
  194.         v1 = v1.normalize();

  195.         // buid a matrix transforming the first base into the second one
  196.         final T[][] array = MathArrays.buildArray(u1.getX().getField(), 3, 3);
  197.         array[0][0] = u1.getX().multiply(v1.getX()).add(u2.getX().multiply(v2.getX())).add(u3.getX().multiply(v3.getX()));
  198.         array[0][1] = u1.getY().multiply(v1.getX()).add(u2.getY().multiply(v2.getX())).add(u3.getY().multiply(v3.getX()));
  199.         array[0][2] = u1.getZ().multiply(v1.getX()).add(u2.getZ().multiply(v2.getX())).add(u3.getZ().multiply(v3.getX()));
  200.         array[1][0] = u1.getX().multiply(v1.getY()).add(u2.getX().multiply(v2.getY())).add(u3.getX().multiply(v3.getY()));
  201.         array[1][1] = u1.getY().multiply(v1.getY()).add(u2.getY().multiply(v2.getY())).add(u3.getY().multiply(v3.getY()));
  202.         array[1][2] = u1.getZ().multiply(v1.getY()).add(u2.getZ().multiply(v2.getY())).add(u3.getZ().multiply(v3.getY()));
  203.         array[2][0] = u1.getX().multiply(v1.getZ()).add(u2.getX().multiply(v2.getZ())).add(u3.getX().multiply(v3.getZ()));
  204.         array[2][1] = u1.getY().multiply(v1.getZ()).add(u2.getY().multiply(v2.getZ())).add(u3.getY().multiply(v3.getZ()));
  205.         array[2][2] = u1.getZ().multiply(v1.getZ()).add(u2.getZ().multiply(v2.getZ())).add(u3.getZ().multiply(v3.getZ()));

  206.         T[] quat = mat2quat(array);
  207.         q0 = quat[0];
  208.         q1 = quat[1];
  209.         q2 = quat[2];
  210.         q3 = quat[3];

  211.     }

  212.     /** Build one of the rotations that transform one vector into another one.

  213.      * <p>Except for a possible scale factor, if the instance were
  214.      * applied to the vector u it will produce the vector v. There is an
  215.      * infinite number of such rotations, this constructor choose the
  216.      * one with the smallest associated angle (i.e. the one whose axis
  217.      * is orthogonal to the (u, v) plane). If u and v are colinear, an
  218.      * arbitrary rotation axis is chosen.</p>

  219.      * @param u origin vector
  220.      * @param v desired image of u by the rotation
  221.      * @exception MathArithmeticException if the norm of one of the vectors is zero
  222.      */
  223.     public FieldRotation(final FieldVector3D<T> u, final FieldVector3D<T> v) throws MathArithmeticException {

  224.         final T normProduct = u.getNorm().multiply(v.getNorm());
  225.         if (normProduct.getReal() == 0) {
  226.             throw new MathArithmeticException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR);
  227.         }

  228.         final T dot = FieldVector3D.dotProduct(u, v);

  229.         if (dot.getReal() < ((2.0e-15 - 1.0) * normProduct.getReal())) {
  230.             // special case u = -v: we select a PI angle rotation around
  231.             // an arbitrary vector orthogonal to u
  232.             final FieldVector3D<T> w = u.orthogonal();
  233.             q0 = normProduct.getField().getZero();
  234.             q1 = w.getX().negate();
  235.             q2 = w.getY().negate();
  236.             q3 = w.getZ().negate();
  237.         } else {
  238.             // general case: (u, v) defines a plane, we select
  239.             // the shortest possible rotation: axis orthogonal to this plane
  240.             q0 = dot.divide(normProduct).add(1.0).multiply(0.5).sqrt();
  241.             final T coeff = q0.multiply(normProduct).multiply(2.0).reciprocal();
  242.             final FieldVector3D<T> q = FieldVector3D.crossProduct(v, u);
  243.             q1 = coeff.multiply(q.getX());
  244.             q2 = coeff.multiply(q.getY());
  245.             q3 = coeff.multiply(q.getZ());
  246.         }

  247.     }

  248.     /** Build a rotation from three Cardan or Euler elementary rotations.

  249.      * <p>Cardan rotations are three successive rotations around the
  250.      * canonical axes X, Y and Z, each axis being used once. There are
  251.      * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler
  252.      * rotations are three successive rotations around the canonical
  253.      * axes X, Y and Z, the first and last rotations being around the
  254.      * same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
  255.      * YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
  256.      * <p>Beware that many people routinely use the term Euler angles even
  257.      * for what really are Cardan angles (this confusion is especially
  258.      * widespread in the aerospace business where Roll, Pitch and Yaw angles
  259.      * are often wrongly tagged as Euler angles).</p>

  260.      * @param order order of rotations to use
  261.      * @param alpha1 angle of the first elementary rotation
  262.      * @param alpha2 angle of the second elementary rotation
  263.      * @param alpha3 angle of the third elementary rotation
  264.      */
  265.     public FieldRotation(final RotationOrder order, final T alpha1, final T alpha2, final T alpha3) {
  266.         final T one = alpha1.getField().getOne();
  267.         final FieldRotation<T> r1 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA1()), alpha1);
  268.         final FieldRotation<T> r2 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA2()), alpha2);
  269.         final FieldRotation<T> r3 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA3()), alpha3);
  270.         final FieldRotation<T> composed = r1.applyTo(r2.applyTo(r3));
  271.         q0 = composed.q0;
  272.         q1 = composed.q1;
  273.         q2 = composed.q2;
  274.         q3 = composed.q3;
  275.     }

  276.     /** Convert an orthogonal rotation matrix to a quaternion.
  277.      * @param ort orthogonal rotation matrix
  278.      * @return quaternion corresponding to the matrix
  279.      */
  280.     private T[] mat2quat(final T[][] ort) {

  281.         final T[] quat = MathArrays.buildArray(ort[0][0].getField(), 4);

  282.         // There are different ways to compute the quaternions elements
  283.         // from the matrix. They all involve computing one element from
  284.         // the diagonal of the matrix, and computing the three other ones
  285.         // using a formula involving a division by the first element,
  286.         // which unfortunately can be zero. Since the norm of the
  287.         // quaternion is 1, we know at least one element has an absolute
  288.         // value greater or equal to 0.5, so it is always possible to
  289.         // select the right formula and avoid division by zero and even
  290.         // numerical inaccuracy. Checking the elements in turn and using
  291.         // the first one greater than 0.45 is safe (this leads to a simple
  292.         // test since qi = 0.45 implies 4 qi^2 - 1 = -0.19)
  293.         T s = ort[0][0].add(ort[1][1]).add(ort[2][2]);
  294.         if (s.getReal() > -0.19) {
  295.             // compute q0 and deduce q1, q2 and q3
  296.             quat[0] = s.add(1.0).sqrt().multiply(0.5);
  297.             T inv = quat[0].reciprocal().multiply(0.25);
  298.             quat[1] = inv.multiply(ort[1][2].subtract(ort[2][1]));
  299.             quat[2] = inv.multiply(ort[2][0].subtract(ort[0][2]));
  300.             quat[3] = inv.multiply(ort[0][1].subtract(ort[1][0]));
  301.         } else {
  302.             s = ort[0][0].subtract(ort[1][1]).subtract(ort[2][2]);
  303.             if (s.getReal() > -0.19) {
  304.                 // compute q1 and deduce q0, q2 and q3
  305.                 quat[1] = s.add(1.0).sqrt().multiply(0.5);
  306.                 T inv = quat[1].reciprocal().multiply(0.25);
  307.                 quat[0] = inv.multiply(ort[1][2].subtract(ort[2][1]));
  308.                 quat[2] = inv.multiply(ort[0][1].add(ort[1][0]));
  309.                 quat[3] = inv.multiply(ort[0][2].add(ort[2][0]));
  310.             } else {
  311.                 s = ort[1][1].subtract(ort[0][0]).subtract(ort[2][2]);
  312.                 if (s.getReal() > -0.19) {
  313.                     // compute q2 and deduce q0, q1 and q3
  314.                     quat[2] = s.add(1.0).sqrt().multiply(0.5);
  315.                     T inv = quat[2].reciprocal().multiply(0.25);
  316.                     quat[0] = inv.multiply(ort[2][0].subtract(ort[0][2]));
  317.                     quat[1] = inv.multiply(ort[0][1].add(ort[1][0]));
  318.                     quat[3] = inv.multiply(ort[2][1].add(ort[1][2]));
  319.                 } else {
  320.                     // compute q3 and deduce q0, q1 and q2
  321.                     s = ort[2][2].subtract(ort[0][0]).subtract(ort[1][1]);
  322.                     quat[3] = s.add(1.0).sqrt().multiply(0.5);
  323.                     T inv = quat[3].reciprocal().multiply(0.25);
  324.                     quat[0] = inv.multiply(ort[0][1].subtract(ort[1][0]));
  325.                     quat[1] = inv.multiply(ort[0][2].add(ort[2][0]));
  326.                     quat[2] = inv.multiply(ort[2][1].add(ort[1][2]));
  327.                 }
  328.             }
  329.         }

  330.         return quat;

  331.     }

  332.     /** Revert a rotation.
  333.      * Build a rotation which reverse the effect of another
  334.      * rotation. This means that if r(u) = v, then r.revert(v) = u. The
  335.      * instance is not changed.
  336.      * @return a new rotation whose effect is the reverse of the effect
  337.      * of the instance
  338.      */
  339.     public FieldRotation<T> revert() {
  340.         return new FieldRotation<T>(q0.negate(), q1, q2, q3, false);
  341.     }

  342.     /** Get the scalar coordinate of the quaternion.
  343.      * @return scalar coordinate of the quaternion
  344.      */
  345.     public T getQ0() {
  346.         return q0;
  347.     }

  348.     /** Get the first coordinate of the vectorial part of the quaternion.
  349.      * @return first coordinate of the vectorial part of the quaternion
  350.      */
  351.     public T getQ1() {
  352.         return q1;
  353.     }

  354.     /** Get the second coordinate of the vectorial part of the quaternion.
  355.      * @return second coordinate of the vectorial part of the quaternion
  356.      */
  357.     public T getQ2() {
  358.         return q2;
  359.     }

  360.     /** Get the third coordinate of the vectorial part of the quaternion.
  361.      * @return third coordinate of the vectorial part of the quaternion
  362.      */
  363.     public T getQ3() {
  364.         return q3;
  365.     }

  366.     /** Get the normalized axis of the rotation.
  367.      * @return normalized axis of the rotation
  368.      * @see #FieldRotation(FieldVector3D, RealFieldElement)
  369.      */
  370.     public FieldVector3D<T> getAxis() {
  371.         final T squaredSine = q1.multiply(q1).add(q2.multiply(q2)).add(q3.multiply(q3));
  372.         if (squaredSine.getReal() == 0) {
  373.             final Field<T> field = squaredSine.getField();
  374.             return new FieldVector3D<T>(field.getOne(), field.getZero(), field.getZero());
  375.         } else if (q0.getReal() < 0) {
  376.             T inverse = squaredSine.sqrt().reciprocal();
  377.             return new FieldVector3D<T>(q1.multiply(inverse), q2.multiply(inverse), q3.multiply(inverse));
  378.         }
  379.         final T inverse = squaredSine.sqrt().reciprocal().negate();
  380.         return new FieldVector3D<T>(q1.multiply(inverse), q2.multiply(inverse), q3.multiply(inverse));
  381.     }

  382.     /** Get the angle of the rotation.
  383.      * @return angle of the rotation (between 0 and &pi;)
  384.      * @see #FieldRotation(FieldVector3D, RealFieldElement)
  385.      */
  386.     public T getAngle() {
  387.         if ((q0.getReal() < -0.1) || (q0.getReal() > 0.1)) {
  388.             return q1.multiply(q1).add(q2.multiply(q2)).add(q3.multiply(q3)).sqrt().asin().multiply(2);
  389.         } else if (q0.getReal() < 0) {
  390.             return q0.negate().acos().multiply(2);
  391.         }
  392.         return q0.acos().multiply(2);
  393.     }

  394.     /** Get the Cardan or Euler angles corresponding to the instance.

  395.      * <p>The equations show that each rotation can be defined by two
  396.      * different values of the Cardan or Euler angles set. For example
  397.      * if Cardan angles are used, the rotation defined by the angles
  398.      * a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
  399.      * the rotation defined by the angles &pi; + a<sub>1</sub>, &pi;
  400.      * - a<sub>2</sub> and &pi; + a<sub>3</sub>. This method implements
  401.      * the following arbitrary choices:</p>
  402.      * <ul>
  403.      *   <li>for Cardan angles, the chosen set is the one for which the
  404.      *   second angle is between -&pi;/2 and &pi;/2 (i.e its cosine is
  405.      *   positive),</li>
  406.      *   <li>for Euler angles, the chosen set is the one for which the
  407.      *   second angle is between 0 and &pi; (i.e its sine is positive).</li>
  408.      * </ul>

  409.      * <p>Cardan and Euler angle have a very disappointing drawback: all
  410.      * of them have singularities. This means that if the instance is
  411.      * too close to the singularities corresponding to the given
  412.      * rotation order, it will be impossible to retrieve the angles. For
  413.      * Cardan angles, this is often called gimbal lock. There is
  414.      * <em>nothing</em> to do to prevent this, it is an intrinsic problem
  415.      * with Cardan and Euler representation (but not a problem with the
  416.      * rotation itself, which is perfectly well defined). For Cardan
  417.      * angles, singularities occur when the second angle is close to
  418.      * -&pi;/2 or +&pi;/2, for Euler angle singularities occur when the
  419.      * second angle is close to 0 or &pi;, this implies that the identity
  420.      * rotation is always singular for Euler angles!</p>

  421.      * @param order rotation order to use
  422.      * @return an array of three angles, in the order specified by the set
  423.      * @exception CardanEulerSingularityException if the rotation is
  424.      * singular with respect to the angles set specified
  425.      */
  426.     public T[] getAngles(final RotationOrder order)
  427.         throws CardanEulerSingularityException {

  428.         if (order == RotationOrder.XYZ) {

  429.             // r (+K) coordinates are :
  430.             //  sin (theta), -cos (theta) sin (phi), cos (theta) cos (phi)
  431.             // (-r) (+I) coordinates are :
  432.             // cos (psi) cos (theta), -sin (psi) cos (theta), sin (theta)
  433.             final // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
  434.             FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
  435.             final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
  436.             if  ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
  437.                 throw new CardanEulerSingularityException(true);
  438.             }
  439.             return buildArray(v1.getY().negate().atan2(v1.getZ()),
  440.                               v2.getZ().asin(),
  441.                               v2.getY().negate().atan2(v2.getX()));

  442.         } else if (order == RotationOrder.XZY) {

  443.             // r (+J) coordinates are :
  444.             // -sin (psi), cos (psi) cos (phi), cos (psi) sin (phi)
  445.             // (-r) (+I) coordinates are :
  446.             // cos (theta) cos (psi), -sin (psi), sin (theta) cos (psi)
  447.             // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
  448.             final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
  449.             final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
  450.             if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
  451.                 throw new CardanEulerSingularityException(true);
  452.             }
  453.             return buildArray(v1.getZ().atan2(v1.getY()),
  454.                               v2.getY().asin().negate(),
  455.                               v2.getZ().atan2(v2.getX()));

  456.         } else if (order == RotationOrder.YXZ) {

  457.             // r (+K) coordinates are :
  458.             //  cos (phi) sin (theta), -sin (phi), cos (phi) cos (theta)
  459.             // (-r) (+J) coordinates are :
  460.             // sin (psi) cos (phi), cos (psi) cos (phi), -sin (phi)
  461.             // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
  462.             final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
  463.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
  464.             if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
  465.                 throw new CardanEulerSingularityException(true);
  466.             }
  467.             return buildArray(v1.getX().atan2(v1.getZ()),
  468.                               v2.getZ().asin().negate(),
  469.                               v2.getX().atan2(v2.getY()));

  470.         } else if (order == RotationOrder.YZX) {

  471.             // r (+I) coordinates are :
  472.             // cos (psi) cos (theta), sin (psi), -cos (psi) sin (theta)
  473.             // (-r) (+J) coordinates are :
  474.             // sin (psi), cos (phi) cos (psi), -sin (phi) cos (psi)
  475.             // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
  476.             final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
  477.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
  478.             if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
  479.                 throw new CardanEulerSingularityException(true);
  480.             }
  481.             return buildArray(v1.getZ().negate().atan2(v1.getX()),
  482.                               v2.getX().asin(),
  483.                               v2.getZ().negate().atan2(v2.getY()));

  484.         } else if (order == RotationOrder.ZXY) {

  485.             // r (+J) coordinates are :
  486.             // -cos (phi) sin (psi), cos (phi) cos (psi), sin (phi)
  487.             // (-r) (+K) coordinates are :
  488.             // -sin (theta) cos (phi), sin (phi), cos (theta) cos (phi)
  489.             // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
  490.             final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
  491.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
  492.             if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
  493.                 throw new CardanEulerSingularityException(true);
  494.             }
  495.             return buildArray(v1.getX().negate().atan2(v1.getY()),
  496.                               v2.getY().asin(),
  497.                               v2.getX().negate().atan2(v2.getZ()));

  498.         } else if (order == RotationOrder.ZYX) {

  499.             // r (+I) coordinates are :
  500.             //  cos (theta) cos (psi), cos (theta) sin (psi), -sin (theta)
  501.             // (-r) (+K) coordinates are :
  502.             // -sin (theta), sin (phi) cos (theta), cos (phi) cos (theta)
  503.             // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
  504.             final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
  505.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
  506.             if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
  507.                 throw new CardanEulerSingularityException(true);
  508.             }
  509.             return buildArray(v1.getY().atan2(v1.getX()),
  510.                               v2.getX().asin().negate(),
  511.                               v2.getY().atan2(v2.getZ()));

  512.         } else if (order == RotationOrder.XYX) {

  513.             // r (+I) coordinates are :
  514.             //  cos (theta), sin (phi1) sin (theta), -cos (phi1) sin (theta)
  515.             // (-r) (+I) coordinates are :
  516.             // cos (theta), sin (theta) sin (phi2), sin (theta) cos (phi2)
  517.             // and we can choose to have theta in the interval [0 ; PI]
  518.             final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
  519.             final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
  520.             if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
  521.                 throw new CardanEulerSingularityException(false);
  522.             }
  523.             return buildArray(v1.getY().atan2(v1.getZ().negate()),
  524.                               v2.getX().acos(),
  525.                               v2.getY().atan2(v2.getZ()));

  526.         } else if (order == RotationOrder.XZX) {

  527.             // r (+I) coordinates are :
  528.             //  cos (psi), cos (phi1) sin (psi), sin (phi1) sin (psi)
  529.             // (-r) (+I) coordinates are :
  530.             // cos (psi), -sin (psi) cos (phi2), sin (psi) sin (phi2)
  531.             // and we can choose to have psi in the interval [0 ; PI]
  532.             final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
  533.             final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
  534.             if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
  535.                 throw new CardanEulerSingularityException(false);
  536.             }
  537.             return buildArray(v1.getZ().atan2(v1.getY()),
  538.                               v2.getX().acos(),
  539.                               v2.getZ().atan2(v2.getY().negate()));

  540.         } else if (order == RotationOrder.YXY) {

  541.             // r (+J) coordinates are :
  542.             //  sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
  543.             // (-r) (+J) coordinates are :
  544.             // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
  545.             // and we can choose to have phi in the interval [0 ; PI]
  546.             final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
  547.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
  548.             if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
  549.                 throw new CardanEulerSingularityException(false);
  550.             }
  551.             return buildArray(v1.getX().atan2(v1.getZ()),
  552.                               v2.getY().acos(),
  553.                               v2.getX().atan2(v2.getZ().negate()));

  554.         } else if (order == RotationOrder.YZY) {

  555.             // r (+J) coordinates are :
  556.             //  -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
  557.             // (-r) (+J) coordinates are :
  558.             // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
  559.             // and we can choose to have psi in the interval [0 ; PI]
  560.             final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
  561.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
  562.             if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
  563.                 throw new CardanEulerSingularityException(false);
  564.             }
  565.             return buildArray(v1.getZ().atan2(v1.getX().negate()),
  566.                               v2.getY().acos(),
  567.                               v2.getZ().atan2(v2.getX()));

  568.         } else if (order == RotationOrder.ZXZ) {

  569.             // r (+K) coordinates are :
  570.             //  sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
  571.             // (-r) (+K) coordinates are :
  572.             // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
  573.             // and we can choose to have phi in the interval [0 ; PI]
  574.             final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
  575.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
  576.             if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
  577.                 throw new CardanEulerSingularityException(false);
  578.             }
  579.             return buildArray(v1.getX().atan2(v1.getY().negate()),
  580.                               v2.getZ().acos(),
  581.                               v2.getX().atan2(v2.getY()));

  582.         } else { // last possibility is ZYZ

  583.             // r (+K) coordinates are :
  584.             //  cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
  585.             // (-r) (+K) coordinates are :
  586.             // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
  587.             // and we can choose to have theta in the interval [0 ; PI]
  588.             final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
  589.             final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
  590.             if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
  591.                 throw new CardanEulerSingularityException(false);
  592.             }
  593.             return buildArray(v1.getY().atan2(v1.getX()),
  594.                               v2.getZ().acos(),
  595.                               v2.getY().atan2(v2.getX().negate()));

  596.         }

  597.     }

  598.     /** Create a dimension 3 array.
  599.      * @param a0 first array element
  600.      * @param a1 second array element
  601.      * @param a2 third array element
  602.      * @return new array
  603.      */
  604.     private T[] buildArray(final T a0, final T a1, final T a2) {
  605.         final T[] array = MathArrays.buildArray(a0.getField(), 3);
  606.         array[0] = a0;
  607.         array[1] = a1;
  608.         array[2] = a2;
  609.         return array;
  610.     }

  611.     /** Create a constant vector.
  612.      * @param x abscissa
  613.      * @param y ordinate
  614.      * @param z height
  615.      * @return a constant vector
  616.      */
  617.     private FieldVector3D<T> vector(final double x, final double y, final double z) {
  618.         final T zero = q0.getField().getZero();
  619.         return new FieldVector3D<T>(zero.add(x), zero.add(y), zero.add(z));
  620.     }

  621.     /** Get the 3X3 matrix corresponding to the instance
  622.      * @return the matrix corresponding to the instance
  623.      */
  624.     public T[][] getMatrix() {

  625.         // products
  626.         final T q0q0  = q0.multiply(q0);
  627.         final T q0q1  = q0.multiply(q1);
  628.         final T q0q2  = q0.multiply(q2);
  629.         final T q0q3  = q0.multiply(q3);
  630.         final T q1q1  = q1.multiply(q1);
  631.         final T q1q2  = q1.multiply(q2);
  632.         final T q1q3  = q1.multiply(q3);
  633.         final T q2q2  = q2.multiply(q2);
  634.         final T q2q3  = q2.multiply(q3);
  635.         final T q3q3  = q3.multiply(q3);

  636.         // create the matrix
  637.         final T[][] m = MathArrays.buildArray(q0.getField(), 3, 3);

  638.         m [0][0] = q0q0.add(q1q1).multiply(2).subtract(1);
  639.         m [1][0] = q1q2.subtract(q0q3).multiply(2);
  640.         m [2][0] = q1q3.add(q0q2).multiply(2);

  641.         m [0][1] = q1q2.add(q0q3).multiply(2);
  642.         m [1][1] = q0q0.add(q2q2).multiply(2).subtract(1);
  643.         m [2][1] = q2q3.subtract(q0q1).multiply(2);

  644.         m [0][2] = q1q3.subtract(q0q2).multiply(2);
  645.         m [1][2] = q2q3.add(q0q1).multiply(2);
  646.         m [2][2] = q0q0.add(q3q3).multiply(2).subtract(1);

  647.         return m;

  648.     }

  649.     /** Convert to a constant vector without derivatives.
  650.      * @return a constant vector
  651.      */
  652.     public Rotation toRotation() {
  653.         return new Rotation(q0.getReal(), q1.getReal(), q2.getReal(), q3.getReal(), false);
  654.     }

  655.     /** Apply the rotation to a vector.
  656.      * @param u vector to apply the rotation to
  657.      * @return a new vector which is the image of u by the rotation
  658.      */
  659.     public FieldVector3D<T> applyTo(final FieldVector3D<T> u) {

  660.         final T x = u.getX();
  661.         final T y = u.getY();
  662.         final T z = u.getZ();

  663.         final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));

  664.         return new FieldVector3D<T>(q0.multiply(x.multiply(q0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
  665.                                     q0.multiply(y.multiply(q0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
  666.                                     q0.multiply(z.multiply(q0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));

  667.     }

  668.     /** Apply the rotation to a vector.
  669.      * @param u vector to apply the rotation to
  670.      * @return a new vector which is the image of u by the rotation
  671.      */
  672.     public FieldVector3D<T> applyTo(final Vector3D u) {

  673.         final double x = u.getX();
  674.         final double y = u.getY();
  675.         final double z = u.getZ();

  676.         final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));

  677.         return new FieldVector3D<T>(q0.multiply(q0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
  678.                                     q0.multiply(q0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
  679.                                     q0.multiply(q0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));

  680.     }

  681.     /** Apply the rotation to a vector stored in an array.
  682.      * @param in an array with three items which stores vector to rotate
  683.      * @param out an array with three items to put result to (it can be the same
  684.      * array as in)
  685.      */
  686.     public void applyTo(final T[] in, final T[] out) {

  687.         final T x = in[0];
  688.         final T y = in[1];
  689.         final T z = in[2];

  690.         final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));

  691.         out[0] = q0.multiply(x.multiply(q0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
  692.         out[1] = q0.multiply(y.multiply(q0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
  693.         out[2] = q0.multiply(z.multiply(q0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);

  694.     }

  695.     /** Apply the rotation to a vector stored in an array.
  696.      * @param in an array with three items which stores vector to rotate
  697.      * @param out an array with three items to put result to
  698.      */
  699.     public void applyTo(final double[] in, final T[] out) {

  700.         final double x = in[0];
  701.         final double y = in[1];
  702.         final double z = in[2];

  703.         final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));

  704.         out[0] = q0.multiply(q0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
  705.         out[1] = q0.multiply(q0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
  706.         out[2] = q0.multiply(q0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);

  707.     }

  708.     /** Apply a rotation to a vector.
  709.      * @param r rotation to apply
  710.      * @param u vector to apply the rotation to
  711.      * @param <T> the type of the field elements
  712.      * @return a new vector which is the image of u by the rotation
  713.      */
  714.     public static <T extends RealFieldElement<T>> FieldVector3D<T> applyTo(final Rotation r, final FieldVector3D<T> u) {

  715.         final T x = u.getX();
  716.         final T y = u.getY();
  717.         final T z = u.getZ();

  718.         final T s = x.multiply(r.getQ1()).add(y.multiply(r.getQ2())).add(z.multiply(r.getQ3()));

  719.         return new FieldVector3D<T>(x.multiply(r.getQ0()).subtract(z.multiply(r.getQ2()).subtract(y.multiply(r.getQ3()))).multiply(r.getQ0()).add(s.multiply(r.getQ1())).multiply(2).subtract(x),
  720.                                     y.multiply(r.getQ0()).subtract(x.multiply(r.getQ3()).subtract(z.multiply(r.getQ1()))).multiply(r.getQ0()).add(s.multiply(r.getQ2())).multiply(2).subtract(y),
  721.                                     z.multiply(r.getQ0()).subtract(y.multiply(r.getQ1()).subtract(x.multiply(r.getQ2()))).multiply(r.getQ0()).add(s.multiply(r.getQ3())).multiply(2).subtract(z));

  722.     }

  723.     /** Apply the inverse of the rotation to a vector.
  724.      * @param u vector to apply the inverse of the rotation to
  725.      * @return a new vector which such that u is its image by the rotation
  726.      */
  727.     public FieldVector3D<T> applyInverseTo(final FieldVector3D<T> u) {

  728.         final T x = u.getX();
  729.         final T y = u.getY();
  730.         final T z = u.getZ();

  731.         final T s  = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
  732.         final T m0 = q0.negate();

  733.         return new FieldVector3D<T>(m0.multiply(x.multiply(m0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
  734.                                     m0.multiply(y.multiply(m0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
  735.                                     m0.multiply(z.multiply(m0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));

  736.     }

  737.     /** Apply the inverse of the rotation to a vector.
  738.      * @param u vector to apply the inverse of the rotation to
  739.      * @return a new vector which such that u is its image by the rotation
  740.      */
  741.     public FieldVector3D<T> applyInverseTo(final Vector3D u) {

  742.         final double x = u.getX();
  743.         final double y = u.getY();
  744.         final double z = u.getZ();

  745.         final T s  = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
  746.         final T m0 = q0.negate();

  747.         return new FieldVector3D<T>(m0.multiply(m0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
  748.                                     m0.multiply(m0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
  749.                                     m0.multiply(m0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));

  750.     }

  751.     /** Apply the inverse of the rotation to a vector stored in an array.
  752.      * @param in an array with three items which stores vector to rotate
  753.      * @param out an array with three items to put result to (it can be the same
  754.      * array as in)
  755.      */
  756.     public void applyInverseTo(final T[] in, final T[] out) {

  757.         final T x = in[0];
  758.         final T y = in[1];
  759.         final T z = in[2];

  760.         final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
  761.         final T m0 = q0.negate();

  762.         out[0] = m0.multiply(x.multiply(m0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
  763.         out[1] = m0.multiply(y.multiply(m0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
  764.         out[2] = m0.multiply(z.multiply(m0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);

  765.     }

  766.     /** Apply the inverse of the rotation to a vector stored in an array.
  767.      * @param in an array with three items which stores vector to rotate
  768.      * @param out an array with three items to put result to
  769.      */
  770.     public void applyInverseTo(final double[] in, final T[] out) {

  771.         final double x = in[0];
  772.         final double y = in[1];
  773.         final double z = in[2];

  774.         final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
  775.         final T m0 = q0.negate();

  776.         out[0] = m0.multiply(m0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
  777.         out[1] = m0.multiply(m0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
  778.         out[2] = m0.multiply(m0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);

  779.     }

  780.     /** Apply the inverse of a rotation to a vector.
  781.      * @param r rotation to apply
  782.      * @param u vector to apply the inverse of the rotation to
  783.      * @param <T> the type of the field elements
  784.      * @return a new vector which such that u is its image by the rotation
  785.      */
  786.     public static <T extends RealFieldElement<T>> FieldVector3D<T> applyInverseTo(final Rotation r, final FieldVector3D<T> u) {

  787.         final T x = u.getX();
  788.         final T y = u.getY();
  789.         final T z = u.getZ();

  790.         final T s  = x.multiply(r.getQ1()).add(y.multiply(r.getQ2())).add(z.multiply(r.getQ3()));
  791.         final double m0 = -r.getQ0();

  792.         return new FieldVector3D<T>(x.multiply(m0).subtract(z.multiply(r.getQ2()).subtract(y.multiply(r.getQ3()))).multiply(m0).add(s.multiply(r.getQ1())).multiply(2).subtract(x),
  793.                                     y.multiply(m0).subtract(x.multiply(r.getQ3()).subtract(z.multiply(r.getQ1()))).multiply(m0).add(s.multiply(r.getQ2())).multiply(2).subtract(y),
  794.                                     z.multiply(m0).subtract(y.multiply(r.getQ1()).subtract(x.multiply(r.getQ2()))).multiply(m0).add(s.multiply(r.getQ3())).multiply(2).subtract(z));

  795.     }

  796.     /** Apply the instance to another rotation.
  797.      * Applying the instance to a rotation is computing the composition
  798.      * in an order compliant with the following rule : let u be any
  799.      * vector and v its image by r (i.e. r.applyTo(u) = v), let w be the image
  800.      * of v by the instance (i.e. applyTo(v) = w), then w = comp.applyTo(u),
  801.      * where comp = applyTo(r).
  802.      * @param r rotation to apply the rotation to
  803.      * @return a new rotation which is the composition of r by the instance
  804.      */
  805.     public FieldRotation<T> applyTo(final FieldRotation<T> r) {
  806.         return new FieldRotation<T>(r.q0.multiply(q0).subtract(r.q1.multiply(q1).add(r.q2.multiply(q2)).add(r.q3.multiply(q3))),
  807.                                     r.q1.multiply(q0).add(r.q0.multiply(q1)).add(r.q2.multiply(q3).subtract(r.q3.multiply(q2))),
  808.                                     r.q2.multiply(q0).add(r.q0.multiply(q2)).add(r.q3.multiply(q1).subtract(r.q1.multiply(q3))),
  809.                                     r.q3.multiply(q0).add(r.q0.multiply(q3)).add(r.q1.multiply(q2).subtract(r.q2.multiply(q1))),
  810.                                     false);
  811.     }

  812.     /** Apply the instance to another rotation.
  813.      * Applying the instance to a rotation is computing the composition
  814.      * in an order compliant with the following rule : let u be any
  815.      * vector and v its image by r (i.e. r.applyTo(u) = v), let w be the image
  816.      * of v by the instance (i.e. applyTo(v) = w), then w = comp.applyTo(u),
  817.      * where comp = applyTo(r).
  818.      * @param r rotation to apply the rotation to
  819.      * @return a new rotation which is the composition of r by the instance
  820.      */
  821.     public FieldRotation<T> applyTo(final Rotation r) {
  822.         return new FieldRotation<T>(q0.multiply(r.getQ0()).subtract(q1.multiply(r.getQ1()).add(q2.multiply(r.getQ2())).add(q3.multiply(r.getQ3()))),
  823.                                     q0.multiply(r.getQ1()).add(q1.multiply(r.getQ0())).add(q3.multiply(r.getQ2()).subtract(q2.multiply(r.getQ3()))),
  824.                                     q0.multiply(r.getQ2()).add(q2.multiply(r.getQ0())).add(q1.multiply(r.getQ3()).subtract(q3.multiply(r.getQ1()))),
  825.                                     q0.multiply(r.getQ3()).add(q3.multiply(r.getQ0())).add(q2.multiply(r.getQ1()).subtract(q1.multiply(r.getQ2()))),
  826.                                     false);
  827.     }

  828.     /** Apply a rotation to another rotation.
  829.      * Applying a rotation to another rotation is computing the composition
  830.      * in an order compliant with the following rule : let u be any
  831.      * vector and v its image by rInner (i.e. rInner.applyTo(u) = v), let w be the image
  832.      * of v by rOuter (i.e. rOuter.applyTo(v) = w), then w = comp.applyTo(u),
  833.      * where comp = applyTo(rOuter, rInner).
  834.      * @param r1 rotation to apply
  835.      * @param rInner rotation to apply the rotation to
  836.      * @param <T> the type of the field elements
  837.      * @return a new rotation which is the composition of r by the instance
  838.      */
  839.     public static <T extends RealFieldElement<T>> FieldRotation<T> applyTo(final Rotation r1, final FieldRotation<T> rInner) {
  840.         return new FieldRotation<T>(rInner.q0.multiply(r1.getQ0()).subtract(rInner.q1.multiply(r1.getQ1()).add(rInner.q2.multiply(r1.getQ2())).add(rInner.q3.multiply(r1.getQ3()))),
  841.                                     rInner.q1.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ1())).add(rInner.q2.multiply(r1.getQ3()).subtract(rInner.q3.multiply(r1.getQ2()))),
  842.                                     rInner.q2.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ2())).add(rInner.q3.multiply(r1.getQ1()).subtract(rInner.q1.multiply(r1.getQ3()))),
  843.                                     rInner.q3.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ3())).add(rInner.q1.multiply(r1.getQ2()).subtract(rInner.q2.multiply(r1.getQ1()))),
  844.                                     false);
  845.     }

  846.     /** Apply the inverse of the instance to another rotation.
  847.      * Applying the inverse of the instance to a rotation is computing
  848.      * the composition in an order compliant with the following rule :
  849.      * let u be any vector and v its image by r (i.e. r.applyTo(u) = v),
  850.      * let w be the inverse image of v by the instance
  851.      * (i.e. applyInverseTo(v) = w), then w = comp.applyTo(u), where
  852.      * comp = applyInverseTo(r).
  853.      * @param r rotation to apply the rotation to
  854.      * @return a new rotation which is the composition of r by the inverse
  855.      * of the instance
  856.      */
  857.     public FieldRotation<T> applyInverseTo(final FieldRotation<T> r) {
  858.         return new FieldRotation<T>(r.q0.multiply(q0).add(r.q1.multiply(q1).add(r.q2.multiply(q2)).add(r.q3.multiply(q3))).negate(),
  859.                                     r.q0.multiply(q1).add(r.q2.multiply(q3).subtract(r.q3.multiply(q2))).subtract(r.q1.multiply(q0)),
  860.                                     r.q0.multiply(q2).add(r.q3.multiply(q1).subtract(r.q1.multiply(q3))).subtract(r.q2.multiply(q0)),
  861.                                     r.q0.multiply(q3).add(r.q1.multiply(q2).subtract(r.q2.multiply(q1))).subtract(r.q3.multiply(q0)),
  862.                                     false);
  863.     }

  864.     /** Apply the inverse of the instance to another rotation.
  865.      * Applying the inverse of the instance to a rotation is computing
  866.      * the composition in an order compliant with the following rule :
  867.      * let u be any vector and v its image by r (i.e. r.applyTo(u) = v),
  868.      * let w be the inverse image of v by the instance
  869.      * (i.e. applyInverseTo(v) = w), then w = comp.applyTo(u), where
  870.      * comp = applyInverseTo(r).
  871.      * @param r rotation to apply the rotation to
  872.      * @return a new rotation which is the composition of r by the inverse
  873.      * of the instance
  874.      */
  875.     public FieldRotation<T> applyInverseTo(final Rotation r) {
  876.         return new FieldRotation<T>(q0.multiply(r.getQ0()).add(q1.multiply(r.getQ1()).add(q2.multiply(r.getQ2())).add(q3.multiply(r.getQ3()))).negate(),
  877.                                     q1.multiply(r.getQ0()).add(q3.multiply(r.getQ2()).subtract(q2.multiply(r.getQ3()))).subtract(q0.multiply(r.getQ1())),
  878.                                     q2.multiply(r.getQ0()).add(q1.multiply(r.getQ3()).subtract(q3.multiply(r.getQ1()))).subtract(q0.multiply(r.getQ2())),
  879.                                     q3.multiply(r.getQ0()).add(q2.multiply(r.getQ1()).subtract(q1.multiply(r.getQ2()))).subtract(q0.multiply(r.getQ3())),
  880.                                     false);
  881.     }

  882.     /** Apply the inverse of a rotation to another rotation.
  883.      * Applying the inverse of a rotation to another rotation is computing
  884.      * the composition in an order compliant with the following rule :
  885.      * let u be any vector and v its image by rInner (i.e. rInner.applyTo(u) = v),
  886.      * let w be the inverse image of v by rOuter
  887.      * (i.e. rOuter.applyInverseTo(v) = w), then w = comp.applyTo(u), where
  888.      * comp = applyInverseTo(rOuter, rInner).
  889.      * @param rOuter rotation to apply the rotation to
  890.      * @param rInner rotation to apply the rotation to
  891.      * @param <T> the type of the field elements
  892.      * @return a new rotation which is the composition of r by the inverse
  893.      * of the instance
  894.      */
  895.     public static <T extends RealFieldElement<T>> FieldRotation<T> applyInverseTo(final Rotation rOuter, final FieldRotation<T> rInner) {
  896.         return new FieldRotation<T>(rInner.q0.multiply(rOuter.getQ0()).add(rInner.q1.multiply(rOuter.getQ1()).add(rInner.q2.multiply(rOuter.getQ2())).add(rInner.q3.multiply(rOuter.getQ3()))).negate(),
  897.                                     rInner.q0.multiply(rOuter.getQ1()).add(rInner.q2.multiply(rOuter.getQ3()).subtract(rInner.q3.multiply(rOuter.getQ2()))).subtract(rInner.q1.multiply(rOuter.getQ0())),
  898.                                     rInner.q0.multiply(rOuter.getQ2()).add(rInner.q3.multiply(rOuter.getQ1()).subtract(rInner.q1.multiply(rOuter.getQ3()))).subtract(rInner.q2.multiply(rOuter.getQ0())),
  899.                                     rInner.q0.multiply(rOuter.getQ3()).add(rInner.q1.multiply(rOuter.getQ2()).subtract(rInner.q2.multiply(rOuter.getQ1()))).subtract(rInner.q3.multiply(rOuter.getQ0())),
  900.                                     false);
  901.     }

  902.     /** Perfect orthogonality on a 3X3 matrix.
  903.      * @param m initial matrix (not exactly orthogonal)
  904.      * @param threshold convergence threshold for the iterative
  905.      * orthogonality correction (convergence is reached when the
  906.      * difference between two steps of the Frobenius norm of the
  907.      * correction is below this threshold)
  908.      * @return an orthogonal matrix close to m
  909.      * @exception NotARotationMatrixException if the matrix cannot be
  910.      * orthogonalized with the given threshold after 10 iterations
  911.      */
  912.     private T[][] orthogonalizeMatrix(final T[][] m, final double threshold)
  913.         throws NotARotationMatrixException {

  914.         T x00 = m[0][0];
  915.         T x01 = m[0][1];
  916.         T x02 = m[0][2];
  917.         T x10 = m[1][0];
  918.         T x11 = m[1][1];
  919.         T x12 = m[1][2];
  920.         T x20 = m[2][0];
  921.         T x21 = m[2][1];
  922.         T x22 = m[2][2];
  923.         double fn = 0;
  924.         double fn1;

  925.         final T[][] o = MathArrays.buildArray(m[0][0].getField(), 3, 3);

  926.         // iterative correction: Xn+1 = Xn - 0.5 * (Xn.Mt.Xn - M)
  927.         int i = 0;
  928.         while (++i < 11) {

  929.             // Mt.Xn
  930.             final T mx00 = m[0][0].multiply(x00).add(m[1][0].multiply(x10)).add(m[2][0].multiply(x20));
  931.             final T mx10 = m[0][1].multiply(x00).add(m[1][1].multiply(x10)).add(m[2][1].multiply(x20));
  932.             final T mx20 = m[0][2].multiply(x00).add(m[1][2].multiply(x10)).add(m[2][2].multiply(x20));
  933.             final T mx01 = m[0][0].multiply(x01).add(m[1][0].multiply(x11)).add(m[2][0].multiply(x21));
  934.             final T mx11 = m[0][1].multiply(x01).add(m[1][1].multiply(x11)).add(m[2][1].multiply(x21));
  935.             final T mx21 = m[0][2].multiply(x01).add(m[1][2].multiply(x11)).add(m[2][2].multiply(x21));
  936.             final T mx02 = m[0][0].multiply(x02).add(m[1][0].multiply(x12)).add(m[2][0].multiply(x22));
  937.             final T mx12 = m[0][1].multiply(x02).add(m[1][1].multiply(x12)).add(m[2][1].multiply(x22));
  938.             final T mx22 = m[0][2].multiply(x02).add(m[1][2].multiply(x12)).add(m[2][2].multiply(x22));

  939.             // Xn+1
  940.             o[0][0] = x00.subtract(x00.multiply(mx00).add(x01.multiply(mx10)).add(x02.multiply(mx20)).subtract(m[0][0]).multiply(0.5));
  941.             o[0][1] = x01.subtract(x00.multiply(mx01).add(x01.multiply(mx11)).add(x02.multiply(mx21)).subtract(m[0][1]).multiply(0.5));
  942.             o[0][2] = x02.subtract(x00.multiply(mx02).add(x01.multiply(mx12)).add(x02.multiply(mx22)).subtract(m[0][2]).multiply(0.5));
  943.             o[1][0] = x10.subtract(x10.multiply(mx00).add(x11.multiply(mx10)).add(x12.multiply(mx20)).subtract(m[1][0]).multiply(0.5));
  944.             o[1][1] = x11.subtract(x10.multiply(mx01).add(x11.multiply(mx11)).add(x12.multiply(mx21)).subtract(m[1][1]).multiply(0.5));
  945.             o[1][2] = x12.subtract(x10.multiply(mx02).add(x11.multiply(mx12)).add(x12.multiply(mx22)).subtract(m[1][2]).multiply(0.5));
  946.             o[2][0] = x20.subtract(x20.multiply(mx00).add(x21.multiply(mx10)).add(x22.multiply(mx20)).subtract(m[2][0]).multiply(0.5));
  947.             o[2][1] = x21.subtract(x20.multiply(mx01).add(x21.multiply(mx11)).add(x22.multiply(mx21)).subtract(m[2][1]).multiply(0.5));
  948.             o[2][2] = x22.subtract(x20.multiply(mx02).add(x21.multiply(mx12)).add(x22.multiply(mx22)).subtract(m[2][2]).multiply(0.5));

  949.             // correction on each elements
  950.             final double corr00 = o[0][0].getReal() - m[0][0].getReal();
  951.             final double corr01 = o[0][1].getReal() - m[0][1].getReal();
  952.             final double corr02 = o[0][2].getReal() - m[0][2].getReal();
  953.             final double corr10 = o[1][0].getReal() - m[1][0].getReal();
  954.             final double corr11 = o[1][1].getReal() - m[1][1].getReal();
  955.             final double corr12 = o[1][2].getReal() - m[1][2].getReal();
  956.             final double corr20 = o[2][0].getReal() - m[2][0].getReal();
  957.             final double corr21 = o[2][1].getReal() - m[2][1].getReal();
  958.             final double corr22 = o[2][2].getReal() - m[2][2].getReal();

  959.             // Frobenius norm of the correction
  960.             fn1 = corr00 * corr00 + corr01 * corr01 + corr02 * corr02 +
  961.                   corr10 * corr10 + corr11 * corr11 + corr12 * corr12 +
  962.                   corr20 * corr20 + corr21 * corr21 + corr22 * corr22;

  963.             // convergence test
  964.             if (FastMath.abs(fn1 - fn) <= threshold) {
  965.                 return o;
  966.             }

  967.             // prepare next iteration
  968.             x00 = o[0][0];
  969.             x01 = o[0][1];
  970.             x02 = o[0][2];
  971.             x10 = o[1][0];
  972.             x11 = o[1][1];
  973.             x12 = o[1][2];
  974.             x20 = o[2][0];
  975.             x21 = o[2][1];
  976.             x22 = o[2][2];
  977.             fn  = fn1;

  978.         }

  979.         // the algorithm did not converge after 10 iterations
  980.         throw new NotARotationMatrixException(LocalizedFormats.UNABLE_TO_ORTHOGONOLIZE_MATRIX,
  981.                                               i - 1);

  982.     }

  983.     /** Compute the <i>distance</i> between two rotations.
  984.      * <p>The <i>distance</i> is intended here as a way to check if two
  985.      * rotations are almost similar (i.e. they transform vectors the same way)
  986.      * or very different. It is mathematically defined as the angle of
  987.      * the rotation r that prepended to one of the rotations gives the other
  988.      * one:</p>
  989.      * <pre>
  990.      *        r<sub>1</sub>(r) = r<sub>2</sub>
  991.      * </pre>
  992.      * <p>This distance is an angle between 0 and &pi;. Its value is the smallest
  993.      * possible upper bound of the angle in radians between r<sub>1</sub>(v)
  994.      * and r<sub>2</sub>(v) for all possible vectors v. This upper bound is
  995.      * reached for some v. The distance is equal to 0 if and only if the two
  996.      * rotations are identical.</p>
  997.      * <p>Comparing two rotations should always be done using this value rather
  998.      * than for example comparing the components of the quaternions. It is much
  999.      * more stable, and has a geometric meaning. Also comparing quaternions
  1000.      * components is error prone since for example quaternions (0.36, 0.48, -0.48, -0.64)
  1001.      * and (-0.36, -0.48, 0.48, 0.64) represent exactly the same rotation despite
  1002.      * their components are different (they are exact opposites).</p>
  1003.      * @param r1 first rotation
  1004.      * @param r2 second rotation
  1005.      * @param <T> the type of the field elements
  1006.      * @return <i>distance</i> between r1 and r2
  1007.      */
  1008.     public static <T extends RealFieldElement<T>> T distance(final FieldRotation<T> r1, final FieldRotation<T> r2) {
  1009.         return r1.applyInverseTo(r2).getAngle();
  1010.     }

  1011. }