|
SUMO - Simulation of Urban MObility
|
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 } 00032 00033 EulerSpiralLookupTable::~EulerSpiralLookupTable() 00034 { 00035 } 00036 00037 //delta theta values for the table (tells you about the accuracy of the lookup) 00038 double EulerSpiralLookupTable::dt() 00039 { 00040 return _dt; 00041 } 00042 00043 double EulerSpiralLookupTable::theta(int N) 00044 { 00045 return _theta[N]; 00046 } 00047 00048 double EulerSpiralLookupTable::k0(double start_angle, double end_angle) 00049 { 00050 //assume it is already 0-2Pi 00051 double sangle, eangle; 00052 00053 if (start_angle>M_PI) sangle = start_angle-2*M_PI; 00054 else sangle = start_angle; 00055 00056 if (end_angle>M_PI) eangle = end_angle-2*M_PI; 00057 else eangle = end_angle; 00058 00059 //output bilinear interpolated data from the tables 00060 int ilow, ihigh, jlow, jhigh; 00061 00062 ilow = (int)floor((sangle+M_PI)/_dt); 00063 ihigh = (int)ceil((sangle+M_PI)/_dt); 00064 00065 jlow = (int)floor((eangle+M_PI)/_dt); 00066 jhigh = (int)ceil((eangle+M_PI)/_dt); 00067 00068 double slow = _theta[ilow]; 00069 double elow = _theta[jlow]; 00070 00071 double a = (sangle - slow)/_dt; 00072 double b = (eangle - elow)/_dt; 00073 00074 double k0 = (1-a)*(1-b)*ES_k0[ilow][jlow] + a*(1-b)*ES_k0[ihigh][jlow] + 00075 (1-a)*b*ES_k0[ilow][jhigh] + a*b*ES_k0[ihigh][jhigh]; 00076 00077 return k0; 00078 } 00079 00080 double EulerSpiralLookupTable::k1(double start_angle, double end_angle) 00081 { 00082 //output bilinear interpolated data from the tables 00083 00084 //assume it is already 0-2Pi 00085 double sangle, eangle; 00086 00087 if (start_angle>M_PI) sangle = start_angle-2*M_PI; 00088 else sangle = start_angle; 00089 00090 if (end_angle>M_PI) eangle = end_angle-2*M_PI; 00091 else eangle = end_angle; 00092 00093 //output bilinear interpolated data from the tables 00094 int ilow, ihigh, jlow, jhigh; 00095 00096 ilow = (int)floor((sangle+M_PI)/_dt); 00097 ihigh = (int)ceil((sangle+M_PI)/_dt); 00098 00099 jlow = (int)floor((eangle+M_PI)/_dt); 00100 jhigh = (int)ceil((eangle+M_PI)/_dt); 00101 00102 double slow = _theta[ilow]; 00103 double elow = _theta[jlow]; 00104 00105 double a = (sangle - slow)/_dt; 00106 double b = (eangle - elow)/_dt; 00107 00108 double k1 = (1-a)*(1-b)*ES_k1[ilow][jlow] + a*(1-b)*ES_k1[ihigh][jlow] + 00109 (1-a)*b*ES_k1[ilow][jhigh] + a*b*ES_k1[ihigh][jhigh]; 00110 00111 return k1; 00112 } 00113 00114 double EulerSpiralLookupTable::gamma(double start_angle, double end_angle) 00115 { 00116 //output bilinear interpolated data from the tables 00117 00118 //assume it is already 0-2Pi 00119 double sangle, eangle; 00120 00121 if (start_angle>M_PI) sangle = start_angle-2*M_PI; 00122 else sangle = start_angle; 00123 00124 if (end_angle>M_PI) eangle = end_angle-2*M_PI; 00125 else eangle = end_angle; 00126 00127 //output bilinear interpolated data from the tables 00128 int ilow, ihigh, jlow, jhigh; 00129 00130 ilow = (int)floor((sangle+M_PI)/_dt); 00131 ihigh = (int)ceil((sangle+M_PI)/_dt); 00132 00133 jlow = (int)floor((eangle+M_PI)/_dt); 00134 jhigh = (int)ceil((eangle+M_PI)/_dt); 00135 00136 double slow = _theta[ilow]; 00137 double elow = _theta[jlow]; 00138 00139 double a = (sangle - slow)/_dt; 00140 double b = (eangle - elow)/_dt; 00141 00142 double gamma = (1-a)*(1-b)*ES_gamma[ilow][jlow] + a*(1-b)*ES_gamma[ihigh][jlow] + 00143 (1-a)*b*ES_gamma[ilow][jhigh] + a*b*ES_gamma[ihigh][jhigh]; 00144 00145 return gamma; 00146 } 00147 00148 double EulerSpiralLookupTable::L(double start_angle, double end_angle) 00149 { 00150 //output bilinear interpolated data from the tables 00151 00152 //assume it is already 0-2Pi 00153 double sangle, eangle; 00154 00155 if (start_angle>M_PI) sangle = start_angle-2*M_PI; 00156 else sangle = start_angle; 00157 00158 if (end_angle>M_PI) eangle = end_angle-2*M_PI; 00159 else eangle = end_angle; 00160 00161 //output bilinear interpolated data from the tables 00162 int ilow, ihigh, jlow, jhigh; 00163 00164 ilow = (int)floor((sangle+M_PI)/_dt); 00165 ihigh = (int)ceil((sangle+M_PI)/_dt); 00166 00167 jlow = (int)floor((eangle+M_PI)/_dt); 00168 jhigh = (int)ceil((eangle+M_PI)/_dt); 00169 00170 double slow = _theta[ilow]; 00171 double elow = _theta[jlow]; 00172 00173 double a = (sangle - slow)/_dt; 00174 double b = (eangle - elow)/_dt; 00175 00176 double L = (1-a)*(1-b)*ES_L[ilow][jlow] + a*(1-b)*ES_L[ihigh][jlow] + 00177 (1-a)*b*ES_L[ilow][jhigh] + a*b*ES_L[ihigh][jhigh]; 00178 00179 return L; 00180 } 00181 00182 00183 // Computes the Euler spiral for the given params 00184 //if the global lookup table is available, it looks up the ES params first and then optimizes them 00185 //this should dramatically cut down in the time to optimize 00186 void EulerSpiral::compute_es_params () 00187 { 00188 //compute scaling distance 00189 double d = euc_distance(params.start_pt, params.end_pt); 00190 params.psi = angle0To2Pi(atan2(params.end_pt.y()-params.start_pt.y(),params.end_pt.x()-params.start_pt.x())); 00191 00192 //degeneracy check 00193 if (d<eError) 00194 return; 00195 00196 //first compute a biarc estimate 00197 _bi_arc_estimate.set_start_params(params.start_pt, params.start_angle); 00198 _bi_arc_estimate.set_end_params(params.end_pt, params.end_angle); 00199 _bi_arc_estimate.compute_biarc_params(); 00200 00201 //get the total turning angle::This is an important parameter because 00202 //it defines the one solution out of many possible solutions 00203 params.turningAngle = _bi_arc_estimate.params.K1*_bi_arc_estimate.params.L1 + 00204 _bi_arc_estimate.params.K2*_bi_arc_estimate.params.L2; 00205 00206 //From here on, normlize the parameters and use these to perform the optimization 00207 00208 double k0_init_est = _bi_arc_estimate.params.K1*d; 00209 double L_init_est = _bi_arc_estimate.params.L()/d; 00210 double dstep = 0.1; 00211 00212 //Alternately, we can get the initial values from the lookup table and perform 00213 //the optimization from there 00214 //double k0_init_est = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->k0(CCW(params.psi, params.start_angle), CCW(params.psi, params.end_angle)); 00215 //double L_init_est = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->L(CCW(params.psi, params.start_angle), CCW(params.psi, params.end_angle)); 00216 //double dstep = globalEulerSpiralLookupTable->get_globalEulerSpiralLookupTable()->dt()/4; 00217 00218 //then perform a simple gradient descent to find the real solution 00219 double error = compute_error(k0_init_est, L_init_est); 00220 double prev_error = error; 00221 00222 double k0 = k0_init_est; 00223 double L = L_init_est; 00224 00225 double e1, e2, e3, e4; 00226 00227 for (int i=0;i<MAX_NUM_ITERATIONS;i++) 00228 { 00229 if (error<eError) 00230 break; 00231 00232 e1 = compute_error(k0 + dstep, L); 00233 e2 = compute_error(k0 - dstep, L); 00234 e3 = compute_error(k0, L + dstep); 00235 if (L>dstep) e4 = compute_error(k0, L - dstep); 00236 00237 error = MIN2(MIN2(e1,e2),MIN2(e3,e4)); 00238 00239 if (error>prev_error) 00240 { 00241 dstep = dstep/2; 00242 continue; 00243 } 00244 00245 if (error==e1) k0 = k0 + dstep; 00246 else if (error==e2) k0 = k0 - dstep; 00247 else if (error==e3) L = L + dstep; 00248 else if (error==e4) L = L - dstep; 00249 00250 prev_error = error; 00251 } 00252 00253 //store the parameters 00254 params.K0 = k0/d; 00255 params.L = L*d; 00256 params.gamma = 2*(params.turningAngle - k0*L)/(L*L)/(d*d); 00257 params.K2 = (k0 + params.gamma*L)/d; 00258 params.error = error; 00259 } 00260 00261 //compute the extrinsic points 00262 void EulerSpiral::computeSpiral(std::vector<Point2D<double> > &spiral, double ds, int NPts) 00263 { 00264 if (ds==0 && NPts==0){ 00265 //use default values 00266 NPts = 100; 00267 ds = params.L/NPts; 00268 } 00269 00270 spiral.clear(); 00271 spiral.push_back(params.start_pt); 00272 00273 double s=ds; 00274 if (NPts == 0) 00275 NPts = (int) (params.L / ds); 00276 for (int i=1; i<NPts; i++,s+=ds){ 00277 Point2D<double> cur_pt = compute_end_pt(s); 00278 spiral.push_back(cur_pt); 00279 } 00280 spiral.push_back(params.end_pt); 00281 } 00282 00284 // Supporting functions 00286 00287 Point2D<double> EulerSpiral::compute_es_point(EulerSpiralParams& es_params, double arclength, bool bNormalized) 00288 { 00289 params = es_params; 00290 return compute_end_pt(params.K0, params.gamma, arclength, bNormalized); 00291 } 00292 00293 Point2D<double> EulerSpiral::compute_end_pt(double arclength, bool bNormalized) 00294 { 00295 return compute_end_pt(params.K0, params.gamma, arclength, bNormalized); 00296 } 00297 00298 Point2D<double> EulerSpiral::compute_end_pt(double k0, double gamma, double L, bool bNormalized) 00299 { 00300 Point2D<double> start_pt; 00301 Point2D<double> end_pt; 00302 00303 double theta; 00304 00305 if (bNormalized){ 00306 start_pt = Point2D<double>(0,0); 00307 theta = CCW(params.psi, params.start_angle); 00308 } 00309 else { 00310 start_pt = params.start_pt; 00311 theta = params.start_angle; 00312 } 00313 00314 if (L==0) 00315 return start_pt; 00316 00317 if (fabs(gamma)<eGamma) 00318 { 00319 if (fabs(k0)<eK) 00320 { 00321 //straight line 00322 end_pt.setX(start_pt.getX()+L*cos(theta)); 00323 end_pt.setY(start_pt.getY()+L*sin(theta)); 00324 } 00325 else 00326 { 00327 //circle 00328 double const_term = 1.0/k0; 00329 end_pt.setX(start_pt.getX()+const_term*(sin(k0*L+theta)-sin(theta))); 00330 end_pt.setY(start_pt.getY()-const_term*(cos(k0*L+theta)-cos(theta))); 00331 } 00332 return end_pt; 00333 } 00334 00335 double const1 = sqrt(M_PI*fabs(gamma)); 00336 double const2 = sqrt(M_PI/fabs(gamma)); 00337 00338 Point2D<double> fresnel1 = get_fresnel_integral((k0+gamma*L)/const1); 00339 Point2D<double> fresnel2 = get_fresnel_integral(k0/const1); 00340 00341 double C = (fresnel1.getX() - fresnel2.getX()); 00342 if(gamma<0) { 00343 C *= -1.; 00344 } 00345 double S = fresnel1.getY() - fresnel2.getY(); 00346 00347 double cos_term = cos(theta-((k0*k0)/(2.0*gamma))); 00348 double sin_term = sin(theta-((k0*k0)/(2.0*gamma))); 00349 00350 end_pt.setX(start_pt.getX() + const2*(C*cos_term - S*sin_term)); 00351 end_pt.setY(start_pt.getY() + const2*(C*sin_term + S*cos_term)); 00352 00353 return end_pt; 00354 } 00355 00356 inline double EulerSpiral::compute_error(double k0, double L) 00357 { 00358 //assumes normalized parameters 00359 00360 //compute the endpoint of the Euler spiral with the given intrinsic parameters 00361 double gamma = 2*(params.turningAngle - k0*L)/(L*L); 00362 Point2D<double> cur_end_pt = compute_end_pt(k0, gamma, L, true); 00363 00364 //the error is the distance between the current end point and the desired end point 00365 return euc_distance(Point2D<double>(1,0), cur_end_pt); 00366 } 00367 00368 // Fresnel Integral code from Numerical recipes in C 00369 // EPS is the relative error; 00370 // MAXIT is the maximum number of iterations allowed; 00371 // FPMIN is a number near the smallest representable floating-point number; 00372 // XMIN is the dividing line between using the series and continued fraction. 00373 00374 #define EPS 6.0e-8 00375 #define MAXIT 100 00376 #define FPMIN 1.0e-30 00377 #define XMIN 1.5 00378 00379 //Computes the Fresnel integrals S(x) and C(x) for all real x 00380 Point2D<double> EulerSpiral::get_fresnel_integral(double x) 00381 { 00382 bool odd; 00383 int k,n; 00384 double a,ax,fact,pix2,sign,sum,sumc,sums,term,test; 00385 00386 std::complex<double> b,cc,d,h,del,cs; 00387 Point2D<double> result; 00388 00389 ax=fabs(x); 00390 if (ax < sqrt(FPMIN)) 00391 { 00392 //Special case: avoid failure of convergence test because of undeflow. 00393 result.setY(0.0); 00394 result.setX(ax); 00395 } 00396 else { 00397 if (ax <= XMIN) 00398 { 00399 // Evaluate both series simultaneously. 00400 sum=sums=0.0; 00401 sumc=ax; 00402 sign=1.0; 00403 fact=(M_PI/2.0)*ax*ax; 00404 odd=true; 00405 term=ax; 00406 n=3; 00407 00408 for (k=1;k<=MAXIT;k++) 00409 { 00410 term *= fact/k; 00411 sum += sign*term/n; 00412 test=fabs(sum)*EPS; 00413 if (odd) 00414 { 00415 sign = -sign; 00416 sums=sum; 00417 sum=sumc; 00418 } 00419 else { 00420 sumc=sum; 00421 sum=sums; 00422 } 00423 00424 if (term < test) break; 00425 odd=!odd; 00426 n +=2; 00427 } 00428 00429 if (k > MAXIT) 00430 std::cout << "series failed in fresnel" << std::endl; 00431 00432 result.setY(sums); 00433 result.setX(sumc); 00434 } 00435 else { 00436 // Evaluate continued fraction by modified Lentz's method 00437 pix2=M_PI*ax*ax; 00438 b = std::complex<double>(1.0,-pix2); 00439 cc = std::complex<double>(1.0/FPMIN,0.0); 00440 d=h = std::complex<double>(1.0,0.0)/b; 00441 n = -1; 00442 00443 for (k=2;k<=MAXIT;k++) 00444 { 00445 n +=2; 00446 a = -n*(n+1); 00447 b= b+std::complex<double>(4.0,0.0); 00448 d=(std::complex<double>(1.0,0.0)/((a*d)+b)); 00449 00450 //Denominators cannot be zero 00451 cc=(b+(std::complex<double>(a,0.0)/cc)); 00452 00453 del=(cc*d); 00454 h=h*del; 00455 if ((fabs(del.real()-1.0)+fabs(del.imag())) < EPS) 00456 break; 00457 } 00458 if (k > MAXIT) 00459 std::cout << "cf failed in frenel" << std::endl; 00460 00461 h=std::complex<double>(ax,-ax)*h; 00462 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 ); 00463 00464 result.setX(cs.real()); 00465 result.setY(cs.imag()); 00466 } 00467 } 00468 00469 if (x<0){ //use antisymmetry 00470 result = -1*result; 00471 } 00472 00473 return result; 00474 } 00475