Home | History | Annotate | Download | only in test
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 #include "main.h"
     11 #include <Eigen/Geometry>
     12 #include <Eigen/LU>
     13 #include <Eigen/SVD>
     14 
     15 template<typename T>
     16 Matrix<T,2,1> angleToVec(T a)
     17 {
     18   return Matrix<T,2,1>(std::cos(a), std::sin(a));
     19 }
     20 
     21 // This permits to workaround a bug in clang/llvm code generation.
     22 template<typename T>
     23 EIGEN_DONT_INLINE
     24 void dont_over_optimize(T& x) { volatile typename T::Scalar tmp = x(0); x(0) = tmp; }
     25 
     26 template<typename Scalar, int Mode, int Options> void non_projective_only()
     27 {
     28     /* this test covers the following files:
     29      Cross.h Quaternion.h, Transform.cpp
     30   */
     31   typedef Matrix<Scalar,3,1> Vector3;
     32   typedef Quaternion<Scalar> Quaternionx;
     33   typedef AngleAxis<Scalar> AngleAxisx;
     34   typedef Transform<Scalar,3,Mode,Options> Transform3;
     35   typedef DiagonalMatrix<Scalar,3> AlignedScaling3;
     36   typedef Translation<Scalar,3> Translation3;
     37 
     38   Vector3 v0 = Vector3::Random(),
     39           v1 = Vector3::Random();
     40 
     41   Transform3 t0, t1, t2;
     42 
     43   Scalar a = internal::random<Scalar>(-Scalar(EIGEN_PI), Scalar(EIGEN_PI));
     44 
     45   Quaternionx q1, q2;
     46 
     47   q1 = AngleAxisx(a, v0.normalized());
     48 
     49   t0 = Transform3::Identity();
     50   VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity());
     51 
     52   t0.linear() = q1.toRotationMatrix();
     53 
     54   v0 << 50, 2, 1;
     55   t0.scale(v0);
     56 
     57   VERIFY_IS_APPROX( (t0 * Vector3(1,0,0)).template head<3>().norm(), v0.x());
     58 
     59   t0.setIdentity();
     60   t1.setIdentity();
     61   v1 << 1, 2, 3;
     62   t0.linear() = q1.toRotationMatrix();
     63   t0.pretranslate(v0);
     64   t0.scale(v1);
     65   t1.linear() = q1.conjugate().toRotationMatrix();
     66   t1.prescale(v1.cwiseInverse());
     67   t1.translate(-v0);
     68 
     69   VERIFY((t0 * t1).matrix().isIdentity(test_precision<Scalar>()));
     70 
     71   t1.fromPositionOrientationScale(v0, q1, v1);
     72   VERIFY_IS_APPROX(t1.matrix(), t0.matrix());
     73   VERIFY_IS_APPROX(t1*v1, t0*v1);
     74 
     75   // translation * vector
     76   t0.setIdentity();
     77   t0.translate(v0);
     78   VERIFY_IS_APPROX((t0 * v1).template head<3>(), Translation3(v0) * v1);
     79 
     80   // AlignedScaling * vector
     81   t0.setIdentity();
     82   t0.scale(v0);
     83   VERIFY_IS_APPROX((t0 * v1).template head<3>(), AlignedScaling3(v0) * v1);
     84 }
     85 
     86 template<typename Scalar, int Mode, int Options> void transformations()
     87 {
     88   /* this test covers the following files:
     89      Cross.h Quaternion.h, Transform.cpp
     90   */
     91   using std::cos;
     92   using std::abs;
     93   typedef Matrix<Scalar,3,3> Matrix3;
     94   typedef Matrix<Scalar,4,4> Matrix4;
     95   typedef Matrix<Scalar,2,1> Vector2;
     96   typedef Matrix<Scalar,3,1> Vector3;
     97   typedef Matrix<Scalar,4,1> Vector4;
     98   typedef Quaternion<Scalar> Quaternionx;
     99   typedef AngleAxis<Scalar> AngleAxisx;
    100   typedef Transform<Scalar,2,Mode,Options> Transform2;
    101   typedef Transform<Scalar,3,Mode,Options> Transform3;
    102   typedef typename Transform3::MatrixType MatrixType;
    103   typedef DiagonalMatrix<Scalar,3> AlignedScaling3;
    104   typedef Translation<Scalar,2> Translation2;
    105   typedef Translation<Scalar,3> Translation3;
    106 
    107   Vector3 v0 = Vector3::Random(),
    108           v1 = Vector3::Random();
    109   Matrix3 matrot1, m;
    110 
    111   Scalar a = internal::random<Scalar>(-Scalar(EIGEN_PI), Scalar(EIGEN_PI));
    112   Scalar s0 = internal::random<Scalar>(), s1 = internal::random<Scalar>();
    113 
    114   while(v0.norm() < test_precision<Scalar>()) v0 = Vector3::Random();
    115   while(v1.norm() < test_precision<Scalar>()) v1 = Vector3::Random();
    116 
    117   VERIFY_IS_APPROX(v0, AngleAxisx(a, v0.normalized()) * v0);
    118   VERIFY_IS_APPROX(-v0, AngleAxisx(Scalar(EIGEN_PI), v0.unitOrthogonal()) * v0);
    119   if(abs(cos(a)) > test_precision<Scalar>())
    120   {
    121     VERIFY_IS_APPROX(cos(a)*v0.squaredNorm(), v0.dot(AngleAxisx(a, v0.unitOrthogonal()) * v0));
    122   }
    123   m = AngleAxisx(a, v0.normalized()).toRotationMatrix().adjoint();
    124   VERIFY_IS_APPROX(Matrix3::Identity(), m * AngleAxisx(a, v0.normalized()));
    125   VERIFY_IS_APPROX(Matrix3::Identity(), AngleAxisx(a, v0.normalized()) * m);
    126 
    127   Quaternionx q1, q2;
    128   q1 = AngleAxisx(a, v0.normalized());
    129   q2 = AngleAxisx(a, v1.normalized());
    130 
    131   // rotation matrix conversion
    132   matrot1 = AngleAxisx(Scalar(0.1), Vector3::UnitX())
    133           * AngleAxisx(Scalar(0.2), Vector3::UnitY())
    134           * AngleAxisx(Scalar(0.3), Vector3::UnitZ());
    135   VERIFY_IS_APPROX(matrot1 * v1,
    136        AngleAxisx(Scalar(0.1), Vector3(1,0,0)).toRotationMatrix()
    137     * (AngleAxisx(Scalar(0.2), Vector3(0,1,0)).toRotationMatrix()
    138     * (AngleAxisx(Scalar(0.3), Vector3(0,0,1)).toRotationMatrix() * v1)));
    139 
    140   // angle-axis conversion
    141   AngleAxisx aa = AngleAxisx(q1);
    142   VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1);
    143 
    144   // The following test is stable only if 2*angle != angle and v1 is not colinear with axis
    145   if( (abs(aa.angle()) > test_precision<Scalar>()) && (abs(aa.axis().dot(v1.normalized()))<(Scalar(1)-Scalar(4)*test_precision<Scalar>())) )
    146   {
    147     VERIFY( !(q1 * v1).isApprox(Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1) );
    148   }
    149 
    150   aa.fromRotationMatrix(aa.toRotationMatrix());
    151   VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1);
    152   // The following test is stable only if 2*angle != angle and v1 is not colinear with axis
    153   if( (abs(aa.angle()) > test_precision<Scalar>()) && (abs(aa.axis().dot(v1.normalized()))<(Scalar(1)-Scalar(4)*test_precision<Scalar>())) )
    154   {
    155     VERIFY( !(q1 * v1).isApprox(Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1) );
    156   }
    157 
    158   // AngleAxis
    159   VERIFY_IS_APPROX(AngleAxisx(a,v1.normalized()).toRotationMatrix(),
    160     Quaternionx(AngleAxisx(a,v1.normalized())).toRotationMatrix());
    161 
    162   AngleAxisx aa1;
    163   m = q1.toRotationMatrix();
    164   aa1 = m;
    165   VERIFY_IS_APPROX(AngleAxisx(m).toRotationMatrix(),
    166     Quaternionx(m).toRotationMatrix());
    167 
    168   // Transform
    169   // TODO complete the tests !
    170   a = 0;
    171   while (abs(a)<Scalar(0.1))
    172     a = internal::random<Scalar>(-Scalar(0.4)*Scalar(EIGEN_PI), Scalar(0.4)*Scalar(EIGEN_PI));
    173   q1 = AngleAxisx(a, v0.normalized());
    174   Transform3 t0, t1, t2;
    175 
    176   // first test setIdentity() and Identity()
    177   t0.setIdentity();
    178   VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity());
    179   t0.matrix().setZero();
    180   t0 = Transform3::Identity();
    181   VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity());
    182 
    183   t0.setIdentity();
    184   t1.setIdentity();
    185   v1 << 1, 2, 3;
    186   t0.linear() = q1.toRotationMatrix();
    187   t0.pretranslate(v0);
    188   t0.scale(v1);
    189   t1.linear() = q1.conjugate().toRotationMatrix();
    190   t1.prescale(v1.cwiseInverse());
    191   t1.translate(-v0);
    192 
    193   VERIFY((t0 * t1).matrix().isIdentity(test_precision<Scalar>()));
    194 
    195   t1.fromPositionOrientationScale(v0, q1, v1);
    196   VERIFY_IS_APPROX(t1.matrix(), t0.matrix());
    197 
    198   t0.setIdentity(); t0.scale(v0).rotate(q1.toRotationMatrix());
    199   t1.setIdentity(); t1.scale(v0).rotate(q1);
    200   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    201 
    202   t0.setIdentity(); t0.scale(v0).rotate(AngleAxisx(q1));
    203   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    204 
    205   VERIFY_IS_APPROX(t0.scale(a).matrix(), t1.scale(Vector3::Constant(a)).matrix());
    206   VERIFY_IS_APPROX(t0.prescale(a).matrix(), t1.prescale(Vector3::Constant(a)).matrix());
    207 
    208   // More transform constructors, operator=, operator*=
    209 
    210   Matrix3 mat3 = Matrix3::Random();
    211   Matrix4 mat4;
    212   mat4 << mat3 , Vector3::Zero() , Vector4::Zero().transpose();
    213   Transform3 tmat3(mat3), tmat4(mat4);
    214   if(Mode!=int(AffineCompact))
    215     tmat4.matrix()(3,3) = Scalar(1);
    216   VERIFY_IS_APPROX(tmat3.matrix(), tmat4.matrix());
    217 
    218   Scalar a3 = internal::random<Scalar>(-Scalar(EIGEN_PI), Scalar(EIGEN_PI));
    219   Vector3 v3 = Vector3::Random().normalized();
    220   AngleAxisx aa3(a3, v3);
    221   Transform3 t3(aa3);
    222   Transform3 t4;
    223   t4 = aa3;
    224   VERIFY_IS_APPROX(t3.matrix(), t4.matrix());
    225   t4.rotate(AngleAxisx(-a3,v3));
    226   VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity());
    227   t4 *= aa3;
    228   VERIFY_IS_APPROX(t3.matrix(), t4.matrix());
    229 
    230   do {
    231     v3 = Vector3::Random();
    232     dont_over_optimize(v3);
    233   } while (v3.cwiseAbs().minCoeff()<NumTraits<Scalar>::epsilon());
    234   Translation3 tv3(v3);
    235   Transform3 t5(tv3);
    236   t4 = tv3;
    237   VERIFY_IS_APPROX(t5.matrix(), t4.matrix());
    238   t4.translate((-v3).eval());
    239   VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity());
    240   t4 *= tv3;
    241   VERIFY_IS_APPROX(t5.matrix(), t4.matrix());
    242 
    243   AlignedScaling3 sv3(v3);
    244   Transform3 t6(sv3);
    245   t4 = sv3;
    246   VERIFY_IS_APPROX(t6.matrix(), t4.matrix());
    247   t4.scale(v3.cwiseInverse());
    248   VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity());
    249   t4 *= sv3;
    250   VERIFY_IS_APPROX(t6.matrix(), t4.matrix());
    251 
    252   // matrix * transform
    253   VERIFY_IS_APPROX((t3.matrix()*t4).matrix(), (t3*t4).matrix());
    254 
    255   // chained Transform product
    256   VERIFY_IS_APPROX(((t3*t4)*t5).matrix(), (t3*(t4*t5)).matrix());
    257 
    258   // check that Transform product doesn't have aliasing problems
    259   t5 = t4;
    260   t5 = t5*t5;
    261   VERIFY_IS_APPROX(t5, t4*t4);
    262 
    263   // 2D transformation
    264   Transform2 t20, t21;
    265   Vector2 v20 = Vector2::Random();
    266   Vector2 v21 = Vector2::Random();
    267   for (int k=0; k<2; ++k)
    268     if (abs(v21[k])<Scalar(1e-3)) v21[k] = Scalar(1e-3);
    269   t21.setIdentity();
    270   t21.linear() = Rotation2D<Scalar>(a).toRotationMatrix();
    271   VERIFY_IS_APPROX(t20.fromPositionOrientationScale(v20,a,v21).matrix(),
    272     t21.pretranslate(v20).scale(v21).matrix());
    273 
    274   t21.setIdentity();
    275   t21.linear() = Rotation2D<Scalar>(-a).toRotationMatrix();
    276   VERIFY( (t20.fromPositionOrientationScale(v20,a,v21)
    277         * (t21.prescale(v21.cwiseInverse()).translate(-v20))).matrix().isIdentity(test_precision<Scalar>()) );
    278 
    279   // Transform - new API
    280   // 3D
    281   t0.setIdentity();
    282   t0.rotate(q1).scale(v0).translate(v0);
    283   // mat * aligned scaling and mat * translation
    284   t1 = (Matrix3(q1) * AlignedScaling3(v0)) * Translation3(v0);
    285   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    286   t1 = (Matrix3(q1) * Eigen::Scaling(v0)) * Translation3(v0);
    287   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    288   t1 = (q1 * Eigen::Scaling(v0)) * Translation3(v0);
    289   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    290   // mat * transformation and aligned scaling * translation
    291   t1 = Matrix3(q1) * (AlignedScaling3(v0) * Translation3(v0));
    292   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    293 
    294 
    295   t0.setIdentity();
    296   t0.scale(s0).translate(v0);
    297   t1 = Eigen::Scaling(s0) * Translation3(v0);
    298   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    299   t0.prescale(s0);
    300   t1 = Eigen::Scaling(s0) * t1;
    301   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    302 
    303   t0 = t3;
    304   t0.scale(s0);
    305   t1 = t3 * Eigen::Scaling(s0,s0,s0);
    306   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    307   t0.prescale(s0);
    308   t1 = Eigen::Scaling(s0,s0,s0) * t1;
    309   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    310 
    311   t0 = t3;
    312   t0.scale(s0);
    313   t1 = t3 * Eigen::Scaling(s0);
    314   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    315   t0.prescale(s0);
    316   t1 = Eigen::Scaling(s0) * t1;
    317   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    318 
    319   t0.setIdentity();
    320   t0.prerotate(q1).prescale(v0).pretranslate(v0);
    321   // translation * aligned scaling and transformation * mat
    322   t1 = (Translation3(v0) * AlignedScaling3(v0)) * Transform3(q1);
    323   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    324   // scaling * mat and translation * mat
    325   t1 = Translation3(v0) * (AlignedScaling3(v0) * Transform3(q1));
    326   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    327 
    328   t0.setIdentity();
    329   t0.scale(v0).translate(v0).rotate(q1);
    330   // translation * mat and aligned scaling * transformation
    331   t1 = AlignedScaling3(v0) * (Translation3(v0) * Transform3(q1));
    332   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    333   // transformation * aligned scaling
    334   t0.scale(v0);
    335   t1 *= AlignedScaling3(v0);
    336   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    337   t1 = AlignedScaling3(v0) * (Translation3(v0) * Transform3(q1));
    338   t1 = t1 * v0.asDiagonal();
    339   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    340   // transformation * translation
    341   t0.translate(v0);
    342   t1 = t1 * Translation3(v0);
    343   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    344   // translation * transformation
    345   t0.pretranslate(v0);
    346   t1 = Translation3(v0) * t1;
    347   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    348 
    349   // transform * quaternion
    350   t0.rotate(q1);
    351   t1 = t1 * q1;
    352   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    353 
    354   // translation * quaternion
    355   t0.translate(v1).rotate(q1);
    356   t1 = t1 * (Translation3(v1) * q1);
    357   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    358 
    359   // aligned scaling * quaternion
    360   t0.scale(v1).rotate(q1);
    361   t1 = t1 * (AlignedScaling3(v1) * q1);
    362   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    363 
    364   // quaternion * transform
    365   t0.prerotate(q1);
    366   t1 = q1 * t1;
    367   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    368 
    369   // quaternion * translation
    370   t0.rotate(q1).translate(v1);
    371   t1 = t1 * (q1 * Translation3(v1));
    372   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    373 
    374   // quaternion * aligned scaling
    375   t0.rotate(q1).scale(v1);
    376   t1 = t1 * (q1 * AlignedScaling3(v1));
    377   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    378 
    379   // test transform inversion
    380   t0.setIdentity();
    381   t0.translate(v0);
    382   do {
    383     t0.linear().setRandom();
    384   } while(t0.linear().jacobiSvd().singularValues()(2)<test_precision<Scalar>());
    385   Matrix4 t044 = Matrix4::Zero();
    386   t044(3,3) = 1;
    387   t044.block(0,0,t0.matrix().rows(),4) = t0.matrix();
    388   VERIFY_IS_APPROX(t0.inverse(Affine).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4));
    389   t0.setIdentity();
    390   t0.translate(v0).rotate(q1);
    391   t044 = Matrix4::Zero();
    392   t044(3,3) = 1;
    393   t044.block(0,0,t0.matrix().rows(),4) = t0.matrix();
    394   VERIFY_IS_APPROX(t0.inverse(Isometry).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4));
    395 
    396   Matrix3 mat_rotation, mat_scaling;
    397   t0.setIdentity();
    398   t0.translate(v0).rotate(q1).scale(v1);
    399   t0.computeRotationScaling(&mat_rotation, &mat_scaling);
    400   VERIFY_IS_APPROX(t0.linear(), mat_rotation * mat_scaling);
    401   VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity());
    402   VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1));
    403   t0.computeScalingRotation(&mat_scaling, &mat_rotation);
    404   VERIFY_IS_APPROX(t0.linear(), mat_scaling * mat_rotation);
    405   VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity());
    406   VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1));
    407 
    408   // test casting
    409   Transform<float,3,Mode> t1f = t1.template cast<float>();
    410   VERIFY_IS_APPROX(t1f.template cast<Scalar>(),t1);
    411   Transform<double,3,Mode> t1d = t1.template cast<double>();
    412   VERIFY_IS_APPROX(t1d.template cast<Scalar>(),t1);
    413 
    414   Translation3 tr1(v0);
    415   Translation<float,3> tr1f = tr1.template cast<float>();
    416   VERIFY_IS_APPROX(tr1f.template cast<Scalar>(),tr1);
    417   Translation<double,3> tr1d = tr1.template cast<double>();
    418   VERIFY_IS_APPROX(tr1d.template cast<Scalar>(),tr1);
    419 
    420   AngleAxis<float> aa1f = aa1.template cast<float>();
    421   VERIFY_IS_APPROX(aa1f.template cast<Scalar>(),aa1);
    422   AngleAxis<double> aa1d = aa1.template cast<double>();
    423   VERIFY_IS_APPROX(aa1d.template cast<Scalar>(),aa1);
    424 
    425   Rotation2D<Scalar> r2d1(internal::random<Scalar>());
    426   Rotation2D<float> r2d1f = r2d1.template cast<float>();
    427   VERIFY_IS_APPROX(r2d1f.template cast<Scalar>(),r2d1);
    428   Rotation2D<double> r2d1d = r2d1.template cast<double>();
    429   VERIFY_IS_APPROX(r2d1d.template cast<Scalar>(),r2d1);
    430 
    431   for(int k=0; k<100; ++k)
    432   {
    433     Scalar angle = internal::random<Scalar>(-100,100);
    434     Rotation2D<Scalar> rot2(angle);
    435     VERIFY( rot2.smallestPositiveAngle() >= 0 );
    436     VERIFY( rot2.smallestPositiveAngle() <= Scalar(2)*Scalar(EIGEN_PI) );
    437     VERIFY_IS_APPROX( angleToVec(rot2.smallestPositiveAngle()), angleToVec(rot2.angle()) );
    438 
    439     VERIFY( rot2.smallestAngle() >= -Scalar(EIGEN_PI) );
    440     VERIFY( rot2.smallestAngle() <=  Scalar(EIGEN_PI) );
    441     VERIFY_IS_APPROX( angleToVec(rot2.smallestAngle()), angleToVec(rot2.angle()) );
    442 
    443     Matrix<Scalar,2,2> rot2_as_mat(rot2);
    444     Rotation2D<Scalar> rot3(rot2_as_mat);
    445     VERIFY_IS_APPROX( angleToVec(rot2.smallestAngle()),  angleToVec(rot3.angle()) );
    446   }
    447 
    448   s0 = internal::random<Scalar>(-100,100);
    449   s1 = internal::random<Scalar>(-100,100);
    450   Rotation2D<Scalar> R0(s0), R1(s1);
    451 
    452   t20 = Translation2(v20) * (R0 * Eigen::Scaling(s0));
    453   t21 = Translation2(v20) * R0 * Eigen::Scaling(s0);
    454   VERIFY_IS_APPROX(t20,t21);
    455 
    456   t20 = Translation2(v20) * (R0 * R0.inverse() * Eigen::Scaling(s0));
    457   t21 = Translation2(v20) * Eigen::Scaling(s0);
    458   VERIFY_IS_APPROX(t20,t21);
    459 
    460   VERIFY_IS_APPROX(s0, (R0.slerp(0, R1)).angle());
    461   VERIFY_IS_APPROX( angleToVec(R1.smallestPositiveAngle()), angleToVec((R0.slerp(1, R1)).smallestPositiveAngle()) );
    462   VERIFY_IS_APPROX(R0.smallestPositiveAngle(), (R0.slerp(0.5, R0)).smallestPositiveAngle());
    463 
    464   if(std::cos(s0)>0)
    465     VERIFY_IS_MUCH_SMALLER_THAN((R0.slerp(0.5, R0.inverse())).smallestAngle(), Scalar(1));
    466   else
    467     VERIFY_IS_APPROX(Scalar(EIGEN_PI), (R0.slerp(0.5, R0.inverse())).smallestPositiveAngle());
    468 
    469   // Check path length
    470   Scalar l = 0;
    471   int path_steps = 100;
    472   for(int k=0; k<path_steps; ++k)
    473   {
    474     Scalar a1 = R0.slerp(Scalar(k)/Scalar(path_steps), R1).angle();
    475     Scalar a2 = R0.slerp(Scalar(k+1)/Scalar(path_steps), R1).angle();
    476     l += std::abs(a2-a1);
    477   }
    478   VERIFY(l<=Scalar(EIGEN_PI)*(Scalar(1)+NumTraits<Scalar>::epsilon()*Scalar(path_steps/2)));
    479 
    480   // check basic features
    481   {
    482     Rotation2D<Scalar> r1;           // default ctor
    483     r1 = Rotation2D<Scalar>(s0);     // copy assignment
    484     VERIFY_IS_APPROX(r1.angle(),s0);
    485     Rotation2D<Scalar> r2(r1);       // copy ctor
    486     VERIFY_IS_APPROX(r2.angle(),s0);
    487   }
    488 
    489   {
    490     Transform3 t32(Matrix4::Random()), t33, t34;
    491     t34 = t33 = t32;
    492     t32.scale(v0);
    493     t33*=AlignedScaling3(v0);
    494     VERIFY_IS_APPROX(t32.matrix(), t33.matrix());
    495     t33 = t34 * AlignedScaling3(v0);
    496     VERIFY_IS_APPROX(t32.matrix(), t33.matrix());
    497   }
    498 
    499 }
    500 
    501 template<typename A1, typename A2, typename P, typename Q, typename V, typename H>
    502 void transform_associativity_left(const A1& a1, const A2& a2, const P& p, const Q& q, const V& v, const H& h)
    503 {
    504   VERIFY_IS_APPROX( q*(a1*v), (q*a1)*v );
    505   VERIFY_IS_APPROX( q*(a2*v), (q*a2)*v );
    506   VERIFY_IS_APPROX( q*(p*h).hnormalized(),  ((q*p)*h).hnormalized() );
    507 }
    508 
    509 template<typename A1, typename A2, typename P, typename Q, typename V, typename H>
    510 void transform_associativity2(const A1& a1, const A2& a2, const P& p, const Q& q, const V& v, const H& h)
    511 {
    512   VERIFY_IS_APPROX( a1*(q*v), (a1*q)*v );
    513   VERIFY_IS_APPROX( a2*(q*v), (a2*q)*v );
    514   VERIFY_IS_APPROX( p *(q*v).homogeneous(), (p *q)*v.homogeneous() );
    515 
    516   transform_associativity_left(a1, a2,p, q, v, h);
    517 }
    518 
    519 template<typename Scalar, int Dim, int Options,typename RotationType>
    520 void transform_associativity(const RotationType& R)
    521 {
    522   typedef Matrix<Scalar,Dim,1> VectorType;
    523   typedef Matrix<Scalar,Dim+1,1> HVectorType;
    524   typedef Matrix<Scalar,Dim,Dim> LinearType;
    525   typedef Matrix<Scalar,Dim+1,Dim+1> MatrixType;
    526   typedef Transform<Scalar,Dim,AffineCompact,Options> AffineCompactType;
    527   typedef Transform<Scalar,Dim,Affine,Options> AffineType;
    528   typedef Transform<Scalar,Dim,Projective,Options> ProjectiveType;
    529   typedef DiagonalMatrix<Scalar,Dim> ScalingType;
    530   typedef Translation<Scalar,Dim> TranslationType;
    531 
    532   AffineCompactType A1c; A1c.matrix().setRandom();
    533   AffineCompactType A2c; A2c.matrix().setRandom();
    534   AffineType A1(A1c);
    535   AffineType A2(A2c);
    536   ProjectiveType P1; P1.matrix().setRandom();
    537   VectorType v1 = VectorType::Random();
    538   VectorType v2 = VectorType::Random();
    539   HVectorType h1 = HVectorType::Random();
    540   Scalar s1 = internal::random<Scalar>();
    541   LinearType L = LinearType::Random();
    542   MatrixType M = MatrixType::Random();
    543 
    544   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, A2, v2, h1) );
    545   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, A2c, v2, h1) );
    546   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, v1.asDiagonal(), v2, h1) );
    547   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, ScalingType(v1), v2, h1) );
    548   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, Scaling(v1), v2, h1) );
    549   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, Scaling(s1), v2, h1) );
    550   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, TranslationType(v1), v2, h1) );
    551   CALL_SUBTEST( transform_associativity_left(A1c, A1, P1, L, v2, h1) );
    552   CALL_SUBTEST( transform_associativity2(A1c, A1, P1, R, v2, h1) );
    553 
    554   VERIFY_IS_APPROX( A1*(M*h1), (A1*M)*h1 );
    555   VERIFY_IS_APPROX( A1c*(M*h1), (A1c*M)*h1 );
    556   VERIFY_IS_APPROX( P1*(M*h1), (P1*M)*h1 );
    557 
    558   VERIFY_IS_APPROX( M*(A1*h1), (M*A1)*h1 );
    559   VERIFY_IS_APPROX( M*(A1c*h1), (M*A1c)*h1 );
    560   VERIFY_IS_APPROX( M*(P1*h1),  ((M*P1)*h1) );
    561 }
    562 
    563 template<typename Scalar> void transform_alignment()
    564 {
    565   typedef Transform<Scalar,3,Projective,AutoAlign> Projective3a;
    566   typedef Transform<Scalar,3,Projective,DontAlign> Projective3u;
    567 
    568   EIGEN_ALIGN_MAX Scalar array1[16];
    569   EIGEN_ALIGN_MAX Scalar array2[16];
    570   EIGEN_ALIGN_MAX Scalar array3[16+1];
    571   Scalar* array3u = array3+1;
    572 
    573   Projective3a *p1 = ::new(reinterpret_cast<void*>(array1)) Projective3a;
    574   Projective3u *p2 = ::new(reinterpret_cast<void*>(array2)) Projective3u;
    575   Projective3u *p3 = ::new(reinterpret_cast<void*>(array3u)) Projective3u;
    576 
    577   p1->matrix().setRandom();
    578   *p2 = *p1;
    579   *p3 = *p1;
    580 
    581   VERIFY_IS_APPROX(p1->matrix(), p2->matrix());
    582   VERIFY_IS_APPROX(p1->matrix(), p3->matrix());
    583 
    584   VERIFY_IS_APPROX( (*p1) * (*p1), (*p2)*(*p3));
    585 
    586   #if defined(EIGEN_VECTORIZE) && EIGEN_MAX_STATIC_ALIGN_BYTES>0
    587   if(internal::packet_traits<Scalar>::Vectorizable)
    588     VERIFY_RAISES_ASSERT((::new(reinterpret_cast<void*>(array3u)) Projective3a));
    589   #endif
    590 }
    591 
    592 template<typename Scalar, int Dim, int Options> void transform_products()
    593 {
    594   typedef Matrix<Scalar,Dim+1,Dim+1> Mat;
    595   typedef Transform<Scalar,Dim,Projective,Options> Proj;
    596   typedef Transform<Scalar,Dim,Affine,Options> Aff;
    597   typedef Transform<Scalar,Dim,AffineCompact,Options> AffC;
    598 
    599   Proj p; p.matrix().setRandom();
    600   Aff a; a.linear().setRandom(); a.translation().setRandom();
    601   AffC ac = a;
    602 
    603   Mat p_m(p.matrix()), a_m(a.matrix());
    604 
    605   VERIFY_IS_APPROX((p*p).matrix(), p_m*p_m);
    606   VERIFY_IS_APPROX((a*a).matrix(), a_m*a_m);
    607   VERIFY_IS_APPROX((p*a).matrix(), p_m*a_m);
    608   VERIFY_IS_APPROX((a*p).matrix(), a_m*p_m);
    609   VERIFY_IS_APPROX((ac*a).matrix(), a_m*a_m);
    610   VERIFY_IS_APPROX((a*ac).matrix(), a_m*a_m);
    611   VERIFY_IS_APPROX((p*ac).matrix(), p_m*a_m);
    612   VERIFY_IS_APPROX((ac*p).matrix(), a_m*p_m);
    613 }
    614 
    615 void test_geo_transformations()
    616 {
    617   for(int i = 0; i < g_repeat; i++) {
    618     CALL_SUBTEST_1(( transformations<double,Affine,AutoAlign>() ));
    619     CALL_SUBTEST_1(( non_projective_only<double,Affine,AutoAlign>() ));
    620 
    621     CALL_SUBTEST_2(( transformations<float,AffineCompact,AutoAlign>() ));
    622     CALL_SUBTEST_2(( non_projective_only<float,AffineCompact,AutoAlign>() ));
    623     CALL_SUBTEST_2(( transform_alignment<float>() ));
    624 
    625     CALL_SUBTEST_3(( transformations<double,Projective,AutoAlign>() ));
    626     CALL_SUBTEST_3(( transformations<double,Projective,DontAlign>() ));
    627     CALL_SUBTEST_3(( transform_alignment<double>() ));
    628 
    629     CALL_SUBTEST_4(( transformations<float,Affine,RowMajor|AutoAlign>() ));
    630     CALL_SUBTEST_4(( non_projective_only<float,Affine,RowMajor>() ));
    631 
    632     CALL_SUBTEST_5(( transformations<double,AffineCompact,RowMajor|AutoAlign>() ));
    633     CALL_SUBTEST_5(( non_projective_only<double,AffineCompact,RowMajor>() ));
    634 
    635     CALL_SUBTEST_6(( transformations<double,Projective,RowMajor|AutoAlign>() ));
    636     CALL_SUBTEST_6(( transformations<double,Projective,RowMajor|DontAlign>() ));
    637 
    638 
    639     CALL_SUBTEST_7(( transform_products<double,3,RowMajor|AutoAlign>() ));
    640     CALL_SUBTEST_7(( transform_products<float,2,AutoAlign>() ));
    641 
    642     CALL_SUBTEST_8(( transform_associativity<double,2,ColMajor>(Rotation2D<double>(internal::random<double>()*double(EIGEN_PI))) ));
    643     CALL_SUBTEST_8(( transform_associativity<double,3,ColMajor>(Quaterniond::UnitRandom()) ));
    644   }
    645 }
    646