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 Scalar, int Mode, int Options> void non_projective_only()
     16 {
     17     /* this test covers the following files:
     18      Cross.h Quaternion.h, Transform.cpp
     19   */
     20   typedef Matrix<Scalar,2,2> Matrix2;
     21   typedef Matrix<Scalar,3,3> Matrix3;
     22   typedef Matrix<Scalar,4,4> Matrix4;
     23   typedef Matrix<Scalar,2,1> Vector2;
     24   typedef Matrix<Scalar,3,1> Vector3;
     25   typedef Matrix<Scalar,4,1> Vector4;
     26   typedef Quaternion<Scalar> Quaternionx;
     27   typedef AngleAxis<Scalar> AngleAxisx;
     28   typedef Transform<Scalar,2,Mode,Options> Transform2;
     29   typedef Transform<Scalar,3,Mode,Options> Transform3;
     30   typedef Transform<Scalar,2,Isometry,Options> Isometry2;
     31   typedef Transform<Scalar,3,Isometry,Options> Isometry3;
     32   typedef typename Transform3::MatrixType MatrixType;
     33   typedef DiagonalMatrix<Scalar,2> AlignedScaling2;
     34   typedef DiagonalMatrix<Scalar,3> AlignedScaling3;
     35   typedef Translation<Scalar,2> Translation2;
     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(M_PI), Scalar(M_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   typedef Matrix<Scalar,2,2> Matrix2;
     92   typedef Matrix<Scalar,3,3> Matrix3;
     93   typedef Matrix<Scalar,4,4> Matrix4;
     94   typedef Matrix<Scalar,2,1> Vector2;
     95   typedef Matrix<Scalar,3,1> Vector3;
     96   typedef Matrix<Scalar,4,1> Vector4;
     97   typedef Quaternion<Scalar> Quaternionx;
     98   typedef AngleAxis<Scalar> AngleAxisx;
     99   typedef Transform<Scalar,2,Mode,Options> Transform2;
    100   typedef Transform<Scalar,3,Mode,Options> Transform3;
    101   typedef Transform<Scalar,2,Isometry,Options> Isometry2;
    102   typedef Transform<Scalar,3,Isometry,Options> Isometry3;
    103   typedef typename Transform3::MatrixType MatrixType;
    104   typedef DiagonalMatrix<Scalar,2> AlignedScaling2;
    105   typedef DiagonalMatrix<Scalar,3> AlignedScaling3;
    106   typedef Translation<Scalar,2> Translation2;
    107   typedef Translation<Scalar,3> Translation3;
    108 
    109   Vector3 v0 = Vector3::Random(),
    110           v1 = Vector3::Random();
    111   Matrix3 matrot1, m;
    112 
    113   Scalar a = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI));
    114   Scalar s0 = internal::random<Scalar>();
    115 
    116   VERIFY_IS_APPROX(v0, AngleAxisx(a, v0.normalized()) * v0);
    117   VERIFY_IS_APPROX(-v0, AngleAxisx(Scalar(M_PI), v0.unitOrthogonal()) * v0);
    118   VERIFY_IS_APPROX(internal::cos(a)*v0.squaredNorm(), v0.dot(AngleAxisx(a, v0.unitOrthogonal()) * v0));
    119   m = AngleAxisx(a, v0.normalized()).toRotationMatrix().adjoint();
    120   VERIFY_IS_APPROX(Matrix3::Identity(), m * AngleAxisx(a, v0.normalized()));
    121   VERIFY_IS_APPROX(Matrix3::Identity(), AngleAxisx(a, v0.normalized()) * m);
    122 
    123   Quaternionx q1, q2;
    124   q1 = AngleAxisx(a, v0.normalized());
    125   q2 = AngleAxisx(a, v1.normalized());
    126 
    127   // rotation matrix conversion
    128   matrot1 = AngleAxisx(Scalar(0.1), Vector3::UnitX())
    129           * AngleAxisx(Scalar(0.2), Vector3::UnitY())
    130           * AngleAxisx(Scalar(0.3), Vector3::UnitZ());
    131   VERIFY_IS_APPROX(matrot1 * v1,
    132        AngleAxisx(Scalar(0.1), Vector3(1,0,0)).toRotationMatrix()
    133     * (AngleAxisx(Scalar(0.2), Vector3(0,1,0)).toRotationMatrix()
    134     * (AngleAxisx(Scalar(0.3), Vector3(0,0,1)).toRotationMatrix() * v1)));
    135 
    136   // angle-axis conversion
    137   AngleAxisx aa = AngleAxisx(q1);
    138   VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1);
    139   VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1);
    140 
    141   aa.fromRotationMatrix(aa.toRotationMatrix());
    142   VERIFY_IS_APPROX(q1 * v1, Quaternionx(aa) * v1);
    143   VERIFY_IS_NOT_APPROX(q1 * v1, Quaternionx(AngleAxisx(aa.angle()*2,aa.axis())) * v1);
    144 
    145   // AngleAxis
    146   VERIFY_IS_APPROX(AngleAxisx(a,v1.normalized()).toRotationMatrix(),
    147     Quaternionx(AngleAxisx(a,v1.normalized())).toRotationMatrix());
    148 
    149   AngleAxisx aa1;
    150   m = q1.toRotationMatrix();
    151   aa1 = m;
    152   VERIFY_IS_APPROX(AngleAxisx(m).toRotationMatrix(),
    153     Quaternionx(m).toRotationMatrix());
    154 
    155   // Transform
    156   // TODO complete the tests !
    157   a = 0;
    158   while (internal::abs(a)<Scalar(0.1))
    159     a = internal::random<Scalar>(-Scalar(0.4)*Scalar(M_PI), Scalar(0.4)*Scalar(M_PI));
    160   q1 = AngleAxisx(a, v0.normalized());
    161   Transform3 t0, t1, t2;
    162 
    163   // first test setIdentity() and Identity()
    164   t0.setIdentity();
    165   VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity());
    166   t0.matrix().setZero();
    167   t0 = Transform3::Identity();
    168   VERIFY_IS_APPROX(t0.matrix(), Transform3::MatrixType::Identity());
    169 
    170   t0.setIdentity();
    171   t1.setIdentity();
    172   v1 << 1, 2, 3;
    173   t0.linear() = q1.toRotationMatrix();
    174   t0.pretranslate(v0);
    175   t0.scale(v1);
    176   t1.linear() = q1.conjugate().toRotationMatrix();
    177   t1.prescale(v1.cwiseInverse());
    178   t1.translate(-v0);
    179 
    180   VERIFY((t0 * t1).matrix().isIdentity(test_precision<Scalar>()));
    181 
    182   t1.fromPositionOrientationScale(v0, q1, v1);
    183   VERIFY_IS_APPROX(t1.matrix(), t0.matrix());
    184 
    185   t0.setIdentity(); t0.scale(v0).rotate(q1.toRotationMatrix());
    186   t1.setIdentity(); t1.scale(v0).rotate(q1);
    187   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    188 
    189   t0.setIdentity(); t0.scale(v0).rotate(AngleAxisx(q1));
    190   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    191 
    192   VERIFY_IS_APPROX(t0.scale(a).matrix(), t1.scale(Vector3::Constant(a)).matrix());
    193   VERIFY_IS_APPROX(t0.prescale(a).matrix(), t1.prescale(Vector3::Constant(a)).matrix());
    194 
    195   // More transform constructors, operator=, operator*=
    196 
    197   Matrix3 mat3 = Matrix3::Random();
    198   Matrix4 mat4;
    199   mat4 << mat3 , Vector3::Zero() , Vector4::Zero().transpose();
    200   Transform3 tmat3(mat3), tmat4(mat4);
    201   if(Mode!=int(AffineCompact))
    202     tmat4.matrix()(3,3) = Scalar(1);
    203   VERIFY_IS_APPROX(tmat3.matrix(), tmat4.matrix());
    204 
    205   Scalar a3 = internal::random<Scalar>(-Scalar(M_PI), Scalar(M_PI));
    206   Vector3 v3 = Vector3::Random().normalized();
    207   AngleAxisx aa3(a3, v3);
    208   Transform3 t3(aa3);
    209   Transform3 t4;
    210   t4 = aa3;
    211   VERIFY_IS_APPROX(t3.matrix(), t4.matrix());
    212   t4.rotate(AngleAxisx(-a3,v3));
    213   VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity());
    214   t4 *= aa3;
    215   VERIFY_IS_APPROX(t3.matrix(), t4.matrix());
    216 
    217   v3 = Vector3::Random();
    218   Translation3 tv3(v3);
    219   Transform3 t5(tv3);
    220   t4 = tv3;
    221   VERIFY_IS_APPROX(t5.matrix(), t4.matrix());
    222   t4.translate(-v3);
    223   VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity());
    224   t4 *= tv3;
    225   VERIFY_IS_APPROX(t5.matrix(), t4.matrix());
    226 
    227   AlignedScaling3 sv3(v3);
    228   Transform3 t6(sv3);
    229   t4 = sv3;
    230   VERIFY_IS_APPROX(t6.matrix(), t4.matrix());
    231   t4.scale(v3.cwiseInverse());
    232   VERIFY_IS_APPROX(t4.matrix(), MatrixType::Identity());
    233   t4 *= sv3;
    234   VERIFY_IS_APPROX(t6.matrix(), t4.matrix());
    235 
    236   // matrix * transform
    237   VERIFY_IS_APPROX((t3.matrix()*t4).matrix(), (t3*t4).matrix());
    238 
    239   // chained Transform product
    240   VERIFY_IS_APPROX(((t3*t4)*t5).matrix(), (t3*(t4*t5)).matrix());
    241 
    242   // check that Transform product doesn't have aliasing problems
    243   t5 = t4;
    244   t5 = t5*t5;
    245   VERIFY_IS_APPROX(t5, t4*t4);
    246 
    247   // 2D transformation
    248   Transform2 t20, t21;
    249   Vector2 v20 = Vector2::Random();
    250   Vector2 v21 = Vector2::Random();
    251   for (int k=0; k<2; ++k)
    252     if (internal::abs(v21[k])<Scalar(1e-3)) v21[k] = Scalar(1e-3);
    253   t21.setIdentity();
    254   t21.linear() = Rotation2D<Scalar>(a).toRotationMatrix();
    255   VERIFY_IS_APPROX(t20.fromPositionOrientationScale(v20,a,v21).matrix(),
    256     t21.pretranslate(v20).scale(v21).matrix());
    257 
    258   t21.setIdentity();
    259   t21.linear() = Rotation2D<Scalar>(-a).toRotationMatrix();
    260   VERIFY( (t20.fromPositionOrientationScale(v20,a,v21)
    261         * (t21.prescale(v21.cwiseInverse()).translate(-v20))).matrix().isIdentity(test_precision<Scalar>()) );
    262 
    263   // Transform - new API
    264   // 3D
    265   t0.setIdentity();
    266   t0.rotate(q1).scale(v0).translate(v0);
    267   // mat * aligned scaling and mat * translation
    268   t1 = (Matrix3(q1) * AlignedScaling3(v0)) * Translation3(v0);
    269   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    270   t1 = (Matrix3(q1) * Eigen::Scaling(v0)) * Translation3(v0);
    271   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    272   t1 = (q1 * Eigen::Scaling(v0)) * Translation3(v0);
    273   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    274   // mat * transformation and aligned scaling * translation
    275   t1 = Matrix3(q1) * (AlignedScaling3(v0) * Translation3(v0));
    276   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    277 
    278 
    279   t0.setIdentity();
    280   t0.scale(s0).translate(v0);
    281   t1 = Eigen::Scaling(s0) * Translation3(v0);
    282   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    283   t0.prescale(s0);
    284   t1 = Eigen::Scaling(s0) * t1;
    285   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    286 
    287   t0 = t3;
    288   t0.scale(s0);
    289   t1 = t3 * Eigen::Scaling(s0,s0,s0);
    290   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    291   t0.prescale(s0);
    292   t1 = Eigen::Scaling(s0,s0,s0) * t1;
    293   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    294 
    295 
    296   t0.setIdentity();
    297   t0.prerotate(q1).prescale(v0).pretranslate(v0);
    298   // translation * aligned scaling and transformation * mat
    299   t1 = (Translation3(v0) * AlignedScaling3(v0)) * Transform3(q1);
    300   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    301   // scaling * mat and translation * mat
    302   t1 = Translation3(v0) * (AlignedScaling3(v0) * Transform3(q1));
    303   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    304 
    305   t0.setIdentity();
    306   t0.scale(v0).translate(v0).rotate(q1);
    307   // translation * mat and aligned scaling * transformation
    308   t1 = AlignedScaling3(v0) * (Translation3(v0) * Transform3(q1));
    309   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    310   // transformation * aligned scaling
    311   t0.scale(v0);
    312   t1 *= AlignedScaling3(v0);
    313   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    314   // transformation * translation
    315   t0.translate(v0);
    316   t1 = t1 * Translation3(v0);
    317   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    318   // translation * transformation
    319   t0.pretranslate(v0);
    320   t1 = Translation3(v0) * t1;
    321   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    322 
    323   // transform * quaternion
    324   t0.rotate(q1);
    325   t1 = t1 * q1;
    326   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    327 
    328   // translation * quaternion
    329   t0.translate(v1).rotate(q1);
    330   t1 = t1 * (Translation3(v1) * q1);
    331   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    332 
    333   // aligned scaling * quaternion
    334   t0.scale(v1).rotate(q1);
    335   t1 = t1 * (AlignedScaling3(v1) * q1);
    336   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    337 
    338   // quaternion * transform
    339   t0.prerotate(q1);
    340   t1 = q1 * t1;
    341   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    342 
    343   // quaternion * translation
    344   t0.rotate(q1).translate(v1);
    345   t1 = t1 * (q1 * Translation3(v1));
    346   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    347 
    348   // quaternion * aligned scaling
    349   t0.rotate(q1).scale(v1);
    350   t1 = t1 * (q1 * AlignedScaling3(v1));
    351   VERIFY_IS_APPROX(t0.matrix(), t1.matrix());
    352 
    353   // test transform inversion
    354   t0.setIdentity();
    355   t0.translate(v0);
    356   t0.linear().setRandom();
    357   Matrix4 t044 = Matrix4::Zero();
    358   t044(3,3) = 1;
    359   t044.block(0,0,t0.matrix().rows(),4) = t0.matrix();
    360   VERIFY_IS_APPROX(t0.inverse(Affine).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4));
    361   t0.setIdentity();
    362   t0.translate(v0).rotate(q1);
    363   t044 = Matrix4::Zero();
    364   t044(3,3) = 1;
    365   t044.block(0,0,t0.matrix().rows(),4) = t0.matrix();
    366   VERIFY_IS_APPROX(t0.inverse(Isometry).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4));
    367 
    368   Matrix3 mat_rotation, mat_scaling;
    369   t0.setIdentity();
    370   t0.translate(v0).rotate(q1).scale(v1);
    371   t0.computeRotationScaling(&mat_rotation, &mat_scaling);
    372   VERIFY_IS_APPROX(t0.linear(), mat_rotation * mat_scaling);
    373   VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity());
    374   VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1));
    375   t0.computeScalingRotation(&mat_scaling, &mat_rotation);
    376   VERIFY_IS_APPROX(t0.linear(), mat_scaling * mat_rotation);
    377   VERIFY_IS_APPROX(mat_rotation*mat_rotation.adjoint(), Matrix3::Identity());
    378   VERIFY_IS_APPROX(mat_rotation.determinant(), Scalar(1));
    379 
    380   // test casting
    381   Transform<float,3,Mode> t1f = t1.template cast<float>();
    382   VERIFY_IS_APPROX(t1f.template cast<Scalar>(),t1);
    383   Transform<double,3,Mode> t1d = t1.template cast<double>();
    384   VERIFY_IS_APPROX(t1d.template cast<Scalar>(),t1);
    385 
    386   Translation3 tr1(v0);
    387   Translation<float,3> tr1f = tr1.template cast<float>();
    388   VERIFY_IS_APPROX(tr1f.template cast<Scalar>(),tr1);
    389   Translation<double,3> tr1d = tr1.template cast<double>();
    390   VERIFY_IS_APPROX(tr1d.template cast<Scalar>(),tr1);
    391 
    392   AngleAxis<float> aa1f = aa1.template cast<float>();
    393   VERIFY_IS_APPROX(aa1f.template cast<Scalar>(),aa1);
    394   AngleAxis<double> aa1d = aa1.template cast<double>();
    395   VERIFY_IS_APPROX(aa1d.template cast<Scalar>(),aa1);
    396 
    397   Rotation2D<Scalar> r2d1(internal::random<Scalar>());
    398   Rotation2D<float> r2d1f = r2d1.template cast<float>();
    399   VERIFY_IS_APPROX(r2d1f.template cast<Scalar>(),r2d1);
    400   Rotation2D<double> r2d1d = r2d1.template cast<double>();
    401   VERIFY_IS_APPROX(r2d1d.template cast<Scalar>(),r2d1);
    402 
    403   t20 = Translation2(v20) * (Rotation2D<Scalar>(s0) * Scaling(s0));
    404   t21 = Translation2(v20) * Rotation2D<Scalar>(s0) * Scaling(s0);
    405   VERIFY_IS_APPROX(t20,t21);
    406 }
    407 
    408 template<typename Scalar> void transform_alignment()
    409 {
    410   typedef Transform<Scalar,3,Projective,AutoAlign> Projective3a;
    411   typedef Transform<Scalar,3,Projective,DontAlign> Projective3u;
    412 
    413   EIGEN_ALIGN16 Scalar array1[16];
    414   EIGEN_ALIGN16 Scalar array2[16];
    415   EIGEN_ALIGN16 Scalar array3[16+1];
    416   Scalar* array3u = array3+1;
    417 
    418   Projective3a *p1 = ::new(reinterpret_cast<void*>(array1)) Projective3a;
    419   Projective3u *p2 = ::new(reinterpret_cast<void*>(array2)) Projective3u;
    420   Projective3u *p3 = ::new(reinterpret_cast<void*>(array3u)) Projective3u;
    421 
    422   p1->matrix().setRandom();
    423   *p2 = *p1;
    424   *p3 = *p1;
    425 
    426   VERIFY_IS_APPROX(p1->matrix(), p2->matrix());
    427   VERIFY_IS_APPROX(p1->matrix(), p3->matrix());
    428 
    429   VERIFY_IS_APPROX( (*p1) * (*p1), (*p2)*(*p3));
    430 
    431   #if defined(EIGEN_VECTORIZE) && EIGEN_ALIGN_STATICALLY
    432   if(internal::packet_traits<Scalar>::Vectorizable)
    433     VERIFY_RAISES_ASSERT((::new(reinterpret_cast<void*>(array3u)) Projective3a));
    434   #endif
    435 }
    436 
    437 template<typename Scalar, int Dim, int Options> void transform_products()
    438 {
    439   typedef Matrix<Scalar,Dim+1,Dim+1> Mat;
    440   typedef Transform<Scalar,Dim,Projective,Options> Proj;
    441   typedef Transform<Scalar,Dim,Affine,Options> Aff;
    442   typedef Transform<Scalar,Dim,AffineCompact,Options> AffC;
    443 
    444   Proj p; p.matrix().setRandom();
    445   Aff a; a.linear().setRandom(); a.translation().setRandom();
    446   AffC ac = a;
    447 
    448   Mat p_m(p.matrix()), a_m(a.matrix());
    449 
    450   VERIFY_IS_APPROX((p*p).matrix(), p_m*p_m);
    451   VERIFY_IS_APPROX((a*a).matrix(), a_m*a_m);
    452   VERIFY_IS_APPROX((p*a).matrix(), p_m*a_m);
    453   VERIFY_IS_APPROX((a*p).matrix(), a_m*p_m);
    454   VERIFY_IS_APPROX((ac*a).matrix(), a_m*a_m);
    455   VERIFY_IS_APPROX((a*ac).matrix(), a_m*a_m);
    456   VERIFY_IS_APPROX((p*ac).matrix(), p_m*a_m);
    457   VERIFY_IS_APPROX((ac*p).matrix(), a_m*p_m);
    458 }
    459 
    460 void test_geo_transformations()
    461 {
    462   for(int i = 0; i < g_repeat; i++) {
    463     CALL_SUBTEST_1(( transformations<double,Affine,AutoAlign>() ));
    464     CALL_SUBTEST_1(( non_projective_only<double,Affine,AutoAlign>() ));
    465 
    466     CALL_SUBTEST_2(( transformations<float,AffineCompact,AutoAlign>() ));
    467     CALL_SUBTEST_2(( non_projective_only<float,AffineCompact,AutoAlign>() ));
    468     CALL_SUBTEST_2(( transform_alignment<float>() ));
    469 
    470     CALL_SUBTEST_3(( transformations<double,Projective,AutoAlign>() ));
    471     CALL_SUBTEST_3(( transformations<double,Projective,DontAlign>() ));
    472     CALL_SUBTEST_3(( transform_alignment<double>() ));
    473 
    474     CALL_SUBTEST_4(( transformations<float,Affine,RowMajor|AutoAlign>() ));
    475     CALL_SUBTEST_4(( non_projective_only<float,Affine,RowMajor>() ));
    476 
    477     CALL_SUBTEST_5(( transformations<double,AffineCompact,RowMajor|AutoAlign>() ));
    478     CALL_SUBTEST_5(( non_projective_only<double,AffineCompact,RowMajor>() ));
    479 
    480     CALL_SUBTEST_6(( transformations<double,Projective,RowMajor|AutoAlign>() ));
    481     CALL_SUBTEST_6(( transformations<double,Projective,RowMajor|DontAlign>() ));
    482 
    483 
    484     CALL_SUBTEST_7(( transform_products<double,3,RowMajor|AutoAlign>() ));
    485     CALL_SUBTEST_7(( transform_products<float,2,AutoAlign>() ));
    486   }
    487 }
    488