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 }