moab
AffineXform.cpp
Go to the documentation of this file.
00001 
00023 #include "AffineXform.hpp"
00024 #include "moab/Interface.hpp"
00025 #include <assert.h>
00026 
00027 
00028 namespace moab {
00029 
00030 // Don't include tag-related stuff in test build because we don't
00031 // link to MOAB to test all other functionality.  
00032 #ifndef TEST
00033 
00034 const char* const AFFINE_XFORM_TAG_NAME = "AFFINE_TRANSFORM";
00035 
00036 ErrorCode AffineXform::get_tag( Tag& tag_out,
00037                                     Interface* interface,
00038                                     const char* tagname )
00039 {
00040   assert( sizeof(AffineXform) == 12*sizeof(double) );
00041   
00042   if (!tagname)
00043     tagname = AFFINE_XFORM_TAG_NAME;
00044  
00045   return interface->tag_get_handle( tagname, 
00046                                     sizeof(AffineXform),
00047                                     MB_TYPE_DOUBLE,
00048                                     tag_out,
00049                                     MB_TAG_BYTES|MB_TAG_CREAT|MB_TAG_DENSE );
00050 }
00051 
00052 #endif //TEST
00053 
00054 AffineXform AffineXform::rotation( const double* from_vec, const double* to_vec )
00055 {
00056   CartVect from(from_vec);
00057   CartVect to(to_vec);
00058   CartVect a = from * to;
00059   double len = a.length();
00060   
00061   // If input vectors are not parallel (the normal case)
00062   if (len >= std::numeric_limits<double>::epsilon()) {
00063     from.normalize();
00064     to.normalize();
00065     return rotation( from % to, (from * to).length(), a/len );
00066   }
00067   
00068   // Vectors are parallel:
00069   //
00070   // If vectors are in same direction then rotation is identity (no transform)
00071   if (from % to >= 0.0)
00072     return AffineXform(); 
00073     
00074   // Parallel vectors in opposite directions:
00075   //
00076   // NOTE:  This case is ill-defined.  There are infinitely
00077   // many rotations that can align the two vectors.  The angle
00078   // of rotation is 180 degrees, but the axis of rotation may 
00079   // be any unit vector orthogonal to the input vectors.
00080   //
00081   from.normalize();
00082   double lenxy = std::sqrt( from[0]*from[0] + from[1]*from[1] );
00083   CartVect axis( -from[0]*from[2]/lenxy, 
00084                  -from[1]*from[2]/lenxy,
00085                                   lenxy );
00086   return rotation( -1, 0, axis );
00087 }
00088 
00089   
00090 } // namespace moab
00091 
00092 
00093 #ifdef TEST // ******************* Unit Test code ***********************
00094 
00095 using namespace moab;
00096 
00097 #include <iostream>
00098 #define ASSERT_VECTORS_EQUAL(A, B) assert_vectors_equal( (A), (B), #A, #B, __LINE__ )
00099 #define ASSERT_DOUBLES_EQUAL(A, B) assert_doubles_equal( (A), (B), #A, #B, __LINE__ )
00100 #define ASSERT(B) assert_bool( (B), #B, __LINE__ )
00101 
00102 const double TOL = 1e-6;
00103 
00104 int error_count = 0;
00105 
00106 void assert_vectors_equal( const double* a, const double* b,
00107                            const char* sa, const char* sb,
00108                            int lineno )
00109 {
00110   if (fabs(a[0] - b[0]) > TOL ||
00111       fabs(a[1] - b[1]) > TOL ||
00112       fabs(a[2] - b[2]) > TOL) {
00113     std::cerr << "Assertion failed at line " << lineno << std::endl
00114               << "\t" << sa << " == " << sb << std::endl
00115               << "\t[" << a[0] << ", " << a[1] << ", " << a[2] << "] == ["
00116               << b[0] << ", " << b[1] << ", " << b[2] << "]" << std::endl;
00117     ++error_count;
00118   }
00119 }
00120 
00121 void assert_vectors_equal( const CartVect& a, const CartVect& b, 
00122                            const char* sa, const char* sb,
00123                            int lineno )
00124 {
00125   assert_vectors_equal( a.array(), b.array(), sa, sb, lineno );
00126 }
00127 
00128 void assert_doubles_equal( double a, double b, const char* sa, const char* sb, int lineno )
00129 {
00130   if (fabs(a - b) > TOL) {
00131     std::cerr << "Assertion failed at line " << lineno << std::endl
00132               << "\t" << sa << " == " << sb << std::endl
00133               << "\t" << a << " == " << b << std::endl;
00134     ++error_count;
00135   }
00136 }
00137 
00138 void assert_bool( bool b, const char* sb, int lineno )
00139 {
00140   if (!b) {
00141     std::cerr << "Assertion failed at line " << lineno << std::endl
00142               << "\t" << sb << std::endl;
00143     ++error_count;
00144   }
00145 }
00146 
00147 
00148 const CartVect point1( 0.0, 0.0, 0.0 ), point2( 3.5, 1000, -200 );
00149 const CartVect vect1( 0.0, 0.0, -100.0 ), vect2( 1.0, 0.0, 1.0 );
00150 
00151 
00152 void test_none()
00153 {
00154     // default xform should do nothing.
00155   CartVect output;
00156   AffineXform none;
00157   none.xform_point( point1.array(), output.array() );
00158   ASSERT_VECTORS_EQUAL( output, point1 );
00159   none.xform_point( point2.array(), output.array() );
00160   ASSERT_VECTORS_EQUAL( output, point2 );
00161   none.xform_vector( vect1.array(), output.array() );
00162   ASSERT_VECTORS_EQUAL( output, vect1 );
00163   none.xform_vector( vect2.array(), output.array() );
00164   ASSERT_VECTORS_EQUAL( output, vect2 );
00165 }
00166 
00167 void test_translation()
00168 {
00169   CartVect offset( 1.0, 2.0, 3.0 );
00170   CartVect output;
00171   
00172   AffineXform move = AffineXform::translation( offset.array() );
00173   
00174   // test that points are moved by offset
00175   move.xform_point( point1.array(), output.array() );
00176   ASSERT_VECTORS_EQUAL( output, point1 + offset );
00177   move.xform_point( point2.array(), output.array() );
00178   ASSERT_VECTORS_EQUAL( output, point2 + offset );
00179   
00180   // vectors should not be changed by a translation
00181   move.xform_vector( vect1.array(), output.array() );
00182   ASSERT_VECTORS_EQUAL( output, vect1 );
00183   move.xform_vector( vect2.array(), output.array() );
00184   ASSERT_VECTORS_EQUAL( output, vect2 );
00185 }
00186 
00187 void test_rotation()
00188 {
00189   CartVect output;
00190   
00191   // rotate 90 degress about Z axis
00192   
00193   AffineXform rot = AffineXform::rotation( M_PI/2.0, CartVect(0,0,1).array() );
00194   ASSERT_DOUBLES_EQUAL( rot.matrix().determinant(), 1.0 );
00195   
00196   rot.xform_point( point1.array(), output.array() );
00197   ASSERT_VECTORS_EQUAL( output, point1 ); // origin not affected by transform
00198   
00199   CartVect expectedz( -point2[1], point2[0], point2[2] ); // in first quadrant
00200   rot.xform_point( point2.array(), output.array() );
00201   ASSERT_DOUBLES_EQUAL( output.length(), point2.length() );
00202   ASSERT_VECTORS_EQUAL( output, expectedz );
00203   
00204   rot.xform_vector( vect1.array(), output.array() );
00205   ASSERT_DOUBLES_EQUAL( output.length(), vect1.length() );
00206   ASSERT_VECTORS_EQUAL( output, vect1 );
00207   
00208   rot.xform_vector( vect2.array(), output.array() );
00209   ASSERT_DOUBLES_EQUAL( output.length(), vect2.length() );
00210   ASSERT_VECTORS_EQUAL( output, CartVect( 0, 1, 1 ) );
00211   
00212   // rotate 90 degress about Y axis
00213   
00214   rot = AffineXform::rotation( M_PI/2.0, CartVect(0,1,0).array() );
00215   ASSERT_DOUBLES_EQUAL( rot.matrix().determinant(), 1.0 );
00216   
00217   rot.xform_point( point1.array(), output.array() );
00218   ASSERT_VECTORS_EQUAL( output, point1 ); // origin not affected by transform
00219   
00220   CartVect expectedy( point2[2], point2[1], -point2[0] ); // in second quadrant
00221   rot.xform_point( point2.array(), output.array() );
00222   ASSERT_DOUBLES_EQUAL( output.length(), point2.length() );
00223   ASSERT_VECTORS_EQUAL( output, expectedy );
00224   
00225   rot.xform_vector( vect1.array(), output.array() );
00226   ASSERT_DOUBLES_EQUAL( output.length(), vect1.length() );
00227   ASSERT_VECTORS_EQUAL( output, CartVect(-100,0,0) );
00228   
00229   rot.xform_vector( vect2.array(), output.array() );
00230   ASSERT_DOUBLES_EQUAL( output.length(), vect2.length() );
00231   ASSERT_VECTORS_EQUAL( output, CartVect( 1, 0, -1 ) );
00232   
00233   // rotate 90 degress about X axis
00234   
00235   rot = AffineXform::rotation( M_PI/2.0, CartVect(1,0,0).array() );
00236   ASSERT_DOUBLES_EQUAL( rot.matrix().determinant(), 1.0 );
00237   
00238   rot.xform_point( point1.array(), output.array() );
00239   ASSERT_VECTORS_EQUAL( output, point1 ); // origin not affected by transform
00240   
00241   CartVect expectedx( point2[0], -point2[2], point2[1] ); // in third quadrant
00242   rot.xform_point( point2.array(), output.array() );
00243   ASSERT_DOUBLES_EQUAL( output.length(), point2.length() );
00244   ASSERT_VECTORS_EQUAL( output, expectedx );
00245   
00246   rot.xform_vector( vect1.array(), output.array() );
00247   ASSERT_DOUBLES_EQUAL( output.length(), vect1.length() );
00248   ASSERT_VECTORS_EQUAL( output, CartVect(0,100,0) );
00249   
00250   rot.xform_vector( vect2.array(), output.array() );
00251   ASSERT_DOUBLES_EQUAL( output.length(), vect2.length() );
00252   ASSERT_VECTORS_EQUAL( output, CartVect( 1, -1, 0 ) );
00253   
00254   // rotate 180 degrees about vector in XY plane
00255   
00256   rot = AffineXform::rotation( M_PI, CartVect( 1, 1, 0 ).array() );
00257   ASSERT_DOUBLES_EQUAL( rot.matrix().determinant(), 1.0 );
00258   
00259   rot.xform_point( point1.array(), output.array() );
00260   ASSERT_VECTORS_EQUAL( output, point1 ); // origin not affected by transform
00261   
00262   rot.xform_point( point2.array(), output.array() );
00263   ASSERT_DOUBLES_EQUAL( output.length(), point2.length() );
00264   ASSERT_VECTORS_EQUAL( output, CartVect( point2[1], point2[0], -point2[2] ) );
00265   
00266   rot.xform_vector( vect1.array(), output.array() );
00267   ASSERT_DOUBLES_EQUAL( output.length(), vect1.length() );
00268   ASSERT_VECTORS_EQUAL( output, -vect1 ); // vector is in xy plane
00269   
00270   rot.xform_vector( vect2.array(), output.array() );
00271   ASSERT_DOUBLES_EQUAL( output.length(), vect2.length() );
00272   ASSERT_VECTORS_EQUAL( output, CartVect( 0, 1, -1 ) );
00273 }
00274 
00275 void test_rotation_from_vec( )
00276 {
00277   CartVect v1( 1, 1, 1 );
00278   CartVect v2( 1, 0, 0 );
00279   AffineXform rot = AffineXform::rotation( v1.array(), v2.array() );
00280   CartVect result;
00281   rot.xform_vector( v1.array(), result.array() );
00282     // vectors should be parallel, but not same length
00283   ASSERT_DOUBLES_EQUAL( result.length(), v1.length() );
00284   result.normalize();
00285   ASSERT_VECTORS_EQUAL( result, v2 );
00286   
00287   double v3[] = { -1, 0, 0 };
00288   rot = AffineXform::rotation( v3, v2.array() );
00289   rot.xform_vector( v3, result.array() );
00290   ASSERT_VECTORS_EQUAL( result, v2 );
00291 }
00292 
00293 CartVect refl( const CartVect& vect, const CartVect& norm )
00294 {
00295   CartVect n(norm);
00296   n.normalize();
00297   double d = vect % n;
00298   return vect - 2 * d * n;
00299 }
00300 
00301 void test_reflection()
00302 {
00303   CartVect output;
00304   
00305   // reflect about XY plane
00306   AffineXform ref = AffineXform::reflection( CartVect( 0, 0, 1 ).array() );
00307   ASSERT_DOUBLES_EQUAL( ref.matrix().determinant(), -1.0 );
00308   ref.xform_point( point1.array(), output.array() );
00309   ASSERT_VECTORS_EQUAL( output, point1 );
00310   ref.xform_point( point2.array(), output.array() );
00311   ASSERT_DOUBLES_EQUAL( output.length(), point2.length() );
00312   ASSERT_VECTORS_EQUAL( output, CartVect(point2[0],point2[1],-point2[2]) );
00313   ref.xform_vector( vect1.array(), output.array() );
00314   ASSERT_DOUBLES_EQUAL( output.length(), vect1.length() );
00315   ASSERT_VECTORS_EQUAL( output, -vect1 );
00316   ref.xform_vector( vect2.array(), output.array() );
00317   ASSERT_DOUBLES_EQUAL( output.length(), vect2.length() );
00318   ASSERT_VECTORS_EQUAL( output, CartVect(1,0,-1) );
00319   
00320   // reflect about arbitrary palne
00321   CartVect norm( 3, 2, 1 );
00322   ref = AffineXform::reflection( norm.array() );
00323   ASSERT_DOUBLES_EQUAL( ref.matrix().determinant(), -1.0 );
00324   ref.xform_point( point1.array(), output.array() );
00325   ASSERT_VECTORS_EQUAL( output, point1 );
00326   ref.xform_point( point2.array(), output.array() );
00327   ASSERT_DOUBLES_EQUAL( output.length(), point2.length() );
00328   ASSERT_VECTORS_EQUAL( output, refl(point2,norm) );
00329   ref.xform_vector( vect1.array(), output.array() );
00330   ASSERT_DOUBLES_EQUAL( output.length(), vect1.length() );
00331   ASSERT_VECTORS_EQUAL( output, refl(vect1,norm) );
00332   ref.xform_vector( vect2.array(), output.array() );
00333   ASSERT_DOUBLES_EQUAL( output.length(), vect2.length() );
00334   ASSERT_VECTORS_EQUAL( output, refl(vect2,norm) );
00335 }
00336 
00337 void test_scale()
00338 {
00339   CartVect output;
00340   
00341   AffineXform scale = AffineXform::scale( 1.0 );
00342   ASSERT(!scale.scale());
00343   scale = AffineXform::scale( -1.0 );
00344   ASSERT(!scale.scale());
00345   
00346   //scale in X only
00347   scale = AffineXform::scale( CartVect(2,1,1).array() );
00348   ASSERT(scale.scale());
00349   scale.xform_point( point1.array(), output.array() );
00350   ASSERT_VECTORS_EQUAL( output, CartVect(2*point1[0],point1[1],point1[2]) );
00351   scale.xform_point( point2.array(), output.array() );
00352   ASSERT_VECTORS_EQUAL( output, CartVect(2*point2[0],point2[1],point2[2]) );
00353   scale.xform_vector( vect1.array(), output.array() );
00354   ASSERT_VECTORS_EQUAL( output, CartVect(2*vect1[0],vect1[1],vect1[2]) );
00355   scale.xform_vector( vect2.array(), output.array() );
00356   ASSERT_VECTORS_EQUAL( output, CartVect(2*vect2[0],vect2[1],vect2[2]) );
00357   
00358   // scale in all
00359   scale = AffineXform::scale( CartVect(0.5,0.5,0.5).array() );
00360   ASSERT(scale.scale());
00361   scale.xform_point( point1.array(), output.array() );
00362   ASSERT_VECTORS_EQUAL( output, 0.5*point1 );
00363   scale.xform_point( point2.array(), output.array() );
00364   ASSERT_VECTORS_EQUAL( output, 0.5*point2 );
00365   scale.xform_vector( vect1.array(), output.array() );
00366   ASSERT_VECTORS_EQUAL( output, 0.5*vect1 );
00367   scale.xform_vector( vect2.array(), output.array() );
00368   ASSERT_VECTORS_EQUAL( output, 0.5*vect2 );
00369 }
00370 
00371 void test_scale_point()
00372 {
00373   const double point[] = {2,3,4};
00374   const double f[] = { 0.2, 0.1, 0.3 };
00375   double result[3];
00376   AffineXform scale = AffineXform::scale( f, point );
00377   scale.xform_point( point, result );
00378   ASSERT_VECTORS_EQUAL( result, point );
00379   
00380   const double delta[3] = { 1, 0, 2 };
00381   const double pt2[] = { point[0]+delta[0],
00382                          point[1]+delta[1],
00383                          point[2]+delta[2] };
00384   scale = AffineXform::scale( f, point );
00385   scale.xform_point( pt2, result );
00386   
00387   const double expected[] = { point[0] + f[0]*delta[0],
00388                               point[1] + f[1]*delta[1],
00389                               point[2] + f[2]*delta[2] };
00390   ASSERT_VECTORS_EQUAL( result, expected );
00391 }
00392 
00393 void test_accumulate()
00394 {
00395   CartVect indiv, accum;
00396   
00397   // build an group of transforms.  make sure translation is somewhere in the middle
00398   AffineXform move, scal, rot1, rot2, refl;
00399   move = AffineXform::translation( CartVect( 5, -5, 1 ).array() );
00400   scal = AffineXform::scale( CartVect( 1, 0.5, 2 ).array() );
00401   rot1 = AffineXform::rotation( M_PI/3, CartVect( 0.5, 0.5, 1 ).array() );
00402   rot2 = AffineXform::rotation( M_PI/4, CartVect( 1.0, 0.0, 0.0 ).array() );
00403   refl = AffineXform::reflection( CartVect( -1, -1, 0 ).array() );
00404   AffineXform accu;
00405   accu.accumulate( scal );
00406   accu.accumulate( rot1 );
00407   accu.accumulate( move );
00408   accu.accumulate( refl );
00409   accu.accumulate( rot2 );
00410   
00411   accu.xform_point( point1.array(), accum.array() );
00412   scal.xform_point( point1.array(), indiv.array() );
00413   rot1.xform_point( indiv.array() );
00414   move.xform_point( indiv.array() );
00415   refl.xform_point( indiv.array() );
00416   rot2.xform_point( indiv.array() );
00417   ASSERT_VECTORS_EQUAL( accum, indiv );
00418   
00419   accu.xform_point( point2.array(), accum.array() );
00420   scal.xform_point( point2.array(), indiv.array() );
00421   rot1.xform_point( indiv.array() );
00422   move.xform_point( indiv.array() );
00423   refl.xform_point( indiv.array() );
00424   rot2.xform_point( indiv.array() );
00425   ASSERT_VECTORS_EQUAL( accum, indiv );
00426   
00427   accu.xform_vector( vect1.array(), accum.array() );
00428   scal.xform_vector( vect1.array(), indiv.array() );
00429   rot1.xform_vector( indiv.array() );
00430   move.xform_vector( indiv.array() );
00431   refl.xform_vector( indiv.array() );
00432   rot2.xform_vector( indiv.array() );
00433   ASSERT_VECTORS_EQUAL( accum, indiv );
00434   
00435   accu.xform_vector( vect2.array(), accum.array() );
00436   scal.xform_vector( vect2.array(), indiv.array() );
00437   rot1.xform_vector( indiv.array() );
00438   move.xform_vector( indiv.array() );
00439   refl.xform_vector( indiv.array() );
00440   rot2.xform_vector( indiv.array() );
00441   ASSERT_VECTORS_EQUAL( accum, indiv );
00442 }
00443 
00444 void test_inversion() {
00445   CartVect result;
00446   
00447   // build an group of transforms.  make sure translation is somewhere in the middle
00448   AffineXform move, scal, rot1, rot2, refl;
00449   move = AffineXform::translation( CartVect( 5, -5, 1 ).array() );
00450   scal = AffineXform::scale( CartVect( 1, 0.5, 2 ).array() );
00451   rot1 = AffineXform::rotation( M_PI/3, CartVect( 0.5, 0.5, 1 ).array() );
00452   rot2 = AffineXform::rotation( M_PI/4, CartVect( 1.0, 0.0, 0.0 ).array() );
00453   refl = AffineXform::reflection( CartVect( -1, -1, 0 ).array() );
00454   AffineXform acc;
00455   acc.accumulate( scal );
00456   acc.accumulate( rot1 );
00457   acc.accumulate( move );
00458   acc.accumulate( refl );
00459   acc.accumulate( rot2 );
00460   
00461   AffineXform inv = acc.inverse();
00462   
00463   acc.xform_point( point1.array(), result.array() );
00464   inv.xform_point( result.array() );
00465   ASSERT_VECTORS_EQUAL( point1, result );
00466   
00467   acc.xform_point( point2.array(), result.array() );
00468   inv.xform_point( result.array() );
00469   ASSERT_VECTORS_EQUAL( point2, result );
00470   
00471   acc.xform_vector( vect1.array(), result.array() );
00472   inv.xform_vector( result.array() );
00473   ASSERT_VECTORS_EQUAL( vect1, result );
00474   
00475   acc.xform_vector( vect2.array(), result.array() );
00476   inv.xform_vector( result.array() );
00477   ASSERT_VECTORS_EQUAL( vect2, result );
00478 }
00479   
00480 void test_is_reflection()
00481 {
00482   AffineXform refl1, refl2, scale;
00483   refl1 = AffineXform::reflection( CartVect( -1, -1, 0).array() );
00484   refl2 = AffineXform::reflection( CartVect(  1,  0, 0).array() );
00485   scale = AffineXform::scale( CartVect( -1, 1, 1 ).array() );
00486   
00487   ASSERT( refl1.reflection() );
00488   ASSERT( refl2.reflection() );
00489   ASSERT( scale.reflection() );
00490   
00491   AffineXform inv1, inv2, inv3;
00492   inv1 = refl1.inverse();
00493   inv2 = refl2.inverse();
00494   inv3 = scale.inverse();
00495   
00496   ASSERT( inv1.reflection() );
00497   ASSERT( inv2.reflection() );
00498   ASSERT( inv3.reflection() );
00499   
00500   refl1.accumulate( refl2 );
00501   refl2.accumulate( scale );
00502   ASSERT( ! refl1.reflection() );
00503   ASSERT( ! refl2.reflection() );
00504   
00505   AffineXform rot, mov;
00506   rot = AffineXform::rotation( M_PI/4, CartVect(1,1,1).array() );
00507   mov = AffineXform::translation( CartVect(-5,6,7).array() );
00508   ASSERT( !rot.reflection() );
00509   ASSERT( !mov.reflection() );
00510 }
00511 
00512 int main()
00513 {
00514   test_none();
00515   test_translation();
00516   test_rotation();
00517   test_reflection();
00518   test_rotation_from_vec();
00519   test_scale();
00520   test_scale_point();
00521   test_accumulate();
00522   test_inversion();
00523   test_is_reflection();
00524   return error_count;
00525 }
00526 
00527 #endif  // #ifdef TEST
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines