|
ORTS
|
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 //===================================================================