判斷線段相交是否相交

一.矢量基本知識算法

    由於後面的計算須要一些矢量的基本知識,這裏只是簡單的列舉以下,若是須要更加詳細的信息,能夠自行搜索wikipedia或google。函數

1.矢量的概念:若是一條線段的端點是有次序之分的,咱們把這種線段成爲有向線段(directed segment)。若是有向線段p1p2的起點p1在座標原點,咱們能夠把它稱爲矢量(vector)p2。google

2.矢量加減法:設二維矢量P = ( x1, y1 ),Q = ( x2 , y2 ),則矢量加法定義爲: P + Q = ( x1 + x2 , y1 + y2 ),一樣的,矢量減法定義爲: P - Q = ( x1 - x2 , y1 - y2 )。顯然有性質 P + Q = Q + P,P - Q = - ( Q - P )。code

3.矢量的叉積:計算矢量叉積是與直線和線段相關算法的核心部分。設矢量P = ( x1, y1 ),Q = ( x2, y2 ),則矢量叉積定義爲由(0,0)、p一、p2和p1+p2所組成的平行四邊形的帶符號的面積,即:P × Q = x1*y2 - x2*y1,其結果是一個標量。顯然有性質 P × Q = - ( Q × P ) 和 P × ( - Q ) = - ( P × Q )。通常在不加說明的狀況下,本文下述算法中全部的點都看做矢量,兩點的加減法就是矢量相加減,而點的乘法則看做矢量叉積。ip

叉積的一個很是重要性質是能夠經過它的符號判斷兩矢量相互之間的順逆時針關係:io

  若 P × Q > 0 , 則P在Q的順時針方向。class

  若 P × Q < 0 , 則P在Q的逆時針方向。搜索

  若 P × Q = 0 , 則P與Q共線,但可能同向也可能反向。程序

4.折線段的拐向判斷:折線段的拐向判斷方法能夠直接由矢量叉積的性質推出。對於有公共端點的線段p0p1和p1p2,經過計算(p2 - p0) × (p1 - p0)的符號即可以肯定折線段的拐向:方法

  若(p2 - p0) × (p1 - p0) > 0,則p0p1在p1點拐向右側後獲得p1p2。

  若(p2 - p0) × (p1 - p0) < 0,則p0p1在p1點拐向左側後獲得p1p2。

  若(p2 - p0) × (p1 - p0) = 0,則p0、p一、p2三點共線。

這一條判斷也可用來判斷點在線段或直線的哪一測。

爲了後文的敘述方便,先定義幾個結構:

struct point{
    int x;
    int y;
};
struct v{
     point start;
     point end;
};

    計算兩條直線的叉積(cross production), 這裏因爲定義的都是二維的狀況,本質上說,在平面上兩個向量的叉積應該是垂直平面的,這裏函數返回的整數值即爲z軸上的值:

int crossProduct(v* v1, v* v2){
     v vt1, vt2;
    int result = 0;
    
     vt1.start.x = 0;
     vt1.start.y = 0;
     vt1.end.x = v1->end.x - v1->start.x;
     vt1.end.y = v1->end.y - v1->start.y;
    
     vt2.start.x = 0;
     vt2.start.y = 0;
     vt2.end.x = v2->end.x - v2->start.x;
     vt2.end.y = v2->end.y - v2->start.y;
    
     result = vt1.end.x * vt2.end.y - vt2.end.x * vt1.end.y;
    return result;
}

二.判斷兩條直線相交

    先來看一個簡單的狀況,即判斷兩條直線是否相交。

第一個可能會想到的辦法,就是判斷斜率,這個在中學時代就學過了,不過斜率須要考慮垂直的特殊狀況,比較麻煩。更好的辦法或許是計算兩個向量的叉積,若是爲0,則是平行或者重合的,不然兩直線相交。

代碼就不貼了,直接調用上面的函數就ok了。


三.判斷兩線段相交

經典方法,就是跨立試驗了,即若是一條線段跨過另外一條線段,則線段的兩個端點分別在另外一條線段的兩側。可是,還須要檢測邊界狀況,即兩條線段中可能某條線段的某個端點正好落在另外一條線段上。這也是算法導論中介紹的算法。

程序模擬以下:

int direction(point* pi, point* pj, point* pk){
     point p1, p2;
    
     p1.x = pk->x - pi->x;
     p1.y = pk->y - pi->y;
    
     p2.x = pj->x - pi->x;
     p2.y = pj->y - pi->y;
    
    return crossProduct(&p1, &p2);
}
int onSegment(point* pi, point* pj, point* pk){
    int minx, miny, maxx, maxy;
    if (pi->x > pj->x){
         minx = pj->x;
         maxx = pi->x;   
     }
    else{
         minx = pi->x;
         maxx = pj->x;
     }
    
    if (pi->y > pj->y){
         miny = pj->y;
         maxy = pi->y;   
     }
    else{
         miny = pi->y;
         maxy = pj->y;
     }
    
    if (minx <= pk->x && pk->x <= maxx && miny <= pk->y && pk->y <= maxy)
        return 1;
    else
        return 0;
}
int segmentIntersect(point* p1, point* p2, point* p3, point* p4){
    int d1 = direction(p3, p4, p1);
    int d2 = direction(p3, p4, p2);
    int d3 = direction(p1, p2, p3);
    int d4 = direction(p1, p2, p4);
    if (d1 * d2 < 0 && d3 * d4 < 0)
        return 1;
    else if (!d1 && onSegment(p3, p4, p1))
        return 1;
    else if (!d2 && onSegment(p3, p4, p2))
        return 1;
    else if (!d3 && onSegment(p1, p2, p3))
        return 1;
    else if (!d4 && onSegment(p1, p2, p4))
        return 1;
    else
        return 0;
}

實際上,若是想改進上述算法,還能夠在跨立試驗前加一步,就是先作快速排斥試驗。那就是,先分別判斷以兩條線段爲對角線的矩形是否相交,若是不相交,則兩個線段確定不相交。


四.判斷兩條線段相交,而後計算交點

設一條線段爲L0=P1P2, 另外一條線段或直線爲L1=Q1Q2, 要計算的就是L0和L1的交點。

1.首先判斷L0和L1是否相交(方法已在前文討論過), 若是不相交則沒有交點, 不然說明L0和L1必定有交點, 下面就將L0和L1都看做直線來考慮. 

2.若是P1和P2橫座標相同, 即L0平行於Y軸 

a)若L1也平行於Y軸 

    i.若P1的縱座標和Q1的縱座標相同, 說明L0和L1共線, 假如L1是直線的話他們有無窮的交點, 假如L1是線段的話可用"計算兩條共線線段的交點"的算法求他們的交點(該方法在前文已討論過);

    ii.不然說明L0和L1平行, 他們沒有交點; 

b)若L1不平行於Y軸, 則交點橫座標爲P1的橫座標, 代入到L1的直線方程中能夠計算出交點縱座標; 

3.若是P1和P2橫座標不一樣, 可是Q1和Q2橫座標相同, 即L1平行於Y軸, 則交點橫座標爲Q1的橫座標, 代入到L0的直線方程中能夠計算出交點縱座標; 

4.若是P1和P2縱座標相同, 即L0平行於X軸

a)若L1也平行於X軸,

    i.若P1的橫座標和Q1的橫座標相同, 說明L0和L1共線, 假如L1是直線的話他們有無窮的交點, 假如L1是線段的話可用"計算兩條共線線段的交點"的算法求他們的交點(該方法在前文已討論過);

    ii.不然說明L0和L1平行, 他們沒有交點; 

b)若L1不平行於X軸, 則交點縱座標爲P1的縱座標, 代入到L1的直線方程中能夠計算出交點橫座標; 

5.若是P1和P2縱座標不一樣, 可是Q1和Q2縱座標相同, 即L1平行於X軸, 則交點縱座標爲Q1的縱座標, 代入到L0的直線方程中能夠計算出交點橫座標; 

6.剩下的狀況就是L1和L0的斜率均存在且不爲0的狀況 

a)計算出L0的斜率K0, L1的斜率K1;

b)若是K1 = K2 

    i.若是Q1在L0上, 則說明L0和L1共線, 假如L1是直線的話有無窮交點, 假如L1是線段的話可用"計算兩條共線線段的交點"的算法求他們的交點(該方法在前文已討論過);

    ii.若是Q1不在L0上, 則說明L0和L1平行, 他們沒有交點.

c)聯立兩直線的方程組能夠解出交點來

這個算法並不複雜, 可是要分狀況討論清楚, 尤爲是當兩條線段共線的狀況須要單獨考慮, 因此在前文將求兩條共線線段的算法單獨寫出來. 另外, 一開始就先利用矢量叉乘判斷線段與線段(或直線)是否相交, 若是結果是相交, 那麼在後面就能夠將線段所有看做直線來考慮. 須要注意的是, 咱們能夠將直線或線段方程改寫爲ax+by+c=0的形式, 這樣一來上述過程的部分步驟能夠合併, 縮短了代碼長度, 可是因爲先要求出參數, 這種算法將花費更多的時間.

相關文章
相關標籤/搜索