求两条空间直线的最近距离,以及他们最近距离线的两点坐标

设有两空间线段

  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));
    }
}

猜你喜欢

转载自blog.csdn.net/liangsongjun/article/details/82704938