三维空间两直线/线段最短距离、线段计算算法

1 篇文章 0 订阅
1 篇文章 0 订阅

设有两空间线段
1. Ls ,其起点、终点坐标为 s0s1 ,方向向量 u⃗ =s1s0
2. Lt ,其起点、终点坐标为 t0t1 ,方向向量 v⃗ =t1t0
记两线段对应的直线为 lslt ,采用向量表示法如下:

ls=s0+csu⃗ 

lt=t0+ctv⃗ 

0csct1 时,上述两式表达
设最短距离两点分别为 sj tj ,则有
sj=s0+scu⃗ 

tj=t0+scv⃗ 

其中 sc tc sj tj 两点所对应的标量。

记向量 w⃗  = sjtj ,记向量 w⃗ 0 = s0t0 ,根据下图可以得出:
向量示意图

w⃗ =s0+scu⃗ (t0+tcv⃗ )
即:
w⃗ =w⃗ 0+scu⃗ tcv⃗ (1)

如果 st 两条直线不平行、重合,则存在唯一的两点 sctc 使线段 sctc lslt 最近两点的连线。同时,线段 sctc 也是唯一与两条直线同时垂直的线段。转换为向量表达即为:
u⃗ w⃗ =0v⃗ w⃗ =0

将公式1代入上述两式可得
u⃗ u⃗ scu⃗ v⃗ tc=u⃗ w⃗ 0(2)

v⃗ u⃗ scv⃗ v⃗ tc=v⃗ w⃗ 0(3)

a=u⃗ u⃗ b=u⃗ v⃗ c=v⃗ v⃗ d=u⃗ w⃗ 0e=v⃗ w⃗ 0 ,代入上述方程则可得:
sc=becdacb2(4)

tc=aebdacb2(5)

注意到上式中分母:
acb2=u⃗ u⃗ ×v⃗ v⃗ u⃗ v⃗ 2

acb2=|u⃗ |2|v⃗ |2(|u⃗ ||v⃗ |cosq)2=(|u⃗ ||v⃗ |sinq)20

acb2=0 时,公式2和公式3相互独立,则两直线平行,直线间的距离为一常数,我们可以在任意一条直线上指定一固定点,然后代入公式求距离。我们可以指定 sc=0 然后可以求得 tc=db=ec
当求出 sctc 我们即可求得 sjtj 两点,则两点之间的距离可按下式求解:
d(ls,lt)=|sjtj|=|s0t0+(becd)u⃗ (aebd)v⃗ acb2|

具体实现代码如下(C#实现):

public bool IsEqual(double d1, double d2)
{
    if (Math.Abs(d1 - d2) < 1e-7)
        return true;
    return false;
}

public double SqureDistanceSegmentToSegment(double x1, double y1, double z1,
                                            double x2, double y2, double z2,
                                            double x3, double y3, double z3,
                                            double x4, double y4, double z4)
{
    // 解析几何通用解法,可以求出点的位置,判断点是否在线段上
    // 算法描述:设两条无限长度直线s、t,起点为s0、t0,方向向量为u、v
    // 最短直线两点:在s1上为s0+sc*u,在t上的为t0+tc*v
    // 记向量w为(s0+sc*u)-(t0+tc*v),记向量w0=s0-t0
    // 记a=u*u,b=u*v,c=v*v,d=u*w0,e=v*w0——(a);
    // 由于u*w=、v*w=0,将w=-tc*v+w0+sc*u带入前两式得:
    // (u*u)*sc - (u*v)*tc = -u*w0  (公式2)
    // (v*u)*sc - (v*v)*tc = -v*w0  (公式3)
    // 再将前式(a)带入可得sc=(be-cd)/(ac-b2)、tc=(ae-bd)/(ac-b2)——(b)
    // 注意到ac-b2=|u|2|v|2-(|u||v|cosq)2=(|u||v|sinq)2不小于0
    // 所以可以根据公式(b)判断sc、tc符号和sc、tc与1的关系即可分辨最近点是否在线段内
    // 当ac-b2=0时,(公式2)(公式3)独立,表示两条直线平行。可令sc=0单独解出tc
    // 最终距离d(L1、L2)=|(P0-Q0)+[(be-cd)*u-(ae-bd)v]/(ac-b2)|
    double ux = x2 - x1;
    double uy = y2 - y1;
    double uz = z2 - z1;

    double vx = x4 - x3;
    double vy = y4 - y3;
    double vz = z4 - z3;

    double wx = x1 - x3;
    double wy = y1 - y3;
    double wz = z1 - z3;

    double a = (ux * ux + uy * uy + uz * uz); //u*u
    double b = (ux * vx + uy * vy + uz * vz); //u*v
    double c = (vx * vx + vy * vy + vz * vz); //v*v
    double d = (ux * wx + uy * wy + uz * wz); //u*w 
    double e = (vx * wx + vy * wy + vz * wz); //v*w
    double dt = a * c - b * b;

    double sd = dt;
    double td = dt;

    double sn = 0.0;//sn = be-cd
    double tn = 0.0;//tn = ae-bd

    if (IsEqual(dt, 0.0))
    {
        //两直线平行
        sn = 0.0;    //在s上指定取s0
        sd = 1.00;   //防止计算时除0错误

        tn = e;      //按(公式3)求tc
        td = c;
    }
    else
    {
        sn = (b * e - c * d);
        tn = (a * e - b * d);
        if (sn < 0.0)
        {
            //最近点在s起点以外,同平行条件
            sn = 0.0;
            tn = e;
            td = c;
        }
        else if (sn > sd)
        {
            //最近点在s终点以外(即sc>1,则取sc=1)
            sn = sd;
            tn = e + b; //按(公式3)计算
            td = c;
        }
    }
    if (tn < 0.0)
    {
        //最近点在t起点以外
        tn = 0.0;
        if (-d < 0.0) //按(公式2)计算,如果等号右边小于0,则sc也小于零,取sc=0
            sn = 0.0;
        else if (-d > a) //按(公式2)计算,如果sc大于1,取sc=1
            sn = sd;
        else
        {
            sn = -d;
            sd = a;
        }
    }
    else if (tn > td)
    {
        tn = td;
        if ((-d + b) < 0.0)
            sn = 0.0;
        else if ((-d + b) > a)
            sn = sd;
        else
        {
            sn = (-d + b);
            sd = a;
        }
    }

    double sc = 0.0;
    double tc = 0.0;

    if (IsEqual(sn, 0.0))
        sc = 0.0;
    else
        sc = sn / sd;

    if (IsEqual(tn, 0.0))
        tc = 0.0;
    else
        tc = tn / td;

    double dx = wx + (sc * ux) - (tc * vx);
    double dy = wy + (sc * uy) - (tc * vy);
    double dz = wz + (sc * uz) - (tc * vz);
    return dx * dx + dy * dy + dz * dz;
}

参考文献:http://geomalgorithms.com/a07-_distance.html

  • 15
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值