如何判斷一個多邊形是否合法 (Swift 程式碼實現)
利用無人機對一片區域進行測繪前,我們會先在地圖上框選一個區域,然後再規劃飛行的路線,而需要測繪的這片區域往往是一個多邊形。在 MeshKit
iOS
中,我們加入了多邊形區域的編輯功能,其中就涉及判斷使用者所編輯出來的多邊形是否合法的問題。
首先我們要確定一個標準: 怎麼樣才算一個不合法的多邊形 ?我們可以簡單地通過下面這幅圖來解釋一下:

我們可以看出前面兩個分別是凹多邊形和凸多邊形,而最後一張則是我們所說的不合法多邊形,可以看出這個不合法的多邊形的特徵就是: 它存在某條邊與另外一條邊相交的情況 。
那麼要判斷一個多邊形是否合法,我們只要判斷組成多邊形的所有線段是否存在相交的情況即可,當然,我們這裡所說的相交是 規範相交 ,即 交點不線上段的端點上 。
好了,那麼現在的問題可以簡化成: 如何判斷兩條線段是否規範相交 。
演算法解析
這裡我們需要藉助 向量的叉積 來進行判斷。
叉積,又稱向量積,是對三維空間中的兩個向量的二元運算。
這裡推薦 3Blue1Brown 的視訊 來快速回顧一下叉積的概念(下面的兩幅截圖來自此視訊)。我們只需知道叉積的結果是有正負的,比如我們以向量 為標準,如下圖,向量 在 的 順時針方向 ,那麼 :

如果向量 在 的 逆時針方向 ,那麼 :

那麼我們如何利用叉積的特性運用到判斷線段是否相交上呢?
我們先看下面最直接的一個線段相交的情況:

線段 和 線段 明視訊記憶體在一個交點,從上面這張圖我們可以做一個簡單的結論: 如果一條的線段的兩個端點在另外一條線段兩側,那麼這兩條線段可能相交 ,注意這裡說的是可能相交,稍後會講到另外一種情況。
我們可以將上面的圖轉換為向量的情況來看:

是不是覺得似曾相識,這跟上面提到的叉積的情況是不是很類似? 向量 在 向量 在 的順時針方向,那麼:
用 A 表示 的叉積結果,用 B 表示 的叉積結果,那麼 一條的線段的兩個端點在另外一條線段兩側 這個幾何現象可以用這個公式表示 :
我們前面提到 如果一條的線段的兩個端點在另外一條線段兩側,那麼這兩條線段可能相交 ,為什麼是可能相交呢?如果我們將 線段 往右邊移動一下,會存在下面這種情況:

兩側,但是它們並沒有相交。
那麼如何排除這種情況呢?其實很簡單,我們之前都是以線段 作為主視角,如果將主視角換成線段 ,那麼我們很容易看出 線段 的兩個端點並沒有在 線段 的兩側。所以我們再次看回上面相交的那幅圖,為了能夠充分的判斷兩條線段相交,這次以 為主視角看待這個問題,求叉積:

綜上,我們可以得出:
當 && 的時候,兩條線段規範相交。 至於向量的叉積如何運算,這裡就不細寫了,給出一張計算草稿給大家過目一下:

示例程式碼
根據計算草稿的內容,我們就很容易通過程式碼來實現了:
private func isIntersect(line1: (CGPoint, CGPoint), line2: (CGPoint, CGPoint)) -> Bool { let p1 = line1.0 let p2 = line1.1 let q1 = line2.0 let q2 = line2.1 let a1 = (p2.x - p1.x) * (q1.y - p1.y) - (q1.x - p1.x) * (p2.y - p1.y) let a2 = (p2.x - p1.x) * (q2.y - p1.y) - (q2.x - p1.x) * (p2.y - p1.y) let b1 = (q2.x - q1.x) * (p1.y - q1.y) - (p1.x - q1.x) * (q2.y - q1.y) let b2 = (q2.x - q1.x) * (p2.y - q1.y) - (p2.x - q1.x) * (q2.y - q1.y) if a1 * a2 < 0 && b1 * b2 < 0 { return true } return false } 複製程式碼
由於筆者能力有限,文中如有錯誤還請各位讀者不吝賜教。