Eigen  5.0.1
 
Loading...
Searching...
No Matches
EulerAngles.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
5// Copyright (C) 2023 Juraj Oršulić, University of Zagreb <juraj.orsulic@fer.hr>
6//
7// This Source Code Form is subject to the terms of the Mozilla
8// Public License v. 2.0. If a copy of the MPL was not distributed
9// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10// SPDX-License-Identifier: MPL-2.0
11
12#ifndef EIGEN_EULERANGLES_H
13#define EIGEN_EULERANGLES_H
14
15// IWYU pragma: private
16#include "./InternalHeaderCheck.h"
17
18namespace Eigen {
19
45template <typename Derived>
47 Index a0, Index a1, Index a2) const {
48 /* Implemented from Graphics Gems IV */
49 EIGEN_STATIC_ASSERT_MATRIX_SPECIFIC_SIZE(Derived, 3, 3)
50
52
53 const Index odd = ((a0 + 1) % 3 == a1) ? 0 : 1;
54 const Index i = a0;
55 const Index j = (a0 + 1 + odd) % 3;
56 const Index k = (a0 + 2 - odd) % 3;
57
58 if (a0 == a2) {
59 // Proper Euler angles (same first and last axis).
60 // The i, j, k indices enable addressing the input matrix as the XYX archetype matrix (see Graphics Gems IV),
61 // where e.g. coeff(k, i) means third column, first row in the XYX archetype matrix:
62 // c2 s2s1 s2c1
63 // s2s3 -c2s1s3 + c1c3 -c2c1s3 - s1c3
64 // -s2c3 c2s1c3 + c1s3 c2c1c3 - s1s3
65
66 // Note: s2 is always positive.
67 Scalar s2 = numext::hypot(coeff(j, i), coeff(k, i));
68 if (odd) {
69 res[0] = numext::atan2(coeff(j, i), coeff(k, i));
70 // s2 is always positive, so res[1] will be within the canonical [0, pi] range
71 res[1] = numext::atan2(s2, coeff(i, i));
72 } else {
73 // In the !odd case, signs of all three angles are flipped at the very end. To keep the solution within the
74 // canonical range, we flip the solution and make res[1] always negative here (since s2 is always positive,
75 // -atan2(s2, c2) will always be negative). The final flip at the end due to !odd will thus make res[1] positive
76 // and canonical. NB: in the general case, there are two correct solutions, but only one is canonical. For proper
77 // Euler angles, flipping from one solution to the other involves flipping the sign of the second angle res[1] and
78 // adding/subtracting pi to the first and third angles. The addition/subtraction of pi to the first angle res[0]
79 // is handled here by flipping the signs of arguments to atan2, while the calculation of the third angle does not
80 // need special adjustment since it uses the adjusted res[0] as the input and produces a correct result.
81 res[0] = numext::atan2(-coeff(j, i), -coeff(k, i));
82 res[1] = -numext::atan2(s2, coeff(i, i));
83 }
84
85 // With a=(0,1,0), we have i=0; j=1; k=2, and after computing the first two angles,
86 // we can compute their respective rotation, and apply its inverse to M. Since the result must
87 // be a rotation around x, we have:
88 //
89 // c2 s1.s2 c1.s2 1 0 0
90 // 0 c1 -s1 * M = 0 c3 s3
91 // -s2 s1.c2 c1.c2 0 -s3 c3
92 //
93 // Thus: m11.c1 - m21.s1 = c3 & m12.c1 - m22.s1 = s3
94
95 // Recover sin(res[0]) and cos(res[0]) from the atan2 arguments directly,
96 // avoiding a redundant sin+cos evaluation. s2 = hypot(coeff(j,i), coeff(k,i))
97 // is the norm of the atan2 arguments (with sign adjustment for !odd).
98 Scalar s1, c1;
99 if (s2 > NumTraits<Scalar>::epsilon()) {
100 Scalar inv_s2 = Scalar(1) / s2;
101 if (odd) {
102 // res[0] = atan2(coeff(j,i), coeff(k,i))
103 s1 = coeff(j, i) * inv_s2;
104 c1 = coeff(k, i) * inv_s2;
105 } else {
106 // res[0] = atan2(-coeff(j,i), -coeff(k,i))
107 s1 = -coeff(j, i) * inv_s2;
108 c1 = -coeff(k, i) * inv_s2;
109 }
110 } else {
111 // Gimbal lock (s2 ≈ 0): recover sin/cos from the computed angle.
112 s1 = numext::sin(res[0]);
113 c1 = numext::cos(res[0]);
114 }
115 res[2] = numext::atan2(c1 * coeff(j, k) - s1 * coeff(k, k), c1 * coeff(j, j) - s1 * coeff(k, j));
116 } else {
117 // Tait-Bryan angles (all three axes are different; typically used for yaw-pitch-roll calculations).
118 // The i, j, k indices enable addressing the input matrix as the XYZ archetype matrix (see Graphics Gems IV),
119 // where e.g. coeff(k, i) means third column, first row in the XYZ archetype matrix:
120 // c2c3 s2s1c3 - c1s3 s2c1c3 + s1s3
121 // c2s3 s2s1s3 + c1c3 s2c1s3 - s1c3
122 // -s2 c2s1 c2c1
123
124 // Recover sin(res[0]) and cos(res[0]) from the atan2 arguments directly:
125 // res[0] = atan2(coeff(j,k), coeff(k,k))
126 // sin(res[0]) = coeff(j,k) / hypot(coeff(j,k), coeff(k,k))
127 // cos(res[0]) = coeff(k,k) / hypot(coeff(j,k), coeff(k,k))
128 Scalar n1 = numext::hypot(coeff(j, k), coeff(k, k));
129 res[0] = numext::atan2(coeff(j, k), coeff(k, k));
130
131 Scalar c2 = numext::hypot(coeff(i, i), coeff(i, j));
132 // c2 is always positive, so the following atan2 will always return a result in the correct canonical middle angle
133 // range [-pi/2, pi/2]
134 res[1] = numext::atan2(-coeff(i, k), c2);
135
136 Scalar s1, c1;
137 if (n1 > NumTraits<Scalar>::epsilon()) {
138 Scalar inv_n1 = Scalar(1) / n1;
139 s1 = coeff(j, k) * inv_n1;
140 c1 = coeff(k, k) * inv_n1;
141 } else {
142 // Gimbal lock: coeff(j,k) and coeff(k,k) are both near zero.
143 // Fall back to sin/cos of the computed angle.
144 s1 = numext::sin(res[0]);
145 c1 = numext::cos(res[0]);
146 }
147 res[2] = numext::atan2(s1 * coeff(k, i) - c1 * coeff(j, i), c1 * coeff(j, j) - s1 * coeff(k, j));
148 }
149 if (!odd) {
150 res = -res;
151 }
152
153 return res;
154}
155
168template <typename Derived>
170 Index a0, Index a1, Index a2) const {
171 /* Implemented from Graphics Gems IV */
172 EIGEN_STATIC_ASSERT_MATRIX_SPECIFIC_SIZE(Derived, 3, 3)
173
175
176 const Index odd = ((a0 + 1) % 3 == a1) ? 0 : 1;
177 const Index i = a0;
178 const Index j = (a0 + 1 + odd) % 3;
179 const Index k = (a0 + 2 - odd) % 3;
180
181 if (a0 == a2) {
182 res[0] = numext::atan2(coeff(j, i), coeff(k, i));
183 if ((odd && res[0] < Scalar(0)) || ((!odd) && res[0] > Scalar(0))) {
184 if (res[0] > Scalar(0)) {
185 res[0] -= Scalar(EIGEN_PI);
186 } else {
187 res[0] += Scalar(EIGEN_PI);
188 }
189
190 Scalar s2 = numext::hypot(coeff(j, i), coeff(k, i));
191 res[1] = -numext::atan2(s2, coeff(i, i));
192 } else {
193 Scalar s2 = numext::hypot(coeff(j, i), coeff(k, i));
194 res[1] = numext::atan2(s2, coeff(i, i));
195 }
196
197 // With a=(0,1,0), we have i=0; j=1; k=2, and after computing the first two angles,
198 // we can compute their respective rotation, and apply its inverse to M. Since the result must
199 // be a rotation around x, we have:
200 //
201 // c2 s1.s2 c1.s2 1 0 0
202 // 0 c1 -s1 * M = 0 c3 s3
203 // -s2 s1.c2 c1.c2 0 -s3 c3
204 //
205 // Thus: m11.c1 - m21.s1 = c3 & m12.c1 - m22.s1 = s3
206
207 Scalar s1 = numext::sin(res[0]);
208 Scalar c1 = numext::cos(res[0]);
209 res[2] = numext::atan2(c1 * coeff(j, k) - s1 * coeff(k, k), c1 * coeff(j, j) - s1 * coeff(k, j));
210 } else {
211 res[0] = numext::atan2(coeff(j, k), coeff(k, k));
212 Scalar c2 = numext::hypot(coeff(i, i), coeff(i, j));
213 if ((odd && res[0] < Scalar(0)) || ((!odd) && res[0] > Scalar(0))) {
214 if (res[0] > Scalar(0)) {
215 res[0] -= Scalar(EIGEN_PI);
216 } else {
217 res[0] += Scalar(EIGEN_PI);
218 }
219 res[1] = numext::atan2(-coeff(i, k), -c2);
220 } else {
221 res[1] = numext::atan2(-coeff(i, k), c2);
222 }
223 Scalar s1 = numext::sin(res[0]);
224 Scalar c1 = numext::cos(res[0]);
225 res[2] = numext::atan2(s1 * coeff(k, i) - c1 * coeff(j, i), c1 * coeff(j, j) - s1 * coeff(k, j));
226 }
227 if (!odd) {
228 res = -res;
229 }
230
231 return res;
232}
233
234} // end namespace Eigen
235
236#endif // EIGEN_EULERANGLES_H
typename internal::traits< Derived >::Scalar Scalar
Definition DenseBase.h:63
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
Matrix< Scalar, 3, 1 > eulerAngles(Index a0, Index a1, Index a2) const
Definition EulerAngles.h:169
Matrix< Scalar, 3, 1 > canonicalEulerAngles(Index a0, Index a1, Index a2) const
Definition EulerAngles.h:46