Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

gfx/Segment3.cpp

Go to the documentation of this file.
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 

Generated on Thu Jul 29 15:56:19 2004 for OpenSim by doxygen 1.3.6