幾何與代數演算法

線段相交測試(segment-intersection test)

兩條直線幾乎總會在某處相交,但兩條有限線段——各是一條直線上、有兩個端點的一段——卻可能不相交,因為交點也許落在某一段(或兩段)之外。想像桌上躺著兩支鉛筆:它們真的碰到了嗎,還是只有延長後才會相遇?乾淨地判斷任意兩線段是否相交,而且不必算出(可能是分數的)交點,是碰撞偵測、地圖疊合,以及在一大堆線段中找出所有交點的掃描線演算法的基本構件。

乾淨的做法只用到四次方向測試。對線段 P1P2 與 P3P4,分別算出 P3 與 P4 相對於有向線 P1->P2 的轉向,以及 P1 與 P2 相對於有向線 P3->P4 的轉向。一般規則是:當 P3 與 P4 落在直線 P1P2 的兩側(兩個方向值正負相反)且 P1 與 P2 落在直線 P3P4 的兩側時,兩線段真正交叉。直覺上,若 P3 在 P1P2 左側而 P4 在右側,則線段 P3P4 必定橫切過直線 P1P2;對另一對端點要求對稱的條件,兩線段就一定真正相遇。由於每個方向測試只是一個叉積,整個測試在整數上精確且不用除法。

誠實要面對的麻煩是邊界情形,也就是某個方向值算出為零時:某端點恰好落在另一線段上,或兩線段共線且重疊。這些需要特別處理——通常加一個快速的「點是否在線段範圍內」的包圍盒檢查,確認那個接觸的端點確實落在另一線段的範圍內。略過這些退化情形,正是初學者幾何程式的經典錯誤。每一對的測試在 O(1) 時間完成;要在 n 條線段中找出所有交點,則交給掃描線範式處理,而非檢查全部 O(n^2) 對。

線段 (0,0)-(4,4) 與 (0,4)-(4,0):(0,4) 與 (4,0) 相對於線 (0,0)->(4,4) 的方向值正負相反,且 (0,0) 與 (4,4) 相對於線 (0,4)->(4,0) 也相反——故相交(於 (2,2))。線段 (0,0)-(1,1) 與 (2,2)-(3,3):共線(所有方向值皆零)但包圍盒不重疊,故不相交。

兩對端點都「分居兩側」就表示真正交叉;方向值為零時需另做「點在線段上」的檢查。

四方向規則處理真正的交叉;共線重疊與端點接觸的情形會給出零方向值,必須另外檢查,否則測試會在不知不覺中出錯。

又稱
do two segments cross判斷兩線段是否相交線段交叉測試