如何判斷一個多邊形是否合法 (Swift 代碼實現)

背景及問題分析

利用無人機對一片區域進行測繪前,咱們會先在地圖上框選一個區域,而後再規劃飛行的路線,而須要測繪的這片區域每每是一個多邊形。在 MeshKit iOS 中,咱們加入了多邊形區域的編輯功能,其中就涉及判斷用戶所編輯出來的多邊形是否合法的問題。算法

首先咱們要肯定一個標準:怎麼樣纔算一個不合法的多邊形 ?咱們能夠簡單地經過下面這幅圖來解釋一下: ide

咱們能夠看出前面兩個分別是凹多邊形和凸多邊形,而最後一張則是咱們所說的不合法多邊形,能夠看出這個不合法的多邊形的特徵就是:它存在某條邊與另一條邊相交的狀況spa

那麼要判斷一個多邊形是否合法,咱們只要判斷組成多邊形的全部線段是否存在相交的狀況便可,固然,咱們這裏所說的相交是 規範相交 ,即 交點不在線段的端點上3d

好了,那麼如今的問題能夠簡化成:如何判斷兩條線段是否規範相交code

算法解析

這裏咱們須要藉助 向量的叉積 來進行判斷。cdn

叉積,又稱向量積,是對三維空間中的兩個向量的二元運算。視頻

這裏推薦 3Blue1Brown 的 視頻 來快速回顧一下叉積的概念(下面的兩幅截圖來自此視頻)。咱們只需知道叉積的結果是有正負的,好比咱們以向量 \vec{v} 爲標準,以下圖,向量 \vec{w}\vec{v}順時針方向,那麼 \vec{v} \times \vec{w} < 0blog

若是向量 \vec{w}\vec{v}逆時針方向,那麼 \vec{v} \times \vec{w} > 0get

那麼咱們如何利用叉積的特性運用到判斷線段是否相交上呢?it

咱們先看下面最直接的一個線段相交的狀況:

線段 P_1P_2 和 線段 Q_1Q_2 明顯存在一個交點,從上面這張圖咱們能夠作一個簡單的結論:若是一條的線段的兩個端點在另一條線段兩側,那麼這兩條線段可能相交,注意這裏說的是可能相交,稍後會講到另一種狀況。

咱們能夠將上面的圖轉換爲向量的狀況來看:

是否是以爲似曾相識,這跟上面提到的叉積的狀況是否是很相似? 向量 \vec{P_1Q_1}\vec{P_1P_2} 向量 \vec{P_1Q_2}\vec{P_1P_2} 的順時針方向,那麼:\vec{P_1P_2} \times \vec{P_1Q_2} < 0

用 A 表示 \vec{P_1P_2} \times \vec{P_1Q_1} 的叉積結果,用 B 表示 \vec{P_1P_2} \times \vec{P_1Q_2} 的叉積結果,那麼 一條的線段的兩個端點在另一條線段兩側 這個幾何現象能夠用這個公式表示 :A{\times}B<0

咱們前面提到 若是一條的線段的兩個端點在另一條線段兩側,那麼這兩條線段可能相交 ,爲何是可能相交呢?若是咱們將 線段 Q_1Q_2 往右邊移動一下,會存在下面這種狀況:

從上圖能夠看出,線段 Q_1Q_2 的兩個端點在線段 P_1P_2 兩側,可是它們並無相交。

那麼如何排除這種狀況呢?其實很簡單,咱們以前都是以線段 P_1P_2 做爲主視角,若是將主視角換成線段 Q_1Q_2,那麼咱們很容易看出 線段 P_1P_2 的兩個端點並無在 線段 Q_1Q_2 的兩側。因此咱們再次看回上面相交的那幅圖,爲了可以充分的判斷兩條線段相交,此次以 Q_1Q_2 爲主視角看待這個問題,求叉積:

向量 \vec{Q_1P_2}\vec{Q_1Q_2} 的逆時針方向,那麼: \vec{Q_1Q_2} \times \vec{Q_1P_2} > 0 向量 \vec{Q_1P_1}\vec{Q_1Q_2} 的順時針方向,那麼: \vec{Q_1Q_2} \times \vec{Q_1P_1} < 0

綜上,咱們能夠得出:

A = \vec{P_1P_2} \times \vec{P_1Q_1}
B = \vec{P_1P_2} \times \vec{P_1Q_2}
C = \vec{Q_1Q_2} \times \vec{Q_1P_1}
D = \vec{Q_1Q_2} \times \vec{Q_1P_2}

A{\times} B < 0 && C \times D < 0 的時候,兩條線段規範相交。 至於向量的叉積如何運算,這裏就不細寫了,給出一張計算草稿給你們過目一下:

示例代碼

根據計算草稿的內容,咱們就很容易經過代碼來實現了:

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
}
複製代碼

因爲筆者能力有限,文中若有錯誤還請各位讀者不吝賜教。

相關文章
相關標籤/搜索