00001 /**************************************************************************** 00002 Copyright (C)2003 David Jung <opensim@pobox.com> 00003 00004 This program/file is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or 00007 (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. (http://www.gnu.org) 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 $Id: Segment3.cpp 1030 2004-02-11 20:46:17Z jungd $ 00019 $Revision: 1.1 $ 00020 $Date: 2004-02-11 15:46:17 -0500 (Wed, 11 Feb 2004) $ 00021 $Author: jungd $ 00022 00023 ****************************************************************************/ 00024 00025 #include <math.h> 00026 00027 #include <gfx/Segment3> 00028 #include <gfx/Line3> 00029 00030 using base::Point3; 00031 using gfx::Segment3; 00032 using gfx::Line3; 00033 00034 00035 bool Segment3::contains(const Point3& p) const 00036 { 00037 // if p isn't on the line through s-e then it isn't on the segment either 00038 if (!Line3(*this).contains(p)) return false; 00039 00040 // it is on the line, so just check the components are within range 00041 return (p.x >= Math::minimum(s.x,e.x)-consts::epsilon) && (p.x <= Math::maximum(s.x,e.x)+consts::epsilon) 00042 && (p.y >= Math::minimum(s.y,e.y)-consts::epsilon) && (p.y <= Math::maximum(s.y,e.y)+consts::epsilon) 00043 && (p.z >= Math::minimum(s.z,e.z)-consts::epsilon) && (p.z <= Math::maximum(s.z,e.z)+consts::epsilon); 00044 } 00045 00046 00047 Real Segment3::distanceTo(const Point3& p) const 00048 { 00049 return (p-pointClosestTo(p)).length(); 00050 } 00051 00052 00053 Point3 Segment3::pointClosestTo(const Point3& p) const 00054 { 00055 if ( s.equals(e) || s.equals(p) ) return s; 00056 00057 Vector3 d(e-s); 00058 Real u = ( dot(d,p-s) / dot(d,d) ); 00059 if (u <= 0) return s; 00060 if (u >= 1) return e; 00061 return s+u*d; 00062 } 00063 00064 00065 // this implementation and its explanation was basically ripped from the XEngine code by Martin Ecker 00066 // (email: martin.ecker@liwest.at - see http://xengine.sourceforge.net ) 00067 // it looks a lot like the Magic Software algorithm ( http://www.magic-software.com ) 00068 /* 00069 Given two segments X1(s) = P1 + s * D1 and X2(t) = P2 + t * D2 with s, t in 00070 [0, 1] we can determine the minimal distance using the squared-distance 00071 function for any two points on the segments: 00072 Q(s, t) = |X1(s) - X2(t)|^2 00073 The function Q(s, t) is quadratic in s and t and can be represented as 00074 Q(s, t) = as^2 + 2bst + ct^2 +2ds + 2et + f 00075 with 00076 a = D1 * D1 00077 b = -D1 * D2 00078 c = D2 * D2 00079 d = D1 * (P1 - P2) 00080 e = -D2 * (P1 - P2) 00081 f = (P1 - P2) * (P1 - P2) 00082 See the line-to-line distance algorithm for a more detailed description of this. 00083 00084 The basic problem is to find the minimum of the gradient dQ of Q where dQ = (0, 0). 00085 However, since we're dealing with segments, the resulting s and t must be in 00086 [0, 1]. The minimum can occur anywhere on Q and s and t could be outside the 00087 interval [0, 1]. So we have to distinguish between interior points and end 00088 points. In particular, we have to distinguish between 8 regions where Q(s, t) 00089 can live. These are: 00090 1: s > 1, t in [0, 1] 00091 2: s > 1, t > 1 00092 3: s in [0, 1], t > 1 00093 4: s < 0, t > 1 00094 5: s < 0, t in [0, 1] 00095 6: s < 0, t < 0 00096 7: s in [0, 1], t < 0 00097 8: s > 1, t < 0 00098 00099 The following graph shows the possible regions. Region 0 is where s and t 00100 are in [0, 1] and that is the unit square where we have to find the minimum 00101 distance of the two segments. 00102 00103 t 00104 00105 4 | 3 | 2 00106 --------------- 00107 5 | 0 | 1 00108 --------------- s 00109 | | 00110 6 | 7 | 8 00111 | | 00112 00113 Suppose we're in region 1, therefore the minimum of Q occurs somewhere where 00114 s > 1 but t is in [0, 1]. We now need to find a value Q(s, t) with s = 1 00115 so that we still get the minimum for all t in [0, 1]. This can be done by 00116 minimizing the function F(t) = Q(1, t) which is a one-dimensional problem. 00117 So we just determine the t where F'(t) = 0 where t in [0, 1]. Of course the 00118 minimum of F(t) can occur outside the interval [0, 1]. In that case we 00119 either choose t = 0 (if the minimum t < 0) or t = 1 (if the minimum t > 1). 00120 Regions 3, 5 and 7 are handled similarly. For region 3 we find the minimum 00121 of Q(s, 1). For region 5 we find the minimum of Q(0, t) and for region 7 00122 we find the minimum of Q(s, 0). 00123 00124 Regions 2, 4, 6 and 8 require slightly different handling. Suppose we're in 00125 region 2 and thus the minimum occurs at s > 1 and t > 1. In this case 00126 the minimum for s and t in [0, 1] could occur at either s = 1 or t = 1. 00127 Since the global minimum occurs in region 2 where s > 1 and t > 1,the 00128 gradient dQ(1, 1) at the corner (1, 1) cannot point point inside region 0 00129 where s and t are in [0, 1]. If dQ = (Qs, Qt) where Qs and Qt are the partial 00130 derivatives of Q, then the partial derivatives cannot both be negative. 00131 The signs of Qs(1, 1) and Qt(1, 1) determine whether we have to use s = 1 00132 or the edge t = 1. If Qs(1, 1) > 0 then the minimum occurs on the edge t = 1 00133 since Q(s, 1) < Q(1, 1) for s < 1. If Qt(1, 1) > 0 then the minimum occurs 00134 on the edge s = 1. Now we can use the approach we've already used for 00135 regions 1, 3, 5 and 7 to determine the minimum on that edge (and whether 00136 it occurs on an end point or interior point of [0, 1]). 00137 00138 So far we have only looked at the case where the determinant of Q(s, t) 00139 ac - b^2 > 0 (for segments that are not parallel). For line segments that 00140 are parallel, where ac - b^2 = 0, we can simplify the calculations by 00141 projecting the second segment onto the first line. 00142 We do this by projecting the start-point P2 and the end-point P2 + D2 00143 of the second line segment on the first line segment like so: 00144 Bstart = P1 + tstart * D1 = P1 + (D1 * (P2 - P1)) / (D1 * D1) * D1 = P1 - d / a * D1 00145 Bend = P1 + tend * D1 = P1 + (D1 * (P2 + D2 - P1)) / (D1 * D1) * D1 = P1 - (b + d) / a * D1 00146 where a, b and d are coefficients of Q(s, t) as shown before. Now the problem 00147 is just to find the relative position of the segment (P1, P1 + D1) to (Bstart, Bend). 00148 If they overlap there is an infinite number of pairs with minimum distance 00149 and so we can just choose one point on an overlapping part of a segment and 00150 and calculate the distance to the orthogonal interior point of the other segment. 00151 To further simplify, it is enough to compare the interval [tstart, tend] with 00152 [0, 1]. To do that we need to first find out the order of [tstart, tend]. 00153 Or in other words, we need to find out if it's really [tstart, tend] or rather 00154 [tend, tstart] because the direction vector of the second line segment 00155 points in the other direction of the first line segment. Once that is done 00156 it is a simple matter of comparing the intervals to find out the minimum 00157 values for s and t. Note that tstart = -d / a corresponds to t = 0 and 00158 tend = -(b + d) / a corresponds to t = 1. 00159 */ 00160 00161 Segment3 Segment3::shortestSegmentBetween(const Segment3& s2, Real& ds) const 00162 { 00163 const Segment3& s1(*this); 00164 00165 Real s, t; 00166 Vector3 d1(s1.e-s1.s); 00167 Vector3 d2(s2.e-s2.s); 00168 Vector3 diff = s1.s - s2.s; 00169 Real a = dot(d1,d1); 00170 Real b = -dot(d1,d2); 00171 Real c = dot(d2,d2); 00172 Real d = dot(d1,diff); 00173 Real f = dot(diff,diff); 00174 Real det = Math::abs(a * c - Math::sqr(b)); 00175 00176 if (det >= consts::epsilon) 00177 { 00178 // segments are not parallel 00179 Real e = -dot(d2,diff); 00180 t = b * d - a * e; 00181 s = b * e - c * d; 00182 00183 if (t >= 0) { 00184 if (t <= det) { // t <= 1 ? 00185 if (s >= 0) { 00186 if (s <= det) { // s <= 1? 00187 // region 0, s and t in [0, 1] 00188 det = 1 / det; 00189 s *= det; 00190 t *= det; 00191 ds = s * (a * s + 2 * b * t + 2 * d) + t * (c * t + 2 * e) + f; 00192 } 00193 else { // s > 1 00194 // region 1, s > 1 and t in [0, 1] 00195 // we'll set s = 1 and thus have to minimize 00196 // Q(1, t) = F(t) = a + 2bt + ct^2 + 2d + 2et + f 00197 // F'(t) = 2b + 2ct + 2e = 0 ==> t = -(b + e) / c 00198 s = 1; 00199 Real temp = b + e; 00200 if (temp >= 0) { // t < 0? 00201 t = 0; 00202 ds = a + 2 * d + f; 00203 } 00204 else if (-temp > c) { // t > 1? 00205 t = 1; 00206 ds = a + c + f + 2 * (temp + d); 00207 } 00208 else { 00209 t = -temp / c; 00210 ds = a + temp * t + 2 * d + f; 00211 } 00212 } 00213 } 00214 else { // s < 0 00215 // region 5, s < 0, t in [0, 1] 00216 // we'll set s = 0 and have to minimize 00217 // Q(0, t) = F(t) = ct^2 + 2et + f 00218 // F'(t) = 2ct + 2e = 0 ==> t = -e / c 00219 s = 0; 00220 if (e >= 0) { // t < 0? 00221 t = 0; 00222 ds = f; 00223 } 00224 else if (-e > c) { // t > 1? 00225 t = 1; 00226 ds = c + 2 * e + f; 00227 } 00228 else { 00229 t = -e / c; 00230 ds = e * t + f; 00231 } 00232 } 00233 } 00234 else { // t > 1 00235 if (s >= 0) { 00236 if (s <= det) { // s <= 1? 00237 // region 3, s in [0, 1], t > 1 00238 // in this case t = 1 and we have to minimize 00239 // Q(s, 1) = F(s) = as^2 + 2bs + c + 2ds + 2e + f 00240 // F'(s) = 2as + 2b + 2d = 0 ==> s = -(b + d) / a 00241 t = 1; 00242 Real temp = b + d; 00243 if (temp >= 0) { // s < 0? 00244 s = 0; 00245 ds = c + 2 * e + f; 00246 } 00247 else if (-temp > a) { // s > 1? 00248 s = 1; 00249 ds = a + c + f + 2 * (temp + e); 00250 } 00251 else { 00252 s = -temp / a; 00253 ds = temp * s + c + 2 * e + f; 00254 } 00255 } 00256 else { // s > 1 00257 // region 2, s > 1, t > 1 00258 if (a + b + d > 0) { // Qs(1, 1) > 0? 00259 // t = 1, so we determine the minimum of Q(s, 1) just like in region 3 00260 // since Q(s, 1) < Q(1, 1) for s < 1 we can omit the second test for s > 1 00261 // because the minimum definitely won't be there 00262 t = 1; 00263 Real temp = b + d; 00264 if (temp >= 0) { // s < 0? 00265 s = 0; 00266 ds = c + 2 * e + f; 00267 } 00268 else { 00269 s = -temp / a; 00270 ds = temp * s + c + 2 * e + f; 00271 } 00272 } 00273 else { 00274 // s = 1, so we determine the minimum of Q(1, t) just like in region 1 00275 s = 1; 00276 Real temp = b + e; 00277 if (temp >= 0) { // t < 0? 00278 t = 0; 00279 ds = a + 2 * d + f; 00280 } 00281 else if (-temp > c) { // t > 1? 00282 t = 1; 00283 ds = a + c + f + 2 * (temp + d); 00284 } 00285 else { 00286 t = -temp / c; 00287 ds = a + temp * t + 2 * d + f; 00288 } 00289 } 00290 } 00291 } 00292 else { // s < 0 00293 // region 4, s < 0, t > 1 00294 if (b + d < 0) { // Qs(0, 1) < 0? 00295 // t = 1, so we determine the minimum of Q(s, 1) just like in region 3 00296 // since Q(s, 1) < Q(0, 1) for s > 0 we can omit the test for s < 0 00297 // because the minimum definitely won't be there 00298 t = 1; 00299 Real temp = b + d; 00300 if (-temp > a) { // s > 1? 00301 s = 1; 00302 ds = a + c + f + 2 * (temp + e); 00303 } 00304 else { 00305 s = -temp / a; 00306 ds = temp * s + c + 2 * e + f; 00307 } 00308 } 00309 else { 00310 // s = 0, so we determine the minimum of Q(0, t) just like in region 5 00311 s = 0; 00312 if (e >= 0) { // t < 0? 00313 t = 0; 00314 ds = f; 00315 } 00316 else if (-e > c) { // t > 1? 00317 t = 1; 00318 ds = c + 2 * e + f; 00319 } 00320 else { 00321 t = -e / c; 00322 ds = e * t + f; 00323 } 00324 } 00325 } 00326 } 00327 } 00328 else { // t < 0 00329 if (s >= 0) { 00330 if (s <= det) { // s <= 1? 00331 // region 7, s in [0, 1], t < 0 00332 // we'll use t = 0 and have to minimize 00333 // Q(s, 0) = F(s) = as^2 + 2ds + f 00334 // F'(s) = 2as + 2d = 0 ==> s = -d / a 00335 t = 0; 00336 if (d >= 0) { // s < 0? 00337 s = 0; 00338 ds = f; 00339 } 00340 else if (-d > a) { // s > 1? 00341 s = 1; 00342 ds = a + 2 * d + f; 00343 } 00344 else { 00345 s = -d / a; 00346 ds = d * s + f; 00347 } 00348 } 00349 else { // s > 1 00350 // region 8, s > 1, t < 0 00351 if (a + d > 0) { // Qs(1, 0) > 0 00352 // t = 0, so we determine the minimum of Q(s, 0) just like in region 7 00353 // since Q(s, 0) < Q(1, 0) for s < 1 we can omit the test for s > 1 00354 // because the minimum definitely won't be there 00355 t = 0; 00356 if (d >= 0) { // s < 0? 00357 s = 0; 00358 ds = f; 00359 } 00360 else { 00361 s = -d / a; 00362 ds = d * s + f; 00363 } 00364 } 00365 else { 00366 // s = 1, so we determine the minimum of Q(1, t) just like in region 1 00367 s = 1; 00368 Real temp = b + e; 00369 if (temp >= 0) { // t < 0? 00370 t = 0; 00371 ds = a + 2 * d + f; 00372 } 00373 else if (-temp > c) { // t > 1? 00374 t = 1; 00375 ds = a + c + f + 2 * (temp + d); 00376 } 00377 else { 00378 t = -temp / c; 00379 ds = a + temp * t + 2 * d + f; 00380 } 00381 } 00382 } 00383 } 00384 else { // s < 0 00385 // region 6, s < 0, t < 0 00386 if (d < 0) { // Qs(0, 0) < 0 00387 // t = 0, so we determine the minimum of Q(s, 0) just like in region 7 00388 // since Q(s, 0) < Q(0, 0) for s > 0 we can omit the test for s < 0 00389 // because the minimum definitely won't be there 00390 t = 0; 00391 if (-d > a) { // s > 1? 00392 s = 1; 00393 ds = a + 2 * d + f; 00394 } 00395 else { 00396 s = -d / a; 00397 ds = d * s + f; 00398 } 00399 } 00400 else { 00401 // s = 0, so we determine the minimum of Q(0, t) just like in region 5 00402 s = 0; 00403 if (e >= 0) { // t < 0? 00404 t = 0; 00405 ds = f; 00406 } 00407 else if (-e > c) { // t > 1? 00408 t = 1; 00409 ds = c + 2 * e + f; 00410 } 00411 else { 00412 t = -e / c; 00413 ds = e * t + f; 00414 } 00415 } 00416 } 00417 } 00418 } 00419 else { 00420 // segments are parallel 00421 00422 // check for ordering of [tstart, tend] interval or in other words, 00423 // check whether -d / a > -(b + d) / a which can be simplified to 0 < b 00424 if (b > 0) { 00425 // compare [-(b + d) / a, -d / a] with [0, 1] 00426 if (d > 0) { // if -d / a <= 0 then the minimum is at s = 0, t = 0 00427 s = t = 0; 00428 ds = f; 00429 } 00430 else if (-d <= a) { // if 0 < -d / a <= 1 then minimum is at s = -d / a, t = 0 00431 s = -d / a; 00432 t = 0; 00433 ds = d * s + f; 00434 } 00435 else { 00436 Real e = -dot(d2,diff); 00437 Real temp = b + d; 00438 s = 1; 00439 if (-temp >= a) { // if -(b + d) / a >= 1 then the minimum is at s = 1, t = 1 00440 t = 1; 00441 ds = a + c + f + 2 * (temp + e); 00442 } 00443 else { // else the minimum is at s = 1, t = -(a + d) / b 00444 t = -(a + d) / b; 00445 ds = -a + f + t * (c * -t + 2 * e); 00446 } 00447 } 00448 } 00449 else { 00450 // compare [-d / a, -(b + d) / a] with [0, 1] 00451 if (-d >= a) { // if -d / a >= 1 then minimum is at s = 1, t = 0 00452 s = 1; 00453 t = 0; 00454 ds = a + 2 * d + f; 00455 } 00456 else if (d <= 0) { // if 0 <= -d / a < 1 then minimum is at s = -d / a, t = 0 00457 s = -d / a; 00458 t = 0; 00459 ds = d * s + f; 00460 } 00461 else { 00462 Real e = -dot(d2,diff); 00463 s = 0; 00464 if (b > -d) { // if -(b + d) / a <= 0 then minimum is at s = 0, t = 1 00465 t = 1; 00466 ds = c + 2 * e + f; 00467 } 00468 else { // else minimum is at s = 0, t = -d / b 00469 t = -d / b; 00470 ds = t * (c * t + 2 * e) + f; 00471 } 00472 } 00473 } 00474 } 00475 00476 ds = Math::abs(ds); 00477 00478 return Segment3( s1.s + s*d1, s2.s + t*d2 ); 00479 } 00480