001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.math3.geometry.euclidean.threed;
019
020import java.io.Serializable;
021
022import org.apache.commons.math3.RealFieldElement;
023import org.apache.commons.math3.Field;
024import org.apache.commons.math3.exception.MathArithmeticException;
025import org.apache.commons.math3.exception.MathIllegalArgumentException;
026import org.apache.commons.math3.exception.util.LocalizedFormats;
027import org.apache.commons.math3.util.FastMath;
028import org.apache.commons.math3.util.MathArrays;
029
030/**
031 * This class is a re-implementation of {@link Rotation} using {@link RealFieldElement}.
032 * <p>Instance of this class are guaranteed to be immutable.</p>
033 *
034 * @param <T> the type of the field elements
035 * @see FieldVector3D
036 * @see RotationOrder
037 * @since 3.2
038 */
039
040public class FieldRotation<T extends RealFieldElement<T>> implements Serializable {
041
042    /** Serializable version identifier */
043    private static final long serialVersionUID = 20130224l;
044
045    /** Scalar coordinate of the quaternion. */
046    private final T q0;
047
048    /** First coordinate of the vectorial part of the quaternion. */
049    private final T q1;
050
051    /** Second coordinate of the vectorial part of the quaternion. */
052    private final T q2;
053
054    /** Third coordinate of the vectorial part of the quaternion. */
055    private final T q3;
056
057    /** Build a rotation from the quaternion coordinates.
058     * <p>A rotation can be built from a <em>normalized</em> quaternion,
059     * i.e. a quaternion for which q<sub>0</sub><sup>2</sup> +
060     * q<sub>1</sub><sup>2</sup> + q<sub>2</sub><sup>2</sup> +
061     * q<sub>3</sub><sup>2</sup> = 1. If the quaternion is not normalized,
062     * the constructor can normalize it in a preprocessing step.</p>
063     * <p>Note that some conventions put the scalar part of the quaternion
064     * as the 4<sup>th</sup> component and the vector part as the first three
065     * components. This is <em>not</em> our convention. We put the scalar part
066     * as the first component.</p>
067     * @param q0 scalar part of the quaternion
068     * @param q1 first coordinate of the vectorial part of the quaternion
069     * @param q2 second coordinate of the vectorial part of the quaternion
070     * @param q3 third coordinate of the vectorial part of the quaternion
071     * @param needsNormalization if true, the coordinates are considered
072     * not to be normalized, a normalization preprocessing step is performed
073     * before using them
074     */
075    public FieldRotation(final T q0, final T q1, final T q2, final T q3, final boolean needsNormalization) {
076
077        if (needsNormalization) {
078            // normalization preprocessing
079            final T inv =
080                    q0.multiply(q0).add(q1.multiply(q1)).add(q2.multiply(q2)).add(q3.multiply(q3)).sqrt().reciprocal();
081            this.q0 = inv.multiply(q0);
082            this.q1 = inv.multiply(q1);
083            this.q2 = inv.multiply(q2);
084            this.q3 = inv.multiply(q3);
085        } else {
086            this.q0 = q0;
087            this.q1 = q1;
088            this.q2 = q2;
089            this.q3 = q3;
090        }
091
092    }
093
094    /** Build a rotation from an axis and an angle.
095     * <p>We use the convention that angles are oriented according to
096     * the effect of the rotation on vectors around the axis. That means
097     * that if (i, j, k) is a direct frame and if we first provide +k as
098     * the axis and &pi;/2 as the angle to this constructor, and then
099     * {@link #applyTo(FieldVector3D) apply} the instance to +i, we will get
100     * +j.</p>
101     * <p>Another way to represent our convention is to say that a rotation
102     * of angle &theta; about the unit vector (x, y, z) is the same as the
103     * rotation build from quaternion components { cos(-&theta;/2),
104     * x * sin(-&theta;/2), y * sin(-&theta;/2), z * sin(-&theta;/2) }.
105     * Note the minus sign on the angle!</p>
106     * <p>On the one hand this convention is consistent with a vectorial
107     * perspective (moving vectors in fixed frames), on the other hand it
108     * is different from conventions with a frame perspective (fixed vectors
109     * viewed from different frames) like the ones used for example in spacecraft
110     * attitude community or in the graphics community.</p>
111     * @param axis axis around which to rotate
112     * @param angle rotation angle.
113     * @exception MathIllegalArgumentException if the axis norm is zero
114     * @deprecated as of 3.6, replaced with {@link
115     * #FieldRotation(FieldVector3D, RealFieldElement, RotationConvention)}
116     */
117    @Deprecated
118    public FieldRotation(final FieldVector3D<T> axis, final T angle)
119        throws MathIllegalArgumentException {
120        this(axis, angle, RotationConvention.VECTOR_OPERATOR);
121    }
122
123    /** Build a rotation from an axis and an angle.
124     * <p>We use the convention that angles are oriented according to
125     * the effect of the rotation on vectors around the axis. That means
126     * that if (i, j, k) is a direct frame and if we first provide +k as
127     * the axis and &pi;/2 as the angle to this constructor, and then
128     * {@link #applyTo(FieldVector3D) apply} the instance to +i, we will get
129     * +j.</p>
130     * <p>Another way to represent our convention is to say that a rotation
131     * of angle &theta; about the unit vector (x, y, z) is the same as the
132     * rotation build from quaternion components { cos(-&theta;/2),
133     * x * sin(-&theta;/2), y * sin(-&theta;/2), z * sin(-&theta;/2) }.
134     * Note the minus sign on the angle!</p>
135     * <p>On the one hand this convention is consistent with a vectorial
136     * perspective (moving vectors in fixed frames), on the other hand it
137     * is different from conventions with a frame perspective (fixed vectors
138     * viewed from different frames) like the ones used for example in spacecraft
139     * attitude community or in the graphics community.</p>
140     * @param axis axis around which to rotate
141     * @param angle rotation angle.
142     * @param convention convention to use for the semantics of the angle
143     * @exception MathIllegalArgumentException if the axis norm is zero
144     * @since 3.6
145     */
146    public FieldRotation(final FieldVector3D<T> axis, final T angle, final RotationConvention convention)
147        throws MathIllegalArgumentException {
148
149        final T norm = axis.getNorm();
150        if (norm.getReal() == 0) {
151            throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_AXIS);
152        }
153
154        final T halfAngle = angle.multiply(convention == RotationConvention.VECTOR_OPERATOR ? -0.5 : 0.5);
155        final T coeff = halfAngle.sin().divide(norm);
156
157        q0 = halfAngle.cos();
158        q1 = coeff.multiply(axis.getX());
159        q2 = coeff.multiply(axis.getY());
160        q3 = coeff.multiply(axis.getZ());
161
162    }
163
164    /** Build a rotation from a 3X3 matrix.
165
166     * <p>Rotation matrices are orthogonal matrices, i.e. unit matrices
167     * (which are matrices for which m.m<sup>T</sup> = I) with real
168     * coefficients. The module of the determinant of unit matrices is
169     * 1, among the orthogonal 3X3 matrices, only the ones having a
170     * positive determinant (+1) are rotation matrices.</p>
171
172     * <p>When a rotation is defined by a matrix with truncated values
173     * (typically when it is extracted from a technical sheet where only
174     * four to five significant digits are available), the matrix is not
175     * orthogonal anymore. This constructor handles this case
176     * transparently by using a copy of the given matrix and applying a
177     * correction to the copy in order to perfect its orthogonality. If
178     * the Frobenius norm of the correction needed is above the given
179     * threshold, then the matrix is considered to be too far from a
180     * true rotation matrix and an exception is thrown.<p>
181
182     * @param m rotation matrix
183     * @param threshold convergence threshold for the iterative
184     * orthogonality correction (convergence is reached when the
185     * difference between two steps of the Frobenius norm of the
186     * correction is below this threshold)
187
188     * @exception NotARotationMatrixException if the matrix is not a 3X3
189     * matrix, or if it cannot be transformed into an orthogonal matrix
190     * with the given threshold, or if the determinant of the resulting
191     * orthogonal matrix is negative
192
193     */
194    public FieldRotation(final T[][] m, final double threshold)
195        throws NotARotationMatrixException {
196
197        // dimension check
198        if ((m.length != 3) || (m[0].length != 3) ||
199                (m[1].length != 3) || (m[2].length != 3)) {
200            throw new NotARotationMatrixException(
201                                                  LocalizedFormats.ROTATION_MATRIX_DIMENSIONS,
202                                                  m.length, m[0].length);
203        }
204
205        // compute a "close" orthogonal matrix
206        final T[][] ort = orthogonalizeMatrix(m, threshold);
207
208        // check the sign of the determinant
209        final T d0 = ort[1][1].multiply(ort[2][2]).subtract(ort[2][1].multiply(ort[1][2]));
210        final T d1 = ort[0][1].multiply(ort[2][2]).subtract(ort[2][1].multiply(ort[0][2]));
211        final T d2 = ort[0][1].multiply(ort[1][2]).subtract(ort[1][1].multiply(ort[0][2]));
212        final T det =
213                ort[0][0].multiply(d0).subtract(ort[1][0].multiply(d1)).add(ort[2][0].multiply(d2));
214        if (det.getReal() < 0.0) {
215            throw new NotARotationMatrixException(
216                                                  LocalizedFormats.CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT,
217                                                  det);
218        }
219
220        final T[] quat = mat2quat(ort);
221        q0 = quat[0];
222        q1 = quat[1];
223        q2 = quat[2];
224        q3 = quat[3];
225
226    }
227
228    /** Build the rotation that transforms a pair of vectors into another pair.
229
230     * <p>Except for possible scale factors, if the instance were applied to
231     * the pair (u<sub>1</sub>, u<sub>2</sub>) it will produce the pair
232     * (v<sub>1</sub>, v<sub>2</sub>).</p>
233
234     * <p>If the angular separation between u<sub>1</sub> and u<sub>2</sub> is
235     * not the same as the angular separation between v<sub>1</sub> and
236     * v<sub>2</sub>, then a corrected v'<sub>2</sub> will be used rather than
237     * v<sub>2</sub>, the corrected vector will be in the (&pm;v<sub>1</sub>,
238     * +v<sub>2</sub>) half-plane.</p>
239
240     * @param u1 first vector of the origin pair
241     * @param u2 second vector of the origin pair
242     * @param v1 desired image of u1 by the rotation
243     * @param v2 desired image of u2 by the rotation
244     * @exception MathArithmeticException if the norm of one of the vectors is zero,
245     * or if one of the pair is degenerated (i.e. the vectors of the pair are collinear)
246     */
247    public FieldRotation(FieldVector3D<T> u1, FieldVector3D<T> u2, FieldVector3D<T> v1, FieldVector3D<T> v2)
248        throws MathArithmeticException {
249
250        // build orthonormalized base from u1, u2
251        // this fails when vectors are null or collinear, which is forbidden to define a rotation
252        final FieldVector3D<T> u3 = FieldVector3D.crossProduct(u1, u2).normalize();
253        u2 = FieldVector3D.crossProduct(u3, u1).normalize();
254        u1 = u1.normalize();
255
256        // build an orthonormalized base from v1, v2
257        // this fails when vectors are null or collinear, which is forbidden to define a rotation
258        final FieldVector3D<T> v3 = FieldVector3D.crossProduct(v1, v2).normalize();
259        v2 = FieldVector3D.crossProduct(v3, v1).normalize();
260        v1 = v1.normalize();
261
262        // buid a matrix transforming the first base into the second one
263        final T[][] array = MathArrays.buildArray(u1.getX().getField(), 3, 3);
264        array[0][0] = u1.getX().multiply(v1.getX()).add(u2.getX().multiply(v2.getX())).add(u3.getX().multiply(v3.getX()));
265        array[0][1] = u1.getY().multiply(v1.getX()).add(u2.getY().multiply(v2.getX())).add(u3.getY().multiply(v3.getX()));
266        array[0][2] = u1.getZ().multiply(v1.getX()).add(u2.getZ().multiply(v2.getX())).add(u3.getZ().multiply(v3.getX()));
267        array[1][0] = u1.getX().multiply(v1.getY()).add(u2.getX().multiply(v2.getY())).add(u3.getX().multiply(v3.getY()));
268        array[1][1] = u1.getY().multiply(v1.getY()).add(u2.getY().multiply(v2.getY())).add(u3.getY().multiply(v3.getY()));
269        array[1][2] = u1.getZ().multiply(v1.getY()).add(u2.getZ().multiply(v2.getY())).add(u3.getZ().multiply(v3.getY()));
270        array[2][0] = u1.getX().multiply(v1.getZ()).add(u2.getX().multiply(v2.getZ())).add(u3.getX().multiply(v3.getZ()));
271        array[2][1] = u1.getY().multiply(v1.getZ()).add(u2.getY().multiply(v2.getZ())).add(u3.getY().multiply(v3.getZ()));
272        array[2][2] = u1.getZ().multiply(v1.getZ()).add(u2.getZ().multiply(v2.getZ())).add(u3.getZ().multiply(v3.getZ()));
273
274        T[] quat = mat2quat(array);
275        q0 = quat[0];
276        q1 = quat[1];
277        q2 = quat[2];
278        q3 = quat[3];
279
280    }
281
282    /** Build one of the rotations that transform one vector into another one.
283
284     * <p>Except for a possible scale factor, if the instance were
285     * applied to the vector u it will produce the vector v. There is an
286     * infinite number of such rotations, this constructor choose the
287     * one with the smallest associated angle (i.e. the one whose axis
288     * is orthogonal to the (u, v) plane). If u and v are collinear, an
289     * arbitrary rotation axis is chosen.</p>
290
291     * @param u origin vector
292     * @param v desired image of u by the rotation
293     * @exception MathArithmeticException if the norm of one of the vectors is zero
294     */
295    public FieldRotation(final FieldVector3D<T> u, final FieldVector3D<T> v) throws MathArithmeticException {
296
297        final T normProduct = u.getNorm().multiply(v.getNorm());
298        if (normProduct.getReal() == 0) {
299            throw new MathArithmeticException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR);
300        }
301
302        final T dot = FieldVector3D.dotProduct(u, v);
303
304        if (dot.getReal() < ((2.0e-15 - 1.0) * normProduct.getReal())) {
305            // special case u = -v: we select a PI angle rotation around
306            // an arbitrary vector orthogonal to u
307            final FieldVector3D<T> w = u.orthogonal();
308            q0 = normProduct.getField().getZero();
309            q1 = w.getX().negate();
310            q2 = w.getY().negate();
311            q3 = w.getZ().negate();
312        } else {
313            // general case: (u, v) defines a plane, we select
314            // the shortest possible rotation: axis orthogonal to this plane
315            q0 = dot.divide(normProduct).add(1.0).multiply(0.5).sqrt();
316            final T coeff = q0.multiply(normProduct).multiply(2.0).reciprocal();
317            final FieldVector3D<T> q = FieldVector3D.crossProduct(v, u);
318            q1 = coeff.multiply(q.getX());
319            q2 = coeff.multiply(q.getY());
320            q3 = coeff.multiply(q.getZ());
321        }
322
323    }
324
325    /** Build a rotation from three Cardan or Euler elementary rotations.
326
327     * <p>Cardan rotations are three successive rotations around the
328     * canonical axes X, Y and Z, each axis being used once. There are
329     * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler
330     * rotations are three successive rotations around the canonical
331     * axes X, Y and Z, the first and last rotations being around the
332     * same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
333     * YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
334     * <p>Beware that many people routinely use the term Euler angles even
335     * for what really are Cardan angles (this confusion is especially
336     * widespread in the aerospace business where Roll, Pitch and Yaw angles
337     * are often wrongly tagged as Euler angles).</p>
338
339     * @param order order of rotations to use
340     * @param alpha1 angle of the first elementary rotation
341     * @param alpha2 angle of the second elementary rotation
342     * @param alpha3 angle of the third elementary rotation
343     * @deprecated as of 3.6, replaced with {@link
344     * #FieldRotation(RotationOrder, RotationConvention,
345     * RealFieldElement, RealFieldElement, RealFieldElement)}
346     */
347    @Deprecated
348    public FieldRotation(final RotationOrder order, final T alpha1, final T alpha2, final T alpha3) {
349        this(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3);
350    }
351
352    /** Build a rotation from three Cardan or Euler elementary rotations.
353
354     * <p>Cardan rotations are three successive rotations around the
355     * canonical axes X, Y and Z, each axis being used once. There are
356     * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler
357     * rotations are three successive rotations around the canonical
358     * axes X, Y and Z, the first and last rotations being around the
359     * same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
360     * YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
361     * <p>Beware that many people routinely use the term Euler angles even
362     * for what really are Cardan angles (this confusion is especially
363     * widespread in the aerospace business where Roll, Pitch and Yaw angles
364     * are often wrongly tagged as Euler angles).</p>
365
366     * @param order order of rotations to compose, from left to right
367     * (i.e. we will use {@code r1.compose(r2.compose(r3, convention), convention)})
368     * @param convention convention to use for the semantics of the angle
369     * @param alpha1 angle of the first elementary rotation
370     * @param alpha2 angle of the second elementary rotation
371     * @param alpha3 angle of the third elementary rotation
372     * @since 3.6
373     */
374    public FieldRotation(final RotationOrder order, final RotationConvention convention,
375                         final T alpha1, final T alpha2, final T alpha3) {
376        final T one = alpha1.getField().getOne();
377        final FieldRotation<T> r1 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA1()), alpha1, convention);
378        final FieldRotation<T> r2 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA2()), alpha2, convention);
379        final FieldRotation<T> r3 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA3()), alpha3, convention);
380        final FieldRotation<T> composed = r1.compose(r2.compose(r3, convention), convention);
381        q0 = composed.q0;
382        q1 = composed.q1;
383        q2 = composed.q2;
384        q3 = composed.q3;
385    }
386
387    /** Convert an orthogonal rotation matrix to a quaternion.
388     * @param ort orthogonal rotation matrix
389     * @return quaternion corresponding to the matrix
390     */
391    private T[] mat2quat(final T[][] ort) {
392
393        final T[] quat = MathArrays.buildArray(ort[0][0].getField(), 4);
394
395        // There are different ways to compute the quaternions elements
396        // from the matrix. They all involve computing one element from
397        // the diagonal of the matrix, and computing the three other ones
398        // using a formula involving a division by the first element,
399        // which unfortunately can be zero. Since the norm of the
400        // quaternion is 1, we know at least one element has an absolute
401        // value greater or equal to 0.5, so it is always possible to
402        // select the right formula and avoid division by zero and even
403        // numerical inaccuracy. Checking the elements in turn and using
404        // the first one greater than 0.45 is safe (this leads to a simple
405        // test since qi = 0.45 implies 4 qi^2 - 1 = -0.19)
406        T s = ort[0][0].add(ort[1][1]).add(ort[2][2]);
407        if (s.getReal() > -0.19) {
408            // compute q0 and deduce q1, q2 and q3
409            quat[0] = s.add(1.0).sqrt().multiply(0.5);
410            T inv = quat[0].reciprocal().multiply(0.25);
411            quat[1] = inv.multiply(ort[1][2].subtract(ort[2][1]));
412            quat[2] = inv.multiply(ort[2][0].subtract(ort[0][2]));
413            quat[3] = inv.multiply(ort[0][1].subtract(ort[1][0]));
414        } else {
415            s = ort[0][0].subtract(ort[1][1]).subtract(ort[2][2]);
416            if (s.getReal() > -0.19) {
417                // compute q1 and deduce q0, q2 and q3
418                quat[1] = s.add(1.0).sqrt().multiply(0.5);
419                T inv = quat[1].reciprocal().multiply(0.25);
420                quat[0] = inv.multiply(ort[1][2].subtract(ort[2][1]));
421                quat[2] = inv.multiply(ort[0][1].add(ort[1][0]));
422                quat[3] = inv.multiply(ort[0][2].add(ort[2][0]));
423            } else {
424                s = ort[1][1].subtract(ort[0][0]).subtract(ort[2][2]);
425                if (s.getReal() > -0.19) {
426                    // compute q2 and deduce q0, q1 and q3
427                    quat[2] = s.add(1.0).sqrt().multiply(0.5);
428                    T inv = quat[2].reciprocal().multiply(0.25);
429                    quat[0] = inv.multiply(ort[2][0].subtract(ort[0][2]));
430                    quat[1] = inv.multiply(ort[0][1].add(ort[1][0]));
431                    quat[3] = inv.multiply(ort[2][1].add(ort[1][2]));
432                } else {
433                    // compute q3 and deduce q0, q1 and q2
434                    s = ort[2][2].subtract(ort[0][0]).subtract(ort[1][1]);
435                    quat[3] = s.add(1.0).sqrt().multiply(0.5);
436                    T inv = quat[3].reciprocal().multiply(0.25);
437                    quat[0] = inv.multiply(ort[0][1].subtract(ort[1][0]));
438                    quat[1] = inv.multiply(ort[0][2].add(ort[2][0]));
439                    quat[2] = inv.multiply(ort[2][1].add(ort[1][2]));
440                }
441            }
442        }
443
444        return quat;
445
446    }
447
448    /** Revert a rotation.
449     * Build a rotation which reverse the effect of another
450     * rotation. This means that if r(u) = v, then r.revert(v) = u. The
451     * instance is not changed.
452     * @return a new rotation whose effect is the reverse of the effect
453     * of the instance
454     */
455    public FieldRotation<T> revert() {
456        return new FieldRotation<T>(q0.negate(), q1, q2, q3, false);
457    }
458
459    /** Get the scalar coordinate of the quaternion.
460     * @return scalar coordinate of the quaternion
461     */
462    public T getQ0() {
463        return q0;
464    }
465
466    /** Get the first coordinate of the vectorial part of the quaternion.
467     * @return first coordinate of the vectorial part of the quaternion
468     */
469    public T getQ1() {
470        return q1;
471    }
472
473    /** Get the second coordinate of the vectorial part of the quaternion.
474     * @return second coordinate of the vectorial part of the quaternion
475     */
476    public T getQ2() {
477        return q2;
478    }
479
480    /** Get the third coordinate of the vectorial part of the quaternion.
481     * @return third coordinate of the vectorial part of the quaternion
482     */
483    public T getQ3() {
484        return q3;
485    }
486
487    /** Get the normalized axis of the rotation.
488     * @return normalized axis of the rotation
489     * @see #FieldRotation(FieldVector3D, RealFieldElement)
490     * @deprecated as of 3.6, replaced with {@link #getAxis(RotationConvention)}
491     */
492    @Deprecated
493    public FieldVector3D<T> getAxis() {
494        return getAxis(RotationConvention.VECTOR_OPERATOR);
495    }
496
497    /** Get the normalized axis of the rotation.
498     * <p>
499     * Note that as {@link #getAngle()} always returns an angle
500     * between 0 and &pi;, changing the convention changes the
501     * direction of the axis, not the sign of the angle.
502     * </p>
503     * @param convention convention to use for the semantics of the angle
504     * @return normalized axis of the rotation
505     * @see #FieldRotation(FieldVector3D, RealFieldElement)
506     * @since 3.6
507     */
508    public FieldVector3D<T> getAxis(final RotationConvention convention) {
509        final T squaredSine = q1.multiply(q1).add(q2.multiply(q2)).add(q3.multiply(q3));
510        if (squaredSine.getReal() == 0) {
511            final Field<T> field = squaredSine.getField();
512            return new FieldVector3D<T>(convention == RotationConvention.VECTOR_OPERATOR ? field.getOne(): field.getOne().negate(),
513                                        field.getZero(),
514                                        field.getZero());
515        } else {
516            final double sgn = convention == RotationConvention.VECTOR_OPERATOR ? +1 : -1;
517            if (q0.getReal() < 0) {
518                T inverse = squaredSine.sqrt().reciprocal().multiply(sgn);
519                return new FieldVector3D<T>(q1.multiply(inverse), q2.multiply(inverse), q3.multiply(inverse));
520            }
521            final T inverse = squaredSine.sqrt().reciprocal().negate().multiply(sgn);
522            return new FieldVector3D<T>(q1.multiply(inverse), q2.multiply(inverse), q3.multiply(inverse));
523        }
524    }
525
526    /** Get the angle of the rotation.
527     * @return angle of the rotation (between 0 and &pi;)
528     * @see #FieldRotation(FieldVector3D, RealFieldElement)
529     */
530    public T getAngle() {
531        if ((q0.getReal() < -0.1) || (q0.getReal() > 0.1)) {
532            return q1.multiply(q1).add(q2.multiply(q2)).add(q3.multiply(q3)).sqrt().asin().multiply(2);
533        } else if (q0.getReal() < 0) {
534            return q0.negate().acos().multiply(2);
535        }
536        return q0.acos().multiply(2);
537    }
538
539    /** Get the Cardan or Euler angles corresponding to the instance.
540
541     * <p>The equations show that each rotation can be defined by two
542     * different values of the Cardan or Euler angles set. For example
543     * if Cardan angles are used, the rotation defined by the angles
544     * a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
545     * the rotation defined by the angles &pi; + a<sub>1</sub>, &pi;
546     * - a<sub>2</sub> and &pi; + a<sub>3</sub>. This method implements
547     * the following arbitrary choices:</p>
548     * <ul>
549     *   <li>for Cardan angles, the chosen set is the one for which the
550     *   second angle is between -&pi;/2 and &pi;/2 (i.e its cosine is
551     *   positive),</li>
552     *   <li>for Euler angles, the chosen set is the one for which the
553     *   second angle is between 0 and &pi; (i.e its sine is positive).</li>
554     * </ul>
555
556     * <p>Cardan and Euler angle have a very disappointing drawback: all
557     * of them have singularities. This means that if the instance is
558     * too close to the singularities corresponding to the given
559     * rotation order, it will be impossible to retrieve the angles. For
560     * Cardan angles, this is often called gimbal lock. There is
561     * <em>nothing</em> to do to prevent this, it is an intrinsic problem
562     * with Cardan and Euler representation (but not a problem with the
563     * rotation itself, which is perfectly well defined). For Cardan
564     * angles, singularities occur when the second angle is close to
565     * -&pi;/2 or +&pi;/2, for Euler angle singularities occur when the
566     * second angle is close to 0 or &pi;, this implies that the identity
567     * rotation is always singular for Euler angles!</p>
568
569     * @param order rotation order to use
570     * @return an array of three angles, in the order specified by the set
571     * @exception CardanEulerSingularityException if the rotation is
572     * singular with respect to the angles set specified
573     * @deprecated as of 3.6, replaced with {@link #getAngles(RotationOrder, RotationConvention)}
574     */
575    @Deprecated
576    public T[] getAngles(final RotationOrder order)
577        throws CardanEulerSingularityException {
578        return getAngles(order, RotationConvention.VECTOR_OPERATOR);
579    }
580
581    /** Get the Cardan or Euler angles corresponding to the instance.
582
583     * <p>The equations show that each rotation can be defined by two
584     * different values of the Cardan or Euler angles set. For example
585     * if Cardan angles are used, the rotation defined by the angles
586     * a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
587     * the rotation defined by the angles &pi; + a<sub>1</sub>, &pi;
588     * - a<sub>2</sub> and &pi; + a<sub>3</sub>. This method implements
589     * the following arbitrary choices:</p>
590     * <ul>
591     *   <li>for Cardan angles, the chosen set is the one for which the
592     *   second angle is between -&pi;/2 and &pi;/2 (i.e its cosine is
593     *   positive),</li>
594     *   <li>for Euler angles, the chosen set is the one for which the
595     *   second angle is between 0 and &pi; (i.e its sine is positive).</li>
596     * </ul>
597
598     * <p>Cardan and Euler angle have a very disappointing drawback: all
599     * of them have singularities. This means that if the instance is
600     * too close to the singularities corresponding to the given
601     * rotation order, it will be impossible to retrieve the angles. For
602     * Cardan angles, this is often called gimbal lock. There is
603     * <em>nothing</em> to do to prevent this, it is an intrinsic problem
604     * with Cardan and Euler representation (but not a problem with the
605     * rotation itself, which is perfectly well defined). For Cardan
606     * angles, singularities occur when the second angle is close to
607     * -&pi;/2 or +&pi;/2, for Euler angle singularities occur when the
608     * second angle is close to 0 or &pi;, this implies that the identity
609     * rotation is always singular for Euler angles!</p>
610
611     * @param order rotation order to use
612     * @param convention convention to use for the semantics of the angle
613     * @return an array of three angles, in the order specified by the set
614     * @exception CardanEulerSingularityException if the rotation is
615     * singular with respect to the angles set specified
616     * @since 3.6
617     */
618    public T[] getAngles(final RotationOrder order, RotationConvention convention)
619        throws CardanEulerSingularityException {
620
621        if (convention == RotationConvention.VECTOR_OPERATOR) {
622            if (order == RotationOrder.XYZ) {
623
624                // r (+K) coordinates are :
625                //  sin (theta), -cos (theta) sin (phi), cos (theta) cos (phi)
626                // (-r) (+I) coordinates are :
627                // cos (psi) cos (theta), -sin (psi) cos (theta), sin (theta)
628                final // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
629                FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
630                final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
631                if  ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
632                    throw new CardanEulerSingularityException(true);
633                }
634                return buildArray(v1.getY().negate().atan2(v1.getZ()),
635                                  v2.getZ().asin(),
636                                  v2.getY().negate().atan2(v2.getX()));
637
638            } else if (order == RotationOrder.XZY) {
639
640                // r (+J) coordinates are :
641                // -sin (psi), cos (psi) cos (phi), cos (psi) sin (phi)
642                // (-r) (+I) coordinates are :
643                // cos (theta) cos (psi), -sin (psi), sin (theta) cos (psi)
644                // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
645                final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
646                final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
647                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
648                    throw new CardanEulerSingularityException(true);
649                }
650                return buildArray(v1.getZ().atan2(v1.getY()),
651                                  v2.getY().asin().negate(),
652                                  v2.getZ().atan2(v2.getX()));
653
654            } else if (order == RotationOrder.YXZ) {
655
656                // r (+K) coordinates are :
657                //  cos (phi) sin (theta), -sin (phi), cos (phi) cos (theta)
658                // (-r) (+J) coordinates are :
659                // sin (psi) cos (phi), cos (psi) cos (phi), -sin (phi)
660                // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
661                final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
662                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
663                if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
664                    throw new CardanEulerSingularityException(true);
665                }
666                return buildArray(v1.getX().atan2(v1.getZ()),
667                                  v2.getZ().asin().negate(),
668                                  v2.getX().atan2(v2.getY()));
669
670            } else if (order == RotationOrder.YZX) {
671
672                // r (+I) coordinates are :
673                // cos (psi) cos (theta), sin (psi), -cos (psi) sin (theta)
674                // (-r) (+J) coordinates are :
675                // sin (psi), cos (phi) cos (psi), -sin (phi) cos (psi)
676                // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
677                final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
678                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
679                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
680                    throw new CardanEulerSingularityException(true);
681                }
682                return buildArray(v1.getZ().negate().atan2(v1.getX()),
683                                  v2.getX().asin(),
684                                  v2.getZ().negate().atan2(v2.getY()));
685
686            } else if (order == RotationOrder.ZXY) {
687
688                // r (+J) coordinates are :
689                // -cos (phi) sin (psi), cos (phi) cos (psi), sin (phi)
690                // (-r) (+K) coordinates are :
691                // -sin (theta) cos (phi), sin (phi), cos (theta) cos (phi)
692                // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
693                final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
694                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
695                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
696                    throw new CardanEulerSingularityException(true);
697                }
698                return buildArray(v1.getX().negate().atan2(v1.getY()),
699                                  v2.getY().asin(),
700                                  v2.getX().negate().atan2(v2.getZ()));
701
702            } else if (order == RotationOrder.ZYX) {
703
704                // r (+I) coordinates are :
705                //  cos (theta) cos (psi), cos (theta) sin (psi), -sin (theta)
706                // (-r) (+K) coordinates are :
707                // -sin (theta), sin (phi) cos (theta), cos (phi) cos (theta)
708                // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
709                final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
710                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
711                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
712                    throw new CardanEulerSingularityException(true);
713                }
714                return buildArray(v1.getY().atan2(v1.getX()),
715                                  v2.getX().asin().negate(),
716                                  v2.getY().atan2(v2.getZ()));
717
718            } else if (order == RotationOrder.XYX) {
719
720                // r (+I) coordinates are :
721                //  cos (theta), sin (phi1) sin (theta), -cos (phi1) sin (theta)
722                // (-r) (+I) coordinates are :
723                // cos (theta), sin (theta) sin (phi2), sin (theta) cos (phi2)
724                // and we can choose to have theta in the interval [0 ; PI]
725                final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
726                final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
727                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
728                    throw new CardanEulerSingularityException(false);
729                }
730                return buildArray(v1.getY().atan2(v1.getZ().negate()),
731                                  v2.getX().acos(),
732                                  v2.getY().atan2(v2.getZ()));
733
734            } else if (order == RotationOrder.XZX) {
735
736                // r (+I) coordinates are :
737                //  cos (psi), cos (phi1) sin (psi), sin (phi1) sin (psi)
738                // (-r) (+I) coordinates are :
739                // cos (psi), -sin (psi) cos (phi2), sin (psi) sin (phi2)
740                // and we can choose to have psi in the interval [0 ; PI]
741                final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
742                final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
743                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
744                    throw new CardanEulerSingularityException(false);
745                }
746                return buildArray(v1.getZ().atan2(v1.getY()),
747                                  v2.getX().acos(),
748                                  v2.getZ().atan2(v2.getY().negate()));
749
750            } else if (order == RotationOrder.YXY) {
751
752                // r (+J) coordinates are :
753                //  sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
754                // (-r) (+J) coordinates are :
755                // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
756                // and we can choose to have phi in the interval [0 ; PI]
757                final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
758                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
759                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
760                    throw new CardanEulerSingularityException(false);
761                }
762                return buildArray(v1.getX().atan2(v1.getZ()),
763                                  v2.getY().acos(),
764                                  v2.getX().atan2(v2.getZ().negate()));
765
766            } else if (order == RotationOrder.YZY) {
767
768                // r (+J) coordinates are :
769                //  -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
770                // (-r) (+J) coordinates are :
771                // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
772                // and we can choose to have psi in the interval [0 ; PI]
773                final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
774                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
775                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
776                    throw new CardanEulerSingularityException(false);
777                }
778                return buildArray(v1.getZ().atan2(v1.getX().negate()),
779                                  v2.getY().acos(),
780                                  v2.getZ().atan2(v2.getX()));
781
782            } else if (order == RotationOrder.ZXZ) {
783
784                // r (+K) coordinates are :
785                //  sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
786                // (-r) (+K) coordinates are :
787                // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
788                // and we can choose to have phi in the interval [0 ; PI]
789                final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
790                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
791                if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
792                    throw new CardanEulerSingularityException(false);
793                }
794                return buildArray(v1.getX().atan2(v1.getY().negate()),
795                                  v2.getZ().acos(),
796                                  v2.getX().atan2(v2.getY()));
797
798            } else { // last possibility is ZYZ
799
800                // r (+K) coordinates are :
801                //  cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
802                // (-r) (+K) coordinates are :
803                // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
804                // and we can choose to have theta in the interval [0 ; PI]
805                final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
806                final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
807                if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
808                    throw new CardanEulerSingularityException(false);
809                }
810                return buildArray(v1.getY().atan2(v1.getX()),
811                                  v2.getZ().acos(),
812                                  v2.getY().atan2(v2.getX().negate()));
813
814            }
815        } else {
816            if (order == RotationOrder.XYZ) {
817
818                // r (Vector3D.plusI) coordinates are :
819                //  cos (theta) cos (psi), -cos (theta) sin (psi), sin (theta)
820                // (-r) (Vector3D.plusK) coordinates are :
821                // sin (theta), -sin (phi) cos (theta), cos (phi) cos (theta)
822                // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
823                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
824                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
825                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
826                    throw new CardanEulerSingularityException(true);
827                }
828                return buildArray(v2.getY().negate().atan2(v2.getZ()),
829                                  v2.getX().asin(),
830                                  v1.getY().negate().atan2(v1.getX()));
831
832            } else if (order == RotationOrder.XZY) {
833
834                // r (Vector3D.plusI) coordinates are :
835                // cos (psi) cos (theta), -sin (psi), cos (psi) sin (theta)
836                // (-r) (Vector3D.plusJ) coordinates are :
837                // -sin (psi), cos (phi) cos (psi), sin (phi) cos (psi)
838                // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
839                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
840                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
841                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
842                    throw new CardanEulerSingularityException(true);
843                }
844                return buildArray(v2.getZ().atan2(v2.getY()),
845                                  v2.getX().asin().negate(),
846                                  v1.getZ().atan2(v1.getX()));
847
848            } else if (order == RotationOrder.YXZ) {
849
850                // r (Vector3D.plusJ) coordinates are :
851                // cos (phi) sin (psi), cos (phi) cos (psi), -sin (phi)
852                // (-r) (Vector3D.plusK) coordinates are :
853                // sin (theta) cos (phi), -sin (phi), cos (theta) cos (phi)
854                // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
855                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
856                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
857                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
858                    throw new CardanEulerSingularityException(true);
859                }
860                return buildArray(v2.getX().atan2(v2.getZ()),
861                                  v2.getY().asin().negate(),
862                                  v1.getX().atan2(v1.getY()));
863
864            } else if (order == RotationOrder.YZX) {
865
866                // r (Vector3D.plusJ) coordinates are :
867                // sin (psi), cos (psi) cos (phi), -cos (psi) sin (phi)
868                // (-r) (Vector3D.plusI) coordinates are :
869                // cos (theta) cos (psi), sin (psi), -sin (theta) cos (psi)
870                // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
871                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
872                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
873                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
874                    throw new CardanEulerSingularityException(true);
875                }
876                return buildArray(v2.getZ().negate().atan2(v2.getX()),
877                                  v2.getY().asin(),
878                                  v1.getZ().negate().atan2(v1.getY()));
879
880            } else if (order == RotationOrder.ZXY) {
881
882                // r (Vector3D.plusK) coordinates are :
883                //  -cos (phi) sin (theta), sin (phi), cos (phi) cos (theta)
884                // (-r) (Vector3D.plusJ) coordinates are :
885                // -sin (psi) cos (phi), cos (psi) cos (phi), sin (phi)
886                // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
887                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
888                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
889                if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
890                    throw new CardanEulerSingularityException(true);
891                }
892                return buildArray(v2.getX().negate().atan2(v2.getY()),
893                                  v2.getZ().asin(),
894                                  v1.getX().negate().atan2(v1.getZ()));
895
896            } else if (order == RotationOrder.ZYX) {
897
898                // r (Vector3D.plusK) coordinates are :
899                //  -sin (theta), cos (theta) sin (phi), cos (theta) cos (phi)
900                // (-r) (Vector3D.plusI) coordinates are :
901                // cos (psi) cos (theta), sin (psi) cos (theta), -sin (theta)
902                // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
903                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
904                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
905                if  ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
906                    throw new CardanEulerSingularityException(true);
907                }
908                return buildArray(v2.getY().atan2(v2.getX()),
909                                  v2.getZ().asin().negate(),
910                                  v1.getY().atan2(v1.getZ()));
911
912            } else if (order == RotationOrder.XYX) {
913
914                // r (Vector3D.plusI) coordinates are :
915                //  cos (theta), sin (phi2) sin (theta), cos (phi2) sin (theta)
916                // (-r) (Vector3D.plusI) coordinates are :
917                // cos (theta), sin (theta) sin (phi1), -sin (theta) cos (phi1)
918                // and we can choose to have theta in the interval [0 ; PI]
919                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
920                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
921                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
922                    throw new CardanEulerSingularityException(false);
923                }
924                return buildArray(v2.getY().atan2(v2.getZ().negate()),
925                                  v2.getX().acos(),
926                                  v1.getY().atan2(v1.getZ()));
927
928            } else if (order == RotationOrder.XZX) {
929
930                // r (Vector3D.plusI) coordinates are :
931                //  cos (psi), -cos (phi2) sin (psi), sin (phi2) sin (psi)
932                // (-r) (Vector3D.plusI) coordinates are :
933                // cos (psi), sin (psi) cos (phi1), sin (psi) sin (phi1)
934                // and we can choose to have psi in the interval [0 ; PI]
935                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
936                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
937                if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
938                    throw new CardanEulerSingularityException(false);
939                }
940                return buildArray(v2.getZ().atan2(v2.getY()),
941                                  v2.getX().acos(),
942                                  v1.getZ().atan2(v1.getY().negate()));
943
944            } else if (order == RotationOrder.YXY) {
945
946                // r (Vector3D.plusJ) coordinates are :
947                // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
948                // (-r) (Vector3D.plusJ) coordinates are :
949                //  sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
950                // and we can choose to have phi in the interval [0 ; PI]
951                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
952                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
953                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
954                    throw new CardanEulerSingularityException(false);
955                }
956                return buildArray(v2.getX().atan2(v2.getZ()),
957                                  v2.getY().acos(),
958                                  v1.getX().atan2(v1.getZ().negate()));
959
960            } else if (order == RotationOrder.YZY) {
961
962                // r (Vector3D.plusJ) coordinates are :
963                // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
964                // (-r) (Vector3D.plusJ) coordinates are :
965                //  -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
966                // and we can choose to have psi in the interval [0 ; PI]
967                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
968                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
969                if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
970                    throw new CardanEulerSingularityException(false);
971                }
972                return buildArray(v2.getZ().atan2(v2.getX().negate()),
973                                  v2.getY().acos(),
974                                  v1.getZ().atan2(v1.getX()));
975
976            } else if (order == RotationOrder.ZXZ) {
977
978                // r (Vector3D.plusK) coordinates are :
979                // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
980                // (-r) (Vector3D.plusK) coordinates are :
981                //  sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
982                // and we can choose to have phi in the interval [0 ; PI]
983                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
984                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
985                if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
986                    throw new CardanEulerSingularityException(false);
987                }
988                return buildArray(v2.getX().atan2(v2.getY().negate()),
989                                  v2.getZ().acos(),
990                                  v1.getX().atan2(v1.getY()));
991
992            } else { // last possibility is ZYZ
993
994                // r (Vector3D.plusK) coordinates are :
995                // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
996                // (-r) (Vector3D.plusK) coordinates are :
997                //  cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
998                // and we can choose to have theta in the interval [0 ; PI]
999                FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
1000                FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
1001                if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
1002                    throw new CardanEulerSingularityException(false);
1003                }
1004                return buildArray(v2.getY().atan2(v2.getX()),
1005                                  v2.getZ().acos(),
1006                                  v1.getY().atan2(v1.getX().negate()));
1007
1008            }
1009        }
1010
1011    }
1012
1013    /** Create a dimension 3 array.
1014     * @param a0 first array element
1015     * @param a1 second array element
1016     * @param a2 third array element
1017     * @return new array
1018     */
1019    private T[] buildArray(final T a0, final T a1, final T a2) {
1020        final T[] array = MathArrays.buildArray(a0.getField(), 3);
1021        array[0] = a0;
1022        array[1] = a1;
1023        array[2] = a2;
1024        return array;
1025    }
1026
1027    /** Create a constant vector.
1028     * @param x abscissa
1029     * @param y ordinate
1030     * @param z height
1031     * @return a constant vector
1032     */
1033    private FieldVector3D<T> vector(final double x, final double y, final double z) {
1034        final T zero = q0.getField().getZero();
1035        return new FieldVector3D<T>(zero.add(x), zero.add(y), zero.add(z));
1036    }
1037
1038    /** Get the 3X3 matrix corresponding to the instance
1039     * @return the matrix corresponding to the instance
1040     */
1041    public T[][] getMatrix() {
1042
1043        // products
1044        final T q0q0  = q0.multiply(q0);
1045        final T q0q1  = q0.multiply(q1);
1046        final T q0q2  = q0.multiply(q2);
1047        final T q0q3  = q0.multiply(q3);
1048        final T q1q1  = q1.multiply(q1);
1049        final T q1q2  = q1.multiply(q2);
1050        final T q1q3  = q1.multiply(q3);
1051        final T q2q2  = q2.multiply(q2);
1052        final T q2q3  = q2.multiply(q3);
1053        final T q3q3  = q3.multiply(q3);
1054
1055        // create the matrix
1056        final T[][] m = MathArrays.buildArray(q0.getField(), 3, 3);
1057
1058        m [0][0] = q0q0.add(q1q1).multiply(2).subtract(1);
1059        m [1][0] = q1q2.subtract(q0q3).multiply(2);
1060        m [2][0] = q1q3.add(q0q2).multiply(2);
1061
1062        m [0][1] = q1q2.add(q0q3).multiply(2);
1063        m [1][1] = q0q0.add(q2q2).multiply(2).subtract(1);
1064        m [2][1] = q2q3.subtract(q0q1).multiply(2);
1065
1066        m [0][2] = q1q3.subtract(q0q2).multiply(2);
1067        m [1][2] = q2q3.add(q0q1).multiply(2);
1068        m [2][2] = q0q0.add(q3q3).multiply(2).subtract(1);
1069
1070        return m;
1071
1072    }
1073
1074    /** Convert to a constant vector without derivatives.
1075     * @return a constant vector
1076     */
1077    public Rotation toRotation() {
1078        return new Rotation(q0.getReal(), q1.getReal(), q2.getReal(), q3.getReal(), false);
1079    }
1080
1081    /** Apply the rotation to a vector.
1082     * @param u vector to apply the rotation to
1083     * @return a new vector which is the image of u by the rotation
1084     */
1085    public FieldVector3D<T> applyTo(final FieldVector3D<T> u) {
1086
1087        final T x = u.getX();
1088        final T y = u.getY();
1089        final T z = u.getZ();
1090
1091        final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1092
1093        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),
1094                                    q0.multiply(y.multiply(q0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
1095                                    q0.multiply(z.multiply(q0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
1096
1097    }
1098
1099    /** Apply the rotation to a vector.
1100     * @param u vector to apply the rotation to
1101     * @return a new vector which is the image of u by the rotation
1102     */
1103    public FieldVector3D<T> applyTo(final Vector3D u) {
1104
1105        final double x = u.getX();
1106        final double y = u.getY();
1107        final double z = u.getZ();
1108
1109        final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1110
1111        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),
1112                                    q0.multiply(q0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
1113                                    q0.multiply(q0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
1114
1115    }
1116
1117    /** Apply the rotation to a vector stored in an array.
1118     * @param in an array with three items which stores vector to rotate
1119     * @param out an array with three items to put result to (it can be the same
1120     * array as in)
1121     */
1122    public void applyTo(final T[] in, final T[] out) {
1123
1124        final T x = in[0];
1125        final T y = in[1];
1126        final T z = in[2];
1127
1128        final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1129
1130        out[0] = q0.multiply(x.multiply(q0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
1131        out[1] = q0.multiply(y.multiply(q0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
1132        out[2] = q0.multiply(z.multiply(q0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
1133
1134    }
1135
1136    /** Apply the rotation to a vector stored in an array.
1137     * @param in an array with three items which stores vector to rotate
1138     * @param out an array with three items to put result to
1139     */
1140    public void applyTo(final double[] in, final T[] out) {
1141
1142        final double x = in[0];
1143        final double y = in[1];
1144        final double z = in[2];
1145
1146        final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1147
1148        out[0] = q0.multiply(q0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
1149        out[1] = q0.multiply(q0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
1150        out[2] = q0.multiply(q0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
1151
1152    }
1153
1154    /** Apply a rotation to a vector.
1155     * @param r rotation to apply
1156     * @param u vector to apply the rotation to
1157     * @param <T> the type of the field elements
1158     * @return a new vector which is the image of u by the rotation
1159     */
1160    public static <T extends RealFieldElement<T>> FieldVector3D<T> applyTo(final Rotation r, final FieldVector3D<T> u) {
1161
1162        final T x = u.getX();
1163        final T y = u.getY();
1164        final T z = u.getZ();
1165
1166        final T s = x.multiply(r.getQ1()).add(y.multiply(r.getQ2())).add(z.multiply(r.getQ3()));
1167
1168        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),
1169                                    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),
1170                                    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));
1171
1172    }
1173
1174    /** Apply the inverse of the rotation to a vector.
1175     * @param u vector to apply the inverse of the rotation to
1176     * @return a new vector which such that u is its image by the rotation
1177     */
1178    public FieldVector3D<T> applyInverseTo(final FieldVector3D<T> u) {
1179
1180        final T x = u.getX();
1181        final T y = u.getY();
1182        final T z = u.getZ();
1183
1184        final T s  = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1185        final T m0 = q0.negate();
1186
1187        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),
1188                                    m0.multiply(y.multiply(m0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
1189                                    m0.multiply(z.multiply(m0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
1190
1191    }
1192
1193    /** Apply the inverse of the rotation to a vector.
1194     * @param u vector to apply the inverse of the rotation to
1195     * @return a new vector which such that u is its image by the rotation
1196     */
1197    public FieldVector3D<T> applyInverseTo(final Vector3D u) {
1198
1199        final double x = u.getX();
1200        final double y = u.getY();
1201        final double z = u.getZ();
1202
1203        final T s  = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1204        final T m0 = q0.negate();
1205
1206        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),
1207                                    m0.multiply(m0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
1208                                    m0.multiply(m0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
1209
1210    }
1211
1212    /** Apply the inverse of the rotation to a vector stored in an array.
1213     * @param in an array with three items which stores vector to rotate
1214     * @param out an array with three items to put result to (it can be the same
1215     * array as in)
1216     */
1217    public void applyInverseTo(final T[] in, final T[] out) {
1218
1219        final T x = in[0];
1220        final T y = in[1];
1221        final T z = in[2];
1222
1223        final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1224        final T m0 = q0.negate();
1225
1226        out[0] = m0.multiply(x.multiply(m0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
1227        out[1] = m0.multiply(y.multiply(m0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
1228        out[2] = m0.multiply(z.multiply(m0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
1229
1230    }
1231
1232    /** Apply the inverse of the rotation to a vector stored in an array.
1233     * @param in an array with three items which stores vector to rotate
1234     * @param out an array with three items to put result to
1235     */
1236    public void applyInverseTo(final double[] in, final T[] out) {
1237
1238        final double x = in[0];
1239        final double y = in[1];
1240        final double z = in[2];
1241
1242        final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
1243        final T m0 = q0.negate();
1244
1245        out[0] = m0.multiply(m0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
1246        out[1] = m0.multiply(m0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
1247        out[2] = m0.multiply(m0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
1248
1249    }
1250
1251    /** Apply the inverse of a rotation to a vector.
1252     * @param r rotation to apply
1253     * @param u vector to apply the inverse of the rotation to
1254     * @param <T> the type of the field elements
1255     * @return a new vector which such that u is its image by the rotation
1256     */
1257    public static <T extends RealFieldElement<T>> FieldVector3D<T> applyInverseTo(final Rotation r, final FieldVector3D<T> u) {
1258
1259        final T x = u.getX();
1260        final T y = u.getY();
1261        final T z = u.getZ();
1262
1263        final T s  = x.multiply(r.getQ1()).add(y.multiply(r.getQ2())).add(z.multiply(r.getQ3()));
1264        final double m0 = -r.getQ0();
1265
1266        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),
1267                                    y.multiply(m0).subtract(x.multiply(r.getQ3()).subtract(z.multiply(r.getQ1()))).multiply(m0).add(s.multiply(r.getQ2())).multiply(2).subtract(y),
1268                                    z.multiply(m0).subtract(y.multiply(r.getQ1()).subtract(x.multiply(r.getQ2()))).multiply(m0).add(s.multiply(r.getQ3())).multiply(2).subtract(z));
1269
1270    }
1271
1272    /** Apply the instance to another rotation.
1273     * <p>
1274     * Calling this method is equivalent to call
1275     * {@link #compose(FieldRotation, RotationConvention)
1276     * compose(r, RotationConvention.VECTOR_OPERATOR)}.
1277     * </p>
1278     * @param r rotation to apply the rotation to
1279     * @return a new rotation which is the composition of r by the instance
1280     */
1281    public FieldRotation<T> applyTo(final FieldRotation<T> r) {
1282        return compose(r, RotationConvention.VECTOR_OPERATOR);
1283    }
1284
1285    /** Compose the instance with another rotation.
1286     * <p>
1287     * If the semantics of the rotations composition corresponds to a
1288     * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
1289     * applying the instance to a rotation is computing the composition
1290     * in an order compliant with the following rule : let {@code u} be any
1291     * vector and {@code v} its image by {@code r1} (i.e.
1292     * {@code r1.applyTo(u) = v}). Let {@code w} be the image of {@code v} by
1293     * rotation {@code r2} (i.e. {@code r2.applyTo(v) = w}). Then
1294     * {@code w = comp.applyTo(u)}, where
1295     * {@code comp = r2.compose(r1, RotationConvention.VECTOR_OPERATOR)}.
1296     * </p>
1297     * <p>
1298     * If the semantics of the rotations composition corresponds to a
1299     * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
1300     * the application order will be reversed. So keeping the exact same
1301     * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
1302     * and  {@code comp} as above, {@code comp} could also be computed as
1303     * {@code comp = r1.compose(r2, RotationConvention.FRAME_TRANSFORM)}.
1304     * </p>
1305     * @param r rotation to apply the rotation to
1306     * @param convention convention to use for the semantics of the angle
1307     * @return a new rotation which is the composition of r by the instance
1308     */
1309    public FieldRotation<T> compose(final FieldRotation<T> r, final RotationConvention convention) {
1310        return convention == RotationConvention.VECTOR_OPERATOR ?
1311                             composeInternal(r) : r.composeInternal(this);
1312    }
1313
1314    /** Compose the instance with another rotation using vector operator convention.
1315     * @param r rotation to apply the rotation to
1316     * @return a new rotation which is the composition of r by the instance
1317     * using vector operator convention
1318     */
1319    private FieldRotation<T> composeInternal(final FieldRotation<T> r) {
1320        return new FieldRotation<T>(r.q0.multiply(q0).subtract(r.q1.multiply(q1).add(r.q2.multiply(q2)).add(r.q3.multiply(q3))),
1321                                    r.q1.multiply(q0).add(r.q0.multiply(q1)).add(r.q2.multiply(q3).subtract(r.q3.multiply(q2))),
1322                                    r.q2.multiply(q0).add(r.q0.multiply(q2)).add(r.q3.multiply(q1).subtract(r.q1.multiply(q3))),
1323                                    r.q3.multiply(q0).add(r.q0.multiply(q3)).add(r.q1.multiply(q2).subtract(r.q2.multiply(q1))),
1324                                    false);
1325    }
1326
1327    /** Apply the instance to another rotation.
1328     * <p>
1329     * Calling this method is equivalent to call
1330     * {@link #compose(Rotation, RotationConvention)
1331     * compose(r, RotationConvention.VECTOR_OPERATOR)}.
1332     * </p>
1333     * @param r rotation to apply the rotation to
1334     * @return a new rotation which is the composition of r by the instance
1335     */
1336    public FieldRotation<T> applyTo(final Rotation r) {
1337        return compose(r, RotationConvention.VECTOR_OPERATOR);
1338    }
1339
1340    /** Compose the instance with another rotation.
1341     * <p>
1342     * If the semantics of the rotations composition corresponds to a
1343     * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
1344     * applying the instance to a rotation is computing the composition
1345     * in an order compliant with the following rule : let {@code u} be any
1346     * vector and {@code v} its image by {@code r1} (i.e.
1347     * {@code r1.applyTo(u) = v}). Let {@code w} be the image of {@code v} by
1348     * rotation {@code r2} (i.e. {@code r2.applyTo(v) = w}). Then
1349     * {@code w = comp.applyTo(u)}, where
1350     * {@code comp = r2.compose(r1, RotationConvention.VECTOR_OPERATOR)}.
1351     * </p>
1352     * <p>
1353     * If the semantics of the rotations composition corresponds to a
1354     * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
1355     * the application order will be reversed. So keeping the exact same
1356     * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
1357     * and  {@code comp} as above, {@code comp} could also be computed as
1358     * {@code comp = r1.compose(r2, RotationConvention.FRAME_TRANSFORM)}.
1359     * </p>
1360     * @param r rotation to apply the rotation to
1361     * @param convention convention to use for the semantics of the angle
1362     * @return a new rotation which is the composition of r by the instance
1363     */
1364    public FieldRotation<T> compose(final Rotation r, final RotationConvention convention) {
1365        return convention == RotationConvention.VECTOR_OPERATOR ?
1366                             composeInternal(r) : applyTo(r, this);
1367    }
1368
1369    /** Compose the instance with another rotation using vector operator convention.
1370     * @param r rotation to apply the rotation to
1371     * @return a new rotation which is the composition of r by the instance
1372     * using vector operator convention
1373     */
1374    private FieldRotation<T> composeInternal(final Rotation r) {
1375        return new FieldRotation<T>(q0.multiply(r.getQ0()).subtract(q1.multiply(r.getQ1()).add(q2.multiply(r.getQ2())).add(q3.multiply(r.getQ3()))),
1376                        q0.multiply(r.getQ1()).add(q1.multiply(r.getQ0())).add(q3.multiply(r.getQ2()).subtract(q2.multiply(r.getQ3()))),
1377                        q0.multiply(r.getQ2()).add(q2.multiply(r.getQ0())).add(q1.multiply(r.getQ3()).subtract(q3.multiply(r.getQ1()))),
1378                        q0.multiply(r.getQ3()).add(q3.multiply(r.getQ0())).add(q2.multiply(r.getQ1()).subtract(q1.multiply(r.getQ2()))),
1379                        false);
1380    }
1381
1382    /** Apply a rotation to another rotation.
1383     * Applying a rotation to another rotation is computing the composition
1384     * in an order compliant with the following rule : let u be any
1385     * vector and v its image by rInner (i.e. rInner.applyTo(u) = v), let w be the image
1386     * of v by rOuter (i.e. rOuter.applyTo(v) = w), then w = comp.applyTo(u),
1387     * where comp = applyTo(rOuter, rInner).
1388     * @param r1 rotation to apply
1389     * @param rInner rotation to apply the rotation to
1390     * @param <T> the type of the field elements
1391     * @return a new rotation which is the composition of r by the instance
1392     */
1393    public static <T extends RealFieldElement<T>> FieldRotation<T> applyTo(final Rotation r1, final FieldRotation<T> rInner) {
1394        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()))),
1395                                    rInner.q1.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ1())).add(rInner.q2.multiply(r1.getQ3()).subtract(rInner.q3.multiply(r1.getQ2()))),
1396                                    rInner.q2.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ2())).add(rInner.q3.multiply(r1.getQ1()).subtract(rInner.q1.multiply(r1.getQ3()))),
1397                                    rInner.q3.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ3())).add(rInner.q1.multiply(r1.getQ2()).subtract(rInner.q2.multiply(r1.getQ1()))),
1398                                    false);
1399    }
1400
1401    /** Apply the inverse of the instance to another rotation.
1402     * <p>
1403     * Calling this method is equivalent to call
1404     * {@link #composeInverse(FieldRotation, RotationConvention)
1405     * composeInverse(r, RotationConvention.VECTOR_OPERATOR)}.
1406     * </p>
1407     * @param r rotation to apply the rotation to
1408     * @return a new rotation which is the composition of r by the inverse
1409     * of the instance
1410     */
1411    public FieldRotation<T> applyInverseTo(final FieldRotation<T> r) {
1412        return composeInverse(r, RotationConvention.VECTOR_OPERATOR);
1413    }
1414
1415    /** Compose the inverse of the instance with another rotation.
1416     * <p>
1417     * If the semantics of the rotations composition corresponds to a
1418     * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
1419     * applying the inverse of the instance to a rotation is computing
1420     * the composition in an order compliant with the following rule :
1421     * let {@code u} be any vector and {@code v} its image by {@code r1}
1422     * (i.e. {@code r1.applyTo(u) = v}). Let {@code w} be the inverse image
1423     * of {@code v} by {@code r2} (i.e. {@code r2.applyInverseTo(v) = w}).
1424     * Then {@code w = comp.applyTo(u)}, where
1425     * {@code comp = r2.composeInverse(r1)}.
1426     * </p>
1427     * <p>
1428     * If the semantics of the rotations composition corresponds to a
1429     * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
1430     * the application order will be reversed, which means it is the
1431     * <em>innermost</em> rotation that will be reversed. So keeping the exact same
1432     * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
1433     * and  {@code comp} as above, {@code comp} could also be computed as
1434     * {@code comp = r1.revert().composeInverse(r2.revert(), RotationConvention.FRAME_TRANSFORM)}.
1435     * </p>
1436     * @param r rotation to apply the rotation to
1437     * @param convention convention to use for the semantics of the angle
1438     * @return a new rotation which is the composition of r by the inverse
1439     * of the instance
1440     */
1441    public FieldRotation<T> composeInverse(final FieldRotation<T> r, final RotationConvention convention) {
1442        return convention == RotationConvention.VECTOR_OPERATOR ?
1443                             composeInverseInternal(r) : r.composeInternal(revert());
1444    }
1445
1446    /** Compose the inverse of the instance with another rotation
1447     * using vector operator convention.
1448     * @param r rotation to apply the rotation to
1449     * @return a new rotation which is the composition of r by the inverse
1450     * of the instance using vector operator convention
1451     */
1452    private FieldRotation<T> composeInverseInternal(FieldRotation<T> r) {
1453        return new FieldRotation<T>(r.q0.multiply(q0).add(r.q1.multiply(q1).add(r.q2.multiply(q2)).add(r.q3.multiply(q3))).negate(),
1454                                    r.q0.multiply(q1).add(r.q2.multiply(q3).subtract(r.q3.multiply(q2))).subtract(r.q1.multiply(q0)),
1455                                    r.q0.multiply(q2).add(r.q3.multiply(q1).subtract(r.q1.multiply(q3))).subtract(r.q2.multiply(q0)),
1456                                    r.q0.multiply(q3).add(r.q1.multiply(q2).subtract(r.q2.multiply(q1))).subtract(r.q3.multiply(q0)),
1457                                    false);
1458    }
1459
1460    /** Apply the inverse of the instance to another rotation.
1461     * <p>
1462     * Calling this method is equivalent to call
1463     * {@link #composeInverse(Rotation, RotationConvention)
1464     * composeInverse(r, RotationConvention.VECTOR_OPERATOR)}.
1465     * </p>
1466     * @param r rotation to apply the rotation to
1467     * @return a new rotation which is the composition of r by the inverse
1468     * of the instance
1469     */
1470    public FieldRotation<T> applyInverseTo(final Rotation r) {
1471        return composeInverse(r, RotationConvention.VECTOR_OPERATOR);
1472    }
1473
1474    /** Compose the inverse of the instance with another rotation.
1475     * <p>
1476     * If the semantics of the rotations composition corresponds to a
1477     * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
1478     * applying the inverse of the instance to a rotation is computing
1479     * the composition in an order compliant with the following rule :
1480     * let {@code u} be any vector and {@code v} its image by {@code r1}
1481     * (i.e. {@code r1.applyTo(u) = v}). Let {@code w} be the inverse image
1482     * of {@code v} by {@code r2} (i.e. {@code r2.applyInverseTo(v) = w}).
1483     * Then {@code w = comp.applyTo(u)}, where
1484     * {@code comp = r2.composeInverse(r1)}.
1485     * </p>
1486     * <p>
1487     * If the semantics of the rotations composition corresponds to a
1488     * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
1489     * the application order will be reversed, which means it is the
1490     * <em>innermost</em> rotation that will be reversed. So keeping the exact same
1491     * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
1492     * and  {@code comp} as above, {@code comp} could also be computed as
1493     * {@code comp = r1.revert().composeInverse(r2.revert(), RotationConvention.FRAME_TRANSFORM)}.
1494     * </p>
1495     * @param r rotation to apply the rotation to
1496     * @param convention convention to use for the semantics of the angle
1497     * @return a new rotation which is the composition of r by the inverse
1498     * of the instance
1499     */
1500    public FieldRotation<T> composeInverse(final Rotation r, final RotationConvention convention) {
1501        return convention == RotationConvention.VECTOR_OPERATOR ?
1502                             composeInverseInternal(r) : applyTo(r, revert());
1503    }
1504
1505    /** Compose the inverse of the instance with another rotation
1506     * using vector operator convention.
1507     * @param r rotation to apply the rotation to
1508     * @return a new rotation which is the composition of r by the inverse
1509     * of the instance using vector operator convention
1510     */
1511    private FieldRotation<T> composeInverseInternal(Rotation r) {
1512        return new FieldRotation<T>(q0.multiply(r.getQ0()).add(q1.multiply(r.getQ1()).add(q2.multiply(r.getQ2())).add(q3.multiply(r.getQ3()))).negate(),
1513                                    q1.multiply(r.getQ0()).add(q3.multiply(r.getQ2()).subtract(q2.multiply(r.getQ3()))).subtract(q0.multiply(r.getQ1())),
1514                                    q2.multiply(r.getQ0()).add(q1.multiply(r.getQ3()).subtract(q3.multiply(r.getQ1()))).subtract(q0.multiply(r.getQ2())),
1515                                    q3.multiply(r.getQ0()).add(q2.multiply(r.getQ1()).subtract(q1.multiply(r.getQ2()))).subtract(q0.multiply(r.getQ3())),
1516                                    false);
1517    }
1518
1519    /** Apply the inverse of a rotation to another rotation.
1520     * Applying the inverse of a rotation to another rotation is computing
1521     * the composition in an order compliant with the following rule :
1522     * let u be any vector and v its image by rInner (i.e. rInner.applyTo(u) = v),
1523     * let w be the inverse image of v by rOuter
1524     * (i.e. rOuter.applyInverseTo(v) = w), then w = comp.applyTo(u), where
1525     * comp = applyInverseTo(rOuter, rInner).
1526     * @param rOuter rotation to apply the rotation to
1527     * @param rInner rotation to apply the rotation to
1528     * @param <T> the type of the field elements
1529     * @return a new rotation which is the composition of r by the inverse
1530     * of the instance
1531     */
1532    public static <T extends RealFieldElement<T>> FieldRotation<T> applyInverseTo(final Rotation rOuter, final FieldRotation<T> rInner) {
1533        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(),
1534                                    rInner.q0.multiply(rOuter.getQ1()).add(rInner.q2.multiply(rOuter.getQ3()).subtract(rInner.q3.multiply(rOuter.getQ2()))).subtract(rInner.q1.multiply(rOuter.getQ0())),
1535                                    rInner.q0.multiply(rOuter.getQ2()).add(rInner.q3.multiply(rOuter.getQ1()).subtract(rInner.q1.multiply(rOuter.getQ3()))).subtract(rInner.q2.multiply(rOuter.getQ0())),
1536                                    rInner.q0.multiply(rOuter.getQ3()).add(rInner.q1.multiply(rOuter.getQ2()).subtract(rInner.q2.multiply(rOuter.getQ1()))).subtract(rInner.q3.multiply(rOuter.getQ0())),
1537                                    false);
1538    }
1539
1540    /** Perfect orthogonality on a 3X3 matrix.
1541     * @param m initial matrix (not exactly orthogonal)
1542     * @param threshold convergence threshold for the iterative
1543     * orthogonality correction (convergence is reached when the
1544     * difference between two steps of the Frobenius norm of the
1545     * correction is below this threshold)
1546     * @return an orthogonal matrix close to m
1547     * @exception NotARotationMatrixException if the matrix cannot be
1548     * orthogonalized with the given threshold after 10 iterations
1549     */
1550    private T[][] orthogonalizeMatrix(final T[][] m, final double threshold)
1551        throws NotARotationMatrixException {
1552
1553        T x00 = m[0][0];
1554        T x01 = m[0][1];
1555        T x02 = m[0][2];
1556        T x10 = m[1][0];
1557        T x11 = m[1][1];
1558        T x12 = m[1][2];
1559        T x20 = m[2][0];
1560        T x21 = m[2][1];
1561        T x22 = m[2][2];
1562        double fn = 0;
1563        double fn1;
1564
1565        final T[][] o = MathArrays.buildArray(m[0][0].getField(), 3, 3);
1566
1567        // iterative correction: Xn+1 = Xn - 0.5 * (Xn.Mt.Xn - M)
1568        int i = 0;
1569        while (++i < 11) {
1570
1571            // Mt.Xn
1572            final T mx00 = m[0][0].multiply(x00).add(m[1][0].multiply(x10)).add(m[2][0].multiply(x20));
1573            final T mx10 = m[0][1].multiply(x00).add(m[1][1].multiply(x10)).add(m[2][1].multiply(x20));
1574            final T mx20 = m[0][2].multiply(x00).add(m[1][2].multiply(x10)).add(m[2][2].multiply(x20));
1575            final T mx01 = m[0][0].multiply(x01).add(m[1][0].multiply(x11)).add(m[2][0].multiply(x21));
1576            final T mx11 = m[0][1].multiply(x01).add(m[1][1].multiply(x11)).add(m[2][1].multiply(x21));
1577            final T mx21 = m[0][2].multiply(x01).add(m[1][2].multiply(x11)).add(m[2][2].multiply(x21));
1578            final T mx02 = m[0][0].multiply(x02).add(m[1][0].multiply(x12)).add(m[2][0].multiply(x22));
1579            final T mx12 = m[0][1].multiply(x02).add(m[1][1].multiply(x12)).add(m[2][1].multiply(x22));
1580            final T mx22 = m[0][2].multiply(x02).add(m[1][2].multiply(x12)).add(m[2][2].multiply(x22));
1581
1582            // Xn+1
1583            o[0][0] = x00.subtract(x00.multiply(mx00).add(x01.multiply(mx10)).add(x02.multiply(mx20)).subtract(m[0][0]).multiply(0.5));
1584            o[0][1] = x01.subtract(x00.multiply(mx01).add(x01.multiply(mx11)).add(x02.multiply(mx21)).subtract(m[0][1]).multiply(0.5));
1585            o[0][2] = x02.subtract(x00.multiply(mx02).add(x01.multiply(mx12)).add(x02.multiply(mx22)).subtract(m[0][2]).multiply(0.5));
1586            o[1][0] = x10.subtract(x10.multiply(mx00).add(x11.multiply(mx10)).add(x12.multiply(mx20)).subtract(m[1][0]).multiply(0.5));
1587            o[1][1] = x11.subtract(x10.multiply(mx01).add(x11.multiply(mx11)).add(x12.multiply(mx21)).subtract(m[1][1]).multiply(0.5));
1588            o[1][2] = x12.subtract(x10.multiply(mx02).add(x11.multiply(mx12)).add(x12.multiply(mx22)).subtract(m[1][2]).multiply(0.5));
1589            o[2][0] = x20.subtract(x20.multiply(mx00).add(x21.multiply(mx10)).add(x22.multiply(mx20)).subtract(m[2][0]).multiply(0.5));
1590            o[2][1] = x21.subtract(x20.multiply(mx01).add(x21.multiply(mx11)).add(x22.multiply(mx21)).subtract(m[2][1]).multiply(0.5));
1591            o[2][2] = x22.subtract(x20.multiply(mx02).add(x21.multiply(mx12)).add(x22.multiply(mx22)).subtract(m[2][2]).multiply(0.5));
1592
1593            // correction on each elements
1594            final double corr00 = o[0][0].getReal() - m[0][0].getReal();
1595            final double corr01 = o[0][1].getReal() - m[0][1].getReal();
1596            final double corr02 = o[0][2].getReal() - m[0][2].getReal();
1597            final double corr10 = o[1][0].getReal() - m[1][0].getReal();
1598            final double corr11 = o[1][1].getReal() - m[1][1].getReal();
1599            final double corr12 = o[1][2].getReal() - m[1][2].getReal();
1600            final double corr20 = o[2][0].getReal() - m[2][0].getReal();
1601            final double corr21 = o[2][1].getReal() - m[2][1].getReal();
1602            final double corr22 = o[2][2].getReal() - m[2][2].getReal();
1603
1604            // Frobenius norm of the correction
1605            fn1 = corr00 * corr00 + corr01 * corr01 + corr02 * corr02 +
1606                  corr10 * corr10 + corr11 * corr11 + corr12 * corr12 +
1607                  corr20 * corr20 + corr21 * corr21 + corr22 * corr22;
1608
1609            // convergence test
1610            if (FastMath.abs(fn1 - fn) <= threshold) {
1611                return o;
1612            }
1613
1614            // prepare next iteration
1615            x00 = o[0][0];
1616            x01 = o[0][1];
1617            x02 = o[0][2];
1618            x10 = o[1][0];
1619            x11 = o[1][1];
1620            x12 = o[1][2];
1621            x20 = o[2][0];
1622            x21 = o[2][1];
1623            x22 = o[2][2];
1624            fn  = fn1;
1625
1626        }
1627
1628        // the algorithm did not converge after 10 iterations
1629        throw new NotARotationMatrixException(LocalizedFormats.UNABLE_TO_ORTHOGONOLIZE_MATRIX,
1630                                              i - 1);
1631
1632    }
1633
1634    /** Compute the <i>distance</i> between two rotations.
1635     * <p>The <i>distance</i> is intended here as a way to check if two
1636     * rotations are almost similar (i.e. they transform vectors the same way)
1637     * or very different. It is mathematically defined as the angle of
1638     * the rotation r that prepended to one of the rotations gives the other
1639     * one:</p>
1640     * <pre>
1641     *        r<sub>1</sub>(r) = r<sub>2</sub>
1642     * </pre>
1643     * <p>This distance is an angle between 0 and &pi;. Its value is the smallest
1644     * possible upper bound of the angle in radians between r<sub>1</sub>(v)
1645     * and r<sub>2</sub>(v) for all possible vectors v. This upper bound is
1646     * reached for some v. The distance is equal to 0 if and only if the two
1647     * rotations are identical.</p>
1648     * <p>Comparing two rotations should always be done using this value rather
1649     * than for example comparing the components of the quaternions. It is much
1650     * more stable, and has a geometric meaning. Also comparing quaternions
1651     * components is error prone since for example quaternions (0.36, 0.48, -0.48, -0.64)
1652     * and (-0.36, -0.48, 0.48, 0.64) represent exactly the same rotation despite
1653     * their components are different (they are exact opposites).</p>
1654     * @param r1 first rotation
1655     * @param r2 second rotation
1656     * @param <T> the type of the field elements
1657     * @return <i>distance</i> between r1 and r2
1658     */
1659    public static <T extends RealFieldElement<T>> T distance(final FieldRotation<T> r1, final FieldRotation<T> r2) {
1660        return r1.composeInverseInternal(r2).getAngle();
1661    }
1662
1663}