ORTS

Distance.C

Go to the documentation of this file.
00001 /** @file Distance.C
00002 
00003     $Id: Distance.C 5279 2007-06-23 02:49:08Z mburo $
00004     $Source$
00005 */
00006 
00007 // This is an ORTS file
00008 // (c) Michael Buro  
00009 // (c) Tim Furtak    
00010 // licensed under the GPL
00011 
00012 #include "Game.H"
00013 #include "Distance.H"
00014 #include "LinAlg.H"
00015 
00016 using namespace std;
00017 
00018 //===================================================================
00019 namespace Distance {
00020 /** Distance from a circle of radius r to a point. */
00021 
00022 inline static real8 dist_c_p(sint4 cx, sint4 cy, sint4 r, sint4 px, sint4 py)
00023 {
00024   real8 dist = sqrt(square(real8(cx-px)) + square(real8(cy-py))) - r;
00025   //  if (dist < 0) dist = 0;
00026   return dist;
00027 }
00028 
00029 //===================================================================
00030 
00031 /** Distance between two points. */
00032 
00033 inline static real8 dist_p_p(sint4 cx, sint4 cy, sint4 px, sint4 py)
00034 {
00035   return sqrt(square(real8(cx-px)) + square(real8(cy-py)));
00036 }
00037 
00038 //===================================================================
00039 
00040 /** Distance from a point to a line segment. */
00041 
00042 inline static real8 dist_p_l(sint4 cx, sint4 cy,
00043                              sint4 x1, sint4 y1, sint4 x2, sint4 y2)
00044 {
00045   sint4 ux = x2 - x1;
00046   sint4 uy = y2 - y1;
00047   sint4 vx = cx - x1;
00048   sint4 vy = cy - y1;
00049 
00050   sint4 u_v = ux * vx + uy * vy; // u dot v
00051   if (u_v <= 0)
00052     return dist_p_p(cx, cy, x1, y1);
00053 
00054   sint4 u_u = ux * ux + uy * uy; // u dot u
00055   if (u_u <= u_v)
00056     return dist_p_p(cx, cy, x2, y2);
00057 
00058   real8 b = real8(u_v) / real8(u_u);
00059   Vec2<real8> p(x1+b*ux - cx, y1+b*uy - cy); // (xy1 + b * u) - c
00060   return p.length();
00061 }
00062 
00063 //===================================================================
00064 
00065 /** Distance between two line segments.
00066     fixme: simple but inefficient hack
00067 */
00068 
00069 inline static real8 dist_l_l(const ScalarPoint &a1, const ScalarPoint &a2,
00070                              const ScalarPoint &b1, const ScalarPoint &b2)
00071 {
00072   sint4 cross1_1 = (a2.x-a1.x) * (b1.y-a1.y) - (a2.y-a1.y) * (b1.x-a1.x);
00073   sint4 cross1_2 = (a2.x-a1.x) * (b2.y-a1.y) - (a2.y-a1.y) * (b2.x-a1.x);
00074 
00075   sint4 cross2_1 = (b2.x-b1.x) * (a1.y-b1.y) - (b2.y-b1.y) * (a1.x-b1.x);
00076   sint4 cross2_2 = (b2.x-b1.x) * (a2.y-b1.y) - (b2.y-b1.y) * (a2.x-b1.x);
00077 
00078   // the lines cross
00079   if ((cross1_1 * cross1_2 < 0) && (cross2_1 * cross2_2 < 0)) return -1;
00080 
00081   real8 d1 = dist_p_l(a1.x, a1.y, b1.x, b1.y, b2.x, b2.y);
00082   real8 d2 = dist_p_l(a2.x, a2.y, b1.x, b1.y, b2.x, b2.y);
00083   real8 d3 = dist_p_l(b1.x, b1.y, a1.x, a1.y, a2.x, a2.y);
00084   real8 d4 = dist_p_l(b2.x, b2.y, a1.x, a1.y, a2.x, a2.y);
00085 
00086   return min( min(d1,d2), min(d3,d4) );
00087 }
00088 
00089 //===================================================================
00090 //===================================================================
00091 //===================================================================
00092 
00093 inline static real8 distance_circle_circle(const Object &e1, const Object &e2)
00094 {
00095   sint4 x1, y1, x2, y2;
00096   e1.get_center(x1, y1);
00097   e2.get_center(x2, y2);
00098 
00099   ScalarPoint delta(x1 - x2, y1 - y2);
00100   Scalar r = FixedPoint::add(e1.get_radius(), e2.get_radius());
00101   real8 dist = delta.l2_norm() - r;
00102   //  dist -= 1; // slack
00103   //  if (dist < 0) dist = 0;
00104   return dist;
00105 }
00106 
00107 //===================================================================
00108 
00109 inline static real8 distance_circle_point(const Object &e1, Scalar px, Scalar py)
00110 {
00111   sint4 x1, y1;
00112   e1.get_center(x1, y1);
00113   return dist_c_p(x1, y1, e1.get_radius(), px, py);
00114 }
00115 
00116 //===================================================================
00117 
00118 /** e1 = circle, e2 = line */
00119 
00120 inline static real8 distance_circle_line(const Object &e1, const Object &e2)
00121 {
00122   sint4 cx, cy;
00123   e1.get_center(cx, cy);
00124   Scalar r  = e1.get_radius();
00125 
00126   sint4 x1, y1, x2, y2;
00127   e2.get_p1(x1, y1);
00128   e2.get_p2(x2, y2);
00129 
00130   if (x1 == x2) {
00131 
00132     // vline |
00133 
00134     if (cy < y1) return dist_c_p(cx, cy, r, x1, y1);
00135     if (cy > y2) return dist_c_p(cx, cy, r, x2, y2);
00136     return dist_c_p(cx, cy, r, x1, cy);
00137     
00138   } else if (y1 == y2) {
00139 
00140     // hline --
00141 
00142     if (cx < x1) return dist_c_p(cx, cy, r, x1, y1);
00143     if (cx > x2) return dist_c_p(cx, cy, r, x2, y2);
00144     return dist_c_p(cx, cy, r, cx, y1);
00145 
00146   } else {
00147     
00148     // arbitrary line /
00149     
00150     real8 d = dist_p_l(cx, cy, x1, y1, x2, y2) - r;
00151     //    if (d < 0) d = 0;
00152     return d;
00153     
00154   }
00155 }
00156 
00157 //===================================================================
00158 /*
00159 inline static real8 distance_circle_polyline(const GameObj &e1, const GameObj &e2)
00160 {
00161 }
00162 */
00163 //===================================================================
00164 
00165 inline static real8 distance_line_point(const Object &e1, Scalar px, Scalar py)
00166 {
00167   sint4 x1, y1, x2, y2;
00168   e1.get_p1(x1, y1);
00169   e1.get_p2(x2, y2);
00170   return dist_p_l(px, py, x1, y1, x2, y2);
00171 }
00172 
00173 //===================================================================
00174 
00175 inline static real8 distance_line_line(const Object &e1, const Object &e2)
00176 {
00177   sint4 e1_x1, e1_y1, e1_x2, e1_y2;
00178   sint4 e2_x1, e2_y1, e2_x2, e2_y2;
00179   e1.get_p1(e1_x1, e1_y1);
00180   e1.get_p2(e1_x2, e1_y2);
00181   e2.get_p1(e2_x1, e2_y1);
00182   e2.get_p2(e2_x2, e2_y2);
00183 
00184   // at least one arbitrary line
00185   if (((e1.get_shape() == Object::LINE) && (e1_x1 != e1_x2) && (e1_y1 != e1_y2)) ||
00186       ((e2.get_shape() == Object::LINE) && (e2_x1 != e2_x2) && (e2_y1 != e2_y2)))
00187     return dist_l_l(ScalarPoint(e1_x1, e1_y1),
00188                     ScalarPoint(e1_x2, e1_y2),
00189                     ScalarPoint(e2_x1, e2_y1),
00190                     ScalarPoint(e2_x2, e2_y2));
00191 
00192   // both vertical
00193   if ((e1_x1 == e1_x2) && (e2_x1 == e2_x2)) {
00194     sint4 y1a = e1_y1;
00195     sint4 y2a = e1_y2;
00196     sint4 y1b = e2_y1;
00197     sint4 y2b = e2_y2;
00198     if (y1a > y2a) swap(y1a, y2a);
00199     if (y1b > y2b) swap(y1b, y2b);
00200     
00201     if (y1a > y2b) return dist_p_p(e1_x1, y1a, e2_x1, y2b);
00202     if (y2a < y1b) return dist_p_p(e1_x1, y2a, e2_x1, y1b);
00203     return real8(abs(e1_x1 - e2_x1));
00204   }
00205 
00206   // both horizontal
00207   if ((e1_y1 == e1_y2) && (e2_y1 == e2_y2)) {
00208     sint4 x1a = e1_x1;
00209     sint4 x2a = e1_x2;
00210     sint4 x1b = e2_x1;
00211     sint4 x2b = e2_x2;
00212     if (x1a > x2a) swap(x1a, x2a);
00213     if (x1b > x2b) swap(x1b, x2b);
00214     
00215     if (x1a > x2b) return dist_p_p(x1a, e1_y1, x2b, e2_y1);
00216     if (x2a < x1b) return dist_p_p(x2a, e1_y1, x1b, e2_y1);
00217     return real8(abs(e1_y1 - e2_y1));
00218   }
00219   
00220   // mixed alignments
00221   
00222   if (e1_x1 == e1_x2) { // e1 -> vertical, e2 -> horizontal
00223     sint4 y1 = min(e1_y1, e1_y2);
00224     sint4 y2 = max(e1_y1, e1_y2);
00225     if (e2_y1 > y2) return dist_p_l(e1_x1, y2, e2_x1, e2_y1, e2_x2, e2_y2);
00226     if (e2_y1 < y1) return dist_p_l(e1_x1, y1, e2_x1, e2_y1, e2_x2, e2_y2);
00227     sint4 x1 = min(e2_x1, e2_x2);
00228     sint4 x2 = max(e2_x1, e2_x2);
00229     if (e1_x1 > x2) return dist_p_l(x2, e2_y1, e1_x1, y1, e1_x1, y2);
00230     if (e1_x1 < x1) return dist_p_l(x1, e2_y1, e1_x1, y1, e1_x1, y2);
00231     return -1; // the lines cross
00232   }
00233 
00234   // e2 -> vertical, e1 -> horizontal
00235   
00236   sint4 y1 = min(e2_y1, e2_y2);
00237   sint4 y2 = max(e2_y1, e2_y2);
00238   if (e1_y1 > y2) return dist_p_l(e2_x1, y2, e1_x1, e1_y1, e1_x2, e1_y2);
00239   if (e1_y1 < y1) return dist_p_l(e2_x1, y1, e1_x1, e1_y1, e1_x2, e1_y2);
00240   sint4 x1 = min(e1_x1, e1_x2);
00241   sint4 x2 = max(e1_x1, e1_x2);
00242   if (e2_x1 > x2) return dist_p_l(x2, e1_y1, e2_x1, y1, e2_x1, y2);
00243   if (e2_x1 < x1) return dist_p_l(x1, e1_y1, e2_x1, y1, e2_x1, y2);
00244   return -1; // the lines cross
00245 }
00246 
00247 //===================================================================
00248 
00249 inline static real8 distance_rectangle_line(const Object &e1, const Object &e2)
00250 {
00251   sint4 o1x1, o1y1, o1x2, o1y2;
00252   sint4 o2x1, o2y1, o2x2, o2y2;
00253   e1.get_p1(o1x1, o1y1);
00254   e1.get_p2(o1x2, o1y2);
00255   e2.get_p1(o2x1, o2y1);
00256   e2.get_p2(o2x2, o2y2);
00257 
00258   if (o2x1 == o2x2) {           // vline |
00259     
00260     const sint4 x = o2x1;
00261     sint4 y1, y2;
00262     if (o2y1 < o2y2) {
00263       y1 = o2y1;
00264       y2 = o2y2;
00265     } else {
00266       y1 = o2y2;
00267       y2 = o2y1;
00268     }
00269     
00270     if (o1x1 >= x) { // line is to the left of the rectangle
00271       if (o1y1 > y2) return dist_p_p(x, y2, o1x1, o1y1);
00272       if (o1y2 < y1) return dist_p_p(x, y1, o1x1, o1y2);
00273       return real8(o1x1 - x);
00274     }
00275     
00276     if (o1x2 <= x) { // line is to the right of the rectangle
00277       if (o1y1 > y2) return dist_p_p(x, y2, o1x2, o1y1);
00278       if (o1y2 < y1) return dist_p_p(x, y1, o1x2, o1y2);
00279       return real8(x - o1x2);
00280     }
00281     
00282     // the x coords of the line are within the rectangle
00283     if (y1 >= o1y2) return real8(y1 - o1y2);
00284     if (y2 <= o1y1) return real8(o1y1 - y2); 
00285     return -1; 
00286     
00287   } else if (o2y1 == o2y2) {    // hline --
00288     
00289     const sint4 y = o2y1;
00290     sint4 x1, x2;
00291     if (o2x1 < o2x2) {
00292       x1 = o2x1;
00293       x2 = o2x2;
00294     } else {
00295       x1 = o2x2;
00296       x2 = o2x1;
00297     }
00298     
00299     if (o1y1 >= y) { // line is above the rectangle
00300       if (o1x1 > x2) return dist_p_p(x2, y, o1x1, o1y1); 
00301       if (o1x2 < x1) return dist_p_p(x1, y, o1x2, o1y1); 
00302       return real8(o1y1 - y);
00303     }
00304     
00305     if (o1y2 <= y) { // line is below the rectangle
00306       if (o1x1 > x2) return dist_p_p(x2, y, o1x1, o1y2); 
00307       if (o1x2 < x1) return dist_p_p(x1, y, o1x2, o1y2); 
00308       return real8(y - o1y2); 
00309     }
00310     
00311     // the y coords of the line are within the rectangle
00312     if (x1 >= o1x2) return real8(x1 - o1x2); 
00313     if (x2 <= o1x1) return real8(o1x1 - x2); 
00314     return -1; 
00315     
00316   } else { // arbitrary line
00317     
00318     sint4 x1 = o2x1;
00319     sint4 x2 = o2x2;
00320     sint4 y1 = o2y1;
00321     sint4 y2 = o2y2;
00322     
00323     // if either of the two line points is inside the rectangle return -1
00324     if ((x1 < o1x2 && o1x1 < x1 && y1 < o1y2 && o1y1 < y1 ) ||
00325         (x2 < o1x2 && o1x1 < x2 && y2 < o1y2 && o1y1 < y2 )) {
00326       return -1;
00327     }
00328     
00329     //      if (x1 > x2) swap(x1, x2);
00330     //      if (y1 > y2) swap(y1, y2);
00331     
00332     // arbitrary line /
00333     ScalarPoint p(x1, y1);
00334     ScalarPoint q(x2, y2);
00335     
00336     ScalarPoint s1(o1x1, o1y1);
00337     ScalarPoint s2(o1x1, o1y2);
00338     ScalarPoint s3(o1x2, o1y2);
00339     ScalarPoint s4(o1x2, o1y1);
00340     
00341     real8 a = dist_l_l(p, q,  s1, s2);
00342     real8 b = dist_l_l(p, q,  s2, s3);
00343     real8 c = dist_l_l(p, q,  s3, s4);
00344     real8 d = dist_l_l(p, q,  s4, s1);
00345     
00346     return min(min(a, b),min(c, d));
00347     
00348   }
00349 
00350 }
00351 
00352 //===================================================================
00353 
00354 /** e1 = circle, e2 = rectangle */
00355 
00356 inline static real8 distance_circle_rectangle(const Object &e1, const Object &e2)
00357 {
00358   sint4 cx, cy;
00359   e1.get_center(cx, cy);
00360   Scalar r = e1.get_radius();
00361 
00362   sint4 o2x1, o2y1, o2x2, o2y2;
00363   e2.get_p1(o2x1, o2y1);
00364   e2.get_p2(o2x2, o2y2);
00365 
00366 
00367   if (cx < o2x1) {
00368     
00369     if (cy < o2y1) return dist_c_p(cx, cy, r, o2x1, o2y1);
00370     if (cy > o2y2) return dist_c_p(cx, cy, r, o2x1, o2y2);
00371     return dist_c_p(cx, cy, r, o2x1, cy);
00372     
00373   }
00374   if (cx > o2x2) {
00375     
00376     if (cy < o2y1) return dist_c_p(cx, cy, r, o2x2, o2y1);
00377     if (cy > o2y2) return dist_c_p(cx, cy, r, o2x2, o2y2);
00378     return dist_c_p(cx, cy, r, o2x2, cy);
00379     
00380   }
00381   
00382   if (cy < o2y1) return dist_c_p(cx, cy, r, cx, o2y1);
00383   if (cy > o2y2) return dist_c_p(cx, cy, r, cx, o2y2);
00384   return -1; // inside the rectangle
00385 }
00386 
00387 //===================================================================
00388 
00389 inline static real8 distance_rectangle_rectangle(const Object &e1, const Object &e2)
00390 {
00391   sint4 o1x1, o1y1, o1x2, o1y2;
00392   sint4 o2x1, o2y1, o2x2, o2y2;
00393   e1.get_p1(o1x1, o1y1);
00394   e1.get_p2(o1x2, o1y2);
00395   e2.get_p1(o2x1, o2y1);
00396   e2.get_p2(o2x2, o2y2);
00397 
00398   if (o1x1 > o2x2) {
00399     if (o1y1 > o2y2) return dist_p_p(o1x1, o1y1, o2x2, o2y2);
00400     if (o1y2 < o2y1) return dist_p_p(o1x1, o1y2, o2x2, o2y1);
00401     return real8(o1x1 - o2x2);
00402   }
00403   
00404   if (o1x2 < o2x1) {
00405     if (o1y1 > o2y2) return dist_p_p(o1x2, o1y1, o2x1, o2y2);
00406     if (o1y2 < o2y1) return dist_p_p(o1x2, o1y2, o2x1, o2y1);
00407     return real8(o2x1 - o1x2);
00408   }
00409   
00410   if (o1y1 > o2y2) return real8(o1y1 - o2y2);
00411   if (o1y2 < o2y1) return real8(o2y1 - o1y2);
00412   return -1; // intersect
00413 }
00414 
00415 //===================================================================
00416 
00417 inline static real8 distance_rectangle_point(const Object &e1, Scalar cx, Scalar cy)
00418 {
00419   sint4 x1, y1, x2, y2;
00420   e1.get_p1(x1, y1);
00421   e1.get_p2(x2, y2);
00422 
00423 
00424   if (cx < x1) {
00425     
00426     if (cy < y1) return dist_p_p(cx, cy, x1, y1);
00427     if (cy > y2) return dist_p_p(cx, cy, x1, y2);
00428     return dist_p_p(cx, cy, x1, cy);
00429     
00430   }
00431   if (cx > x2) {
00432     
00433     if (cy < y1) return dist_p_p(cx, cy, x2, y1);
00434     if (cy > y2) return dist_p_p(cx, cy, x2, y2);
00435     return dist_p_p(cx, cy, x2, cy);
00436     
00437   }
00438   
00439   if (cy < y1) return dist_p_p(cx, cy, cx, y1);
00440   if (cy > y2) return dist_p_p(cx, cy, cx, y2);
00441   return -1; // inside the rectangle
00442 
00443 }
00444 
00445 //===================================================================
00446 
00447 real8 distance(const Object &e1, const Object &e2)
00448 {
00449   Object::Shape s1 = e1.get_shape();
00450   Object::Shape s2 = e2.get_shape();
00451 
00452   // circle - circle
00453   if (s1 == Object::CIRCLE && s2 == Object::CIRCLE)
00454     return distance_circle_circle(e1, e2);
00455 
00456   // circle - [hv]line
00457   if (s1 == Object::CIRCLE && s2 == Object::LINE)
00458     return distance_circle_line(e1, e2);
00459   if (s2 == Object::CIRCLE && s1 == Object::LINE)
00460     return distance_circle_line(e2, e1);
00461 
00462   // circle - rectangle
00463   if (s1 == Object::CIRCLE && s2 == Object::RECTANGLE)
00464     return distance_circle_rectangle(e1, e2);
00465   if (s2 == Object::CIRCLE && s1 == Object::RECTANGLE)
00466     return distance_circle_rectangle(e2, e1);
00467 
00468   // [hv]line - [hv]line
00469   if (s1 == Object::LINE && s2 == Object::LINE)
00470     return distance_line_line(e1, e2);
00471 
00472   // [hv]line - rectangle
00473   if (s1 == Object::RECTANGLE && s2 == Object::LINE)
00474     return distance_rectangle_line(e1, e2);
00475   if (s2 == Object::RECTANGLE && s1 == Object::LINE)
00476     return distance_rectangle_line(e2, e1);
00477 
00478   // rectangle - rectangle
00479   if (s1 == Object::RECTANGLE && s2 == Object::RECTANGLE)
00480     return distance_rectangle_rectangle(e1, e2);
00481 
00482   cout << e1.get_shape() << " " << e2.get_shape() << endl;
00483   //  cout << e1.bp_name() << " " << e2.bp_name() << endl;
00484   sint4 x1, y1, x2, y2;
00485   e1.get_center(x1, y1);
00486   e2.get_center(x2, y2);
00487   cout << x1 << " " << y1 << endl;
00488   cout << x2 << " " << y2 << endl;
00489   ERR("undefined shape pair");
00490   return 1;
00491 }
00492 
00493 //===================================================================
00494 
00495 real8 distance(const Object &e1, Scalar x, Scalar y)
00496 {
00497   Object::Shape s1 = e1.get_shape();
00498 
00499   // circle - point
00500   if (s1 == Object::CIRCLE)
00501     return distance_circle_point(e1, x, y);
00502 
00503   // [hv]line - point
00504   if (s1 == Object::LINE)
00505     return distance_line_point(e1, x, y);
00506 
00507   // rectangle - point
00508   if (s1 == Object::RECTANGLE)
00509     return distance_rectangle_point(e1, x, y);
00510 
00511   cout << e1.get_shape() << " point" << endl;
00512   //  cout << e1.bp_name() << " " << e2.bp_name() << endl;
00513   sint4 x1, y1;
00514   e1.get_center(x1, y1);
00515   cout << x1 << " " << y1 << endl;
00516   cout << x << " " << y << endl;
00517   ERR("undefined shape pair");
00518   return 1;
00519 }
00520 
00521 
00522 } // End of namespace Distance
00523 
00524 //===================================================================
00525 //===================================================================
00526 //===================================================================
00527 
00528 real8 Game::distance(const Object &e1, const Object &e2)
00529 {
00530   return Distance::distance(e1, e2);
00531 }
00532 
00533 //===================================================================


Generated on Fri May 18 2012 03:02:38 for ORTS by Doxygen1.7.3