View Javadoc
1   /*
2    * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.geometry;
17  
18  import com.irurueta.algebra.AlgebraException;
19  import com.irurueta.algebra.ArrayUtils;
20  import com.irurueta.algebra.Matrix;
21  import com.irurueta.algebra.Utils;
22  import com.irurueta.algebra.WrongSizeException;
23  
24  import java.io.Serializable;
25  
26  /**
27   * Abstract class representing a rotation in 3D space.
28   * Subclasses of this class will implement the interface of this class.
29   */
30  @SuppressWarnings("DuplicatedCode")
31  public abstract class Rotation3D implements Serializable {
32  
33      /**
34       * Constant defining threshold to determine whether a matrix is orthogonal
35       * or not and has determinant equal to 1. Rotation matrices must fulfill
36       * those requirements.
37       */
38      public static final double DEFAULT_VALID_THRESHOLD = 1e-12;
39  
40      /**
41       * Constant defining minimum allowed threshold.
42       */
43      public static final double MIN_THRESHOLD = 0.0;
44  
45      /**
46       * Constant defining number of inhomogeneous 3D coordinates.
47       */
48      public static final int INHOM_COORDS = 3;
49  
50      /**
51       * Constant defining number of homogeneous 3D coordinates.
52       */
53      public static final int HOM_COORDS = 4;
54  
55      /**
56       * Constant defining default type if none is provided.
57       */
58      public static final Rotation3DType DEFAULT_TYPE = Rotation3DType.QUATERNION;
59  
60      /**
61       * Default threshold to determine if two instances are equal.
62       */
63      public static final double DEFAULT_COMPARISON_THRESHOLD = 1e-9;
64  
65      /**
66       * Constant defining minimum allowed comparison threshold.
67       */
68      private static final double MIN_COMPARISON_THRESHOLD = 0.0;
69  
70      /**
71       * Empty constructor.
72       */
73      protected Rotation3D() {
74      }
75  
76      /**
77       * Returns type of this rotation.
78       *
79       * @return Type of this rotation.
80       */
81      public abstract Rotation3DType getType();
82  
83      /**
84       * Sets the axis and rotation of this instance.
85       * Once set, points will rotate around provided axis an amount equal to
86       * provided rotation angle in radians.
87       * Note: to avoid numerical instabilities and improve accuracy, axis
88       * coordinates should be normalized (e.g. norm equal to 1).
89       *
90       * @param axis  Array of length 3 containing axis coordinates.
91       * @param theta Amount of rotation in radians.
92       * @throws IllegalArgumentException Raised if provided axis array does not
93       *                                  have length 3.
94       */
95      public final void setAxisAndRotation(final double[] axis, final double theta) {
96          if (axis.length != INHOM_COORDS) {
97              throw new IllegalArgumentException();
98          }
99  
100         setAxisAndRotation(axis[0], axis[1], axis[2], theta);
101     }
102 
103     /**
104      * Sets the axis and rotation of this instance.
105      * Once set, points will rotate around provided axis an amount equal to
106      * provided rotation angle in radians.
107      * Note: to avoid numerical instabilities and improve accuracy, axis
108      * coordinates should be normalized (e.g. norm equal to 1).
109      *
110      * @param axisX X coordinate of rotation axis.
111      * @param axisY Y coordinate of rotation axis.
112      * @param axisZ Z coordinate of rotation axis.
113      * @param theta Amount of rotation in radians.
114      */
115     public abstract void setAxisAndRotation(
116             final double axisX, final double axisY, final double axisZ, final double theta);
117 
118     /**
119      * Returns rotation axis corresponding to this instance as a new array
120      * containing axis coordinates.
121      *
122      * @return Rotation axis coordinates.
123      * @throws RotationException Raised if numerical instabilities happen.
124      */
125     public double[] getRotationAxis() throws RotationException {
126         final var axis = new double[INHOM_COORDS];
127         rotationAxis(axis);
128         return axis;
129     }
130 
131     /**
132      * Returns rotation axis corresponding to this instance.
133      * Result is stored in provided axis array, which must have length 3.
134      *
135      * @param axis Array where axis coordinates will be stored.
136      * @throws IllegalArgumentException Raised if provided array does not have
137      *                                  length 3.
138      * @throws RotationException        Raised if numerical instabilities happen.
139      */
140     public abstract void rotationAxis(final double[] axis) throws RotationException;
141 
142     /**
143      * Returns rotation amount or angle in radians around the rotation axis
144      * associated to this instance.
145      *
146      * @return Rotation angle in radians.
147      * @throws RotationException Raised if numerical instabilities happen.
148      *                           Because internal matrix will always be well-defined (orthogonal and
149      *                           determinant equal to 1), this exception will rarely happen.
150      */
151     public abstract double getRotationAngle() throws RotationException;
152 
153     /**
154      * Returns a 3D rotation which is inverse to this instance.
155      * In other words, the combination of this rotation with its inverse
156      * produces no change.
157      *
158      * @return Inverse 3D rotation.
159      */
160     public abstract Rotation3D inverseRotationAndReturnNew();
161 
162     /**
163      * Sets into provided Rotation3D instance a rotation inverse to this
164      * instance.
165      * The combination of this rotation with its inverse produces no change.
166      *
167      * @param result Instance where inverse rotation will be set.
168      */
169     public abstract void inverseRotation(final Rotation3D result);
170 
171     /**
172      * Reverses the rotation of this instance.
173      */
174     public abstract void inverseRotation();
175 
176     /**
177      * Returns this 3D rotation instance expressed as a 3x3 inhomogeneous
178      * matrix.
179      * This is equivalent to call getInternalMatrix().
180      *
181      * @return Rotation matrix expressed in inhomogeneous coordinates.
182      */
183     public abstract Matrix asInhomogeneousMatrix();
184 
185     /**
186      * Sets into provided Matrix instance this 3D rotation expressed as a
187      * 3x3 inhomogeneous matrix.
188      *
189      * @param result Matrix where rotation will be set.
190      * @throws IllegalArgumentException Raised if provided instance does not
191      *                                  have size 3x3.
192      */
193     public abstract void asInhomogeneousMatrix(final Matrix result);
194 
195     /**
196      * Returns this 3D rotation instance expressed as a 4x4 homogeneous matrix.
197      *
198      * @return Rotation matrix expressed in homogeneous coordinates.
199      */
200     public abstract Matrix asHomogeneousMatrix();
201 
202     /**
203      * Sets into provided Matrix instance this 3D rotation expressed as a
204      * 4x4 homogeneous matrix.
205      *
206      * @param result Matrix where rotation will be set.
207      * @throws IllegalArgumentException Raised if provided instance does not
208      *                                  have size 4x4.
209      */
210     public abstract void asHomogeneousMatrix(final Matrix result);
211 
212     /**
213      * Sets amount of rotation from provided rotation matrix.
214      * Provided matrix must be orthogonal (i.e. squared, non-singular, it's
215      * transpose must be its inverse) and must have determinant equal to 1.
216      * Provided matrix can be expressed in either inhomogeneous (3x3) or
217      * homogeneous (4x4) coordinates.
218      *
219      * @param m         Provided rotation matrix.
220      * @param threshold Threshold to determine whether matrix is orthonormal.
221      * @throws InvalidRotationMatrixException Raised if provided matrix is not
222      *                                        valid (has wrong size, or it is not orthonormal).
223      * @throws IllegalArgumentException       Raised if provided threshold is
224      *                                        negative.
225      *                                        {@link #isValidRotationMatrix(Matrix)}
226      */
227     public final void fromMatrix(final Matrix m, final double threshold) throws InvalidRotationMatrixException {
228         if (m.getRows() == INHOM_COORDS && m.getColumns() == INHOM_COORDS) {
229             // inhomogeneous matrix
230             fromInhomogeneousMatrix(m, threshold);
231         } else if (m.getRows() == HOM_COORDS && m.getColumns() == HOM_COORDS) {
232             // homogeneous matrix
233             fromHomogeneousMatrix(m, threshold);
234         } else {
235             throw new InvalidRotationMatrixException();
236         }
237     }
238 
239     /**
240      * Sets amount of rotation from provided rotation matrix.
241      * Provided matrix must be orthogonal (i.e. squared, non-singular, it's
242      * transpose must be its inverse) and must have determinant equal to 1.
243      * Provided matrix can be expressed in either inhomogeneous (3x3) or
244      * homogeneous (4x4) coordinates.
245      * Because threshold is not provided it is used DEFAULT_VALID_THRESHOLD
246      * instead.
247      *
248      * @param m Provided rotation matrix.
249      * @throws InvalidRotationMatrixException Raised if provided matrix is not
250      *                                        valid (has wrong size, or it is not orthonormal).
251      *                                        {@link #isValidRotationMatrix(Matrix)}
252      */
253     public final void fromMatrix(final Matrix m) throws InvalidRotationMatrixException {
254         fromMatrix(m, DEFAULT_VALID_THRESHOLD);
255     }
256 
257     /**
258      * Sets amount of rotation from provided inhomogeneous rotation matrix.
259      * Provided matrix must be orthogonal (i.e. squared, non-singular, it's
260      * transpose must be its inverse) and must have determinant equal to 1.
261      * Provided matrix must also have size 3x3.
262      *
263      * @param m         Provided rotation matrix.
264      * @param threshold Threshold to determine whether matrix is orthonormal.
265      * @throws InvalidRotationMatrixException Raised if provided matrix is not
266      *                                        valid (has wrong size, or it is not orthonormal).
267      * @throws IllegalArgumentException       Raised if provided threshold is
268      *                                        negative.
269      *                                        {@link #isValidRotationMatrix(Matrix)}
270      */
271     public abstract void fromInhomogeneousMatrix(final Matrix m, final double threshold)
272             throws InvalidRotationMatrixException;
273 
274     /**
275      * Sets amount of rotation from provided inhomogeneous rotation matrix.
276      * Provided matrix must be orthogonal (i.e. squared, non-singular, it's
277      * transpose must be its inverse) and must have determinant equal to 1.
278      * Provided matrix must also have size 3x3.
279      * Because threshold is not provided it is used DEFAULT_VALID_THRESHOLD
280      * instead.
281      *
282      * @param m Provided rotation matrix.
283      * @throws InvalidRotationMatrixException Raised if provided matrix is not
284      *                                        valid (has wrong size, or it is not orthonormal).
285      *                                        {@link #isValidRotationMatrix(Matrix)}
286      */
287     public void fromInhomogeneousMatrix(final Matrix m) throws InvalidRotationMatrixException {
288         fromInhomogeneousMatrix(m, DEFAULT_VALID_THRESHOLD);
289     }
290 
291     /**
292      * Sets amount of rotation from provided homogeneous rotation matrix.
293      * Provided matrix must be orthogonal (i.e. squared, non-singular, it's
294      * transpose must be its inverse) and must have determinant equal to 1.
295      * Provided matrix must also have size 4x4, and its last row and column must
296      * be zero, except for element in last row and column which must be 1.
297      *
298      * @param m         Provided rotation matrix.
299      * @param threshold Threshold to determine whether matrix is orthonormal.
300      * @throws InvalidRotationMatrixException Raised if provided matrix is not
301      *                                        valid (has wrong size, or it is not orthonormal).
302      * @throws IllegalArgumentException       Raised if provided threshold is
303      *                                        negative.
304      *                                        {@link #isValidRotationMatrix(Matrix)}
305      */
306     public abstract void fromHomogeneousMatrix(final Matrix m, final double threshold)
307             throws InvalidRotationMatrixException;
308 
309     /**
310      * Sets amount of rotation from provided homogeneous rotation matrix.
311      * Provided matrix must be orthogonal (i.e. squared, non-singular), its
312      * transpose must be its inverse and must have determinant equal to 1.
313      * Provided matrix must also have size exe, and its last row and column must
314      * be zero, except for element in last row and column which must be 1
315      * Because threshold is not provided it is used DEFAULT_VALID_THRESHOLD
316      * instead.
317      *
318      * @param m Provided rotation matrix.
319      * @throws InvalidRotationMatrixException Raised if provided matrix is not
320      *                                        valid (has wrong size, or it is not orthonormal).
321      *                                        {@link #isValidRotationMatrix(Matrix)}
322      */
323     public void fromHomogeneousMatrix(final Matrix m) throws InvalidRotationMatrixException {
324         fromHomogeneousMatrix(m, DEFAULT_VALID_THRESHOLD);
325     }
326 
327     /**
328      * Rotates a 3D point using the origin of coordinates as the axis of
329      * rotation.
330      * Point will be rotated by the amount of rotation contained in this
331      * instance.
332      *
333      * @param inputPoint  Input point to be rotated.
334      * @param resultPoint Rotated point.
335      */
336     public abstract void rotate(final Point3D inputPoint, final Point3D resultPoint);
337 
338     /**
339      * Returns a 3D point containing a rotated version of provided point.
340      * Point will be rotated using the origin of the coordinates as the axis of
341      * rotation.
342      * Point will be rotated by the amount of rotation contained in this
343      * instance.
344      *
345      * @param point Point to be rotated.
346      * @return Rotated point.
347      */
348     public Point3D rotate(final Point3D point) {
349         final var result = new HomogeneousPoint3D();
350         rotate(point, result);
351         return result;
352     }
353 
354     /**
355      * Rotates a plane using the origin of coordinates as the axis of rotation.
356      * Plane will be rotated by the amount of rotation contained in this
357      * instance.
358      *
359      * @param inputPlane  Input plane to be rotated.
360      * @param resultPlane plane where result is stored.
361      */
362     public void rotate(final Plane inputPlane, final Plane resultPlane) {
363         try {
364             final var r = asHomogeneousMatrix();
365             // because of the duality theorem:
366             // P'*M = 0 --> P*R^-1*R*M = 0 --> P2' = P'*R^-1 and M2 = R*M
367             // where P2 and M2 are rotated plane and point, however rotated
368             // plane uses the inverse rotation, which is the transposed matrix
369             // Hence P2' = P' * R', and by undoing the transposition
370             // P2 = (P' * R')' = R'' * P'' = R * P
371 
372             final var p = new Matrix(Plane.PLANE_NUMBER_PARAMS, 1);
373 
374             // to increase accuracy
375             inputPlane.normalize();
376             p.setElementAt(0, 0, inputPlane.getA());
377             p.setElementAt(1, 0, inputPlane.getB());
378             p.setElementAt(2, 0, inputPlane.getC());
379             p.setElementAt(3, 0, inputPlane.getD());
380 
381             // Rotated plane below is R * P
382             r.multiply(p);
383 
384             resultPlane.setParameters(r.getElementAt(0, 0), r.getElementAt(1, 0),
385                     r.getElementAt(2, 0), r.getElementAt(3, 0));
386         } catch (final WrongSizeException ignore) {
387             // never happens
388         }
389     }
390 
391     /**
392      * Returns a plane containing a rotated version of provided plane.
393      * Plane will be rotated using the origin of the coordinates as the axis of
394      * rotation.
395      * Plane will be rotated by the amount of rotation contained in this
396      * instance.
397      *
398      * @param plane Plane to be rotated.
399      * @return Rotated plane.
400      */
401     public Plane rotate(final Plane plane) {
402         final var result = new Plane();
403         rotate(plane, result);
404         return result;
405     }
406 
407     /**
408      * Returns boolean indicating whether provided matrix is a valid matrix for
409      * a rotation.
410      * Rotation matrices must be orthogonal and must have determinant equal to
411      * 1.
412      *
413      * @param m         Input matrix to be checked.
414      * @param threshold Threshold to determine whether matrix is orthogonal and
415      *                  whether determinant is one.
416      * @return True if matrix is valid, false otherwise.
417      * @throws IllegalArgumentException Raised if provided threshold is
418      *                                  negative.
419      */
420     public static boolean isValidRotationMatrix(final Matrix m, final double threshold) {
421         if (threshold < MIN_THRESHOLD) {
422             throw new IllegalArgumentException();
423         }
424 
425         try {
426             return Utils.isOrthogonal(m, threshold) && Math.abs(Math.abs(Utils.det(m)) - 1.0) < threshold;
427         } catch (final AlgebraException e) {
428             return false;
429         }
430     }
431 
432     /**
433      * Returns boolean indicating whether provided matrix is a valid matrix for
434      * a rotation.
435      * Rotation matrices must be orthogonal and must have determinant equal to 1
436      * Because threshold is not provided, it is used DEFAULT_VALID_THRESHOLD
437      * instead.
438      *
439      * @param m Input matrix to be checked.
440      * @return True if matrix is valid, false otherwise.
441      * @throws IllegalArgumentException Raised if provided threshold is
442      *                                  negative.
443      */
444     public static boolean isValidRotationMatrix(final Matrix m) {
445         return isValidRotationMatrix(m, DEFAULT_VALID_THRESHOLD);
446     }
447 
448     /**
449      * Combines provided rotation with this rotation and returns the result as
450      * a new Rotation3D instance.
451      *
452      * @param rotation Input rotation to be combined.
453      * @return Combined rotation, which is equal to the multiplication of the
454      * internal matrix of provided rotation with the internal matrix of this
455      * instance.
456      */
457     public abstract Rotation3D combineAndReturnNew(final Rotation3D rotation);
458 
459     /**
460      * Combines provided rotation into this rotation resulting in the
461      * multiplication of the internal matrices of both rotations.
462      *
463      * @param rotation Input rotation to be combined.
464      */
465     public abstract void combine(final Rotation3D rotation);
466 
467     /**
468      * Factory method.
469      * Creates a rotation that has no effect on geometric objects using default
470      * type.
471      *
472      * @return A 3D rotation.
473      */
474     public static Rotation3D create() {
475         return create(DEFAULT_TYPE);
476     }
477 
478     /**
479      * Factory method.
480      * Creates a rotation that has no effect on geometric objects using provided
481      * type.
482      *
483      * @param type Rotation type.
484      * @return A 3D rotation.
485      */
486     public static Rotation3D create(final Rotation3DType type) {
487         return switch (type) {
488             case AXIS_ROTATION3D -> new AxisRotation3D();
489             case MATRIX_ROTATION3D -> new MatrixRotation3D();
490             default -> new Quaternion();
491         };
492     }
493 
494     /**
495      * Factory method.
496      * Creates a 3D rotation using provided axis and rotation angle.
497      * Note: to increase accuracy axis coordinates should be normalized.
498      *
499      * @param axis  Array containing rotation axis coordinates.
500      * @param theta Rotation angle around axis expressed in radians.
501      * @return A 3D rotation instance.
502      * @throws IllegalArgumentException Raised if provided axis array does not
503      *                                  have length 3.
504      */
505     public static Rotation3D create(final double[] axis, final double theta) {
506         return create(axis, theta, DEFAULT_TYPE);
507     }
508 
509     /**
510      * Factory method.
511      * Creates a 3D rotation using provided axis, rotation angle and rotation
512      * type.
513      * Note: to increase accuracy axis coordinates should be normalized.
514      *
515      * @param axis  Array containing rotation axis coordinates.
516      * @param theta Rotation angle around axis expressed in radians.
517      * @param type  Rotation type.
518      * @return A 3D rotation instance.
519      * @throws IllegalArgumentException Raised if provided axis array does not
520      *                                  have length 3.
521      */
522     public static Rotation3D create(final double[] axis, final double theta, final Rotation3DType type) {
523         return switch (type) {
524             case AXIS_ROTATION3D -> new AxisRotation3D(axis, theta);
525             case MATRIX_ROTATION3D -> new MatrixRotation3D(axis, theta);
526             default -> new Quaternion(axis, theta);
527         };
528     }
529 
530     /**
531      * Factory method.
532      * Creates a 3D rotation using provided axis coordinates and rotation angle.
533      * Note: to increase accuracy axis coordinates should be normalized.
534      *
535      * @param axisX X coordinate of axis.
536      * @param axisY Y coordinate of axis.
537      * @param axisZ Z coordinate of axis.
538      * @param theta Rotation angle around axis expressed in radians.
539      * @return A 3D rotation instance.
540      */
541     public static Rotation3D create(final double axisX, final double axisY, final double axisZ, final double theta) {
542         return create(axisX, axisY, axisZ, theta, DEFAULT_TYPE);
543     }
544 
545     /**
546      * Factory method.
547      * Creates a 3D rotation using provided axis coordinates, rotation angle and
548      * rotation type.
549      * Note: to increase accuracy axis coordinates should be normalized.
550      *
551      * @param axisX X coordinate of axis.
552      * @param axisY Y coordinate of axis.
553      * @param axisZ Z coordinate of axis.
554      * @param theta Rotation angle around axis expressed in radians.
555      * @param type  Rotation type.
556      * @return A 3D rotation instance.
557      */
558     public static Rotation3D create(
559             final double axisX, final double axisY, final double axisZ, final double theta, final Rotation3DType type) {
560         switch (type) {
561             case AXIS_ROTATION3D:
562                 return new AxisRotation3D(axisX, axisY, axisZ, theta);
563             case MATRIX_ROTATION3D:
564                 return new MatrixRotation3D(axisX, axisY, axisZ, theta);
565             case QUATERNION:
566             default:
567                 final var result = new Quaternion();
568                 result.setAxisAndRotation(axisX, axisY, axisZ, theta);
569                 return result;
570         }
571     }
572 
573     /**
574      * Determines if two Rotation3D instances are equal up to provided threshold
575      * or not (i.e. have the same rotation).
576      *
577      * @param other     other rotation to compare.
578      * @param threshold threshold to determine if they are equal.
579      * @return true if they are equal, false otherwise.
580      * @throws IllegalArgumentException if threshold is negative.
581      * @throws RotationException        if rotation angle or axis cannot be determined.
582      */
583     public boolean equals(final Rotation3D other, final double threshold) throws RotationException {
584 
585         if (threshold < MIN_COMPARISON_THRESHOLD) {
586             throw new IllegalArgumentException();
587         }
588 
589         final var thisAxis = getRotationAxis();
590         final var thisAngle = getRotationAngle();
591         final var otherAxis = other.getRotationAxis();
592         final var otherAngle = other.getRotationAngle();
593 
594         final var cosAngle = ArrayUtils.dotProduct(thisAxis, otherAxis);
595         if (cosAngle >= 0.0) {
596             // axis have same direction
597             final var diffX = thisAxis[0] - otherAxis[0];
598             final var diffY = thisAxis[1] - otherAxis[1];
599             final var diffZ = thisAxis[2] - otherAxis[2];
600             final var sqrNormDiff = diffX * diffX + diffY * diffY + diffZ * diffZ;
601 
602             if (sqrNormDiff > threshold) {
603                 // axes are not equal
604                 return false;
605             }
606 
607             // compare difference of angles
608             return Math.abs(thisAngle - otherAngle) <= threshold
609                     || Math.abs(thisAngle - otherAngle - 2 * Math.PI) <= threshold;
610         } else {
611             // axis might be reversed, hence also angle is reversed
612             final var sumX = thisAxis[0] + otherAxis[0];
613             final var sumY = thisAxis[1] + otherAxis[1];
614             final var sumZ = thisAxis[2] + otherAxis[2];
615             final var sqrNormSum = sumX * sumX + sumY * sumY + sumZ * sumZ;
616 
617             if (sqrNormSum > threshold) {
618                 // axes are not equal
619                 return false;
620             }
621 
622             // compare sum of angles (because rotation angle is also reversed)
623             return Math.abs(thisAngle + otherAngle) <= threshold
624                     || Math.abs(thisAngle + otherAngle - 2 * Math.PI) <= threshold;
625         }
626     }
627 
628     /**
629      * Determines if two Rotation3D instances are equal or not (i.e. have the
630      * same rotation).
631      *
632      * @param other other object to compare.
633      * @return true if they are equal, false otherwise.
634      */
635     public boolean equals(final Rotation3D other) {
636         try {
637             return equals(other, DEFAULT_COMPARISON_THRESHOLD);
638         } catch (final RotationException e) {
639             return false;
640         }
641     }
642 
643     /**
644      * Determines if two Rotation3D instances are equal or not (i.e. have the
645      * same rotation).
646      *
647      * @param obj other object to compare.
648      * @return true if they are equal, false otherwise.
649      */
650     @Override
651     public boolean equals(final Object obj) {
652         if (obj == this) {
653             return true;
654         }
655         if (!(obj instanceof Rotation3D)) {
656             return false;
657         }
658 
659         return equals((Rotation3D) obj);
660     }
661 
662     /**
663      * Hash code to compare instances.
664      *
665      * @return hash code to compare instances.
666      */
667     @Override
668     public int hashCode() {
669         return 5;
670     }
671 
672     /**
673      * Sets values of this rotation from a 3D matrix rotation.
674      *
675      * @param rot 3D matrix rotation to set values from.
676      */
677     public void fromRotation(final MatrixRotation3D rot) {
678         try {
679             fromInhomogeneousMatrix(rot.internalMatrix);
680         } catch (final InvalidRotationMatrixException ignore) {
681             // never thrown
682         }
683     }
684 
685     /**
686      * Sets values of this rotation from a 3D axis rotation.
687      *
688      * @param rot an axis rotation to set values from.
689      */
690     public void fromRotation(final AxisRotation3D rot) {
691         setAxisAndRotation(rot.getAxisX(), rot.getAxisY(), rot.getAxisZ(), rot.getRotationAngle());
692     }
693 
694     /**
695      * Sets values of this rotation from a quaternion.
696      *
697      * @param q a quaternion to set values from.
698      */
699     public abstract void fromRotation(final Quaternion q);
700 
701     /**
702      * Sets values of this rotation from another rotation.
703      *
704      * @param rot a 3D rotation to set values from.
705      * @throws IllegalArgumentException if provided rotation type is
706      *                                  not supported. Only {@link Rotation3DType#AXIS_ROTATION3D},
707      *                                  {@link Rotation3DType#MATRIX_ROTATION3D} and
708      *                                  {@link Rotation3DType#QUATERNION} are supported.
709      */
710     public void fromRotation(final Rotation3D rot) {
711         switch (rot.getType()) {
712             case AXIS_ROTATION3D:
713                 fromRotation((AxisRotation3D) rot);
714                 break;
715             case MATRIX_ROTATION3D:
716                 fromRotation((MatrixRotation3D) rot);
717                 break;
718             case QUATERNION:
719                 fromRotation((Quaternion) rot);
720                 break;
721             default:
722                 throw new IllegalArgumentException();
723         }
724     }
725 
726     /**
727      * Converts this 3D rotation into a matrix rotation storing the result
728      * into provided instance.
729      *
730      * @param result instance where result wil be stored.
731      */
732     public void toMatrixRotation(final MatrixRotation3D result) {
733         result.fromRotation(this);
734     }
735 
736     /**
737      * Converts this 3D rotation into a matrix rotation and returns the result
738      * as a new instance.
739      *
740      * @return a new 3D matrix rotation equivalent to this rotation.
741      */
742     public MatrixRotation3D toMatrixRotation() {
743         final var r = new MatrixRotation3D();
744         toMatrixRotation(r);
745         return r;
746     }
747 
748     /**
749      * Converts this 3D rotation into an axis rotation storing the result into
750      * provided instance.
751      *
752      * @param result instance where result will be stored.
753      */
754     public void toAxisRotation(final AxisRotation3D result) {
755         result.fromRotation(this);
756     }
757 
758     /**
759      * Converts this 3D rotation into an axis rotation and returns the result
760      * as a new instance.
761      *
762      * @return a new axis rotation equivalent to this rotation.
763      */
764     public AxisRotation3D toAxisRotation() {
765         final var r = new AxisRotation3D();
766         toAxisRotation(r);
767         return r;
768     }
769 
770     /**
771      * Converts this 3D rotation into a quaternion storing the result into
772      * provided instance.
773      *
774      * @param result instance where result will be stored.
775      */
776     public void toQuaternion(final Quaternion result) {
777         result.fromRotation(this);
778     }
779 
780     /**
781      * Converts this 3D rotation into a quaternion and returns the result as a
782      * new instance.
783      *
784      * @return a new quaternion equivalent to this rotation.
785      */
786     public Quaternion toQuaternion() {
787         final var q = new Quaternion();
788         toQuaternion(q);
789         return q;
790     }
791 }