Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I need to compute the distance between two line segments in a project. After googling, I found this algorithm and used it to implement a Mathematica version:

distSegToSeg[{p1_, p2_}, {q1_, q2_}] :=
  Module[{small = 10^(-6), u, v, w,
          a, b, c, d, e, D,
          sc, sN, sD,
          tc, tN, tD},
    u = p2 - p1;
    v = q2 - q1;
    w = p1 - q1;
    a = u.u;
    b = u.v;
    c = v.v;
    d = u.w;
    e = v.w;
    D = a*c - b*b;
    sD = D;
    tD = D;
    If[D < small, 
      sN = 0; sD = 1; tN = e; tD = c,
      sN = b*e - c*d;
      tN = (a*e - b*d);
      If[sN < 0, 
        sN = 0.0; tN = e; tD = c,
        If[sN > sD, sN = sD; tN = e + b; tD = c;]]];
    If[tN < 0, 
      tN = 0;
      If[-d < 0, 
        sN = 0,
        If[-d > a, 
          sN = sD, 
          sN = -d; sD = a]],
      If[tN > tD, 
        tN = tD; 
        If[-d + b < 0, 
          sN = 0,
          If[-d + b > a, 
            sN = sD, 
            sN = -d + b; sD = a]]]];
    sc = If[Norm[sN] < small, 0, sN/sD];
    tc = If[Norm[tN] < small, 0, tN/tD];
    N[Norm[w + sc*u - tc*v]]
]

The code is not in good Mathematica style, and it's very slow. Do you have any idea to make this faster? Do you have a better way to compute the distance between two line segments in 3D?

share|improve this question

4 Answers 4

up vote 3 down vote accepted

You can easily see that if the nearest approach of the two infinite lines does not fall within both segments then you must calculate the nearest distance of all four end points to the other segment.

 pointsegdis[{seg_, pointlist_}] := 
       Module[{u = Subtract @@ seg, mean = Plus @@ seg/2},
       {#, (mean - u  Sign@# Min[1, Abs@#]/2) &@(-(2 ( # - mean).u )/u.u) } & 
        /@ pointlist ]
 segsegdis[s1_, s2_] := Module[{u, v, w, d, t1, t2},
   {u, v, w} = Subtract @@ # & /@ {Sequence @@ #, First /@ #} &@{s1, s2};
   If[(d = u.u v.v - (v.u)^2) != 0 &&
       Abs[t2 = 2 (u.u v.w - u.w v.u)/d + 1] <= 1 &&
       Abs[t1 = 2 (v.u v.w - v.v u.w)/d + 1] <= 1,
    Plus @@ (#[[1]] (1 + {1, -1} #[[2]] )) /2 & /@ {{s1, t1}, {s2, t2}} ,
    First@SortBy[Flatten[pointsegdis /@ {{s1, s2}, {s2, s1}}, 1], 
                       Total@((Subtract @@ #)^2) &]
       ] ] ;

 {s1, s2} = RandomReal[{-1, 1}, {2, 2, 3}];
 connect = segsegdis[s1, s2];
 Graphics3D[{Line /@ {s1, s2}, {Red, Line[connect]}}]

enter image description here

This is several orders of magnitude faster than Minimise or FindMinimum

 testcases = RandomReal[{-1, 1}, {100, 2, 2, 3}];
 (aa = Norm[ Subtract @@ segsegdis[#[[1]], #[[2]]] ] & /@ testcases) //Timing // First
 (bb = First@
      Minimize[{  Norm[(#[[1, 1]] (1 - t1)/2 + #[[1, 2]] (1 + t1)/2) -
         (#[[2, 1]] (1 - t2)/2 + #[[2, 2]] (1 + t2)/2) ]  , 
            {-1<=t1 <= 1 , -1 <= t2 <= 1}}, {t1, t2}] & /@ testcases) // Timing //First
 (cc = First@
       FindMinimum[{  
        Norm[(#[[1, 1]] (1 - t1)/2 + #[[1, 2]] (1 + t1)/2) -
         (#[[2, 1]] (1 - t2)/2 + #[[2, 2]] (1 + t2)/2) ]  , 
               {-1 <= t1 <= 1 , -1 <= t2 <= 1}}, {t1, t2}] & /@ testcases) // Timing // First

{0.015600, 12.620481, 2.714417}

verify all three give the same result to reasonable numerical precision

 Max[Abs[aa - bb]] -> 5.82532*10^-10
 Max[Abs[aa - cc]] -> 2.25578*10^-6
share|improve this answer

The link you gave shows how to find the distance between any two points on the lines. You can then use Mathematica's Minimize function to find the shortest distance. Like this.

minDist[{p1_, p2_}, {q1_, q2_}] :=
  Module[{P, Q, u, v, w},
         u = Normalize[p2 - p1];
         v = Normalize[q2 - q1];
         P[s_] := p1 + s u;
         Q[t_] := q1 + t v;
         w[s_, t_] := P[s] - Q[t];
         First[Minimize[Norm[w[s, t]], {s, t}]]
] 

Here, u and v are unit vectors pointing from p1 to p2 and q1 to q2, respectively. The functions P and Q represent the two lines. The function w is a vector going between P[s] and Q[t]. Minimize the length of that vector.

share|improve this answer
    
It's much clear, but I test it , it's much slower than my previous code –  tintin Apr 4 at 0:32

Another way of doing this (http://mathforum.org/library/drmath/view/51980.html) is to find the mutual perpendicular between the two lines using the cross product, converting this to a unit vector, and then using the dot product between that cross product, and any vector going between the two lines. Like this:

newMinDist[{p1_, p2_}, {q1_, q2_}] :=
 Module[
  {u, v, n, w},
  u = p2 - p1;
  v = q2 - q1;
  n = Normalize[Cross[u,v]];
  w = q1 - p1;
  Dot[w,n]
  ]

Again, u and v are vectors headed along the lines. The vector n is a unit vector in the direction of the cross product of u and v (the unit vector normal to both u and v). The w is any vector between the ps and the qs. You could swap any value of 1 or 2 here (for instance q2-p1). Then w.n gives the shortest distance between the lines.

Gives the same result, and Timing shows this method is better than an order of magnitude faster than the previous one.

share|improve this answer

Suppose the equation of two lines are

(x-x1)/A1=(y-y1)/B1=(z-z1)/C1
(x-x2)/A2=(y-y2)/B2=(z-z2)/C2

Then the oriented distance between them is

Det[{{x2-x1,y2-y1,z2-z1},{A1,B1,C1},{A2,B2,C2}}]/Norm[Cross[{A1,B1,C1},{A2,B2,C2}]]

You may need Abs to get the distance

share|improve this answer
    
what I want is the distance of two line segment, not two infinite line –  tintin Apr 4 at 4:00
    
@tintin its the same calculation now just check that your on the segment or the endpoint is closest. –  joojaa Apr 5 at 6:54

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.