1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library
2*bf2c3715SXin Li // for linear algebra.
3*bf2c3715SXin Li //
4*bf2c3715SXin Li // Copyright (C) 2015 Tal Hadad <[email protected]>
5*bf2c3715SXin Li //
6*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla
7*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed
8*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9*bf2c3715SXin Li
10*bf2c3715SXin Li #include "main.h"
11*bf2c3715SXin Li
12*bf2c3715SXin Li #include <unsupported/Eigen/EulerAngles>
13*bf2c3715SXin Li
14*bf2c3715SXin Li using namespace Eigen;
15*bf2c3715SXin Li
16*bf2c3715SXin Li // Unfortunately, we need to specialize it in order to work. (We could add it in main.h test framework)
17*bf2c3715SXin Li template <typename Scalar, class System>
verifyIsApprox(const Eigen::EulerAngles<Scalar,System> & a,const Eigen::EulerAngles<Scalar,System> & b)18*bf2c3715SXin Li bool verifyIsApprox(const Eigen::EulerAngles<Scalar, System>& a, const Eigen::EulerAngles<Scalar, System>& b)
19*bf2c3715SXin Li {
20*bf2c3715SXin Li return verifyIsApprox(a.angles(), b.angles());
21*bf2c3715SXin Li }
22*bf2c3715SXin Li
23*bf2c3715SXin Li // Verify that x is in the approxed range [a, b]
24*bf2c3715SXin Li #define VERIFY_APPROXED_RANGE(a, x, b) \
25*bf2c3715SXin Li do { \
26*bf2c3715SXin Li VERIFY_IS_APPROX_OR_LESS_THAN(a, x); \
27*bf2c3715SXin Li VERIFY_IS_APPROX_OR_LESS_THAN(x, b); \
28*bf2c3715SXin Li } while(0)
29*bf2c3715SXin Li
30*bf2c3715SXin Li const char X = EULER_X;
31*bf2c3715SXin Li const char Y = EULER_Y;
32*bf2c3715SXin Li const char Z = EULER_Z;
33*bf2c3715SXin Li
34*bf2c3715SXin Li template<typename Scalar, class EulerSystem>
verify_euler(const EulerAngles<Scalar,EulerSystem> & e)35*bf2c3715SXin Li void verify_euler(const EulerAngles<Scalar, EulerSystem>& e)
36*bf2c3715SXin Li {
37*bf2c3715SXin Li typedef EulerAngles<Scalar, EulerSystem> EulerAnglesType;
38*bf2c3715SXin Li typedef Matrix<Scalar,3,3> Matrix3;
39*bf2c3715SXin Li typedef Matrix<Scalar,3,1> Vector3;
40*bf2c3715SXin Li typedef Quaternion<Scalar> QuaternionType;
41*bf2c3715SXin Li typedef AngleAxis<Scalar> AngleAxisType;
42*bf2c3715SXin Li
43*bf2c3715SXin Li const Scalar ONE = Scalar(1);
44*bf2c3715SXin Li const Scalar HALF_PI = Scalar(EIGEN_PI / 2);
45*bf2c3715SXin Li const Scalar PI = Scalar(EIGEN_PI);
46*bf2c3715SXin Li
47*bf2c3715SXin Li // It's very important calc the acceptable precision depending on the distance from the pole.
48*bf2c3715SXin Li const Scalar longitudeRadius = std::abs(
49*bf2c3715SXin Li EulerSystem::IsTaitBryan ?
50*bf2c3715SXin Li std::cos(e.beta()) :
51*bf2c3715SXin Li std::sin(e.beta())
52*bf2c3715SXin Li );
53*bf2c3715SXin Li Scalar precision = test_precision<Scalar>() / longitudeRadius;
54*bf2c3715SXin Li
55*bf2c3715SXin Li Scalar betaRangeStart, betaRangeEnd;
56*bf2c3715SXin Li if (EulerSystem::IsTaitBryan)
57*bf2c3715SXin Li {
58*bf2c3715SXin Li betaRangeStart = -HALF_PI;
59*bf2c3715SXin Li betaRangeEnd = HALF_PI;
60*bf2c3715SXin Li }
61*bf2c3715SXin Li else
62*bf2c3715SXin Li {
63*bf2c3715SXin Li if (!EulerSystem::IsBetaOpposite)
64*bf2c3715SXin Li {
65*bf2c3715SXin Li betaRangeStart = 0;
66*bf2c3715SXin Li betaRangeEnd = PI;
67*bf2c3715SXin Li }
68*bf2c3715SXin Li else
69*bf2c3715SXin Li {
70*bf2c3715SXin Li betaRangeStart = -PI;
71*bf2c3715SXin Li betaRangeEnd = 0;
72*bf2c3715SXin Li }
73*bf2c3715SXin Li }
74*bf2c3715SXin Li
75*bf2c3715SXin Li const Vector3 I_ = EulerAnglesType::AlphaAxisVector();
76*bf2c3715SXin Li const Vector3 J_ = EulerAnglesType::BetaAxisVector();
77*bf2c3715SXin Li const Vector3 K_ = EulerAnglesType::GammaAxisVector();
78*bf2c3715SXin Li
79*bf2c3715SXin Li // Is approx checks
80*bf2c3715SXin Li VERIFY(e.isApprox(e));
81*bf2c3715SXin Li VERIFY_IS_APPROX(e, e);
82*bf2c3715SXin Li VERIFY_IS_NOT_APPROX(e, EulerAnglesType(e.alpha() + ONE, e.beta() + ONE, e.gamma() + ONE));
83*bf2c3715SXin Li
84*bf2c3715SXin Li const Matrix3 m(e);
85*bf2c3715SXin Li VERIFY_IS_APPROX(Scalar(m.determinant()), ONE);
86*bf2c3715SXin Li
87*bf2c3715SXin Li EulerAnglesType ebis(m);
88*bf2c3715SXin Li
89*bf2c3715SXin Li // When no roll(acting like polar representation), we have the best precision.
90*bf2c3715SXin Li // One of those cases is when the Euler angles are on the pole, and because it's singular case,
91*bf2c3715SXin Li // the computation returns no roll.
92*bf2c3715SXin Li if (ebis.beta() == 0)
93*bf2c3715SXin Li precision = test_precision<Scalar>();
94*bf2c3715SXin Li
95*bf2c3715SXin Li // Check that eabis in range
96*bf2c3715SXin Li VERIFY_APPROXED_RANGE(-PI, ebis.alpha(), PI);
97*bf2c3715SXin Li VERIFY_APPROXED_RANGE(betaRangeStart, ebis.beta(), betaRangeEnd);
98*bf2c3715SXin Li VERIFY_APPROXED_RANGE(-PI, ebis.gamma(), PI);
99*bf2c3715SXin Li
100*bf2c3715SXin Li const Matrix3 mbis(AngleAxisType(ebis.alpha(), I_) * AngleAxisType(ebis.beta(), J_) * AngleAxisType(ebis.gamma(), K_));
101*bf2c3715SXin Li VERIFY_IS_APPROX(Scalar(mbis.determinant()), ONE);
102*bf2c3715SXin Li VERIFY_IS_APPROX(mbis, ebis.toRotationMatrix());
103*bf2c3715SXin Li /*std::cout << "===================\n" <<
104*bf2c3715SXin Li "e: " << e << std::endl <<
105*bf2c3715SXin Li "eabis: " << eabis.transpose() << std::endl <<
106*bf2c3715SXin Li "m: " << m << std::endl <<
107*bf2c3715SXin Li "mbis: " << mbis << std::endl <<
108*bf2c3715SXin Li "X: " << (m * Vector3::UnitX()).transpose() << std::endl <<
109*bf2c3715SXin Li "X: " << (mbis * Vector3::UnitX()).transpose() << std::endl;*/
110*bf2c3715SXin Li VERIFY(m.isApprox(mbis, precision));
111*bf2c3715SXin Li
112*bf2c3715SXin Li // Test if ea and eabis are the same
113*bf2c3715SXin Li // Need to check both singular and non-singular cases
114*bf2c3715SXin Li // There are two singular cases.
115*bf2c3715SXin Li // 1. When I==K and sin(ea(1)) == 0
116*bf2c3715SXin Li // 2. When I!=K and cos(ea(1)) == 0
117*bf2c3715SXin Li
118*bf2c3715SXin Li // TODO: Make this test work well, and use range saturation function.
119*bf2c3715SXin Li /*// If I==K, and ea[1]==0, then there no unique solution.
120*bf2c3715SXin Li // The remark apply in the case where I!=K, and |ea[1]| is close to +-pi/2.
121*bf2c3715SXin Li if( (i!=k || ea[1]!=0) && (i==k || !internal::isApprox(abs(ea[1]),Scalar(EIGEN_PI/2),test_precision<Scalar>())) )
122*bf2c3715SXin Li VERIFY_IS_APPROX(ea, eabis);*/
123*bf2c3715SXin Li
124*bf2c3715SXin Li // Quaternions
125*bf2c3715SXin Li const QuaternionType q(e);
126*bf2c3715SXin Li ebis = q;
127*bf2c3715SXin Li const QuaternionType qbis(ebis);
128*bf2c3715SXin Li VERIFY(internal::isApprox<Scalar>(std::abs(q.dot(qbis)), ONE, precision));
129*bf2c3715SXin Li //VERIFY_IS_APPROX(eabis, eabis2);// Verify that the euler angles are still the same
130*bf2c3715SXin Li
131*bf2c3715SXin Li // A suggestion for simple product test when will be supported.
132*bf2c3715SXin Li /*EulerAnglesType e2(PI/2, PI/2, PI/2);
133*bf2c3715SXin Li Matrix3 m2(e2);
134*bf2c3715SXin Li VERIFY_IS_APPROX(e*e2, m*m2);*/
135*bf2c3715SXin Li }
136*bf2c3715SXin Li
137*bf2c3715SXin Li template<signed char A, signed char B, signed char C, typename Scalar>
verify_euler_vec(const Matrix<Scalar,3,1> & ea)138*bf2c3715SXin Li void verify_euler_vec(const Matrix<Scalar,3,1>& ea)
139*bf2c3715SXin Li {
140*bf2c3715SXin Li verify_euler(EulerAngles<Scalar, EulerSystem<A, B, C> >(ea[0], ea[1], ea[2]));
141*bf2c3715SXin Li }
142*bf2c3715SXin Li
143*bf2c3715SXin Li template<signed char A, signed char B, signed char C, typename Scalar>
verify_euler_all_neg(const Matrix<Scalar,3,1> & ea)144*bf2c3715SXin Li void verify_euler_all_neg(const Matrix<Scalar,3,1>& ea)
145*bf2c3715SXin Li {
146*bf2c3715SXin Li verify_euler_vec<+A,+B,+C>(ea);
147*bf2c3715SXin Li verify_euler_vec<+A,+B,-C>(ea);
148*bf2c3715SXin Li verify_euler_vec<+A,-B,+C>(ea);
149*bf2c3715SXin Li verify_euler_vec<+A,-B,-C>(ea);
150*bf2c3715SXin Li
151*bf2c3715SXin Li verify_euler_vec<-A,+B,+C>(ea);
152*bf2c3715SXin Li verify_euler_vec<-A,+B,-C>(ea);
153*bf2c3715SXin Li verify_euler_vec<-A,-B,+C>(ea);
154*bf2c3715SXin Li verify_euler_vec<-A,-B,-C>(ea);
155*bf2c3715SXin Li }
156*bf2c3715SXin Li
check_all_var(const Matrix<Scalar,3,1> & ea)157*bf2c3715SXin Li template<typename Scalar> void check_all_var(const Matrix<Scalar,3,1>& ea)
158*bf2c3715SXin Li {
159*bf2c3715SXin Li verify_euler_all_neg<X,Y,Z>(ea);
160*bf2c3715SXin Li verify_euler_all_neg<X,Y,X>(ea);
161*bf2c3715SXin Li verify_euler_all_neg<X,Z,Y>(ea);
162*bf2c3715SXin Li verify_euler_all_neg<X,Z,X>(ea);
163*bf2c3715SXin Li
164*bf2c3715SXin Li verify_euler_all_neg<Y,Z,X>(ea);
165*bf2c3715SXin Li verify_euler_all_neg<Y,Z,Y>(ea);
166*bf2c3715SXin Li verify_euler_all_neg<Y,X,Z>(ea);
167*bf2c3715SXin Li verify_euler_all_neg<Y,X,Y>(ea);
168*bf2c3715SXin Li
169*bf2c3715SXin Li verify_euler_all_neg<Z,X,Y>(ea);
170*bf2c3715SXin Li verify_euler_all_neg<Z,X,Z>(ea);
171*bf2c3715SXin Li verify_euler_all_neg<Z,Y,X>(ea);
172*bf2c3715SXin Li verify_euler_all_neg<Z,Y,Z>(ea);
173*bf2c3715SXin Li }
174*bf2c3715SXin Li
check_singular_cases(const Scalar & singularBeta)175*bf2c3715SXin Li template<typename Scalar> void check_singular_cases(const Scalar& singularBeta)
176*bf2c3715SXin Li {
177*bf2c3715SXin Li typedef Matrix<Scalar,3,1> Vector3;
178*bf2c3715SXin Li const Scalar PI = Scalar(EIGEN_PI);
179*bf2c3715SXin Li
180*bf2c3715SXin Li for (Scalar epsilon = NumTraits<Scalar>::epsilon(); epsilon < 1; epsilon *= Scalar(1.2))
181*bf2c3715SXin Li {
182*bf2c3715SXin Li check_all_var(Vector3(PI/4, singularBeta, PI/3));
183*bf2c3715SXin Li check_all_var(Vector3(PI/4, singularBeta - epsilon, PI/3));
184*bf2c3715SXin Li check_all_var(Vector3(PI/4, singularBeta - Scalar(1.5)*epsilon, PI/3));
185*bf2c3715SXin Li check_all_var(Vector3(PI/4, singularBeta - 2*epsilon, PI/3));
186*bf2c3715SXin Li check_all_var(Vector3(PI*Scalar(0.8), singularBeta - epsilon, Scalar(0.9)*PI));
187*bf2c3715SXin Li check_all_var(Vector3(PI*Scalar(-0.9), singularBeta + epsilon, PI*Scalar(0.3)));
188*bf2c3715SXin Li check_all_var(Vector3(PI*Scalar(-0.6), singularBeta + Scalar(1.5)*epsilon, PI*Scalar(0.3)));
189*bf2c3715SXin Li check_all_var(Vector3(PI*Scalar(-0.5), singularBeta + 2*epsilon, PI*Scalar(0.4)));
190*bf2c3715SXin Li check_all_var(Vector3(PI*Scalar(0.9), singularBeta + epsilon, Scalar(0.8)*PI));
191*bf2c3715SXin Li }
192*bf2c3715SXin Li
193*bf2c3715SXin Li // This one for sanity, it had a problem with near pole cases in float scalar.
194*bf2c3715SXin Li check_all_var(Vector3(PI*Scalar(0.8), singularBeta - Scalar(1E-6), Scalar(0.9)*PI));
195*bf2c3715SXin Li }
196*bf2c3715SXin Li
eulerangles_manual()197*bf2c3715SXin Li template<typename Scalar> void eulerangles_manual()
198*bf2c3715SXin Li {
199*bf2c3715SXin Li typedef Matrix<Scalar,3,1> Vector3;
200*bf2c3715SXin Li typedef Matrix<Scalar,Dynamic,1> VectorX;
201*bf2c3715SXin Li const Vector3 Zero = Vector3::Zero();
202*bf2c3715SXin Li const Scalar PI = Scalar(EIGEN_PI);
203*bf2c3715SXin Li
204*bf2c3715SXin Li check_all_var(Zero);
205*bf2c3715SXin Li
206*bf2c3715SXin Li // singular cases
207*bf2c3715SXin Li check_singular_cases(PI/2);
208*bf2c3715SXin Li check_singular_cases(-PI/2);
209*bf2c3715SXin Li
210*bf2c3715SXin Li check_singular_cases(Scalar(0));
211*bf2c3715SXin Li check_singular_cases(Scalar(-0));
212*bf2c3715SXin Li
213*bf2c3715SXin Li check_singular_cases(PI);
214*bf2c3715SXin Li check_singular_cases(-PI);
215*bf2c3715SXin Li
216*bf2c3715SXin Li // non-singular cases
217*bf2c3715SXin Li VectorX alpha = VectorX::LinSpaced(20, Scalar(-0.99) * PI, PI);
218*bf2c3715SXin Li VectorX beta = VectorX::LinSpaced(20, Scalar(-0.49) * PI, Scalar(0.49) * PI);
219*bf2c3715SXin Li VectorX gamma = VectorX::LinSpaced(20, Scalar(-0.99) * PI, PI);
220*bf2c3715SXin Li for (int i = 0; i < alpha.size(); ++i) {
221*bf2c3715SXin Li for (int j = 0; j < beta.size(); ++j) {
222*bf2c3715SXin Li for (int k = 0; k < gamma.size(); ++k) {
223*bf2c3715SXin Li check_all_var(Vector3(alpha(i), beta(j), gamma(k)));
224*bf2c3715SXin Li }
225*bf2c3715SXin Li }
226*bf2c3715SXin Li }
227*bf2c3715SXin Li }
228*bf2c3715SXin Li
eulerangles_rand()229*bf2c3715SXin Li template<typename Scalar> void eulerangles_rand()
230*bf2c3715SXin Li {
231*bf2c3715SXin Li typedef Matrix<Scalar,3,3> Matrix3;
232*bf2c3715SXin Li typedef Matrix<Scalar,3,1> Vector3;
233*bf2c3715SXin Li typedef Array<Scalar,3,1> Array3;
234*bf2c3715SXin Li typedef Quaternion<Scalar> Quaternionx;
235*bf2c3715SXin Li typedef AngleAxis<Scalar> AngleAxisType;
236*bf2c3715SXin Li
237*bf2c3715SXin Li Scalar a = internal::random<Scalar>(-Scalar(EIGEN_PI), Scalar(EIGEN_PI));
238*bf2c3715SXin Li Quaternionx q1;
239*bf2c3715SXin Li q1 = AngleAxisType(a, Vector3::Random().normalized());
240*bf2c3715SXin Li Matrix3 m;
241*bf2c3715SXin Li m = q1;
242*bf2c3715SXin Li
243*bf2c3715SXin Li Vector3 ea = m.eulerAngles(0,1,2);
244*bf2c3715SXin Li check_all_var(ea);
245*bf2c3715SXin Li ea = m.eulerAngles(0,1,0);
246*bf2c3715SXin Li check_all_var(ea);
247*bf2c3715SXin Li
248*bf2c3715SXin Li // Check with purely random Quaternion:
249*bf2c3715SXin Li q1.coeffs() = Quaternionx::Coefficients::Random().normalized();
250*bf2c3715SXin Li m = q1;
251*bf2c3715SXin Li ea = m.eulerAngles(0,1,2);
252*bf2c3715SXin Li check_all_var(ea);
253*bf2c3715SXin Li ea = m.eulerAngles(0,1,0);
254*bf2c3715SXin Li check_all_var(ea);
255*bf2c3715SXin Li
256*bf2c3715SXin Li // Check with random angles in range [0:pi]x[-pi:pi]x[-pi:pi].
257*bf2c3715SXin Li ea = (Array3::Random() + Array3(1,0,0))*Scalar(EIGEN_PI)*Array3(0.5,1,1);
258*bf2c3715SXin Li check_all_var(ea);
259*bf2c3715SXin Li
260*bf2c3715SXin Li ea[2] = ea[0] = internal::random<Scalar>(0,Scalar(EIGEN_PI));
261*bf2c3715SXin Li check_all_var(ea);
262*bf2c3715SXin Li
263*bf2c3715SXin Li ea[0] = ea[1] = internal::random<Scalar>(0,Scalar(EIGEN_PI));
264*bf2c3715SXin Li check_all_var(ea);
265*bf2c3715SXin Li
266*bf2c3715SXin Li ea[1] = 0;
267*bf2c3715SXin Li check_all_var(ea);
268*bf2c3715SXin Li
269*bf2c3715SXin Li ea.head(2).setZero();
270*bf2c3715SXin Li check_all_var(ea);
271*bf2c3715SXin Li
272*bf2c3715SXin Li ea.setZero();
273*bf2c3715SXin Li check_all_var(ea);
274*bf2c3715SXin Li }
275*bf2c3715SXin Li
EIGEN_DECLARE_TEST(EulerAngles)276*bf2c3715SXin Li EIGEN_DECLARE_TEST(EulerAngles)
277*bf2c3715SXin Li {
278*bf2c3715SXin Li // Simple cast test
279*bf2c3715SXin Li EulerAnglesXYZd onesEd(1, 1, 1);
280*bf2c3715SXin Li EulerAnglesXYZf onesEf = onesEd.cast<float>();
281*bf2c3715SXin Li VERIFY_IS_APPROX(onesEd, onesEf.cast<double>());
282*bf2c3715SXin Li
283*bf2c3715SXin Li // Simple Construction from Vector3 test
284*bf2c3715SXin Li VERIFY_IS_APPROX(onesEd, EulerAnglesXYZd(Vector3d::Ones()));
285*bf2c3715SXin Li
286*bf2c3715SXin Li CALL_SUBTEST_1( eulerangles_manual<float>() );
287*bf2c3715SXin Li CALL_SUBTEST_2( eulerangles_manual<double>() );
288*bf2c3715SXin Li
289*bf2c3715SXin Li for(int i = 0; i < g_repeat; i++) {
290*bf2c3715SXin Li CALL_SUBTEST_3( eulerangles_rand<float>() );
291*bf2c3715SXin Li CALL_SUBTEST_4( eulerangles_rand<double>() );
292*bf2c3715SXin Li }
293*bf2c3715SXin Li
294*bf2c3715SXin Li // TODO: Add tests for auto diff
295*bf2c3715SXin Li // TODO: Add tests for complex numbers
296*bf2c3715SXin Li }
297