點到線段最短距離的運算與點到直線的最短距離的運算二者之間存在一定的差別,即求點到線段最短距離時需要考慮參考點在沿線段方向的投影點是否在線段上,若在線段上才可採用點到直線距離公式,如圖1所示。

求點到線最小距離 python_最短距離

圖1 (a)最短距離為點P與其在線段AB上投影C之間的線段PC

b)最短距離為點P與端點B(或A)所構成的線段PB(或PA)

 

具體算法主要有以下三種:

1、方法——經典算法

該算法直接用高中時所學習到的解析幾何知識對點到線段的距離進行求解。其基本思想是先判斷點在線段端點、點在線上等等的特殊情況,逐步的由特殊到一般,當忽略點在線段上的特殊情況時,判斷點到線段方向的垂線是否落在線段上的方法是通過比較橫縱座標的方式來判斷,最後把不同的判斷情況用不同的幾何方式來進行處理計算得出結果。

由上面敍述的基本思路可以知道這種算法雖然很容易理解和接受,但從算法的實用性的角度分析還是有很大的缺點的,首先是算法複雜,計算量巨大,大量的比較判斷、距離計算、角度計算等等,實際應用中往往是需要求由大量線段組成的折線到某點的最短距離,如此用這樣的算法計算量是不能想象的。其次經典算法中使用的一些簡化運算的函數不利於語言的重新包裝,如果想換編程語言的話,就比較麻煩了。

2、方法二——面積算法

該方法主要是先判斷投影點是否在線段上,投影點在線段延長線上時,最短距離長度為點到端點的線段長度;當投影點在線段上時,先使用海倫公式計算三角形面積,再計算出三角形的高,即為最短距離。

運用面積算法求解點到線段最短距離思路很清晰,也很容易理解。從效率方面考慮,比如需要多次計算平方、根號,這對於大量數據進行運算是負擔很重的。求面積就必須把三條邊長全部求出,並且用到的海倫公式也需要進行開方運算,計算過程顯得繁瑣。

3、方法三——矢量算法

矢量算法過程清晰,如果具有一定的空間幾何基礎,則是解決此類問題時應優先考慮的方法。當需要計算的數據量很大時,這種方式優勢明顯。

由於矢量具有方向性,故一些方向的判斷直接根據其正負號就可以得知,使得其中的一些問題得以很簡單的解決。

用此方法考慮,我們只需要找到向量在方向上的投影,具體如下:

 

求點到線最小距離 python_最短距離_02

上面的是方向上的單位向量,其意義是給所求向量確定方向。是的兩個向量的內積,且AP與AB之間的夾角。是向量長度。

 

 

那麼即為上圖中線段AC的長度值,不帶有方向性。此數值與上述表徵方向的整體構成有大小、有方向的新向量,即為在方向上的投影向量,C為投影點。

根據得到的,由向量的方向性可知:如果情況是上圖(a)所示,那麼0<r<1;如果是如圖(b)所示的情況,那麼r1;如果是如圖(c)所示的情況,那麼得到r0;

特殊情況如點在線段上、點在端點、點在線段延長線上等等的情況全部適用於此公式,只是作為特殊情況出現,無需另作討論。這也是矢量算法思想的優勢所在。

故根據r值的不同,最短距離

求點到線最小距離 python_最短距離_03

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

};

 




 

點到線段最短距離的運算與點到直線的最短距離的運算二者之間存在一定的差別,即求點到線段最短距離時需要考慮參考點在沿線段方向的投影點是否在線段上,若在線段上才可採用點到直線距離公式,如圖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)所示的情況,那麼r1;如果是如圖(c)所示的情況,那麼得到r0;

特殊情況如點在線段上、點在端點、點在線段延長線上等等的情況全部適用於此公式,只是作為特殊情況出現,無需另作討論。這也是矢量算法思想的優勢所在。

故根據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;
    }

};