View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.geometry.euclidean.threed.rotation;
18  
19  import java.util.Arrays;
20  import java.util.Objects;
21  
22  /** <p>
23   * Class representing a sequence of axis-angle rotations. These types of
24   * rotations are commonly called <em>Euler angles</em>, <em>Tait-Bryan angles</em>,
25   * or <em>Cardan angles</em> depending on the properties of the rotation sequence and
26   * the particular use case. A sequence of three rotations around at least two different
27   * axes is sufficient to represent any rotation or orientation in 3 dimensional space.
28   * However, in order to unambiguously represent the rotation, the following information
29   * must be provided along with the rotation angles:
30   * <ul>
31   *      <li><strong>Axis sequence</strong> - The axes that the rotation angles are associated with and
32   *      in what order they occur.
33   *      </li>
34   *      <li><strong>Reference frame</strong> - The reference frame used to define the position of the rotation
35   *      axes. This can either be <em>relative (intrinsic)</em> or <em>absolute (extrinsic)</em>. A relative
36   *      reference frame defines the rotation axes from the point of view of the "thing" being rotated.
37   *      Thus, each rotation after the first occurs around an axis that very well may have been
38   *      moved from its original position by a previous rotation. A good example of this is an
39   *      airplane: the pilot steps through a sequence of rotations, each time moving the airplane
40   *      around its own up/down, left/right, and front/back axes, regardless of how the airplane
41   *      is oriented at the time. In contrast, an absolute reference frame is fixed and does not
42   *      move with each rotation.
43   *      </li>
44   *      <li><strong>Rotation direction</strong> - This defines the rotation direction that angles are measured in.
45   *      This library uses <em>right-handed rotations</em> exclusively. This means that the direction of rotation
46   *      around an axis is the same as the curl of one's fingers when the right hand is placed on the axis
47   *      with the thumb pointing in the axis direction.
48   *      </li>
49   * </ul>
50   *
51   * <p>
52   * Computations involving multiple rotations are generally very complicated when using axis-angle sequences. Therefore, it is recommended
53   * to only use this class to represent angles and orientations when needed in this form, and to use {@link QuaternionRotation}
54   * for everything else. Quaternions are much easier to work with and avoid many of the problems of axis-angle sequence representations,
55   * such as <a href="https://en.wikipedia.org/wiki/Gimbal_lock">gimbal lock</a>.
56   * </p>
57   *
58   * @see <a href="https://en.wikipedia.org/wiki/Euler_angles">Euler Angles</a>
59   * @see QuaternionRotation
60   */
61  public final class AxisAngleSequence {
62      /** Reference frame for defining axis positions. */
63      private final AxisReferenceFrame referenceFrame;
64  
65      /** Axis sequence. */
66      private final AxisSequence axisSequence;
67  
68      /** Angle around the first rotation axis, in radians. */
69      private final double angle1;
70  
71      /** Angle around the second rotation axis, in radians. */
72      private final double angle2;
73  
74      /** Angle around the third rotation axis, in radians. */
75      private final double angle3;
76  
77      /** Construct an instance from its component parts.
78       * @param referenceFrame the axis reference frame
79       * @param axisSequence the axis rotation sequence
80       * @param angle1 angle around the first axis in radians
81       * @param angle2 angle around the second axis in radians
82       * @param angle3 angle around the third axis in radians
83       */
84      public AxisAngleSequence(final AxisReferenceFrame referenceFrame, final AxisSequence axisSequence,
85              final double angle1, final double angle2, final double angle3) {
86          this.referenceFrame = referenceFrame;
87          this.axisSequence = axisSequence;
88  
89          this.angle1 = angle1;
90          this.angle2 = angle2;
91          this.angle3 = angle3;
92      }
93  
94      /** Get the axis reference frame. This defines the position of the rotation axes.
95       * @return the axis reference frame
96       */
97      public AxisReferenceFrame getReferenceFrame() {
98          return referenceFrame;
99      }
100 
101     /** Get the rotation axis sequence.
102      * @return the rotation axis sequence
103      */
104     public AxisSequence getAxisSequence() {
105         return axisSequence;
106     }
107 
108     /** Get the angle of rotation around the first axis, in radians.
109      * @return angle of rotation around the first axis, in radians
110      */
111     public double getAngle1() {
112         return angle1;
113     }
114 
115     /** Get the angle of rotation around the second axis, in radians.
116      * @return angle of rotation around the second axis, in radians
117      */
118     public double getAngle2() {
119         return angle2;
120     }
121 
122     /** Get the angle of rotation around the third axis, in radians.
123      * @return angle of rotation around the third axis, in radians
124      */
125     public double getAngle3() {
126         return angle3;
127     }
128 
129     /** Get the rotation angles as a 3-element array.
130      * @return an array containing the 3 rotation angles
131      */
132     public double[] getAngles() {
133         return new double[]{angle1, angle2, angle3};
134     }
135 
136     /** {@inheritDoc} */
137     @Override
138     public int hashCode() {
139         return 107 * (199 * Objects.hash(referenceFrame, axisSequence)) +
140                 (7 * Double.hashCode(angle1)) +
141                 (11 * Double.hashCode(angle2)) +
142                 (19 * Double.hashCode(angle3));
143     }
144 
145     /** {@inheritDoc} */
146     @Override
147     public boolean equals(final Object obj) {
148         if (this == obj) {
149             return true;
150         }
151         if (!(obj instanceof AxisAngleSequence)) {
152             return false;
153         }
154 
155         final AxisAngleSequence other = (AxisAngleSequence) obj;
156 
157         return this.referenceFrame == other.referenceFrame &&
158                 this.axisSequence == other.axisSequence &&
159                 Double.compare(this.angle1, other.angle1) == 0 &&
160                 Double.compare(this.angle2, other.angle2) == 0 &&
161                 Double.compare(this.angle3, other.angle3) == 0;
162     }
163 
164     /** {@inheritDoc} */
165     @Override
166     public String toString() {
167         final StringBuilder sb = new StringBuilder();
168         sb.append(this.getClass().getSimpleName())
169             .append("[referenceFrame=")
170             .append(referenceFrame)
171             .append(", axisSequence=")
172             .append(axisSequence)
173             .append(", angles=")
174             .append(Arrays.toString(getAngles()))
175             .append(']');
176 
177         return sb.toString();
178     }
179 
180     /** Create a new instance with a reference frame of {@link AxisReferenceFrame#RELATIVE}.
181      * @param axisSequence the axis rotation sequence
182      * @param angle1 angle around the first axis in radians
183      * @param angle2 angle around the second axis in radians
184      * @param angle3 angle around the third axis in radians
185      * @return a new instance with a relative reference frame
186      */
187     public static AxisAngleSequence createRelative(final AxisSequence axisSequence, final double angle1,
188             final double angle2, final double angle3) {
189         return new AxisAngleSequence(AxisReferenceFrame.RELATIVE, axisSequence, angle1, angle2, angle3);
190     }
191 
192     /** Create a new instance with a reference frame of {@link AxisReferenceFrame#ABSOLUTE}.
193      * @param axisSequence the axis rotation sequence
194      * @param angle1 angle around the first axis in radians
195      * @param angle2 angle around the second axis in radians
196      * @param angle3 angle around the third axis in radians
197      * @return a new instance with an absolute reference frame
198      */
199     public static AxisAngleSequence createAbsolute(final AxisSequence axisSequence, final double angle1,
200             final double angle2, final double angle3) {
201         return new AxisAngleSequence(AxisReferenceFrame.ABSOLUTE, axisSequence, angle1, angle2, angle3);
202     }
203 }