core/vgui/vgui_projection_inspector.cxx
Go to the documentation of this file.
00001 // This is core/vgui/vgui_projection_inspector.cxx
00002 #include "vgui_projection_inspector.h"
00003 //:
00004 // \file
00005 // \author fsm
00006 // \brief  See vgui_projection_inspector.h for a description of this file.
00007 
00008 #include <vcl_cassert.h>
00009 #include <vcl_cstdlib.h> // vcl_abort()
00010 #include <vcl_iostream.h>
00011 
00012 #include <vnl/vnl_double_2.h>
00013 #include <vnl/vnl_double_3.h>
00014 #include <vnl/vnl_matlab_print.h>
00015 
00016 #include <vgui/vgui_gl.h>
00017 
00018 //------------------------------------------------------------------------------
00019 
00020 void vgui_projection_inspector::print(vcl_ostream& strm) const
00021 {
00022   strm << "vgui_projection_inspector: {\n"
00023        << "VP = " << vp[0] << ' ' << vp[1] << ' ' << vp[2] << ' ' << vp[3] << '\n';
00024   vnl_matlab_print(strm, P, "P");
00025   vnl_matlab_print(strm, M, "M");
00026   strm << "}\n";
00027 }
00028 
00029 //------------------------------------------------------------------------------
00030 
00031 bool vgui_projection_inspector::back_project(double const x[3],
00032                                              double const p[4],
00033                                              double X[4]) const
00034 {
00035   // get total 4x4 projection matrix :
00036   vnl_double_4x4 T = P*M;
00037 
00038   // get rows of corresponding 3x4 projection matrix :
00039   vnl_double_4 a = T.get_row(0);
00040   vnl_double_4 b = T.get_row(1);
00041   vnl_double_4 c = T.get_row(3);
00042 
00043   // convert viewport coordinates to device coordinates :
00044   double devx = 2*(x[0]-vp[0]*x[2])/vp[2] - x[2];
00045   double devy = 2*(x[1]-vp[1]*x[2])/vp[3] - x[2];
00046   double devw = x[2];
00047 
00048   // compute (dual) pl\"ucker coordinates of backprojected line :
00049   vnl_double_4x4 omega = devx*outer_product(b,c) + devy*outer_product(c,a) + devw*outer_product(a,b);
00050   omega -= omega.transpose();
00051 
00052   // (un)dualize them :
00053   double omegad_[16]={
00054     0          , +omega(2,3), -omega(1,3), +omega(1,2),
00055     -omega(2,3), 0          , +omega(0,3), -omega(0,2),
00056     +omega(1,3), -omega(0,3), 0          , +omega(0,1),
00057     -omega(1,2), +omega(0,2), -omega(0,1), 0
00058   };
00059   vnl_double_4x4 omegad(omegad_);
00060 
00061   // backproject :
00062   vnl_double_4 X_ = omegad * vnl_double_4(p[0],p[1],p[2],p[3]);
00063 
00064   // normalize :
00065   double mag = X_.two_norm();
00066   if (mag == 0)
00067   {
00068     vcl_cerr << "mag=0, X_=" << X_ << vcl_endl;
00069     return false;
00070   }
00071 
00072   // done. normalize (making sure last component is >= 0).
00073   if (X_[3]<0)
00074     X_ *= (-1.0/mag);
00075   else
00076     X_ *= (1.0/mag);
00077   X[0] = X_[0];
00078   X[1] = X_[1];
00079   X[2] = X_[2];
00080   X[3] = X_[3];
00081 
00082   return true;
00083 }
00084 
00085 vnl_vector<double> vgui_projection_inspector::back_project(vnl_double_2 const &x,
00086                                                            vnl_double_4 const &p) const
00087 {
00088   vnl_double_3 x_(x[0],x[1],1.0);
00089   vnl_double_4 X_ = back_project(x_,p);
00090   return (X_/X_[3]).extract(3,0);
00091 }
00092 
00093 vnl_vector<double> vgui_projection_inspector::back_project(vnl_double_3 const &x,
00094                                                            vnl_double_4 const &p) const
00095 {
00096   vnl_double_4 X;
00097   if (!back_project(x.data_block(), p.data_block(), X.data_block()))
00098     X.fill(0);
00099   return X.as_ref();
00100 }
00101 
00102 vnl_vector<double> vgui_projection_inspector::back_project(double x,double y,
00103                                                            vnl_double_4 const &p) const
00104 {
00105   vnl_double_2 xy(x,y);
00106   return back_project(xy,p);
00107 }
00108 
00109 vnl_vector<double> vgui_projection_inspector::back_project(double x,double y,double z,
00110                                                            vnl_double_4 const &p) const
00111 {
00112   vnl_double_3 xyz(x,y,z);
00113   return back_project(xyz,p);
00114 }
00115 
00116 //------------------------------------------------------------------------------
00117 //:
00118 // returns true iff M has the form
00119 // \verbatim
00120 //  * * * *
00121 //  * * * *
00122 //  * * * *
00123 //  0 0 0 *
00124 // \endverbatim
00125 static bool is_affine(const vnl_double_4x4 &M)
00126 {
00127   return M(3,0)==0 && M(3,1)==0 && M(3,2)==0 && M(3,3)!=0;
00128 }
00129 
00130 void vgui_projection_inspector::inspect()
00131 {
00132   // get viewport :
00133   glGetIntegerv(GL_VIEWPORT,(GLint*)vp); // fixed
00134 
00135   // get projection and modelview matrices :
00136   {
00137     glGetDoublev(GL_PROJECTION_MATRIX,P.data_block());
00138     P.inplace_transpose();
00139 
00140     glGetDoublev(GL_MODELVIEW_MATRIX,M.data_block());
00141     M.inplace_transpose();
00142   }
00143 
00144   // compute total transformation T from world to clip coordinates :
00145   vnl_double_4x4 T = P*M;
00146 
00147   // if projection is scaling parallel to axes :
00148   // \verbatim
00149   // [ *     * ]
00150   // [   *   * ]
00151   // [     * * ]
00152   // [       * ]
00153   // \endverbatim
00154   if (is_affine(T) &&
00155       T(1,0)==0 && T(0,1)==0 &&
00156       T(2,0)==0 && T(0,2)==0 &&
00157       T(2,1)==0 && T(1,2)==0 &&
00158       T(3,3)!=0)
00159   {
00160     //
00161     diagonal_scale_3d = true;
00162 
00163     // backproject the corners of the plane {z=0} (in clip coordinates) to
00164     // the plane {z=0} in world coordinates :
00165     x1 = float(((-1)*T(3,3)-T(0,3))/T(0,0));
00166     y1 = float(((-1)*T(3,3)-T(1,3))/T(1,1));
00167 
00168     x2 = float(((+1)*T(3,3)-T(0,3))/T(0,0));
00169     y2 = float(((+1)*T(3,3)-T(1,3))/T(1,1));
00170 
00171     //
00172     s[0] = float(T(0,0)/T(3,3));
00173     s[1] = float(T(1,1)/T(3,3));
00174     s[2] = float(T(2,2)/T(3,3));
00175 
00176     t[0] = float(T(0,3)/T(3,3));
00177     t[1] = float(T(1,3)/T(3,3));
00178     t[2] = float(T(2,3)/T(3,3));
00179   }
00180 
00181   //
00182   else
00183   {
00184     diagonal_scale_3d = false;
00185 #ifdef DEBUG
00186      vcl_cerr << "T =\n" << T << '\n';
00187 #endif
00188   }
00189 }
00190 
00191 void vgui_projection_inspector::window_to_image_coordinates(int x,int y,
00192                                                             float &xi,float &yi) const
00193 {
00194   if (!diagonal_scale_3d)
00195   {
00196     vcl_cerr << "vgui_projection_inspector::window_to_image_coordinates() - ERROR: Need diagonal GL matrices\n";
00197     print(vcl_cerr);
00198     vcl_abort();
00199   }
00200 
00201   // size of viewport :
00202   int winx=vp[2];
00203   int winy=vp[3];
00204 
00205   // subtract offset of viewport :
00206   x -= vp[0];
00207   y -= vp[1];
00208 
00209   //
00210   xi = ((winx-x)*x1 + (     x)*x2)/winx;
00211   yi = ((winy-y)*y1 + (     y)*y2)/winy;
00212 #if 0
00213   yi = ((     y)*y1 + (winy-y)*y2)/winy;
00214 #endif // 0
00215 }
00216 
00217 // This method computes the viewport coordinates of the projection of the point (ix, iy, 0, 1).
00218 void vgui_projection_inspector::image_to_window_coordinates(float ix,float iy,float &wx,float &wy) const
00219 {
00220   assert(diagonal_scale_3d);
00221 
00222   // compute total transformation T from world to clip coordinates :
00223   vnl_double_4x4 T = P*M;
00224 
00225   // world point :
00226   vnl_double_4 img(ix,iy,0.0,1.0);
00227 
00228   // project :
00229   vnl_double_4 win = T*img;
00230 
00231   // normalized coordinates :
00232   float nx = float(win[0]/win[3]);
00233   float ny = float(win[1]/win[3]);
00234 
00235   // convert normalised coordinates to viewport coordinates :
00236   wx = vp[0] + (nx - -1)/2 * vp[2];
00237   wy = vp[1] + (ny - -1)/2 * vp[3];
00238 }
00239 
00240 //-----------------------------------------------------------------------------
00241 bool vgui_projection_inspector::compute_as_2d_affine(int width, int height,
00242                                                      float* offsetX, float* offsetY,
00243                                                      float* scaleX, float* scaleY)
00244 {
00245   if (!diagonal_scale_3d)
00246     return false;
00247 
00248   image_to_window_coordinates(0,0,*offsetX, *offsetY);
00249 
00250   float tmpX, tmpY;
00251   image_to_window_coordinates(width, height, tmpX, tmpY);
00252 
00253   *scaleX =  (tmpX - *offsetX)/width;
00254   *scaleY = -(tmpY - *offsetY)/height;
00255 
00256   return true;
00257 }
00258 
00259 
00260 //-----------------------------------------------------------------------------
00261 bool vgui_projection_inspector::image_viewport(float& a0, float& b0,
00262                                                float& a1, float& b1)
00263 {
00264   if (!diagonal_scale_3d)
00265     return false;
00266 
00267   a0 = x1;
00268   b0 = y1;
00269   a1 = x2;
00270   b1 = y2;
00271 
00272   return true;
00273 }