contrib/mul/vimt3d/vimt3d_transform_3d.cxx
Go to the documentation of this file.
00001 // This is mul/vimt3d/vimt3d_transform_3d.cxx
00002 #include "vimt3d_transform_3d.h"
00003 //:
00004 // \file
00005 // \brief A class to define and apply a 3D transformation up to affine.
00006 // \author Graham Vincent, Tim Cootes
00007 
00008 #include <vcl_cassert.h>
00009 #include <vcl_cstdlib.h>
00010 #include <vcl_sstream.h>
00011 #include <vsl/vsl_indent.h>
00012 #include <vnl/vnl_matrix_fixed.h>
00013 #include <vnl/vnl_vector.h>
00014 #include <vnl/algo/vnl_determinant.h>
00015 #include <vnl/vnl_math.h>
00016 #include <vul/vul_string.h>
00017 #include <vul/vul_sprintf.h>
00018 #include <mbl/mbl_read_props.h>
00019 #include <mbl/mbl_exception.h>
00020 #include <mbl/mbl_parse_sequence.h>
00021 
00022 
00023 //=======================================================================
00024 
00025 vnl_matrix<double> vimt3d_transform_3d::matrix() const
00026 {
00027   vnl_matrix<double> M(4,4);
00028   matrix(M);
00029   return M;
00030 }
00031 
00032 //=======================================================================
00033 
00034 void vimt3d_transform_3d::matrix(vnl_matrix<double>& M) const
00035 {
00036 #if 0 //grv
00037   if ((M.rows()!=4) || (M.columns()!=4)) M.resize(4,4);
00038 #endif
00039   M.set_size(4,4);
00040   double**m_data = M.data_array();
00041   m_data[0][0]=xx_; m_data[0][1]=xy_; m_data[0][2]=xz_; m_data[0][3]=xt_;
00042   m_data[1][0]=yx_; m_data[1][1]=yy_; m_data[1][2]=yz_; m_data[1][3]=yt_;
00043   m_data[2][0]=zx_; m_data[2][1]=zy_; m_data[2][2]=zz_; m_data[2][3]=zt_;
00044   m_data[3][0]=tx_; m_data[3][1]=ty_; m_data[3][2]=tz_; m_data[3][3]=tt_;
00045 }
00046 
00047 //=======================================================================
00048 // Define the transform in terms of a 4x4 homogeneous matrix.
00049 void vimt3d_transform_3d::set_matrix(const vnl_matrix<double>& M) 
00050 {
00051   if (M.rows()!=4 || M.cols()!=4)
00052     mbl_exception_error(mbl_exception_abort("vimt3d_transform_3d::set_matrix(matrix): input matrix must be 4x4"));
00053 
00054   form_=Affine;
00055   xx_=M[0][0]; xy_=M[0][1]; xz_=M[0][2]; xt_=M[0][3];
00056   yx_=M[1][0]; yy_=M[1][1]; yz_=M[1][2]; yt_=M[1][3];
00057   zx_=M[2][0]; zy_=M[2][1]; zz_=M[2][2]; zt_=M[2][3];
00058   tx_=M[3][0]; ty_=M[3][1]; tz_=M[3][2]; tt_=M[3][3];
00059 }
00060 
00061 //=======================================================================
00062 // See also vnl_rotation_matrix(), vgl_rotation_3d, and vnl_quaternion
00063 void vimt3d_transform_3d::angles(double& phi_x, double& phi_y, double& phi_z) const
00064 {
00065   // NB: in affine case will probably have to store s_x, s_y, s_z etc somewhere else!
00066   // also won't work properly in rigid body case either!
00067   double det=+xx_*yy_*zz_-xx_*zy_*yz_-yx_*xy_*zz_+yx_*zy_*xz_+zx_*xy_*yz_-zx_*yy_*xz_;
00068 
00069   double xlen = vcl_sqrt(xx_*xx_ + yx_*yx_ + zx_*zx_)* vnl_math_sgn(det);
00070   double ylen = vcl_sqrt(xy_*xy_ + yy_*yy_ + zy_*zy_)* vnl_math_sgn(det);
00071   double zlen = vcl_sqrt(xz_*xz_ + yz_*yz_ + zz_*zz_)* vnl_math_sgn(det);
00072 
00073   double xx3 = xx_ / xlen;
00074   double xy3 = xy_ / ylen;
00075   double xz3 = xz_ / zlen;
00076   double yz3 = yz_ / zlen;
00077   double zz3 = zz_ / zlen;
00078 
00079   phi_x = vcl_atan2(-yz3,zz3);
00080   phi_y = vcl_atan2(-xz3*vcl_cos(phi_x),zz3);
00081   phi_z=vcl_atan2(-xy3,xx3);
00082 
00083   // nb the equation for phi_z doesn't work in affine case
00084   // because sy and sx aren't necessarily the same
00085 
00086   // calc scaling factor
00087   // ie assuming similarity transform here
00088   // assume s is always positive
00089   // to recover original angle
00090   double s;
00091   if (vcl_sin(phi_y) < 1e-20)
00092     s = 1.0;
00093   else
00094     s = vcl_fabs( xz3/ (-1*vcl_sin(phi_y) ) );
00095 
00096 #ifdef DEBUG
00097   vcl_cout<<"s= "<<s<<vcl_endl;
00098 #endif
00099 
00100   // the angles may be wrong by +-vnl_math::pi - we can
00101   // only tell by checking against the signs
00102   // of the original entries in the rotation matrix
00103   if (vcl_fabs(vcl_sin(phi_y)*s + xz3) > 1e-6)
00104   {
00105     if (phi_y > 0)
00106       phi_y -= vnl_math::pi;
00107     else
00108       phi_y += vnl_math::pi;
00109     //phi_y *= -1;
00110   }
00111 
00112   const double cos_y = vcl_cos(phi_y);
00113 
00114   if (vcl_fabs(vcl_sin(phi_x)*cos_y*s + yz3) > 1e-6 ||
00115       vcl_fabs(vcl_cos(phi_x)*cos_y*s - zz3) > 1e-6)
00116   {
00117     if (phi_x > 0)
00118       phi_x -= vnl_math::pi;
00119     else
00120       phi_x += vnl_math::pi;
00121   }
00122 
00123   if (vcl_fabs(vcl_cos(phi_z)*cos_y*s - xx3) > 1e-6 ||
00124       vcl_fabs(vcl_sin(phi_z)*cos_y*s + xy3) > 1e-6)
00125   {
00126     if (phi_z > 0)
00127       phi_z -= vnl_math::pi;
00128     else
00129       phi_z += vnl_math::pi;
00130   }
00131 
00132   // now compress the angles towards zero as much as possible
00133   // (we can add +-vnl_math::pi to each angle and negate phi_y without changing
00134   // the rotation matrix)
00135   int count = 0;
00136   if (vcl_fabs(phi_x) > vnl_math::pi/2) ++count;
00137   if (vcl_fabs(phi_y) > vnl_math::pi/2) ++count;
00138   if (vcl_fabs(phi_z) > vnl_math::pi/2) ++count;
00139 
00140   if (count > 1)
00141   {
00142     if (phi_x > 0)
00143       phi_x -= vnl_math::pi;
00144     else
00145       phi_x += vnl_math::pi;
00146 
00147     phi_y=-phi_y;
00148     if (phi_y > 0)
00149       phi_y -= vnl_math::pi;
00150     else
00151       phi_y += vnl_math::pi;
00152 
00153     if (phi_z > 0)
00154       phi_z -= vnl_math::pi;
00155     else
00156       phi_z += vnl_math::pi;
00157   }
00158 }
00159 
00160 //=======================================================================
00161 
00162 void vimt3d_transform_3d::params(vnl_vector<double>& v) const
00163 {
00164   switch (form_)
00165   {
00166    case (Identity):
00167     v.set_size(0);
00168     break;
00169    case (Translation):
00170     if (v.size()!=3) v.set_size(3);
00171     v[0]=xt_; v[1]=yt_; v[2]=zt_;
00172     break;
00173    case (ZoomOnly):
00174     if (v.size()!=6) v.set_size(6);
00175     v[0]=xx_; v[1]=yy_; v[2]=zz_;
00176     v[3]=xt_; v[4]=yt_; v[5]=zt_;
00177     break;
00178    case (RigidBody):
00179     if (v.size()!=6) v.set_size(6);
00180     angles(v[0],v[1],v[2]);
00181     v[3]=xt_; v[4]=yt_; v[5]=zt_;
00182     break;
00183    case (Similarity): // not sure this is right - kds
00184                       // I think it's fixed now -dac
00185     if (v.size()!=7) v.set_size(7);
00186     angles(v[1],v[2],v[3]);
00187     // compute scaling factor
00188     v[0]= xx_/ ( vcl_cos( v[2] ) *vcl_cos( v[3] ) );
00189     v[4]=xt_; v[5]=yt_; v[6]=zt_;
00190     break;
00191    case (Affine):     // not sure this is right - kds
00192                       // I'm sure it's not correct -dac
00193     {
00194       v.set_size(9);
00195       // computation of angles doesn't work unless
00196       // sx, sy, sz are all the same!
00197 
00198       angles(v[3],v[4],v[5]);
00199       // try to compute scaling factors
00200       double det=+xx_*yy_*zz_-xx_*zy_*yz_-yx_*xy_*zz_+yx_*zy_*xz_+zx_*xy_*yz_-zx_*yy_*xz_;
00201       v[0]=vcl_sqrt(xx_*xx_ + yx_*yx_ + zx_*zx_)* vnl_math_sgn(det);
00202       v[1]=vcl_sqrt(xy_*xy_ + yy_*yy_ + zy_*zy_)* vnl_math_sgn(det);
00203       v[2]=vcl_sqrt(xz_*xz_ + yz_*yz_ + zz_*zz_)* vnl_math_sgn(det);
00204       v[6]=xt_; v[7]=yt_; v[8]=zt_;
00205       break;
00206     }
00207    default:
00208     mbl_exception_error(mbl_exception_abort(
00209       vul_sprintf("vimt3d_transform_3d::params() Unexpected form: %d", form_) ));
00210   }
00211 }
00212 
00213 
00214 //=======================================================================
00215 void vimt3d_transform_3d::simplify(double tol /*=1e-10*/)
00216 {
00217   double rx, ry, rz;
00218   double sx, sy, sz;
00219   double det;
00220   switch (form_)
00221   {
00222    case Affine:
00223     { // Not really true affine, because shear is forbidden.
00224       angles(rx, ry, rz);
00225       double matrix_form[]= {xx_, yx_, zx_, xy_, yy_, zy_, xz_, yz_, zz_};
00226       vnl_matrix_fixed<double, 3, 3> X(matrix_form);
00227       vnl_matrix_fixed<double, 3, 3> S2 = X.transpose() * X;
00228       // if X=R*S (where S is a diagonal matrix) then X'X = S'*R'*R*S
00229       // if R is a rotation matrix then R'*R=I and so X'X = S'*S = [s_x^2 0 0; 0 s_y^2 0; 0 0 s_z^2]
00230       if (S2(0,1)*S2(0,1) + S2(0,2)*S2(0,2) + S2(1,0)*S2(1,0) + 
00231           S2(1,2)*S2(1,2) + S2(2,0)*S2(2,0) + S2(2,1)*S2(2,1) >= tol*tol*6)
00232         return;
00233 
00234       // mirroring if det is negative;
00235       double mirror=vnl_math_sgn(vnl_determinant(X[0], X[1], X[2]));
00236 
00237       sx = vcl_sqrt(vcl_abs(S2(0,0))) * mirror;
00238       sy = vcl_sqrt(vcl_abs(S2(1,1))) * mirror;
00239       sz = vcl_sqrt(vcl_abs(S2(2,2))) * mirror;
00240       if (vnl_math_sqr(sx-sy) +  vnl_math_sqr(sx-sz) < tol*tol)
00241         this->set_similarity(sx, rx, ry, rz,
00242                              xt_, yt_, zt_ );
00243       else if (rx*rx+ry*ry+rz*rz < tol*tol)
00244         this->set_zoom_only(sx, sy, sz,
00245                             xt_, yt_, zt_);
00246       else
00247         return;
00248       simplify(tol);
00249       return;
00250     }
00251    case Similarity:
00252     angles(rx, ry, rz);
00253     det=+xx_*yy_*zz_-xx_*zy_*yz_-yx_*xy_*zz_+yx_*zy_*xz_+zx_*xy_*yz_-zx_*yy_*xz_;
00254     sx=vcl_sqrt(xx_*xx_ + yx_*yx_ + zx_*zx_)* vnl_math_sgn(det);
00255     if (rx*rx+ry*ry+rz*rz < tol*tol)
00256       this->set_zoom_only(sx, xt_, yt_, zt_);
00257     else if (vnl_math_sqr(sx-1.0) < tol*tol)
00258       this->set_rigid_body(rx, ry, rz, xt_, yt_, zt_);
00259     else
00260       return;
00261     simplify(tol);
00262     return;
00263 
00264    case RigidBody:
00265     angles(rx, ry, rz);
00266     if (rx*rx+ry*ry+rz*rz >= tol*tol)
00267       return;
00268     this->set_translation(xt_, yt_, zt_);
00269     simplify(tol);
00270     return;
00271    case ZoomOnly:
00272     if (vnl_math_sqr(xx_-1.0) + vnl_math_sqr(yy_-1.0) + vnl_math_sqr(zz_-1.0) >= tol*tol)
00273       return;
00274     set_translation(xt_, yt_, zt_);
00275    case Translation:
00276     if (xt_*xt_+yt_*yt_+zt_*zt_<tol*tol)
00277       set_identity();
00278     return;
00279    case Identity:
00280     return;
00281    default:
00282     mbl_exception_error(mbl_exception_abort(
00283       vul_sprintf("vimt3d_transform_3d::simplify() Unexpected form: %d", form_) ));
00284   }
00285 }
00286 
00287 //=======================================================================
00288 
00289 void vimt3d_transform_3d::setCheck(int n1,int n2,const char* str) const
00290 {
00291   if (n1==n2) return;
00292   vcl_ostringstream ss;
00293   ss << "vimt3d_transform_3d::set() " << n1 << " parameters required for "
00294      << str << ". Passed " << n2;
00295   mbl_exception_error(mbl_exception_abort(ss.str()));
00296 }
00297 
00298 //=======================================================================
00299 
00300 void vimt3d_transform_3d::set(const vnl_vector<double>& v, Form form)
00301 {
00302   int n=v.size();
00303 
00304   switch (form)
00305   {
00306    case (Identity):
00307     set_identity();
00308     break;
00309    case (Translation):
00310     setCheck(3,n,"Translation");
00311     set_translation(v[0],v[1],v[2]);
00312     break;
00313    case (ZoomOnly):
00314     setCheck(6,n,"ZoomOnly");
00315     set_zoom_only( v[0],v[1],v[2],
00316                    v[3],v[4],v[5]);
00317     break;
00318    case (RigidBody):
00319     setCheck(6,n,"RigidBody");
00320     set_rigid_body( v[0],v[1],v[2],
00321                     v[3],v[4],v[5]);
00322     break;
00323    case (Similarity):
00324     setCheck(7,n,"Similarity");
00325     set_similarity( v[0],v[1],v[2],
00326                     v[3],v[4],v[5], v[6]);
00327     form_ = Similarity;
00328     inv_uptodate_=false;
00329     break;
00330    case (Affine): // not sure this is right - kds
00331                   // it works unless you call params()
00332                   // later which gives the wrong answer
00333                   // in affine case -dac
00334     setCheck(9,n,"Affine");
00335     set_affine( v[0],v[1],v[2],
00336                 v[3],v[4],v[5],
00337                 v[6],v[7],v[8]);
00338     form_ = Affine;
00339     inv_uptodate_=false;
00340     break;
00341    default:
00342     mbl_exception_error(mbl_exception_abort(
00343       vul_sprintf("vimt3d_transform_3d::set() Unexpected form: %d", form_) ));
00344   }
00345 }
00346 
00347 //=======================================================================
00348 // See also vnl_rotation_matrix(), vgl_rotation_3d, and vnl_quaternion
00349 void vimt3d_transform_3d::setRotMat( double r_x, double r_y, double r_z )
00350 {
00351   double sinx=vcl_sin(r_x);
00352   double siny=vcl_sin(r_y);
00353   double sinz=vcl_sin(r_z);
00354   double cosx=vcl_cos(r_x);
00355   double cosy=vcl_cos(r_y);
00356   double cosz=vcl_cos(r_z);
00357 
00358   // set R_mat = Rx*Ry*Rz
00359   xx_ =  cosy*cosz;
00360   xy_ = -cosy*sinz;
00361   xz_ = -siny;
00362   yx_ =  cosx*sinz - sinx*siny*cosz;
00363   yy_ =  cosx*cosz + sinx*siny*sinz;
00364   yz_ = -sinx*cosy;
00365   zx_ =  sinx*sinz + cosx*siny*cosz;
00366   zy_ =  sinx*cosz - cosx*siny*sinz;
00367   zz_ =  cosx*cosy;
00368 }
00369 
00370 //=======================================================================
00371 
00372 void vimt3d_transform_3d::set_origin( const vgl_point_3d<double> & p )
00373 {
00374   xt_=p.x()*tt_;
00375   yt_=p.y()*tt_;
00376   zt_=p.z()*tt_;
00377 
00378   if (form_ == Identity) form_=Translation;
00379 
00380   inv_uptodate_=false;
00381 }
00382 
00383 //=======================================================================
00384 
00385 void vimt3d_transform_3d::set_identity()
00386 {
00387   if (form_==Identity) return;
00388   form_=Identity;
00389 
00390   xx_=yy_=zz_=tt_=1;
00391   xy_=xz_=xt_=0;
00392   yx_=yz_=yt_=0;
00393   zx_=zy_=zt_=0;
00394   tx_=ty_=tz_=0;
00395   inv_uptodate_=false;
00396 }
00397 
00398 //=======================================================================
00399 
00400 void vimt3d_transform_3d::set_translation(double t_x, double t_y, double t_z)
00401 {
00402   if (t_x==0 && t_y==0 && t_z==0)
00403     set_identity();
00404   else
00405   {
00406     form_=Translation;
00407 
00408     // Set translation (first 3 elements of final column)
00409     xt_=t_x;
00410     yt_=t_y;
00411     zt_=t_z;
00412 
00413     // Set all other elements to defaults
00414     xx_=yy_=zz_=tt_=1;
00415     xy_=xz_=yx_=yz_=zx_=zy_=0;
00416     tx_=ty_=tz_=0;
00417   }
00418 
00419   inv_uptodate_=false;
00420 }
00421 
00422 //=======================================================================
00423 
00424 void vimt3d_transform_3d::set_zoom_only(double s_x, double s_y, double s_z,
00425                                         double t_x, double t_y, double t_z)
00426 {
00427   form_=ZoomOnly;
00428 
00429   // Set scaling (first 3 diagonal elements)
00430   xx_=s_x;
00431   yy_=s_y;
00432   zz_=s_z;
00433 
00434   // Set translation (first 3 elements of final column)
00435   xt_=t_x;
00436   yt_=t_y;
00437   zt_=t_z;
00438 
00439   // Set all other elements to defaults
00440   tx_=ty_=tz_=0;
00441   xy_=xz_=yx_=yz_=zx_=zy_=0;
00442   tt_=1;
00443 
00444   inv_uptodate_=false;
00445 }
00446 
00447 //=======================================================================
00448 
00449 void vimt3d_transform_3d::set_rigid_body(double r_x, double r_y, double r_z,
00450                                          double t_x, double t_y, double t_z)
00451 {
00452   if (r_x==0.0 && r_y==0.0 && r_z==0.0)
00453   {
00454     set_translation(t_x,t_y,t_z);
00455   }
00456   else
00457   {
00458     form_=RigidBody;
00459 
00460     // Set rotation matrix
00461     setRotMat(r_x,r_y,r_z);
00462 
00463     // Set translation (first 3 elements of final column)
00464     xt_=t_x;
00465     yt_=t_y;
00466     zt_=t_z;
00467 
00468     // Set all other elements to defaults
00469     tx_=0; ty_=0; tz_=0; tt_=1;
00470   }
00471 
00472   inv_uptodate_=false;
00473 }
00474 
00475 //: Sets the transformation to be rotation, followed by translation.
00476 // The transformation is separable affine.
00477 // \param q  Unit quaternion defining rotation
00478 void vimt3d_transform_3d::set_rigid_body(const vnl_quaternion<double>& q,
00479                                          double t_x, double t_y, double t_z)
00480 {
00481   if (q.angle()==0.0)
00482   {
00483     set_translation(t_x,t_y,t_z);
00484     return;
00485   }
00486   vnl_matrix_fixed<double,3,3> R = q.rotation_matrix_transpose();
00487   form_=RigidBody;
00488   
00489   // Set rotation terms from R, which is transpose of Rot mat.
00490   xx_=R[0][0];  xy_= R[1][0]; xz_ = R[2][0];
00491   yx_=R[0][1];  yy_= R[1][1]; yz_ = R[2][1];
00492   zx_=R[0][2];  zy_= R[1][2]; zz_ = R[2][2];
00493   
00494   // Set translation
00495   xt_=t_x;
00496   yt_=t_y;
00497   zt_=t_z;
00498 
00499   inv_uptodate_=false;
00500 }
00501 
00502 //: Sets the transformation to be similarity: scale, rotation, followed by translation.
00503 // The transformation is separable affine.
00504 // \param unit_q  Unit quaternion defining rotation
00505 void vimt3d_transform_3d::set_similarity(double s, const vnl_quaternion<double>& q,
00506                       double t_x, double t_y, double t_z)
00507 {
00508   if (q.angle()==0.0)
00509   {
00510     set_zoom_only(s,t_x,t_y,t_z);
00511     return;
00512   }
00513   vnl_matrix_fixed<double,3,3> R = q.rotation_matrix_transpose();
00514   form_=RigidBody;
00515   
00516   // Set scale/rotation terms from R, which is transpose of Rot mat.
00517   xx_=s*R[0][0];  xy_= s*R[1][0]; xz_ = s*R[2][0];
00518   yx_=s*R[0][1];  yy_= s*R[1][1]; yz_ = s*R[2][1];
00519   zx_=s*R[0][2];  zy_= s*R[1][2]; zz_ = s*R[2][2];
00520   
00521   // Set translation
00522   xt_=t_x;
00523   yt_=t_y;
00524   zt_=t_z;
00525 
00526   inv_uptodate_=false;
00527 }
00528 //=======================================================================
00529 
00530 void vimt3d_transform_3d::set_similarity(double s,
00531                                          double r_x, double r_y, double r_z,
00532                                          double t_x, double t_y, double t_z)
00533 {
00534   if (s==1.0)
00535   {
00536     set_rigid_body(r_x,r_y,r_z,t_x,t_y,t_z);
00537   }
00538   else
00539   {
00540     form_=Similarity;
00541 
00542     // Set rotation matrix
00543     setRotMat(r_x,r_y,r_z);
00544 
00545 #ifdef DEBUG // debug test
00546     double r_x1, r_y1, r_z1;
00547     angles( r_x1, r_y1, r_z1 );
00548     vcl_cout << "r_x = " << r_x  << vcl_endl
00549              << "r_x1= " << r_x1 << vcl_endl
00550              << "r_y = " << r_y  << vcl_endl
00551              << "r_y1= " << r_y1 << vcl_endl
00552              << "r_z = " << r_z  << vcl_endl
00553              << "r_z1= " << r_z1 << vcl_endl;
00554 #endif
00555 
00556     // Account for scaling (this actually means that scaling was done BEFORE rotation)
00557     xx_*=s;  xy_*=s;  xz_*=s;
00558     yx_*=s;  yy_*=s;  yz_*=s;
00559     zx_*=s;  zy_*=s;  zz_*=s;
00560 
00561     // Set translation (first 3 elements of final column)
00562     xt_=t_x;
00563     yt_=t_y;
00564     zt_=t_z;
00565 
00566     // Set all other elements to defaults
00567     tx_=0; ty_=0; tz_=0; tt_=1;
00568   }
00569   inv_uptodate_=false;
00570 }
00571 
00572 //=======================================================================
00573 
00574 void vimt3d_transform_3d::set_affine(double s_x, double s_y, double s_z,
00575                                      double r_x, double r_y, double r_z,
00576                                      double t_x, double t_y, double t_z)
00577 {
00578   form_=Affine;
00579 
00580   // Set rotation matrix
00581   setRotMat(r_x,r_y,r_z);
00582 
00583   // Account for scaling (this actually means that scaling was done BEFORE rotation)
00584   xx_*=s_x;  xy_*=s_y;  xz_*=s_z;
00585   yx_*=s_x;  yy_*=s_y;  yz_*=s_z;
00586   zx_*=s_x;  zy_*=s_y;  zz_*=s_z;
00587 
00588   // Set translation (first 3 elements of final column)
00589   xt_=t_x;
00590   yt_=t_y;
00591   zt_=t_z;
00592 
00593   // Set all other elements to defaults
00594   tx_=0; ty_=0; tz_=0; tt_=1;
00595 
00596   inv_uptodate_=false;
00597 }
00598 //=======================================================================
00599 
00600 void vimt3d_transform_3d::set_affine(double s_x, double s_y, double s_z,
00601                                      vgl_vector_3d<double> c_x,
00602                                      vgl_vector_3d<double> c_y,
00603                                      vgl_vector_3d<double> c_z,
00604                                      double t_x, double t_y, double t_z)
00605 {
00606   form_=Affine;
00607 
00608   // Set rotation matrix
00609   xx_ = c_x.x(); xy_ = c_y.x(); xz_ = c_z.x();
00610   yx_ = c_x.y(); yy_ = c_y.y(); yz_ = c_z.y();
00611   zx_ = c_x.z(); zy_ = c_y.z(); zz_ = c_z.z();
00612 
00613 
00614   // Account for scaling (this actually means that scaling was done BEFORE rotation)
00615   xx_*=s_x;  xy_*=s_y;  xz_*=s_z;
00616   yx_*=s_x;  yy_*=s_y;  yz_*=s_z;
00617   zx_*=s_x;  zy_*=s_y;  zz_*=s_z;
00618 
00619   // Set translation (first 3 elements of final column)
00620   xt_=t_x;
00621   yt_=t_y;
00622   zt_=t_z;
00623 
00624   // Set all other elements to defaults
00625   tx_=0; ty_=0; tz_=0; tt_=1;
00626 
00627   inv_uptodate_=false;
00628 }
00629 
00630 //=======================================================================
00631 
00632 void vimt3d_transform_3d::set_affine(const vgl_point_3d<double>& p,
00633                                      const vgl_vector_3d<double>& u,
00634                                      const vgl_vector_3d<double>& v,
00635                                      const vgl_vector_3d<double>& w)
00636 {
00637   form_=Affine;
00638 
00639 #ifndef NDEBUG
00640   const double tol=1e-6;
00641 
00642   // Get normalized vectors
00643   vgl_vector_3d<double> uh = normalized(u);
00644   vgl_vector_3d<double> vh = normalized(v);
00645   vgl_vector_3d<double> wh = normalized(w);
00646 
00647   // Test for orthogonality of input vectors
00648   assert(vcl_fabs(dot_product(uh,vh))<tol);
00649   assert(vcl_fabs(dot_product(vh,wh))<tol);
00650   assert(vcl_fabs(dot_product(wh,uh))<tol);
00651 
00652   // Test for right-handedness of input vectors
00653   assert(vcl_fabs((cross_product(uh,vh)-wh).length())<tol);
00654 #endif
00655 
00656   // Set rotation and scaling (this actually means that scaling was done BEFORE rotation)
00657   xx_=u.x();  xy_=v.x();  xz_=w.x();
00658   yx_=u.y();  yy_=v.y();  yz_=w.y();
00659   zx_=u.z();  zy_=v.z();  zz_=w.z();
00660 
00661   // Set translation (first 3 elements of final column)
00662   xt_=p.x();
00663   yt_=p.y();
00664   zt_=p.z();
00665 
00666   // Set final row with default values (for all transforms up to affine)
00667   tx_=0; ty_=0; tz_=0; tt_=1;
00668 
00669   inv_uptodate_=false;
00670 }
00671 
00672 //=======================================================================
00673 
00674 vimt3d_transform_3d vimt3d_transform_3d::inverse() const
00675 {
00676   if (!inv_uptodate_) calcInverse();
00677 
00678   vimt3d_transform_3d inv;
00679 
00680   inv.xx_ = xx2_; inv.xy_ = xy2_; inv.xz_ = xz2_; inv.xt_ = xt2_;
00681   inv.yx_ = yx2_; inv.yy_ = yy2_; inv.yz_ = yz2_; inv.yt_ = yt2_;
00682   inv.zx_ = zx2_; inv.zy_ = zy2_; inv.zz_ = zz2_; inv.zt_ = zt2_;
00683   inv.tx_ = tx2_; inv.ty_ = ty2_; inv.tz_ = tz2_; inv.tt_ = tt2_;
00684 
00685   inv.xx2_ = xx_; inv.xy2_ = xy_; inv.xz2_ = xz_; inv.xt2_ = xt_;
00686   inv.yx2_ = yx_; inv.yy2_ = yy_; inv.yz2_ = yz_; inv.yt2_ = yt_;
00687   inv.zx2_ = zx_; inv.zy2_ = zy_; inv.zz2_ = zz_; inv.zt2_ = zt_;
00688   inv.tx2_ = tx_; inv.ty2_ = ty_; inv.tz2_ = tz_; inv.tt2_ = tt_;
00689 
00690   inv.form_ = form_;
00691   inv.inv_uptodate_ = true;
00692 
00693   return inv;
00694 }
00695 
00696 //=======================================================================
00697 
00698 void vimt3d_transform_3d::calcInverse() const
00699 {
00700   xx2_=yy2_=zz2_=tt2_=1;
00701   xy2_ = xz2_= xt2_ = yx2_ = yz2_= yt2_ = zx2_ = zy2_ = zt2_ = tx2_ = ty2_ = tz2_ = 0;
00702 
00703   switch (form_)
00704   {
00705    case Identity :
00706     break;
00707    case Translation :
00708     xt2_=-xt_;
00709     yt2_=-yt_;
00710     zt2_=-zt_;
00711     break;
00712    case ZoomOnly :
00713     xx2_=1.0/xx_;
00714     yy2_=1.0/yy_;
00715     zz2_=1.0/zz_;
00716     xt2_=-xt_/xx_;
00717     yt2_=-yt_/yy_;
00718     zt2_=-zt_/zz_;
00719     break;
00720    case RigidBody :
00721     // transpose x,y,z part
00722     xx2_=xx_;
00723     xy2_=yx_;
00724     xz2_=zx_;
00725     yx2_=xy_;
00726     yy2_=yy_;
00727     yz2_=zy_;
00728     zx2_=xz_;
00729     zy2_=yz_;
00730     zz2_=zz_;
00731     xt2_=-(xx2_*xt_ + xy2_*yt_ + xz2_*zt_);
00732     yt2_=-(yx2_*xt_ + yy2_*yt_ + yz2_*zt_);
00733     zt2_=-(zx2_*xt_ + zy2_*yt_ + zz2_*zt_);
00734     break;
00735    case Similarity :
00736    case Affine : {
00737       // affine inverse (plugged in from symbolic matlab)
00738       double det=-xx_*yy_*zz_+xx_*zy_*yz_+yx_*xy_*zz_-yx_*zy_*xz_-zx_*xy_*yz_+zx_*yy_*xz_;
00739       if (det==0)
00740       {
00741         vcl_cerr<<"vimt3d_transform_3d() : No inverse exists for this affine transform (det==0)\n";
00742         vcl_abort();
00743       }
00744 
00745       xx2_=(-yy_*zz_+zy_*yz_)/det;
00746       xy2_=( xy_*zz_-zy_*xz_)/det;
00747       xz2_=(-xy_*yz_+yy_*xz_)/det;
00748       xt2_=(xy_*yz_*zt_-xy_*yt_*zz_-yy_*xz_*zt_+yy_*xt_*zz_+zy_*xz_*yt_-zy_*xt_*yz_)/det;
00749 
00750       yx2_=( yx_*zz_-zx_*yz_)/det;
00751       yy2_=(-xx_*zz_+zx_*xz_)/det;
00752       yz2_=( xx_*yz_-yx_*xz_)/det;
00753       yt2_=(-xx_*yz_*zt_+xx_*yt_*zz_+yx_*xz_*zt_-yx_*xt_*zz_-zx_*xz_*yt_+zx_*xt_*yz_)/det;
00754 
00755       zx2_=(-yx_*zy_+zx_*yy_)/det;
00756       zy2_=( xx_*zy_-zx_*xy_)/det;
00757       zz2_=(-xx_*yy_+yx_*xy_)/det;
00758       zt2_=( xx_*yy_*zt_-xx_*yt_*zy_-yx_*xy_*zt_+yx_*xt_*zy_+zx_*xy_*yt_-zx_*xt_*yy_)/det;
00759 
00760       break; }
00761    default:
00762     mbl_exception_error(mbl_exception_abort(
00763       vul_sprintf("vimt3d_transform_3d::calcInverse() Unexpected form: %d", form_) ));
00764   }
00765 
00766   inv_uptodate_=true;
00767 }
00768 
00769 //=======================================================================
00770 
00771 bool vimt3d_transform_3d::operator==( const vimt3d_transform_3d& t) const
00772 {
00773   return
00774     xx_ == t.xx_ &&
00775     yy_ == t.yy_ &&
00776     zz_ == t.zz_ &&
00777     tt_ == t.tt_ &&
00778     xy_ == t.xy_ &&
00779     xz_ == t.xz_ &&
00780     xt_ == t.xt_ &&
00781     yx_ == t.yx_ &&
00782     yz_ == t.yz_ &&
00783     yt_ == t.yt_ &&
00784     zx_ == t.zx_ &&
00785     zy_ == t.zy_ &&
00786     zt_ == t.zt_ &&
00787     tx_ == t.tx_ &&
00788     ty_ == t.ty_ &&
00789     tz_ == t.tz_ ;
00790 }
00791 
00792 //=======================================================================
00793 //: Calculates the product LR
00794 // \param L  Transform
00795 // \param R  Transform
00796 // \return Transform LR = R followed by L
00797 vimt3d_transform_3d operator*(const vimt3d_transform_3d& L, const vimt3d_transform_3d& R)
00798 {
00799   vimt3d_transform_3d T;
00800 
00801   if (L.form() == vimt3d_transform_3d::Identity)
00802     return R;
00803   else
00804   if (R.form() == vimt3d_transform_3d::Identity)
00805     return L;
00806   else
00807   {
00808   /// full multiplication - inefficient but works for
00809   // arbitrary 4*4 transformation matrix
00810     T.xx_ = L.xx_*R.xx_ + L.xy_*R.yx_+ L.xz_*R.zx_ + L.xt_*R.tx_;
00811     T.xy_ = L.xx_*R.xy_ + L.xy_*R.yy_+ L.xz_*R.zy_ + L.xt_*R.ty_;
00812     T.xz_ = L.xx_*R.xz_ + L.xy_*R.yz_+ L.xz_*R.zz_ + L.xt_*R.tz_;
00813     T.xt_ = L.xx_*R.xt_ + L.xy_*R.yt_+ L.xz_*R.zt_ + L.xt_*R.tt_;
00814 
00815     T.yx_ = L.yx_*R.xx_ + L.yy_*R.yx_+ L.yz_*R.zx_ + L.yt_*R.tx_;
00816     T.yy_ = L.yx_*R.xy_ + L.yy_*R.yy_+ L.yz_*R.zy_ + L.yt_*R.ty_;
00817     T.yz_ = L.yx_*R.xz_ + L.yy_*R.yz_+ L.yz_*R.zz_ + L.yt_*R.tz_;
00818     T.yt_ = L.yx_*R.xt_ + L.yy_*R.yt_+ L.yz_*R.zt_ + L.yt_*R.tt_;
00819 
00820     T.zx_ = L.zx_*R.xx_ + L.zy_*R.yx_+ L.zz_*R.zx_ + L.zt_*R.tx_;
00821     T.zy_ = L.zx_*R.xy_ + L.zy_*R.yy_+ L.zz_*R.zy_ + L.zt_*R.ty_;
00822     T.zz_ = L.zx_*R.xz_ + L.zy_*R.yz_+ L.zz_*R.zz_ + L.zt_*R.tz_;
00823     T.zt_ = L.zx_*R.xt_ + L.zy_*R.yt_+ L.zz_*R.zt_ + L.zt_*R.tt_;
00824 
00825     T.tx_ = L.tx_*R.xx_ + L.ty_*R.yx_+ L.tz_*R.zx_ + L.tt_*R.tx_;
00826     T.ty_ = L.tx_*R.xy_ + L.ty_*R.yy_+ L.tz_*R.zy_ + L.tt_*R.ty_;
00827     T.tz_ = L.tx_*R.xz_ + L.ty_*R.yz_+ L.tz_*R.zz_ + L.tt_*R.tz_;
00828     T.tt_ = L.tx_*R.xt_ + L.ty_*R.yt_+ L.tz_*R.zt_ + L.tt_*R.tt_;
00829 
00830     // now set the type using the type of L and R
00831     // not sure this right - kds
00832     if (R.form() == L.form())
00833       T.form_ = R.form();
00834     else
00835     {
00836       if (R.form() == vimt3d_transform_3d::Affine ||
00837           L.form() == vimt3d_transform_3d::Affine)
00838           T.form_ = vimt3d_transform_3d::Affine;
00839       else
00840       if (R.form() == vimt3d_transform_3d::Similarity ||
00841           L.form() == vimt3d_transform_3d::Similarity)
00842           T.form_ = vimt3d_transform_3d::Similarity;
00843       else
00844       if (R.form() == vimt3d_transform_3d::RigidBody ||
00845           L.form() == vimt3d_transform_3d::RigidBody)
00846       {
00847         if (R.form() == vimt3d_transform_3d::ZoomOnly)
00848         {
00849           if (R.xx_ == R.yy_ &&
00850               R.xx_ == R.zz_)
00851             T.form_ = vimt3d_transform_3d::Similarity;
00852           else
00853             T.form_ = vimt3d_transform_3d::Affine;
00854         }
00855         else
00856         if (L.form() == vimt3d_transform_3d::ZoomOnly)
00857         {
00858           if (L.xx_ == L.yy_ &&
00859               L.xx_ == L.zz_)
00860             T.form_ = vimt3d_transform_3d::Similarity;
00861           else
00862             T.form_ = vimt3d_transform_3d::Affine;
00863         }
00864         else
00865           T.form_ = vimt3d_transform_3d::RigidBody;
00866       }
00867       else
00868       if (R.form() == vimt3d_transform_3d::ZoomOnly ||
00869           L.form() == vimt3d_transform_3d::ZoomOnly)
00870           T.form_ = vimt3d_transform_3d::ZoomOnly;
00871       else
00872         T.form_ = vimt3d_transform_3d::Translation;
00873     }
00874   }
00875 
00876   T.inv_uptodate_ = false;
00877 
00878   return T;
00879 }
00880 
00881 //=======================================================================
00882 void vimt3d_transform_3d::print_summary(vcl_ostream& o) const
00883 {
00884   o << vsl_indent()<< "Form: ";
00885   vsl_indent_inc(o);
00886   switch (form_)
00887   {
00888    case Identity:
00889     o << "Identity\n";
00890     break;
00891 
00892    case Translation: {
00893     vnl_vector<double> p(3);
00894     params(p);
00895     o << "Translation (" << p(0) << ',' << p(1) << ',' << p(2) << ")\n";
00896     break; }
00897 
00898    case ZoomOnly: {
00899     vnl_vector<double> p(6);
00900     params(p);
00901     o << "ZoomOnly\n"
00902       << vsl_indent()<< "scale factor = (" << p(0) << ',' << p(1) << ',' << p(2) << ")\n"
00903       << vsl_indent() << "translation = (" << p(3) << ',' << p(4) << ',' << p(5) << ")\n";
00904     break; }
00905 
00906    case RigidBody: {
00907     vnl_vector<double> p(6);
00908     params(p);
00909     o << "RigidBody\n"
00910       << vsl_indent()<< "angles = " << p(0) << ',' << p(1) << ',' << p(2) << '\n'
00911       << vsl_indent()<< "translation = (" << p(3) << ',' << p(4) << ',' << p(5) << ")\n";
00912     break; }
00913 
00914    case Similarity: {
00915     vnl_vector<double> p(7);
00916     params(p);
00917     // not sure this is right - kds
00918     // returns euler angles of rotation
00919     // which might not be same as rotations specified by set command
00920     // cos euler angles not unique
00921     o << "Similarity\n"
00922       << vsl_indent()<< "scale factor = " << p(0) << '\n'
00923       << vsl_indent()<< "angles = " << p(1) << ',' << p(2) << ',' << p(3) << '\n'
00924       << vsl_indent()<< "translation = (" << p(4) << ',' << p(5) << ',' << p(6) << ")\n";
00925     break; }
00926    case Affine: {
00927     vnl_vector<double> p(9);
00928     params(p);
00929     // not sure this is right - kds
00930     // params(p) call is broken for affine
00931     // only works if sx=sy=sz in set command
00932     o << "Affine\n"
00933       << vsl_indent()<< "scale factors = " << p(0) << ',' << p(1) << ',' << p(2) << '\n'
00934       << vsl_indent()<< "angles = " << p(3) << ',' << p(4) << ',' << p(5) << '\n'
00935       << vsl_indent()<< "translation = (" << p(6) << ',' << p(7) << ',' << p(8) << ")\n";
00936     break; }
00937    default:
00938     mbl_exception_error(mbl_exception_abort(
00939       vul_sprintf("vimt3d_transform_3d::print_summary() Unexpected form: %d", form_) ));
00940   }
00941   vsl_indent_dec(o);
00942 }
00943 
00944 //=======================================================================
00945 // Print class to os
00946 void vimt3d_transform_3d::print_all(vcl_ostream& os) const
00947 {
00948   os << vsl_indent() << "Form: ";
00949   switch (form_)
00950   {
00951    case Identity:
00952     os << "Identity\n";
00953     break;
00954 
00955    case Translation:
00956     os << "Translation\n";
00957     break;
00958 
00959    case ZoomOnly:
00960     os << "ZoomOnly\n";
00961     break;
00962 
00963    case RigidBody:
00964     os << "RigidBody\n";
00965     break;
00966 
00967    case Similarity:
00968     os << "Similarity\n";
00969     break;
00970 
00971    case Affine:
00972     os << "Affine\n";
00973     break;
00974    default:
00975     mbl_exception_error(mbl_exception_abort(
00976       vul_sprintf("vimt3d_transform_3d::print_all() Unexpected form: %d", form_) ));
00977   }
00978 
00979   os << vsl_indent() << "Matrix:\n";
00980   vsl_indent_inc(os);
00981   os << vsl_indent() << xx_ << ' ' << xy_ << ' ' << xz_ << ' ' << xt_ << '\n'
00982      << vsl_indent() << yx_ << ' ' << yy_ << ' ' << yz_ << ' ' << yt_ << '\n'
00983      << vsl_indent() << zx_ << ' ' << zy_ << ' ' << zz_ << ' ' << zt_ << '\n'
00984      << vsl_indent() << tx_ << ' ' << ty_ << ' ' << tz_ << ' ' << tt_ << '\n';
00985   vsl_indent_dec(os);
00986 }
00987 
00988 
00989 //=======================================================================
00990 void vimt3d_transform_3d::config(vcl_istream& is)
00991 {
00992   mbl_read_props_type props = mbl_read_props_ws(is);
00993   vcl_string form = props.get_required_property("form");
00994   vul_string_downcase(form);
00995 
00996   bool done=false;
00997 
00998   if (form == "identity")
00999   {
01000     done=true;
01001     form_ = Identity;
01002   }
01003   else if (form == "translation")
01004     form_ = Translation;
01005   else if (form == "zoomonly")
01006     form_ = ZoomOnly;
01007   else if (form == "rigidbody")
01008     form_ = RigidBody;
01009   else if (form == "similarity")
01010     form_ = Similarity;
01011   else if (form == "affine")
01012     form_ = Affine;
01013   else
01014     throw mbl_exception_parse_error("Unknown transformation: \"" + form + "\"");
01015 
01016   vcl_string vector = props.get_optional_property("vector");
01017   if (!vector.empty())
01018   {
01019     vcl_istringstream ss(vector);
01020 
01021     vcl_vector<double> vec1;
01022     mbl_parse_sequence(ss, vcl_back_inserter(vec1), double());
01023     if (vec1.empty())
01024       throw mbl_exception_parse_error("Could not find elements for transformation vector: \""+vector+"\"");
01025     vnl_vector<double> vec2(&vec1.front(), vec1.size());
01026     try
01027     { // translate exception abort into parse error.
01028       this->set(vec2, form_);
01029     }
01030     catch (mbl_exception_abort & e)
01031     {
01032       throw mbl_exception_parse_error(e.what());
01033     }
01034     done = true;
01035   }
01036   if (!done && form_==Translation)
01037   {
01038     this->set_translation(
01039       vul_string_atof(props.get_required_property("t_x")),
01040       vul_string_atof(props.get_required_property("t_y")),
01041       vul_string_atof(props.get_required_property("t_z")) );
01042     done = true;
01043   }
01044   if (!done && form_==ZoomOnly)
01045   {
01046     vcl_string s_str = props.get_optional_property("s");
01047     if (!s_str.empty())
01048       this->set_zoom_only(
01049         vul_string_atof(s_str),
01050         vul_string_atof(props.get_optional_property("t_x")),
01051         vul_string_atof(props.get_optional_property("t_y")),
01052         vul_string_atof(props.get_optional_property("t_z")) );
01053     else
01054       this->set_zoom_only(
01055         vul_string_atof(props.get_optional_property("s_x")),
01056         vul_string_atof(props.get_optional_property("s_y")),
01057         vul_string_atof(props.get_optional_property("s_z")),
01058         vul_string_atof(props.get_optional_property("t_x")),
01059         vul_string_atof(props.get_optional_property("t_y")),
01060         vul_string_atof(props.get_optional_property("t_z")) );
01061     done = true;
01062   }
01063   if (!done && form_==RigidBody)
01064   {
01065     set_rigid_body(
01066       vul_string_atof(props.get_optional_property("r_x")),
01067       vul_string_atof(props.get_optional_property("r_y")),
01068       vul_string_atof(props.get_optional_property("r_z")),
01069       vul_string_atof(props.get_optional_property("t_x")),
01070       vul_string_atof(props.get_optional_property("t_y")),
01071       vul_string_atof(props.get_optional_property("t_z")) );
01072     done = true;
01073   }
01074 
01075   if (!done) throw mbl_exception_parse_error("Not enough transformation values specified");
01076 
01077   mbl_read_props_look_for_unused_props(
01078     "vimt3d_transform_3d::config", props, mbl_read_props_type());
01079   return;
01080 }
01081 
01082 
01083 //=======================================================================
01084 
01085 void vimt3d_transform_3d::b_write(vsl_b_ostream& bfs) const
01086 {
01087   const short version_no = 2;
01088   vsl_b_write(bfs,version_no);
01089   vsl_b_write(bfs,int(form_));
01090   vsl_b_write(bfs,xx_); vsl_b_write(bfs,xy_); vsl_b_write(bfs,xz_); vsl_b_write(bfs,xt_);
01091   vsl_b_write(bfs,yx_); vsl_b_write(bfs,yy_); vsl_b_write(bfs,yz_); vsl_b_write(bfs,yt_);
01092   vsl_b_write(bfs,zx_); vsl_b_write(bfs,zy_); vsl_b_write(bfs,zz_); vsl_b_write(bfs,zt_);
01093   vsl_b_write(bfs,tx_); vsl_b_write(bfs,ty_); vsl_b_write(bfs,tz_); vsl_b_write(bfs,tt_);
01094 }
01095 
01096 //=======================================================================
01097 
01098 void vimt3d_transform_3d::b_read(vsl_b_istream& bfs)
01099 {
01100   short version;
01101   vsl_b_read(bfs,version);
01102   int f;
01103   switch (version)
01104   {
01105    case 1:
01106     vsl_b_read(bfs,f);
01107     if (f==0) // Old Form enum had "Undefined" as the first value. It is never needed, and so was removed.
01108       set_identity();
01109     else
01110     {
01111       form_=Form(f-1);
01112       vsl_b_read(bfs,xx_); vsl_b_read(bfs,xy_); vsl_b_read(bfs,xz_); vsl_b_read(bfs,xt_);
01113       vsl_b_read(bfs,yx_); vsl_b_read(bfs,yy_); vsl_b_read(bfs,yz_); vsl_b_read(bfs,yt_);
01114       vsl_b_read(bfs,zx_); vsl_b_read(bfs,zy_); vsl_b_read(bfs,zz_); vsl_b_read(bfs,zt_);
01115       vsl_b_read(bfs,tx_); vsl_b_read(bfs,ty_); vsl_b_read(bfs,tz_); vsl_b_read(bfs,tt_);
01116     }
01117     break;
01118    case 2:
01119     vsl_b_read(bfs,f); form_=Form(f);
01120     vsl_b_read(bfs,xx_); vsl_b_read(bfs,xy_); vsl_b_read(bfs,xz_); vsl_b_read(bfs,xt_);
01121     vsl_b_read(bfs,yx_); vsl_b_read(bfs,yy_); vsl_b_read(bfs,yz_); vsl_b_read(bfs,yt_);
01122     vsl_b_read(bfs,zx_); vsl_b_read(bfs,zy_); vsl_b_read(bfs,zz_); vsl_b_read(bfs,zt_);
01123     vsl_b_read(bfs,tx_); vsl_b_read(bfs,ty_); vsl_b_read(bfs,tz_); vsl_b_read(bfs,tt_);
01124     break;
01125    default:
01126     vcl_cerr<<"vimt3d_transform_3d::load : Illegal version number "<<version<<'\n';
01127     vcl_abort();
01128   }
01129 
01130   inv_uptodate_ = 0;
01131 }
01132 
01133 //=======================================================================
01134 
01135 void vsl_b_write(vsl_b_ostream& bfs, const vimt3d_transform_3d& b)
01136 {
01137   b.b_write(bfs);
01138 }
01139 
01140 //=======================================================================
01141 
01142 void vsl_b_read(vsl_b_istream& bfs, vimt3d_transform_3d& b)
01143 {
01144   b.b_read(bfs);
01145 }
01146 
01147 //=======================================================================
01148 
01149 vcl_ostream& operator<<(vcl_ostream& os,const vimt3d_transform_3d& b)
01150 {
01151   vsl_indent_inc(os);
01152   b.print_summary(os);
01153   vsl_indent_dec(os);
01154   return os;
01155 }
01156 
01157 //========================================================================
01158 // Test whether a 3D transform is zoom-only or lesser, i.e. there may
01159 // be translation and (anisotropic) scaling but no rotation.
01160 // This tests only for a commonly-occurring special case; there may
01161 // be other zoom-only transforms that are not detected.
01162 //========================================================================
01163 bool vimt3d_transform_is_zoom_only(const vimt3d_transform_3d& transf,
01164                                    const double zero_tol/*=1e-9*/)
01165 {
01166   // Check whether the top-left 3x3 submatrix part of the transform is
01167   // diagonal with strictly-positive elements. Such cases have zero rotation
01168   // and positive (possibly anisotropic) scaling.
01169   vnl_matrix<double> M = transf.matrix().extract(3,3,0,0);
01170 
01171   // Are any diagonal elements zero or negative?
01172   for (unsigned i=0; i<3; ++i)
01173     if (M(i,i)<=zero_tol) return false;
01174 
01175   // Are any off-diagonal elements non-zero?
01176   for (unsigned j=0; j<3; ++j)
01177   {
01178     for (unsigned i=0; i<3; ++i)
01179     {
01180       if (i!=j && vcl_fabs(M(i,j))>=zero_tol) return false;
01181     }
01182   }
01183 
01184   return true;
01185 }
01186