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 }