AxisAngleSequence.java

  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. import java.util.Arrays;
  19. import java.util.Objects;

  20. /** <p>
  21.  * Class representing a sequence of axis-angle rotations. These types of
  22.  * rotations are commonly called <em>Euler angles</em>, <em>Tait-Bryan angles</em>,
  23.  * or <em>Cardan angles</em> depending on the properties of the rotation sequence and
  24.  * the particular use case. A sequence of three rotations around at least two different
  25.  * axes is sufficient to represent any rotation or orientation in 3 dimensional space.
  26.  * However, in order to unambiguously represent the rotation, the following information
  27.  * must be provided along with the rotation angles:
  28.  * <ul>
  29.  *      <li><strong>Axis sequence</strong> - The axes that the rotation angles are associated with and
  30.  *      in what order they occur.
  31.  *      </li>
  32.  *      <li><strong>Reference frame</strong> - The reference frame used to define the position of the rotation
  33.  *      axes. This can either be <em>relative (intrinsic)</em> or <em>absolute (extrinsic)</em>. A relative
  34.  *      reference frame defines the rotation axes from the point of view of the "thing" being rotated.
  35.  *      Thus, each rotation after the first occurs around an axis that very well may have been
  36.  *      moved from its original position by a previous rotation. A good example of this is an
  37.  *      airplane: the pilot steps through a sequence of rotations, each time moving the airplane
  38.  *      around its own up/down, left/right, and front/back axes, regardless of how the airplane
  39.  *      is oriented at the time. In contrast, an absolute reference frame is fixed and does not
  40.  *      move with each rotation.
  41.  *      </li>
  42.  *      <li><strong>Rotation direction</strong> - This defines the rotation direction that angles are measured in.
  43.  *      This library uses <em>right-handed rotations</em> exclusively. This means that the direction of rotation
  44.  *      around an axis is the same as the curl of one's fingers when the right hand is placed on the axis
  45.  *      with the thumb pointing in the axis direction.
  46.  *      </li>
  47.  * </ul>
  48.  *
  49.  * <p>
  50.  * Computations involving multiple rotations are generally very complicated when using axis-angle sequences. Therefore, it is recommended
  51.  * to only use this class to represent angles and orientations when needed in this form, and to use {@link QuaternionRotation}
  52.  * for everything else. Quaternions are much easier to work with and avoid many of the problems of axis-angle sequence representations,
  53.  * such as <a href="https://en.wikipedia.org/wiki/Gimbal_lock">gimbal lock</a>.
  54.  * </p>
  55.  *
  56.  * @see <a href="https://en.wikipedia.org/wiki/Euler_angles">Euler Angles</a>
  57.  * @see QuaternionRotation
  58.  */
  59. public final class AxisAngleSequence {
  60.     /** Reference frame for defining axis positions. */
  61.     private final AxisReferenceFrame referenceFrame;

  62.     /** Axis sequence. */
  63.     private final AxisSequence axisSequence;

  64.     /** Angle around the first rotation axis, in radians. */
  65.     private final double angle1;

  66.     /** Angle around the second rotation axis, in radians. */
  67.     private final double angle2;

  68.     /** Angle around the third rotation axis, in radians. */
  69.     private final double angle3;

  70.     /** Construct an instance from its component parts.
  71.      * @param referenceFrame the axis reference frame
  72.      * @param axisSequence the axis rotation sequence
  73.      * @param angle1 angle around the first axis in radians
  74.      * @param angle2 angle around the second axis in radians
  75.      * @param angle3 angle around the third axis in radians
  76.      */
  77.     public AxisAngleSequence(final AxisReferenceFrame referenceFrame, final AxisSequence axisSequence,
  78.             final double angle1, final double angle2, final double angle3) {
  79.         this.referenceFrame = referenceFrame;
  80.         this.axisSequence = axisSequence;

  81.         this.angle1 = angle1;
  82.         this.angle2 = angle2;
  83.         this.angle3 = angle3;
  84.     }

  85.     /** Get the axis reference frame. This defines the position of the rotation axes.
  86.      * @return the axis reference frame
  87.      */
  88.     public AxisReferenceFrame getReferenceFrame() {
  89.         return referenceFrame;
  90.     }

  91.     /** Get the rotation axis sequence.
  92.      * @return the rotation axis sequence
  93.      */
  94.     public AxisSequence getAxisSequence() {
  95.         return axisSequence;
  96.     }

  97.     /** Get the angle of rotation around the first axis, in radians.
  98.      * @return angle of rotation around the first axis, in radians
  99.      */
  100.     public double getAngle1() {
  101.         return angle1;
  102.     }

  103.     /** Get the angle of rotation around the second axis, in radians.
  104.      * @return angle of rotation around the second axis, in radians
  105.      */
  106.     public double getAngle2() {
  107.         return angle2;
  108.     }

  109.     /** Get the angle of rotation around the third axis, in radians.
  110.      * @return angle of rotation around the third axis, in radians
  111.      */
  112.     public double getAngle3() {
  113.         return angle3;
  114.     }

  115.     /** Get the rotation angles as a 3-element array.
  116.      * @return an array containing the 3 rotation angles
  117.      */
  118.     public double[] getAngles() {
  119.         return new double[]{angle1, angle2, angle3};
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public int hashCode() {
  124.         return 107 * (199 * Objects.hash(referenceFrame, axisSequence)) +
  125.                 (7 * Double.hashCode(angle1)) +
  126.                 (11 * Double.hashCode(angle2)) +
  127.                 (19 * Double.hashCode(angle3));
  128.     }

  129.     /** {@inheritDoc} */
  130.     @Override
  131.     public boolean equals(final Object obj) {
  132.         if (this == obj) {
  133.             return true;
  134.         }
  135.         if (!(obj instanceof AxisAngleSequence)) {
  136.             return false;
  137.         }

  138.         final AxisAngleSequence other = (AxisAngleSequence) obj;

  139.         return this.referenceFrame == other.referenceFrame &&
  140.                 this.axisSequence == other.axisSequence &&
  141.                 Double.compare(this.angle1, other.angle1) == 0 &&
  142.                 Double.compare(this.angle2, other.angle2) == 0 &&
  143.                 Double.compare(this.angle3, other.angle3) == 0;
  144.     }

  145.     /** {@inheritDoc} */
  146.     @Override
  147.     public String toString() {
  148.         final StringBuilder sb = new StringBuilder();
  149.         sb.append(this.getClass().getSimpleName())
  150.             .append("[referenceFrame=")
  151.             .append(referenceFrame)
  152.             .append(", axisSequence=")
  153.             .append(axisSequence)
  154.             .append(", angles=")
  155.             .append(Arrays.toString(getAngles()))
  156.             .append(']');

  157.         return sb.toString();
  158.     }

  159.     /** Create a new instance with a reference frame of {@link AxisReferenceFrame#RELATIVE}.
  160.      * @param axisSequence the axis rotation sequence
  161.      * @param angle1 angle around the first axis in radians
  162.      * @param angle2 angle around the second axis in radians
  163.      * @param angle3 angle around the third axis in radians
  164.      * @return a new instance with a relative reference frame
  165.      */
  166.     public static AxisAngleSequence createRelative(final AxisSequence axisSequence, final double angle1,
  167.             final double angle2, final double angle3) {
  168.         return new AxisAngleSequence(AxisReferenceFrame.RELATIVE, axisSequence, angle1, angle2, angle3);
  169.     }

  170.     /** Create a new instance with a reference frame of {@link AxisReferenceFrame#ABSOLUTE}.
  171.      * @param axisSequence the axis rotation sequence
  172.      * @param angle1 angle around the first axis in radians
  173.      * @param angle2 angle around the second axis in radians
  174.      * @param angle3 angle around the third axis in radians
  175.      * @return a new instance with an absolute reference frame
  176.      */
  177.     public static AxisAngleSequence createAbsolute(final AxisSequence axisSequence, final double angle1,
  178.             final double angle2, final double angle3) {
  179.         return new AxisAngleSequence(AxisReferenceFrame.ABSOLUTE, axisSequence, angle1, angle2, angle3);
  180.     }
  181. }