1. 程式人生 > >凸多邊形重疊計算

凸多邊形重疊計算

文章目錄

前言

兩個凸多邊形的重疊問題就是對兩個凸多邊形求相交部分的問題。約定凸多邊形指它的邊界和內部,凸多邊形仍用頂點座標的逆時針方向序列確定。

設給出的兩個凸多邊形 P 和 Q 的頂點序列分別是 P1,P2,…,PL 和 Q1,Q2,…,Qm。

假設 P 的邊界上不包含 Q 的頂點 ,Q 的邊界也不包含 P 的頂點。有兩個問題需要解決:

  1. 如何有次序地求出各邊的所有交點 ,
  2. 如何排列求出的交點和原凸多邊形的頂點 , 形成新的凸多邊形的頂點序列。

求交點

為了有次序地求出交點 , 可以在兩個多邊形邊上交替地前進,原則是在哪個多邊形的邊上可能有交點就等待 , 在另一個多邊形的邊上前進。

前進的原則可以自己隨便定義,但是得滿足以下兩個要求(後面會解釋):

  1. 兩個多邊形向前走的機會相同
  2. 所有情況都要覆蓋

初始從 對邊 P 0

P 1 Q 0 Q 1
P_0P_1 與 Q_0Q_1
的求交 開始 , 注意所有求交是線段的求交。這裡規定了 P 0 = P L , Q 0 = Q m P_0=P_L,Q_0=Q_m

設已經算得 P i 1 P i Q j 1 Q j P_{i-1}P_i 和 Q_{j-1}Q_j 的交點

  1. 依可能性判定接下去計算的是 P i P i + 1 Q j 1 Q j P_iP_{i+1} 與 Q_{j-1}Q_j 的交點還是 P i 1 P i Q j Q j + 1 P_{i-1}P_i 和 Q_jQ_{j+1} 的交點
    如果是算第一個,就是 P 多邊形前進。算第二個就是 Q 多邊形前進。
  2. 此外還需考慮 P i Q j P_i 和 Q_j 之間的夾角是否大於 180 度

強調:初始從對邊 P 0 P 1 Q 0 Q 1 P_0P_1 與 Q_0Q_1 的求交開始 , 注意所有求交是線段的求交。

前進情況

P i 1 P i Q j 1 Q j P_{i-1}P_i 和 Q_{j-1}Q_j 的相對位置總共有 8 種,分別如下:

在這裡插入圖片描述
上圖中對於第一行的四種情況可分為一類,第二行的四種情況就是將第一行的 P Q 兩條線段換了下位置。

記憶第一行的四種情況可以採取以下方式:

  1. 將 “短的” P i 1 P i Q j 1 Q j P_{i-1}P_i 和 Q_{j-1}Q_j 看作 0,“長的” 看作 1
  2. 這樣可以上述四種情況可以分別記成 00 01 10 11 形式

對於 P Q 線段的相對位置總共就 8 種情況,對於選哪個前進我們剛才說了兩個原則:

  1. 兩個多邊形向前走的機會相同。在這 8 種情況中我們不能定義成 5 種是 P 前進,3 種是 Q 前進。前進方案是任意定義的。圖中是一種定義方案,概率均為 1/2。而我們也可以定義成第一行全部是 P 前進,第二行全部是 Q 前進。區別是在程式設計的實現不同。
  2. 所有情況都要覆蓋。不能遺漏某種情況,這裡是對程式設計的時候說的。

P i 1 P i Q j 1 Q j P_{i-1}P_i 和 Q_{j-1}Q_j 夾角

在上圖中我們不難發現,對於第一行的四種情況單純只按 P i Q i P i 1 P i Q i 1 Q i P_i Q_i 與 P_{i-1}P_i Q_{i-1}Q_i 的相對位置來劃分的話,是無法與第二行的四種情況區分開來的。比如 情形(1) 和 情形(8)。

在這裡插入圖片描述此圖中的 情形 8 和上圖中的 情形 8 都是一種型別,要注意辨別。

此時我們就要利用 P i 1 P i × Q i 1 Q i P_{i-1}P_i×Q_{i-1}Q_i (向量叉乘)的 z 分量(用右手定則判斷方向)來判斷。

  • 如果該分量大於等於 0,則是前四種情形
  • 否則是後四種情形

虛擬碼

按照第一張圖約定的前進方法,我們可以寫出如下虛擬碼。

/* P、Q為多邊形頂點陣列,l、m為頂點個數*/
void Advance(PONIT P[],int l,POINT Q[],int m){  
	int s;
	s = vector3(P,Q,i,j); //s 置成 PiPi-1 與 QjQj-1 的叉乘的 z 值; vector3 是 unity 的一個 API
	if (s >= 0){
		if((left(P,i,Q,j) && left(Q,j,P,i)) || (right(P,i,Q,j) && left(Q,j,P,i))){ 
			if (i < l) i++; 
			else i = 1;
		}else {
			if(j < m) j++; 
			else j = 1;
		}
	}else{
		if((right(P,i,Q,j) && left(Q,j,P,i)) || (right(P,i,Q,j) && right(Q,j,P,i))){ 
			if (i<l) i++; 
			else i = 1;
		} else{
			if(j < m) j++; 
			else j = 1;
		}
	}
}

排列交點和頂點

為了正確排列求出的交點並加入原兩個凸多邊形部分頂點以形成相交的凸多邊形,可以在每求出一個交點時進行一次輸出。

求出的第一個交點可做一下記錄,如果在以後交替前進求交點的過程中再次求出與第一次求得相同的交點,就知道整個求交過程已經結束了。

求得的交點不是第一個時,為形成交得凸多邊形頂點序列,要區分邊 P i 1 P i P_{i-1}P_i 是進入多邊形 Q,還是走出 Q 兩種情況。(這決定了我們選擇 P i 1 P i P_{i-1} 還是 P_i )

在這裡插入圖片描述

  1. P i 1 P i P_{i-1}P_i 正進入多邊形 Q ,此時應先輸出 上次求得交點後的多邊形Q上的各頂點,再輸出本次交點。
  2. P i 1 P i P_{i-1}P_i 是走出多邊形 Q ,此時應先輸出 上次求得交點後的多邊形P上的各頂點,再輸出本次交點。
    區分這兩種情況,可通過檢查 P i P_i