[数学]点到线段的距离

参考:http://blog.csdn.net/angelazy/article/details/38489293

海伦公式:http://zhidao.baidu.com/question/470074593

 

点到线段最短距离的运算与点到直线的最短距离的运算二者之间存在一定的差别,即求点到线段最短距离时需要考虑参考点在沿线段方向的投影点是否在线段上,若在线段上才可采用点到直线距离公式,如图1所示。

                  

1 (a)最短距离为点P与其在线段AB上投影C之间的线段PC

                                        (b)最短距离为点P与端点B(或A)所构成的线段PB(或PA)

 

具体算法主要有以下三种:

1、方法——经典算法

该算法直接用高中时所学习到的解析几何知识对点到线段的距离进行求解。其基本思想是先判断点在线段端点、点在线上等等的特殊情况,逐步的由特殊到一般,当忽略点在线段上的特殊情况时,判断点到线段方向的垂线是否落在线段上的方法是通过比较横纵坐标的方式来判断,最后把不同的判断情况用不同的几何方式来进行处理计算得出结果。

由上面叙述的基本思路可以知道这种算法虽然很容易理解和接受,但从算法的实用性的角度分析还是有很大的缺点的,首先是算法复杂,计算量巨大,大量的比较判断、距离计算、角度计算等等,实际应用中往往是需要求由大量线段组成的折线到某点的最短距离,如此用这样的算法计算量是不能想象的。其次经典算法中使用的一些简化运算的函数不利于语言的重新包装,如果想换编程语言的话,就比较麻烦了。

2、方法二——面积算法

该方法主要是先判断投影点是否在线段上,投影点在线段延长线上时,最短距离长度为点到端点的线段长度;当投影点在线段上时,先使用海伦公式计算三角形面积,再计算出三角形的高,即为最短距离。

运用面积算法求解点到线段最短距离思路很清晰,也很容易理解。从效率方面考虑,比如需要多次计算平方、根号,这对于大量数据进行运算是负担很重的。求面积就必须把三条边长全部求出,并且用到的海伦公式也需要进行开方运算,计算过程显得繁琐。

3、方法三——矢量算法

矢量算法过程清晰,如果具有一定的空间几何基础,则是解决此类问题时应优先考虑的方法。当需要计算的数据量很大时,这种方式优势明显。

由于矢量具有方向性,故一些方向的判断直接根据其正负号就可以得知,使得其中的一些问题得以很简单的解决。

用此方法考虑,我们只需要找到向量 方向上的投影,具体如下:

 

             


上面的 方向上的单位向量,其意义是给所求向量确定方向。是的两个向量的内积,且   ,其中θ为向量AP与AB之间的夹角。是向量长度。

 

 

那么即为上图中线段AC的长度值,不带有方向性。此数值与上述表征方向的 整体构成有大小、有方向的新向量,即为 方向上的投影向量,C为投影点。

根据得到的,由向量的方向性可知:如果情况是上图(a)所示,那么0<r<1;如果是如图(b)所示的情况,那么r ≥1;如果是如图(c)所示的情况,那么得到r ≤0;

特殊情况如点在线段上、点在端点、点在线段延长线上等等的情况全部适用于此公式,只是作为特殊情况出现,无需另作讨论。这也是矢量算法思想的优势所在。

故根据r值的不同,最短距离

C#代码为:

public static double PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
{
double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
if (cross <= 0) return Math.Sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));

double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if (cross >= d2) return Math.Sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
 
double r = cross / d2;
double px = x1 + (x2 - x1) * r;
double py = y1 + (y2 - y1) * r;
return Math.Sqrt((x - px) * (x - px) + (py - y1) * (py - y1));
}

 

C ++代码【原创】

/************************************************计算点到线段的距离**************************************************
                             /P                                          /P                           P\
                            /                                         /                               \
                           /                                         /                                 \
                          /                                          /                                    \
                        A ----C-------B                    A--------B   C                           C     A ----------B

    长度:                        CP                                BP                                        AP
    计算:d = dot(AP,AB)/|AB|^2
    判断依据:                    if(0<d<1)                    if(d>1)                                if(d<0)
    ************************************************计算点到线段的距离**************************************************/

    /**
    @brief 
    @param[const NaviPoint&] point_A    点A的位置坐标(x,y,z)|(x,y)
    @param[const NaviPoint&] point_B    点B的位置坐标(x,y,z)|(x,y)
    @param[const NaviPoint&] point_C    点C的位置坐标(x,y,z)|(x,y)
    @param[float&]             dot        表示点C与线段AB的相对位置        此为返回值   if(0<dot<1)点C在线段AB的中间区域 if(dot>1)点C在线段AB的右边区域 if(dot<0)点C在线段AB的左边区域
    @param[NaviPoint&]         point_C    点C的位置坐标(x,y,z)|(x,y)    此为返回值
    @return                     float        返回点C到线段AB的最近距离
    */
    float Dis_PointToLineSegment(const NaviPoint& point_A, const NaviPoint& point_B, const NaviPoint& point_P, float &dot, NaviPoint &point_C)
    {
        NaviPoint AP = point_P -  point_A;
        NaviPoint AB = point_B -  point_A;
        float ABdic = AB.length();
        dot = AP.dotProduct(AB)/(ABdic*ABdic);

        NaviPoint AC = AB * dot;
        point_C = AC  + point_A;

        float length;
        if (dot > 1)
        {
            //距离为BP
            NaviPoint BP = point_P - point_B; 
            length = BP.length();
        }else if(dot < 0)
        {
            //距离为AP 
            length = AP.length();
        }else
        {
            //距离为PC
            NaviPoint PC = point_P - point_C; 
            length = PC.length();
        }

        return length;
    }

};

 

posted @ 2015-07-16 17:31  南水之源  阅读(2679)  评论(0编辑  收藏  举报