2016-07-28 22 views
2

私は2つの線分を持っています。線分は始点と終点に3点あります。2本の3D線分の最短距離を見つける

ライン:

class Line 
{ 
    public string Name { get; set; } 
    public Point3D Start { get; set; } = new Point3D(); 
    public Point3D End { get; set; } = new Point3D(); 
} 

3D点座標のX、Y及びZのためのちょうど3倍である

3DPoint:

class Point3D 
{ 
    public double X { get; set; } 
    public double Y { get; set; } 
    public double Z { get; set; } 
} 

質問:

2つの「線」とその距離の「終点」。 [私は何Here is an Image to Better Illustrate What I am trying to Achieve1

public double lineNearLine(Line l1, Line l2) 
    { 
     Vector3D uS = new Vector3D { X = l1.Start.X, Y = l1.Start.Y, Z = l1.Start.Z }; 
     Vector3D uE = new Vector3D { X = l1.End.X, Y = l1.End.Y, Z = l1.End.Z }; 
     Vector3D vS = new Vector3D { X = l2.Start.X, Y = l2.Start.Y, Z = l2.Start.Z }; 
     Vector3D vE = new Vector3D { X = l2.End.X, Y = l2.End.Y, Z = l2.End.Z }; 
     Vector3D w1 = new Vector3D { X = l1.Start.X, Y = l1.Start.Y, Z = l1.Start.Z }; 
     Vector3D w2 = new Vector3D { X = l2.Start.X, Y = l2.Start.Y, Z = l2.Start.Z }; 
     Vector3D u = uE - uS; 
     Vector3D v = vE - vS; 
     Vector3D w = w1 - w2; 
     double a = Vector3D.DotProduct(u, u); 
     double b = Vector3D.DotProduct(u, v); 
     double c = Vector3D.DotProduct(v, v); 
     double d = Vector3D.DotProduct(u, w); 
     double e = Vector3D.DotProduct(v, w); 
     double D = a * c - b * b; 
     double sc, sN, sD = D; 
     double tc, tN, tD = D; 
     if (D < 0.01) 
     { 
      sN = 0; 
      sD = 1; 
      tN = e; 
      tD = c; 
     } 
     else 
     { 
      sN = (b * e - c * d); 
      tN = (a * e - b * d); 
      if (sN < 0) 
      { 
       sN = 0; 
       tN = e; 
       tD = c; 
      } 
      else if (sN > sD) 
      { 
       sN = sD; 
       tN = e + b; 
       tD = c; 
      } 
     } 
     if (tN < 0) 
     { 
      tN = 0; 
      if (-d < 0) 
      { 
       sN = 0; 
      } 
      else if (-d > a) 
      { 
       sN = sD; 
      } 
      else 
      { 
       sN = -d; 
       sD = a; 
      } 
     } 
     else if (tN > tD) 
     { 
      tN = tD; 
      if ((-d + b) < 0) 
      { 
       sN = 0; 
      } 
      else if ((-d + b) > a) 
      { 
       sN = sD; 
      } 
      else 
      { 
       sN = (-d + b); 
       sD = a; 
      } 
     } 
     if (Math.Abs(sN) < 0.01) 
     { 
      sc = 0; 
     } 
     else 
     { 
      sc = sN/sD; 
     } 
     if (Math.Abs(tN) < 0.01) 
     { 
      tc = 0; 
     } 
     else 
     { 
      tc = tN/tD; 
     } 
     Vector3D dP = w + (sc * u) - (tc * v); 
     double distance1 = Math.Sqrt(Vector3D.DotProduct(dP, dP)); 
     return distance1; 
    } 

現在、私は成功した(セグメントセクションにセグメントを使用してAdapted From Here)このコードの2本の線の間の距離を得ることができます必要なもの:

ディスプレイスペースのエンドポイントを特定する方法はありますか上のコードからntベクトル 'dP'?そうでない場合は、最小距離とその距離の終点を見つけるためのより良い方法を提案することができますか?

読んでいただきありがとうございました、お寄せいただきありがとうございました!

The Solution!

巨大なここでは、このソリューション

の背後にある理論のため@IsaacバンBakelにありがとうございました私のコードの完全なは、次のとおりです。でそれらを結ぶ線で表される2つのライン間の最短距離その最短距離。

クラス:

  1. Sharp3D.Math:私はのVector3Dため、このリファレンスを使用しますが、実際には任意の3次元ベクトルクラスが動作します。さらに、要素ごとに減算要素を実行する場合でも、ベクトルは必要ありません。
  2. Point3D:My Personal Point3Dクラス。あなたが好きなだけ、少しでも自由に使用してください。

    class Point3D 
    { 
        public double X { get; set; } 
        public double Y { get; set; } 
        public double Z { get; set; } 
        public Vector3D getVector() 
        { 
         return new Vector3D { X = this.X, Y = this.Y, Z = this.Z }; 
        } 
    
    } 
    
  3. 行:My Personal Lineクラス。あなたが好きなだけ、少しでも自由に使用してください。

    class Line 
    { 
        public string Name { get; set; } 
        public Point3D Start { get; set; } = new Point3D(); 
        public Point3D End { get; set; } = new Point3D(); 
        public double Length 
        { 
         get 
         { 
          return Math.Sqrt(Math.Pow((End.X - Start.X), 2) + Math.Pow((End.Y - Start.Y), 2)); 
         } 
        } 
    } 
    

機能:

  1. ClampPointToLine:クランプ機能は、私はラインにポイントをクランプするために書きました。

    public Point3D ClampPointToLine(Point3D pointToClamp, Line lineToClampTo) 
    { 
        Point3D clampedPoint = new Point3D(); 
        double minX, minY, minZ, maxX, maxY, maxZ; 
        if(lineToClampTo.Start.X <= lineToClampTo.End.X) 
        { 
         minX = lineToClampTo.Start.X; 
         maxX = lineToClampTo.End.X; 
        } 
        else 
        { 
         minX = lineToClampTo.End.X; 
         maxX = lineToClampTo.Start.X; 
        } 
        if (lineToClampTo.Start.Y <= lineToClampTo.End.Y) 
        { 
         minY = lineToClampTo.Start.Y; 
         maxY = lineToClampTo.End.Y; 
        } 
        else 
        { 
         minY = lineToClampTo.End.Y; 
         maxY = lineToClampTo.Start.Y; 
        } 
        if (lineToClampTo.Start.Z <= lineToClampTo.End.Z) 
        { 
         minZ = lineToClampTo.Start.Z; 
         maxZ = lineToClampTo.End.Z; 
        } 
        else 
        { 
         minZ = lineToClampTo.End.Z; 
         maxZ = lineToClampTo.Start.Z; 
        } 
        clampedPoint.X = (pointToClamp.X < minX) ? minX : (pointToClamp.X > maxX) ? maxX : pointToClamp.X; 
        clampedPoint.Y = (pointToClamp.Y < minY) ? minY : (pointToClamp.Y > maxY) ? maxY : pointToClamp.Y; 
        clampedPoint.Z = (pointToClamp.Z < minZ) ? minZ : (pointToClamp.Z > maxZ) ? maxZ : pointToClamp.Z; 
        return clampedPoint; 
    } 
    
  2. distanceBetweenLines:2行の間の最短距離を表す行を返す関数。解決できない場合はnullを返します。

    public Line distBetweenLines(Line l1, Line l2) 
    { 
        Vector3D p1, p2, p3, p4, d1, d2; 
        p1 = l1.Start.getVector(); 
        p2 = l1.End.getVector(); 
        p3 = l2.Start.getVector(); 
        p4 = l2.End.getVector(); 
        d1 = p2 - p1; 
        d2 = p4 - p3; 
        double eq1nCoeff = (d1.X * d2.X) + (d1.Y * d2.Y) + (d1.Z * d2.Z); 
        double eq1mCoeff = (-(Math.Pow(d1.X, 2)) - (Math.Pow(d1.Y, 2)) - (Math.Pow(d1.Z, 2))); 
        double eq1Const = ((d1.X * p3.X) - (d1.X * p1.X) + (d1.Y * p3.Y) - (d1.Y * p1.Y) + (d1.Z * p3.Z) - (d1.Z * p1.Z)); 
        double eq2nCoeff = ((Math.Pow(d2.X, 2)) + (Math.Pow(d2.Y, 2)) + (Math.Pow(d2.Z, 2))); 
        double eq2mCoeff = -(d1.X * d2.X) - (d1.Y * d2.Y) - (d1.Z * d2.Z); 
        double eq2Const = ((d2.X * p3.X) - (d2.X * p1.X) + (d2.Y * p3.Y) - (d2.Y * p2.Y) + (d2.Z * p3.Z) - (d2.Z * p1.Z)); 
        double[,] M = new double[,] { { eq1nCoeff, eq1mCoeff, -eq1Const }, { eq2nCoeff, eq2mCoeff, -eq2Const } }; 
        int rowCount = M.GetUpperBound(0) + 1; 
        // pivoting 
        for (int col = 0; col + 1 < rowCount; col++) if (M[col, col] == 0) 
         // check for zero coefficients 
         { 
          // find non-zero coefficient 
          int swapRow = col + 1; 
          for (; swapRow < rowCount; swapRow++) if (M[swapRow, col] != 0) break; 
    
          if (M[swapRow, col] != 0) // found a non-zero coefficient? 
          { 
           // yes, then swap it with the above 
           double[] tmp = new double[rowCount + 1]; 
           for (int i = 0; i < rowCount + 1; i++) 
           { tmp[i] = M[swapRow, i]; M[swapRow, i] = M[col, i]; M[col, i] = tmp[i]; } 
          } 
          else return null; // no, then the matrix has no unique solution 
         } 
    
        // elimination 
        for (int sourceRow = 0; sourceRow + 1 < rowCount; sourceRow++) 
        { 
         for (int destRow = sourceRow + 1; destRow < rowCount; destRow++) 
         { 
          double df = M[sourceRow, sourceRow]; 
          double sf = M[destRow, sourceRow]; 
          for (int i = 0; i < rowCount + 1; i++) 
           M[destRow, i] = M[destRow, i] * df - M[sourceRow, i] * sf; 
         } 
        } 
    
        // back-insertion 
        for (int row = rowCount - 1; row >= 0; row--) 
        { 
         double f = M[row, row]; 
         if (f == 0) return null; 
    
         for (int i = 0; i < rowCount + 1; i++) M[row, i] /= f; 
         for (int destRow = 0; destRow < row; destRow++) 
         { M[destRow, rowCount] -= M[destRow, row] * M[row, rowCount]; M[destRow, row] = 0; } 
        } 
        double n = M[0, 2]; 
        double m = M[1, 2]; 
        Point3D i1 = new Point3D { X = p1.X + (m * d1.X), Y = p1.Y + (m * d1.Y), Z = p1.Z + (m * d1.Z) }; 
        Point3D i2 = new Point3D { X = p3.X + (n * d2.X), Y = p3.Y + (n * d2.Y), Z = p3.Z + (n * d2.Z) }; 
        Point3D i1Clamped = ClampPointToLine(i1, l1); 
        Point3D i2Clamped = ClampPointToLine(i2, l2); 
        return new Line { Start = i1Clamped, End = i2Clamped }; 
    } 
    

実装:

Line shortestDistanceLine = distBetweenLines(l1, l2); 

結果:

今のところ、これは私のテストでは正確となっています。同じ2行が渡された場合はnullを返します。私はフィードバックを感謝します!

+0

その画像では、目的の点が線の中心にあるように見えます...この例のようなものですか?なぜなら、その無作為な線があれば、ほとんどの場合そうでないからです。それは2本の線の間の可能な最短距離です – Neil

+0

はい、それは私が得ようとしているものの任意の例であるために塗料で作られました。線はランダムに生成されるのではなく、その配置において非常に恣意的である。 – Kikootwo

+0

明確にするために、それはあなたが求めている線分の中点であると言っていますか? –

答えて

2

2本のスキューライン間の最短距離(交差しないライン)は、両方のラインに垂直なラインの距離です。

我々は、既知の点P1、P2のラインL1、および既知点P3、P4のラインL2持ちの場合:

The direction vector of l1 is p2-p1, or d1. 
The direction vector of l2 is p4-p3, or d2. 

を私たちは、それゆえ我々は、V、探しているベクトルは、垂直であることを知っていますこれらの方向ベクトルの両方に:

d1.v = 0 & d2.v = 0 

それとも、あなたが好む場合:

d1x*vx + d1y*vy + d1z*vz = 0 

とS ame for d2。

線l1、l2上の点を取ってみましょう。ここで、vは実際には方向に垂直です。これらの2つの点をそれぞれi1とi2と呼ぶことにします。

Since i1 lies on l1, we can say that i1 = p1 + m*d1, where m is some number. 
Similarly, i2 = p3 + n*d2, where n is another number. 

vは(定義)I1とI2間のベクトルであるので、我々は、V = i2のことを得る - I1。ように

vx = i2x - i1x = (p3x + n*d2x) - (p1x + m*d1x) 

と:

これは、VのX、Y、Zベクトルの置換を与えます。これは、2つの未知数(m、nは)と(2つの内積方程式)2次方程式の私達の数を減らした

d1x * ((p3x + n*d2x) - (p1x + m*d1x)) + ... = 0 

、あなたはので:あなたが今、あなたの内積の式に戻って置き換えることができ

今それらを解決することができます!

mとnを指定すると、i1とi2の元の計算に戻って座標を見つけることができます。

セグメント上のポイントの最短距離をp1-p2とp3-p4の間でのみ望む場合、最短距離は常に垂直に近いので、i1とi2をこれらの座標の範囲でクランプすることができます可能。

+0

これは素晴らしいです、私はちょうど簡単な説明をしました。それらがスキューラインセグメントであるにもかかわらず、それらの間の最短距離は常に両方のラインに直角であるか? – Kikootwo

+1

これは、答えの最後の部分で何を話したかによって異なります。ポイントの範囲をp1からp2に、p3からp4をラインの両端に限定したいですか?はいの場合、それは常に垂直になるとは限りませんが、可能な限り近くにあります。いいえの場合、常に垂直になります。 –

+0

さて、はい、私はそれが常にp1-p2とp3-4の範囲に限定されることを望みます。あなたは 'クランプ'と言いますが、私はそれが何であるか肯定的ではありません。 – Kikootwo

関連する問題