euler.cpp

Go to the documentation of this file.
00001 /*
00002 #include "vcl_iostream.h"
00003 #include <vcl_fstream.h>
00004 #include "vcl_complex.h"
00005 #include "vcl_cmath.h"
00006 */
00007 
00008 #include "points.h"
00009 #include "BiArc.h"
00010 #include "euler.h" 
00011 #include <utils/common/StdDefs.h>
00012 #include <complex>
00013 #include <iostream>
00014 
00015 #define eGamma    1e-8
00016 
00017 //global lookup table: This is only instantiated when required and cleaned upon program exit
00018 static EulerSpiralLookupTable *globalEulerSpiralLookupTable;
00019 
00020 //ensures single instantiation of the global variable
00021 EulerSpiralLookupTable* EulerSpiralLookupTable::get_globalEulerSpiralLookupTable()
00022 {
00023   if (! globalEulerSpiralLookupTable)
00024     globalEulerSpiralLookupTable = new EulerSpiralLookupTable();
00025 
00026   return globalEulerSpiralLookupTable;
00027 }
00028 
00029 EulerSpiralLookupTable::EulerSpiralLookupTable()
00030 {
00031   int i,j;
00032 
00033   //read in the tables from data files if available
00034 /*
00035   vcl_ifstream fp_k0_in("ES_k0.dat", vcl_ios::in | vcl_ios::binary);
00036   vcl_ifstream fp_gamma_in("ES_gamma.dat", vcl_ios::in | vcl_ios::binary);
00037   vcl_ifstream fp_L_in("ES_L.dat", vcl_ios::in | vcl_ios::binary);
00038 
00039   //This number is the first entry in the file
00040   fp_k0_in.read ((char*)&NN, sizeof (NN));
00041   fp_gamma_in.read ((char*)&NN, sizeof (NN));
00042   fp_L_in.read ((char*)&NN, sizeof (NN));
00043 
00044   //COUT << "Number of theta samples " << NN <<endl;
00045   
00046   //initialize the tables to this size
00047   ES_k0 = new double*[NN];
00048   for (i=0; i<NN; i++)
00049     ES_k0[i] = new double [NN];
00050 
00051   ES_k1 = new double*[NN];
00052   for (i=0; i<NN; i++)
00053     ES_k1[i] = new double [NN];
00054 
00055   ES_gamma = new double*[NN];
00056   for (i=0; i<NN; i++)
00057     ES_gamma[i] = new double [NN];
00058 
00059   ES_L = new double*[NN];
00060   for (i=0; i<NN; i++)
00061     ES_L[i] = new double [NN];
00062 
00063   //compute the dtt
00064   _dt = 2*M_PI/NN;
00065 
00066   //create and fill in the theta array
00067   _theta = new double [NN];
00068   double t1 = -M_PI;
00069   for (i=0; i<NN; i++, t1+=_dt)
00070     _theta[i] = t1;
00071 
00072   //now read the files to fill in the lookup tables
00073   for (i=0; i<NN; i++){
00074     for (j=0; j<NN; j++){
00075       
00076       double k0, k1, gamma, L;
00077 
00078       fp_k0_in.read ((char*)&k0, sizeof (k0));
00079       fp_gamma_in.read ((char*)&gamma, sizeof (gamma));
00080       fp_L_in.read ((char*)&L, sizeof (L));
00081       k1 = k0 + gamma*L;
00082 
00083       //store these in the array
00084       ES_k0[i][j] = k0;
00085       ES_k1[i][j] = k1;
00086       ES_gamma[i][j] = gamma;
00087       ES_L[i][j] = L;
00088     }
00089   }
00090 
00091   fp_k0_in.close();
00092   fp_gamma_in.close();
00093   fp_L_in.close();
00094   */
00095 }
00096 
00097 EulerSpiralLookupTable::~EulerSpiralLookupTable()
00098 {
00099   int i;
00100 /*
00101   //delete the arrays
00102   if (ES_k0) {
00103     for (i=0; i<=NN; i++)
00104       delete []ES_k0[i];
00105     delete []ES_k0;
00106   }
00107   ES_k0 = NULL;
00108   if (ES_k1) {
00109     for (i=0; i<=NN; i++)
00110       delete []ES_k1[i];
00111     delete []ES_k1;
00112   }
00113   ES_k1 = NULL;
00114   if (ES_gamma) {
00115     for (i=0; i<=NN; i++)
00116       delete []ES_gamma[i];
00117     delete []ES_gamma;
00118   }
00119   ES_gamma = NULL;
00120   if (ES_L) {
00121     for (i=0; i<=NN; i++)
00122       delete []ES_L[i];
00123     delete []ES_L;
00124   }
00125   ES_L = NULL;
00126 
00127   delete []_theta;
00128   */
00129 }
00130 
00131 //delta theta values for the table (tells you about the accuracy of the lookup)
00132 double EulerSpiralLookupTable::dt()
00133 {
00134   return _dt;
00135 }
00136 
00137 double EulerSpiralLookupTable::theta(int N)
00138 {
00139   return _theta[N];
00140 }
00141 
00142 double EulerSpiralLookupTable::k0(double start_angle, double end_angle)
00143 {
00144   //assume it is already 0-2Pi
00145   double sangle, eangle;
00146 
00147   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00148   else                  sangle = start_angle;
00149 
00150   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00151   else                eangle = end_angle;
00152   
00153   //output bilinear interpolated data from the tables 
00154   int ilow, ihigh, jlow, jhigh;
00155 
00156   ilow = (int)floor((sangle+M_PI)/_dt);
00157   ihigh = (int)ceil((sangle+M_PI)/_dt);
00158 
00159   jlow = (int)floor((eangle+M_PI)/_dt);
00160   jhigh = (int)ceil((eangle+M_PI)/_dt);
00161 
00162   double slow = _theta[ilow];
00163   double shigh = _theta[ihigh];
00164   double elow = _theta[jlow];
00165   double ehigh = _theta[jhigh];
00166 
00167   double a = (sangle - slow)/_dt;
00168   double b = (eangle - elow)/_dt;
00169 
00170   double k0 = (1-a)*(1-b)*ES_k0[ilow][jlow] + a*(1-b)*ES_k0[ihigh][jlow] +
00171               (1-a)*b*ES_k0[ilow][jhigh] + a*b*ES_k0[ihigh][jhigh];
00172 
00173   return k0;
00174 }
00175 
00176 double EulerSpiralLookupTable::k1(double start_angle, double end_angle)
00177 {
00178   //output bilinear interpolated data from the tables
00179 
00180   //assume it is already 0-2Pi
00181   double sangle, eangle;
00182 
00183   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00184   else                  sangle = start_angle;
00185 
00186   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00187   else                eangle = end_angle;
00188   
00189   //output bilinear interpolated data from the tables 
00190   int ilow, ihigh, jlow, jhigh;
00191 
00192   ilow = (int)floor((sangle+M_PI)/_dt);
00193   ihigh = (int)ceil((sangle+M_PI)/_dt);
00194 
00195   jlow = (int)floor((eangle+M_PI)/_dt);
00196   jhigh = (int)ceil((eangle+M_PI)/_dt);
00197 
00198   double slow = _theta[ilow];
00199   double shigh = _theta[ihigh];
00200   double elow = _theta[jlow];
00201   double ehigh = _theta[jhigh];
00202 
00203   double a = (sangle - slow)/_dt;
00204   double b = (eangle - elow)/_dt;
00205 
00206   double k1 = (1-a)*(1-b)*ES_k1[ilow][jlow] + a*(1-b)*ES_k1[ihigh][jlow] +
00207               (1-a)*b*ES_k1[ilow][jhigh] + a*b*ES_k1[ihigh][jhigh];
00208 
00209   return k1;
00210 }
00211 
00212 double EulerSpiralLookupTable::gamma(double start_angle, double end_angle)
00213 {
00214   //output bilinear interpolated data from the tables
00215   
00216   //assume it is already 0-2Pi
00217   double sangle, eangle;
00218 
00219   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00220   else                  sangle = start_angle;
00221 
00222   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00223   else                eangle = end_angle;
00224   
00225   //output bilinear interpolated data from the tables 
00226   int ilow, ihigh, jlow, jhigh;
00227 
00228   ilow = (int)floor((sangle+M_PI)/_dt);
00229   ihigh = (int)ceil((sangle+M_PI)/_dt);
00230 
00231   jlow = (int)floor((eangle+M_PI)/_dt);
00232   jhigh = (int)ceil((eangle+M_PI)/_dt);
00233 
00234   double slow = _theta[ilow];
00235   double shigh = _theta[ihigh];
00236   double elow = _theta[jlow];
00237   double ehigh = _theta[jhigh];
00238 
00239   double a = (sangle - slow)/_dt;
00240   double b = (eangle - elow)/_dt;
00241 
00242   double gamma = (1-a)*(1-b)*ES_gamma[ilow][jlow] + a*(1-b)*ES_gamma[ihigh][jlow] +
00243               (1-a)*b*ES_gamma[ilow][jhigh] + a*b*ES_gamma[ihigh][jhigh];
00244 
00245   return gamma;
00246 }
00247 
00248 double EulerSpiralLookupTable::L(double start_angle, double end_angle)
00249 {
00250   //output bilinear interpolated data from the tables
00251   
00252   //assume it is already 0-2Pi
00253   double sangle, eangle;
00254 
00255   if (start_angle>M_PI) sangle = start_angle-2*M_PI;
00256   else                  sangle = start_angle;
00257 
00258   if (end_angle>M_PI) eangle = end_angle-2*M_PI;
00259   else                eangle = end_angle;
00260   
00261   //output bilinear interpolated data from the tables 
00262   int ilow, ihigh, jlow, jhigh;
00263 
00264   ilow = (int)floor((sangle+M_PI)/_dt);
00265   ihigh = (int)ceil((sangle+M_PI)/_dt);
00266 
00267   jlow = (int)floor((eangle+M_PI)/_dt);
00268   jhigh = (int)ceil((eangle+M_PI)/_dt);
00269 
00270   double slow = _theta[ilow];
00271   double shigh = _theta[ihigh];
00272   double elow = _theta[jlow];
00273   double ehigh = _theta[jhigh];
00274 
00275   double a = (sangle - slow)/_dt;
00276   double b = (eangle - elow)/_dt;
00277 
00278   double L = (1-a)*(1-b)*ES_L[ilow][jlow] + a*(1-b)*ES_L[ihigh][jlow] +
00279               (1-a)*b*ES_L[ilow][jhigh] + a*b*ES_L[ihigh][jhigh];
00280 
00281   return L;
00282 }
00283 
00284 
00285 // Computes the Euler spiral for the given params
00286 //if the global lookup table is available, it looks up the ES params first and then optimizes them
00287 //this should dramatically cut down in the time to optimize
00288 void EulerSpiral::compute_es_params ()
00289 {
00290   //compute scaling distance
00291   double d = euc_distance(params.start_pt, params.end_pt);
00292   params.psi = angle0To2Pi(atan2(params.end_pt.y()-params.start_pt.y(),params.end_pt.x()-params.start_pt.x()));
00293 
00294   //degeneracy check
00295   if (d<eError)
00296     return; 
00297 
00298   //first compute a biarc estimate
00299   _bi_arc_estimate.set_start_params(params.start_pt, params.start_angle);
00300   _bi_arc_estimate.set_end_params(params.end_pt, params.end_angle);
00301   _bi_arc_estimate.compute_biarc_params();
00302 
00303   //get the total turning angle::This is an important parameter because
00304   //it defines the one solution out of many possible solutions
00305   params.turningAngle = _bi_arc_estimate.params.K1*_bi_arc_estimate.params.L1 +
00306                  _bi_arc_estimate.params.K2*_bi_arc_estimate.params.L2;
00307 
00308   //From here on, normlize the parameters and use these to perform the optimization
00309 
00310   double k0_init_est = _bi_arc_estimate.params.K1*d;
00311   double L_init_est = _bi_arc_estimate.params.L()/d;
00312   double dstep = 0.1;
00313 
00314   //Alternately, we can get the initial values from the lookup table and perform 
00315   //the optimization from there
00316   //double k0_init_est = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->k0(CCW(params.psi, params.start_angle), CCW(params.psi, params.end_angle));
00317   //double L_init_est = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->L(CCW(params.psi, params.start_angle), CCW(params.psi, params.end_angle));
00318   //double dstep = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->dt()/4;
00319 
00320   //then perform a simple gradient descent to find the real solution
00321   double error = compute_error(k0_init_est, L_init_est);
00322   double prev_error = error;
00323   
00324   double k0 = k0_init_est;
00325   double L = L_init_est;
00326 
00327   double e1, e2, e3, e4;
00328 
00329   for (int i=0;i<MAX_NUM_ITERATIONS;i++)
00330   {
00331      if (error<eError)
00332         break;
00333     
00334      e1 = compute_error(k0 + dstep, L);
00335      e2 = compute_error(k0 - dstep, L);
00336      e3 = compute_error(k0, L + dstep);
00337      if (L>dstep)  e4 = compute_error(k0, L - dstep);
00338     
00339      error = MIN2(MIN2(e1,e2),MIN2(e3,e4));
00340     
00341      if (error>prev_error)
00342      {
00343        dstep = dstep/2;
00344        continue;
00345      }
00346     
00347      if    (error==e1)  k0 = k0 + dstep;
00348      else if (error==e2) k0 = k0 - dstep;
00349      else if (error==e3) L = L + dstep;
00350      else if (error==e4) L = L - dstep;
00351       
00352      prev_error = error;
00353   }
00354 
00355   //store the parameters
00356   params.K0 = k0/d;
00357   params.L = L*d;
00358   params.gamma = 2*(params.turningAngle - k0*L)/(L*L)/(d*d);
00359   params.K2 = (k0 + params.gamma*L)/d;
00360   params.error = error;
00361 }
00362 
00363 //compute the extrinsic points
00364 void EulerSpiral::computeSpiral(std::vector<Point2D<double> > &spiral, double ds, int NPts)
00365 {
00366   if (ds==0 && NPts==0){
00367     //use default values
00368     NPts = 100;
00369     ds = params.L/NPts;
00370   }
00371 
00372   spiral.clear();
00373   spiral.push_back(params.start_pt);
00374 
00375   double s=ds;
00376   if (NPts == 0) 
00377     NPts = (int) (params.L / ds);
00378   for (int i=1; i<NPts; i++,s+=ds){
00379     Point2D<double> cur_pt = compute_end_pt(s);
00380     spiral.push_back(cur_pt);
00381   }
00382   spiral.push_back(params.end_pt);
00383 }
00384 
00386 // Supporting functions
00388 
00389 Point2D<double> EulerSpiral::compute_es_point(EulerSpiralParams& es_params, double arclength, bool bNormalized)
00390 {
00391   params = es_params;
00392   return compute_end_pt(params.K0, params.gamma, arclength, bNormalized);
00393 }
00394 
00395 Point2D<double> EulerSpiral::compute_end_pt(double arclength, bool bNormalized)
00396 {
00397   return compute_end_pt(params.K0, params.gamma, arclength, bNormalized);
00398 }
00399 
00400 Point2D<double> EulerSpiral::compute_end_pt(double k0, double gamma, double L, bool bNormalized)
00401 {
00402   Point2D<double> start_pt;
00403   Point2D<double> end_pt;
00404 
00405   double theta;
00406 
00407   if (bNormalized){
00408     start_pt = Point2D<double>(0,0);
00409     theta = CCW(params.psi, params.start_angle);
00410   }
00411   else {
00412     start_pt = params.start_pt;
00413     theta = params.start_angle;
00414   }
00415 
00416   if (L==0)
00417     return start_pt;
00418 
00419   if (fabs(gamma)<eGamma) 
00420   { 
00421     if (fabs(k0)<eK)
00422     {
00423       //straight line
00424       end_pt.setX(start_pt.getX()+L*cos(theta));
00425       end_pt.setY(start_pt.getY()+L*sin(theta));
00426     }
00427     else 
00428     { 
00429       //circle
00430       double const_term = 1.0/k0;
00431       end_pt.setX(start_pt.getX()+const_term*(sin(k0*L+theta)-sin(theta)));
00432       end_pt.setY(start_pt.getY()-const_term*(cos(k0*L+theta)-cos(theta))); 
00433     }
00434     return end_pt;
00435   }
00436 
00437   double const1 = sqrt(M_PI*fabs(gamma));
00438   double const2 = sqrt(M_PI/fabs(gamma));
00439 
00440   Point2D<double> fresnel1 = get_fresnel_integral((k0+gamma*L)/const1);
00441   Point2D<double> fresnel2 = get_fresnel_integral(k0/const1);
00442 
00443   double C = (fresnel1.getX() - fresnel2.getX());
00444   if(gamma<0) {
00445       C *= -1.;
00446   }
00447   double S = fresnel1.getY() - fresnel2.getY();
00448 
00449   double cos_term = cos(theta-((k0*k0)/(2.0*gamma)));
00450   double sin_term = sin(theta-((k0*k0)/(2.0*gamma))); 
00451   
00452   end_pt.setX(start_pt.getX() + const2*(C*cos_term - S*sin_term));
00453   end_pt.setY(start_pt.getY() + const2*(C*sin_term + S*cos_term));
00454 
00455   return end_pt;
00456 }
00457 
00458 inline double   EulerSpiral::compute_error(double k0, double L)
00459 {
00460   //assumes normalized parameters
00461 
00462   //compute the endpoint of the Euler spiral with the given intrinsic parameters
00463   double gamma = 2*(params.turningAngle - k0*L)/(L*L);
00464   Point2D<double> cur_end_pt = compute_end_pt(k0, gamma, L, true);
00465 
00466   //the error is the distance between the current end point and the desired end point
00467   return euc_distance(Point2D<double>(1,0), cur_end_pt);
00468 }
00469 
00470 // Fresnel Integral code from Numerical recipes in C
00471 // EPS   is the relative error; 
00472 // MAXIT is the maximum number of iterations allowed; 
00473 // FPMIN is a number near the smallest representable floating-point number;
00474 // XMIN  is the dividing line between using the series and continued fraction.
00475 
00476 #define EPS    6.0e-8
00477 #define MAXIT  100
00478 #define FPMIN  1.0e-30
00479 #define XMIN   1.5
00480 
00481 //Computes the Fresnel integrals S(x) and C(x) for all real x
00482 Point2D<double> EulerSpiral::get_fresnel_integral(double x)
00483 {
00484   bool odd;
00485   int k,n;
00486   double a,ax,fact,pix2,sign,sum,sumc,sums,term,test;
00487 
00488   std::complex<double> b,cc,d,h,del,cs;
00489   Point2D<double> result;
00490 
00491   ax=fabs(x);
00492   if (ax < sqrt(FPMIN)) 
00493   { 
00494     //Special case: avoid failure of convergence test because of undeflow.
00495     result.setY(0.0);
00496     result.setX(ax);
00497   }
00498   else {
00499     if (ax <= XMIN)
00500     {
00501       // Evaluate both series simultaneously.
00502       sum=sums=0.0;
00503       sumc=ax;
00504       sign=1.0;
00505       fact=(M_PI/2.0)*ax*ax;
00506       odd=true;
00507       term=ax;
00508       n=3;
00509 
00510       for (k=1;k<=MAXIT;k++)
00511       { 
00512         term *= fact/k;
00513         sum  += sign*term/n;
00514         test=fabs(sum)*EPS;
00515         if (odd) 
00516         {
00517           sign = -sign;
00518           sums=sum;
00519           sum=sumc;
00520         }
00521         else {
00522           sumc=sum;
00523           sum=sums;
00524         }
00525 
00526         if (term < test) break;
00527         odd=!odd;
00528         n +=2;
00529       }
00530 
00531       if (k > MAXIT) 
00532         std::cout << "series failed in fresnel" << std::endl;
00533 
00534       result.setY(sums); 
00535       result.setX(sumc);
00536     }
00537     else {
00538       // Evaluate continued fraction by modified Lentz's method
00539       pix2=M_PI*ax*ax;
00540       b   = std::complex<double>(1.0,-pix2);
00541       cc  = std::complex<double>(1.0/FPMIN,0.0);
00542       d=h = std::complex<double>(1.0,0.0)/b;
00543       n = -1;
00544 
00545       for (k=2;k<=MAXIT;k++) 
00546       {
00547         n +=2;
00548         a = -n*(n+1);
00549         b= b+std::complex<double>(4.0,0.0);
00550         d=(std::complex<double>(1.0,0.0)/((a*d)+b));
00551 
00552         //Denominators cannot be zero
00553         cc=(b+(std::complex<double>(a,0.0)/cc));
00554 
00555         del=(cc*d);
00556         h=h*del;
00557         if ((fabs(del.real()-1.0)+fabs(del.imag())) < EPS)
00558           break;
00559       }
00560       if (k > MAXIT) 
00561         std::cout << "cf failed in frenel" << std::endl;
00562 
00563       h=std::complex<double>(ax,-ax)*h;
00564       cs=std::complex<double>(0.5,0.5)*(std::complex<double>(1.0,0.0) - std::complex<double>(cos(0.5*pix2),sin(0.5*pix2))*h );
00565 
00566       result.setX(cs.real());
00567       result.setY(cs.imag());
00568     }
00569   }
00570 
00571   if (x<0){ //use antisymmetry
00572     result = -1*result;  
00573   }
00574 
00575   return result;
00576 }
00577 
00578 // write Euler Spiral parameters to file
00579 /*
00580 void EulerSpiral::write_es_info_to_file(vcl_ofstream & fp){
00581 /*
00582 Point2D<double> start_pt;
00583   Point2D<double> end_pt;
00584 
00585   double start_angle;
00586   double end_angle;
00587   double K0;
00588   double K2;
00589   double gamma;
00590   double L;
00591   double turningAngle;
00592   double error;
00593   double psi;
00594 /
00595 
00596   fp << "start_pt: = ( " << (params.start_pt).getX() << 
00597     " , " << (params.start_pt).getY()<< " )" << vcl_endl;
00598   fp << "start_angle = " << params.start_angle << vcl_endl;
00599   fp << "end_pt = ( " << (params.end_pt).getX() << 
00600     " , " << (params.end_pt).getY()<< " )" << vcl_endl;
00601   fp << "end_angle = " << params.end_angle << vcl_endl;
00602   fp << "K0 = " << params.K0 << vcl_endl;
00603   fp << "K2 = " << params.K2 << vcl_endl;
00604   fp << "gamma = " << params.gamma << vcl_endl;
00605   fp << "L = " << params.L << vcl_endl;
00606   fp << "turningAngle = " << params.turningAngle << vcl_endl;
00607   fp << "error = " << params.error << vcl_endl;
00608   fp << "psi = " << params.psi << vcl_endl;
00609 }
00610 */

Generated on Wed May 5 00:06:28 2010 for Sumo - Simulation of Urban MObility by  doxygen 1.5.6