1. 程式人生 > >求兩條空間直線的最近距離,以及他們最近距離線的兩點座標

求兩條空間直線的最近距離,以及他們最近距離線的兩點座標

設有兩空間線段

  1. Ls,其起點、終點座標為s0、s1,方向向量u⃗ =s1−s0

  2. Lt,其起點、終點座標為t0、t1,方向向量v⃗ =t1−t0 記兩線段對應的直線為ls、lt,採用向量表示法如下:

ls=s0+cs⋅u⃗

lt=t0+ct⋅v⃗ 當0≤cs、ct≤1時,上述兩式表達 設最短距離兩點分別為sj、tj,則有

sj=s0+sc⋅u⃗

tj=t0+sc⋅v⃗ 其中sc、tc為sj、tj兩點所對應的標量。

記向量w⃗ =sj−tj,記向量w⃗ 0=s0−t0,根據下圖可以得出:

w⃗ =s0+sc⋅u⃗ −(t0+tc⋅v⃗ )

即:

w⃗ =w⃗ 0+sc⋅u⃗ −tc⋅v⃗ (公式1) 如果s、t兩條直線不平行、重合,則存在唯一的兩點sc、tc使線段sctc−→−為ls、lt最近兩點的連線。同時,線段sctc−→−也是唯一與兩條直線同時垂直的線段。轉換為向量表達即為:

u⃗ ⋅w⃗ =0v⃗ ⋅w⃗ =0 將公式1代入上述兩式可得

(u⃗ ⋅u⃗ )sc−(u⃗ ⋅v⃗ )tc=−u⃗ ⋅w⃗ 0(公式2)

(v⃗ ⋅u⃗ )sc−(v⃗ ⋅v⃗ )tc=−v⃗ ⋅w⃗ 0(公式3) 記a=u⃗ ⋅u⃗ ,b=u⃗ ⋅v⃗ ,c=v⃗ ⋅v⃗ ,d=u⃗ ⋅w⃗ 0,e=v⃗ ⋅w⃗ 0,代入上述方程則可得:

sc=be−cdac−b2(公式4)

tc=ae−bdac−b2(公式5)

注意到上式中分母:

ac−b2=u⃗ ⋅u⃗ ×v⃗ ⋅v⃗ −(u⃗ ⋅v⃗ )2

⇒ac−b2=|u⃗ |2⋅|v⃗ |2−(|u⃗ |⋅|v⃗ |⋅cosq)2=(|u⃗ |⋅|v⃗ |⋅sinq)2≥0

當ac−b2=0時,公式2和公式3相互獨立,則兩直線平行,直線間的距離為一常數,我們可以在任意一條直線上指定一固定點,然後代入公式求距離。我們可以指定sc=0然後可以求得tc=db=ec。 當求出sc、tc我們即可求得sj、tj兩點,則兩點之間的距離可按下式求解:

d(ls,lt)=|sj−tj|=|s0−t0+(be−cd)u⃗ −(ae−bd)v⃗ ac−b2|

相關程式碼:

public class LineALineComputeIn3D {     double PonA_x;//兩直線最近點之A線上的點的x座標     double PonA_y;//兩直線最近點之A線上的點的y座標     double PonA_z;//兩直線最近點之A線上的點的z座標     double PonB_x;//兩直線最近點之B線上的點的x座標     double PonB_y;//兩直線最近點之B線上的點的y座標     double PonB_z;//兩直線最近點之B線上的點的z座標     double distance;//兩直線距離

    //直線A的第一個點     double a1_x ;     double a1_y ;     double a1_z ;     //直線A的第二個點     double a2_x ;     double a2_y ;     double a2_z ;

    //直線B的第一個點     double b1_x;     double b1_y ;     double b1_z ;

    //直線B的第二個點     double b2_x;     double b2_y ;     double b2_z ;          boolean bIsCompute=false;

    //輸入直線A的兩個點,以便獲得A的方程     public void SetLineA(double A1x, double A1y, double A1z, double A2x, double A2y, double A2z)     {         a1_x = A1x;         a1_y = A1y;         a1_z = A1z;

        a2_x = A2x;         a2_y = A2y;         a2_z = A2z;         bIsCompute=false;     }

    //輸入直線B的兩個點,以便獲得B的方程     public void SetLineB(double B1x, double B1y, double B1z, double B2x, double B2y, double B2z)     {         b1_x = B1x;         b1_y = B1y;         b1_z = B1z;

        b2_x = B2x;         b2_y = B2y;         b2_z = B2z;         bIsCompute=false;     }

    //獲取A線上最近點     public double[] GetThePointInLineA()     {         double[] value=new double[3];         if(!bIsCompute){             StartCompute();         }         value[0]=PonA_x;         value[1]=PonA_y;         value[2]=PonA_z;         return value;     }

    //獲取A線上最近點     public double[] GetThePointInLineB()     {         double[] value=new double[3];         if(!bIsCompute){             StartCompute();         }         value[0]=PonB_x;         value[1]=PonB_y;         value[2]=PonB_z;         return value;     }          //獲取兩空間直線的最近距離     public double GetTwoLineDistance(){         if(!bIsCompute){             StartCompute();         }         return distance;     }

    //用SetLineA、SetLineB輸入A、B方程後     //呼叫本函式解出結果     public void StartCompute()     {         //方法來自:http://blog.csdn.net/pi9nc/article/details/11820545

        double d1_x = a2_x - a1_x;         double d1_y = a2_y - a1_y;         double d1_z = a2_z - a1_z;

        double d2_x = b2_x - b1_x;         double d2_y = b2_y - b1_y;         double d2_z = b2_z - b1_z;

        double e_x = b1_x - a1_x;         double e_y = b1_y - a1_y;         double e_z = b1_z - a1_z;

        double cross_e_d2_x=0, cross_e_d2_y=0, cross_e_d2_z=0;         double[] d2= cross(e_x, e_y, e_z, d2_x, d2_y, d2_z);         cross_e_d2_x=d2[0];         cross_e_d2_y=d2[1];         cross_e_d2_z=d2[2];         double cross_e_d1_x, cross_e_d1_y, cross_e_d1_z;         double[] d1= cross(e_x, e_y, e_z, d1_x, d1_y, d1_z);         cross_e_d1_x=d1[0];         cross_e_d1_y=d1[1];         cross_e_d1_z=d1[2];         double cross_d1_d2_x, cross_d1_d2_y, cross_d1_d2_z;         double[] d1d2= cross(d1_x, d1_y, d1_z, d2_x, d2_y, d2_z);         cross_d1_d2_x=d1d2[0];         cross_d1_d2_y=d1d2[1];         cross_d1_d2_z=d1d2[2];

        double t1=0, t2=0;         t1 = dot(cross_e_d2_x, cross_e_d2_y, cross_e_d2_z, cross_d1_d2_x, cross_d1_d2_y, cross_d1_d2_z);         t2 = dot(cross_e_d1_x, cross_e_d1_y, cross_e_d1_z, cross_d1_d2_x, cross_d1_d2_y, cross_d1_d2_z);         double dd = norm(cross_d1_d2_x, cross_d1_d2_y, cross_d1_d2_z);         t1 /= dd*dd;//複合賦值運算子,把左邊的變數除於右邊變數的值賦予右邊的變數,例如:a/=b等價於a=a/b         t2 /= dd*dd;

        //得到最近的位置         PonA_x = (a1_x + (a2_x - a1_x)*t1);         PonA_y = (a1_y + (a2_y - a1_y)*t1);         PonA_z = (a1_z + (a2_z - a1_z)*t1);

        PonB_x = (b1_x + (b2_x - b1_x)*t2);         PonB_y = (b1_y + (b2_y - b1_y)*t2);         PonB_z = (b1_z + (b2_z - b1_z)*t2);

        distance = norm(PonB_x - PonA_x, PonB_y - PonA_y, PonB_z - PonA_z);     }

    //點乘     double dot(double ax, double ay, double az, double bx, double by, double bz) {         return ax*bx + ay*by + az*bz;     }

    //向量叉乘得到法向量     double[] cross(double ax, double ay, double az, double bx, double by, double bz)     {         double[] value=new double[3];         double x = ay*bz - az*by;         double y = az*bx - ax*bz;         double z = ax*by - ay*bx;         value[0]=x;         value[1]=y;         value[2]=z;         return value;     }

    //向量取模     double norm(double ax, double ay, double az) {         return Math.sqrt(dot(ax, ay, az, ax, ay, az));     } }