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 }