contrib/gel/vdgl/vdgl_interpolator_cubic.cxx
Go to the documentation of this file.
00001 // This is gel/vdgl/vdgl_interpolator_cubic.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 
00008 #include "vdgl_interpolator_cubic.h"
00009 #include <vcl_cassert.h>
00010 #include <vcl_cmath.h> // for sqrt()
00011 #include <vnl/vnl_matrix_fixed.h>
00012 #include <vnl/vnl_vector_fixed.h>
00013 #include <vnl/vnl_math.h>
00014 #include <vnl/vnl_numeric_traits.h>
00015 #include <vnl/vnl_inverse.h>
00016 #include <vgl/vgl_point_2d.h>
00017 #include <vsol/vsol_point_2d.h>
00018 #include <vdgl/vdgl_edgel.h>
00019 #include <vdgl/vdgl_edgel_chain.h>
00020 
00021 
00022 vdgl_interpolator_cubic::vdgl_interpolator_cubic(vdgl_edgel_chain_sptr chain)
00023   : vdgl_interpolator(chain)
00024 {
00025   recompute_all();
00026 }
00027 
00028 vdgl_interpolator_cubic::~vdgl_interpolator_cubic()
00029 {
00030 }
00031 
00032 
00033 double vdgl_interpolator_cubic::get_x(double index)
00034 {
00035   const int N = chain_->size();
00036   int a= int(index)-1;
00037   if (index == N-2) --a; // take previous interval if we are exactly on edgel N-2
00038   int b= a+1;
00039   int c= b+1;
00040   int d= c+1;
00041   assert(a >= 0 && d < N);
00042 
00043   vdgl_edgel ae(chain_->edgel(a));
00044   vdgl_edgel be(chain_->edgel(b));
00045   vdgl_edgel ce(chain_->edgel(c));
00046   vdgl_edgel de(chain_->edgel(d));
00047 
00048   vnl_vector_fixed<double,4> A;
00049   vnl_matrix_fixed<double,4,4> M;
00050 
00051   A(0) = ae.get_x(); A(1) = be.get_x();
00052   A(2) = ce.get_x(); A(3) = de.get_x();
00053 
00054   M(0,0)=  a*a*a; M(0,1)= a*a; M(0,2)= a; M(0,3)= 1;
00055   M(1,0)=  b*b*b; M(1,1)= b*b; M(1,2)= b; M(1,3)= 1;
00056   M(2,0)=  c*c*c; M(2,1)= c*c; M(2,2)= c; M(2,3)= 1;
00057   M(3,0)=  d*d*d; M(3,1)= d*d; M(3,2)= d; M(3,3)= 1;
00058 
00059   //  Solving A = M * P for P
00060   vnl_vector_fixed<double,4> P = vnl_inverse(M) * A;
00061 
00062   return P(0) * index * index * index  + P(1) *  index * index + P(2) * index  + P(3);
00063 }
00064 
00065 
00066 double vdgl_interpolator_cubic::get_y(double index)
00067 {
00068   const int N = chain_->size();
00069   int a= int(index)-1;
00070   if (index == N-2) --a; // take previous interval if we are exactly on edgel N-2
00071   int b= a+1;
00072   int c= b+1;
00073   int d= c+1;
00074   assert(a >= 0 && d < N);
00075 
00076   vdgl_edgel ae(chain_->edgel(a));
00077   vdgl_edgel be(chain_->edgel(b));
00078   vdgl_edgel ce(chain_->edgel(c));
00079   vdgl_edgel de(chain_->edgel(d));
00080 
00081   vnl_vector_fixed<double,4> A;
00082   vnl_matrix_fixed<double,4,4> M;
00083 
00084   A(0) = ae.get_y(); A(1) = be.get_y();
00085   A(2) = ce.get_y(); A(3) = de.get_y();
00086 
00087   M(0,0)=  a*a*a; M(0,1)= b*b; M(0,2)= a; M(0,3)= 1;
00088   M(1,0)=  b*b*b; M(1,1)= b*b; M(1,2)= b; M(1,3)= 1;
00089   M(2,0)=  c*c*c; M(2,1)= c*c; M(2,2)= c; M(2,3)= 1;
00090   M(3,0)=  d*d*d; M(3,1)= d*d; M(3,2)= d; M(3,3)= 1;
00091 
00092   //  Solving A = M * P for P
00093   vnl_vector_fixed<double,4> P = vnl_inverse(M) * A;
00094 
00095   return P(0) * index * index * index  + P(1) *  index * index + P(2) * index  + P(3);
00096 }
00097 
00098 double vdgl_interpolator_cubic::get_grad(double index)
00099 {
00100   const int N = chain_->size();
00101   int a= int(index)-1;
00102   if (index == N-2) --a; // take previous interval if we are on edgel N-2
00103   int b= a+1;
00104   int c= b+1;
00105   int d= c+1;
00106   assert(a >= 0 && d < N);
00107 
00108   vdgl_edgel ae(chain_->edgel(a));
00109   vdgl_edgel be(chain_->edgel(b));
00110   vdgl_edgel ce(chain_->edgel(c));
00111   vdgl_edgel de(chain_->edgel(d));
00112 
00113   vnl_vector_fixed<double,4> A;
00114   vnl_matrix_fixed<double,4,4> M;
00115 
00116   A(0) = ae.get_grad(); A(1) = be.get_grad();
00117   A(2) = ce.get_grad(); A(3) = de.get_grad();
00118 
00119   M(0,0)=  a*a*a; M(0,1)= a*a; M(0,2)= a; M(0,3)= 1;
00120   M(1,0)=  b*b*b; M(1,1)= b*b; M(1,2)= b; M(1,3)= 1;
00121   M(2,0)=  c*c*c; M(2,1)= c*c; M(2,2)= c; M(2,3)= 1;
00122   M(3,0)=  d*d*d; M(3,1)= d*d; M(3,2)= d; M(3,3)= 1;
00123 
00124   //  Solving A = M * P for P
00125   vnl_vector_fixed<double,4> P = vnl_inverse(M) * A;
00126 
00127   return P(0) * index * index * index  + P(1) *  index * index + P(2) * index + P(3);
00128 }
00129 
00130 double vdgl_interpolator_cubic::get_theta(double index)
00131 {
00132   const int N = chain_->size();
00133   int a= int(index)-1;
00134   if (index == N-2) --a; // take previous interval if we are on edgel N-2
00135   int b= a+1;
00136   int c= b+1;
00137   int d= c+1;
00138   assert(a >= 0 && d < N);
00139 
00140   vdgl_edgel ae(chain_->edgel(a));
00141   vdgl_edgel be(chain_->edgel(b));
00142   vdgl_edgel ce(chain_->edgel(c));
00143   vdgl_edgel de(chain_->edgel(d));
00144 
00145   vnl_vector_fixed<double,4> A;
00146   vnl_matrix_fixed<double,4,4> M;
00147 
00148   A(0) = ae.get_theta(); A(1) = be.get_theta();
00149   A(2) = ce.get_theta(); A(3) = de.get_theta();
00150 
00151   M(0,0)=  a*a*a; M(0,1)= a*a; M(0,2)= a; M(0,3)= 1;
00152   M(1,0)=  b*b*b; M(1,1)= b*b; M(1,2)= b; M(1,3)= 1;
00153   M(2,0)=  c*c*c; M(2,1)= c*c; M(2,2)= c; M(2,3)= 1;
00154   M(3,0)=  d*d*d; M(3,1)= d*d; M(3,2)= d; M(3,3)= 1;
00155 
00156   //  Solving A = M * P for P
00157   vnl_vector_fixed<double,4> P = vnl_inverse(M) * A;
00158 
00159   return P(0) * index * index * index  + P(1) *  index * index + P(2) * index + P(3);
00160 }
00161 
00162 //: Compute the angle using four adjacent edgels.
00163 //  This is done by fitting a 3rd degree spline through these 4 points,
00164 //  and calculating the derived point at index.
00165 double vdgl_interpolator_cubic::get_tangent_angle(double index)
00166 {
00167   const int N = chain_->size();
00168   int a= int(index)-1;
00169   if (index == N-2) --a; // take previous interval if we are on edgel N-2
00170   int b= a+1;
00171   int c= b+1;
00172   int d= c+1;
00173   assert(a >= 0 && d < N);
00174 
00175   vdgl_edgel ae = chain_->edgel(a),
00176              be = chain_->edgel(b),
00177              ce = chain_->edgel(c),
00178              de = chain_->edgel(d);
00179 
00180   double xa = ae.x(), ya = ae.y(),
00181          xb = be.x(), yb = be.y(),
00182          xc = ce.x(), yc = ce.y(),
00183          xd = de.x(), yd = de.y(),
00184 
00185          x2 = xd-4.5*xc+9*xb-5.5*xa,
00186          y2 = yd-4.5*yc+9*yb-5.5*ya,
00187          x1 = -x2-0.5*xc+4*xb-3.5*xa,
00188          y1 = -y2-0.5*yc+4*yb-3.5*ya,
00189          x0 = x2/2+0.75*xc-3*xb+2.25*xa,
00190          y0 = y2/2+0.75*yc-3*yb+2.25*ya,
00191 
00192          xp = x0*index*index + x1*index + x2/3, // derived point at index
00193          yp = y0*index*index + y1*index + y2/3;
00194 
00195   return vnl_math::deg_per_rad*vcl_atan2(yp, xp);
00196 }
00197 
00198 
00199 double vdgl_interpolator_cubic::get_curvature(double index)
00200 {
00201   const int N = chain_->size();
00202   int a= int(index)-1;
00203   if (index == N-2) --a; // take previous interval if we are on edgel N-2
00204   int b= a+1;
00205   int c= b+1;
00206   int d= c+1;
00207   assert(a >= 0 && d < N);
00208 
00209   vdgl_edgel ae(chain_->edgel(a));
00210   vdgl_edgel be(chain_->edgel(b));
00211   vdgl_edgel ce(chain_->edgel(c));
00212   vdgl_edgel de(chain_->edgel(d));
00213 
00214   vnl_vector_fixed<double,4> A;
00215   vnl_matrix_fixed<double,4,4> M;
00216 
00217   A(0) = ae.get_x(); A(1) = be.get_x();
00218   A(2) = ce.get_x(); A(3) = de.get_x();
00219 
00220   M(0,0)=  a*a*a; M(0,1)= a*a; M(0,2)= a; M(0,3)= 1;
00221   M(1,0)=  b*b*b; M(1,1)= b*b; M(1,2)= b; M(1,3)= 1;
00222   M(2,0)=  c*c*c; M(2,1)= c*c; M(2,2)= c; M(2,3)= 1;
00223   M(3,0)=  d*d*d; M(3,1)= d*d; M(3,2)= d; M(3,3)= 1;
00224 
00225   //  Solving A = M * P for P
00226   vnl_vector_fixed<double,4> P = vnl_inverse(M) * A;
00227 
00228   double x_new= P(0) * index * index * index  + P(1) *  index * index + P(2) * index  + P(3);
00229 
00230   double t2 = 3 * P(0) * x_new * x_new + 2 * P(1) * x_new + P(2);
00231   double t3 = 1 + t2 * t2;
00232   return t2/t3/vcl_sqrt(t3);
00233 }
00234 
00235 
00236 double vdgl_interpolator_cubic::get_length()
00237 {
00238   // length is cached (because it's expensive to compute)
00239   if ( older(chain_.ptr()) )
00240     recompute_all();
00241 
00242   return lengthcache_;
00243 }
00244 
00245 
00246 double vdgl_interpolator_cubic::get_min_x()
00247 {
00248   if ( older(chain_.ptr()) )
00249     recompute_all();
00250 
00251   return minxcache_;
00252 }
00253 
00254 
00255 double vdgl_interpolator_cubic::get_max_x()
00256 {
00257   if ( older(chain_.ptr()) )
00258     recompute_all();
00259 
00260   return maxxcache_;
00261 }
00262 
00263 
00264 double vdgl_interpolator_cubic::get_min_y()
00265 {
00266   if ( older(chain_.ptr()) )
00267     recompute_all();
00268 
00269   return minycache_;
00270 }
00271 
00272 
00273 double vdgl_interpolator_cubic::get_max_y()
00274 {
00275   if ( older(chain_.ptr()) )
00276     recompute_all();
00277 
00278   return maxycache_;
00279 }
00280 
00281 // cache maintenance
00282 
00283 void vdgl_interpolator_cubic::recompute_all()
00284 {
00285   recompute_length();
00286   recompute_bbox();
00287 
00288   touch();
00289 }
00290 
00291 void vdgl_interpolator_cubic::recompute_length()
00292 {
00293   const int N = chain_->size();
00294   lengthcache_= 0;
00295   if (N <= 1) return;
00296 
00297   for (int i=N-1, j=i-1; j>=0; --i,--j)
00298   {
00299     vgl_point_2d<double> p1= chain_->edgel(j).get_pt();
00300     vgl_point_2d<double> p2= chain_->edgel(i).get_pt();
00301 
00302     lengthcache_ += length(p2-p1);
00303   }
00304 }
00305 
00306 void vdgl_interpolator_cubic::recompute_bbox()
00307 {
00308   const int N = chain_->size();
00309   if (N == 0) return;
00310   minxcache_= chain_->edgel(0).get_x();
00311   maxxcache_= chain_->edgel(0).get_x();
00312   minycache_= chain_->edgel(0).get_y();
00313   maxycache_= chain_->edgel(0).get_y();
00314 
00315   for (int i=1; i< N; i++)
00316   {
00317     if (chain_->edgel(i).get_x()< minxcache_) minxcache_= chain_->edgel(i).get_x();
00318     if (chain_->edgel(i).get_x()> maxxcache_) maxxcache_= chain_->edgel(i).get_x();
00319     if (chain_->edgel(i).get_y()< minycache_) minycache_= chain_->edgel(i).get_y();
00320     if (chain_->edgel(i).get_y()> maxycache_) maxycache_= chain_->edgel(i).get_y();
00321   }
00322 }
00323 
00324 vsol_point_2d_sptr vdgl_interpolator_cubic::
00325 closest_point_on_curve ( vsol_point_2d_sptr p )
00326 {
00327   unsigned int n = chain_->size();
00328   if (n==0)
00329     return 0;
00330   double px = p->x(), py = p->y(), dmin = vnl_numeric_traits<double>::maxval;
00331   int imin = 0;
00332   for (unsigned int i = 0; i<n; i++)
00333   {
00334     double x = (*chain_)[i].x(), y = (*chain_)[i].y();
00335     double d = vcl_sqrt((x-px)*(x-px) + (y-py)*(y-py));
00336     if (d<dmin)
00337     {
00338       dmin = d;
00339       imin = i;
00340     }
00341   }
00342   //This is approximate.  Should really differentiate the cubic expression
00343   //on the interval around imin.
00344   px = this->get_x(imin);
00345   py = this->get_y(imin);
00346   return new vsol_point_2d(px, py);
00347 }