Polygon
程度★ 難度★
Polygon
多邊形是由許多條邊端點對著端點串接起來的圖形。
要記錄一個多邊形的資訊,有許多種方法,例如:一、按照連接順序把多邊形的邊放到一個陣列裡面;二、按照連接順序把多邊形的頂點放到一個陣列裡面;三、挑一個頂點作為起點,從起點開始按照連接順序把各條邊的長度、邊與邊之間的夾角放到一個陣列裡面。
這幾種方法的空間複雜度都是O(N),N為多邊形的頂點數目,也可以說是邊的數目。
多邊形依照形狀不同,可細分為不同類型:
Polygon Area
程度★ 難度★★
測量師公式(Surveyor's Formula)
也就是高中所教的行列式公式,用來求多邊形面積。
1 | x1 x2 x3 xN-1 xN x1 |
area = --- * | × × ... × × |
2 | y1 y2 y3 yN-1 yN y1 |
右下斜線相乘取正號,左下斜線相乘取負號,然後通通加起來,除以二。
如果是逆時針旋轉,求出來為正值。
如果是順時針旋轉,求出來為負值,必須再取絕對值。
其實就是相鄰兩點求外積,然後統統加起來,除以二。
【待補圖片】
先研究一下凸多邊形面積。假設凸多邊形的其中一個頂點剛好是原點。現在從原點放射把凸多邊形切割成許多三角形。 我們知道外積可以算三角形面積,所以凸多邊形的面積就很好算了。 接著我們假設原點在凸多邊形外,現在從原點放射把凸多邊形額外切割出幾個凸多邊形外的三角形。 我們知道外積所得之面積有正負號,端看兩條向量的旋轉順序。可以發現凸多邊形外的三角形,外積所得的面積也剛好是負的。於是凸多邊形面積還是很好算。 接著考慮凹多邊形,可以發現負面積的性質仍然存在。 接著考慮簡單多邊形,可以發現負面積的性質仍然存在。 接著考慮任意多邊形,可以發現負面積的性質仍然存在。
時間複雜度為O(N),N為多邊形的頂點數目。
UVa 10060 10652 922
Point in Polygon
程度★ 難度★★
判斷一個點是否在凸多邊形內部
比較直覺的方式,是沿著凸多邊形外圍繞一圈,看看點是不是在每一條邊的同側即可。若發現外積皆小於零,即表示點在多邊形內部:若發現外積等於零,即表示點在凸多邊形上、或在凸多邊形某條邊的延伸線上;若發現外積大於零,則表示點在凸多邊形外部。
另外一種方式,是將點到凸多邊形頂點的各條向量,利用外積運算判斷是否都往同一方向旋轉,如果都是往同一方向旋轉,則表示點在凸多邊形內部;如果中途出現反方向旋轉,則表示點在凸多邊形外部;如果中途出現外積為零的情況,表示點在凸多邊形上,而且就在對應的邊上。
時間複雜度為O(N),N為凸多邊形的頂點數目。
UVa 109 361 476 477 478 10078 10291 10321
判斷一個點是否在凸多邊形內部
凸多邊形內任選一點作為原點(例如所有點的平均值),然後依角度大小排序凸多邊形的所有頂點。之後就可以用Binary Search找出給定的點在哪個夾角之內,最後判斷點是不是在此夾角構成的三角形裡面。
O(NlogN)預處理,O(logN)求答案。
判斷一個點是否在簡單多邊形內部(Ray Casting Algorithm)
簡單多邊形的情況下,可以使用射線演算法:http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html。原理很簡單,從給定點開始,往隨便一個方向(通常是水平往右)射出一條無限長射線,看看穿過多少條邊,如果穿過偶數次,表示點在簡單多邊形外部;如果穿過奇數次,表示點在簡單多邊形內部。
時間複雜度為O(N),N為簡單多邊形的頂點數目。
UVa 634 11030 10348
Polygon Filling
程度★ 難度★★
前言
在3D繪圖當中,我們常常要填滿一個多邊形裡面所有的像素(pixel),讓整塊多邊形擁有顏色。多邊形是浮點數座標,以頂點的形式儲存在資料結構中;像素則是整數座標,整個畫面到處都是。
讀了上一個章節,可以聯想到一個簡單的方式:窮舉畫面上所有像素,判斷每個像素是否在多邊形內,如果是,就填上像素。當畫面與多邊形差不多大,這個方式很有效率;當畫面比多邊形大很多,這個方式會浪費許多時間,讓許多玩FPS遊戲的人抓狂。時間複雜度是O(XYN),其中X與Y是畫面的長與寬,N是簡單多邊形的頂點數目。
讀者也可能很快的想到Flood Fill Algorithm,然而它並不應景。我們不知道多邊形的邊界是畫面上的哪些像素──沒有在畫面上圍出邊界,就無法使用Flood Fill Algorithm。
我們可以先把多邊形的邊界描在畫面上,再來使用Flood Fill Algorithm,這不失為一個好解法。至於下面要介紹的演算法,也是先把多邊形的邊界描出來,不過它的裝填方式,比Flood Fill Algotithm還要有效率多了。
裝填凸多邊形(Scanline Fill Algorithm)
一、先求出凸多邊形的最低像素和最高像素,把凸多邊形的邊,分類為左邊界和右邊界。二、然後建立兩條陣列,陣列大小等同於凸多邊形的垂直高低差,用來儲存垂直方向各個像素的左邊界與右邊界。三、接著依序窮舉凸多邊形的邊,把每條邊碰到的像素位置算出來,作為邊界值,存到陣列。四、最後以水平方向,一條一條填滿多邊形。
求出邊界需時O(N+2H),裝填需時O(2H+A),總共的時間複雜度是O(N+H+A),其中N是凸多邊形的頂點數目,H是凸多邊形的垂直高度差,A是凸多邊形的像素數目。
用Flood Fill Algorithm求出邊界需時O(N+2H),裝填需時O(4A),總共的時間複雜度也是O(N+H+A)。雖然時間複雜度與Scanline Fill Algorithm一樣,然而Flood Fill Algorithm的裝填速度還是慢了一點。
UVa 143 356
裝填簡單多邊形
請參考:http://alienryderflex.com/polygon_fill/。原理與Point in Polygon的演算法是一樣的。
另一種很常用的方式,是將簡單多邊形進行三角剖分,每個三角形各自裝填。
Count Grid Points in Polygon
程度★★★ 難度★★
多邊形的頂點剛好在都格點上
利用Pick's Theorem可以迅速求出簡單多邊形內部會有多少像素,限制是簡單多邊形的頂點必須不偏不倚位於像素上。
簡單多邊形面積 = 簡單多邊形圍住的像素數目 + 簡單多邊形上的像素數目 / 2 - 1
簡單多邊形其中一條邊上面的像素數目,可以利用邊的起點座標與終點座標,以X軸差與Y軸差的最大公因數求得。
UVa 10088
多邊形的頂點在任意位置
【待補文字】
Polygon Intersection(Under Construction!)
程度★★ 難度★★
兩個凸多邊形的交集
可以用旋轉卡尺求解,也可以用掃瞄線求解。O(N)。
外側向量超車擋在內側向量前面,車頭向內。 任選兩向量開始 先繞一圈找到交點 接下來再繞一圈,內側有前進的話,就會漸漸圍出交集 最後到達最初的交點時,結束 要小心退化的情況。
半平面交硬幹 O(NlogN) 平移掃描線 O(N) 交互前進 O(N) - 極大/極小頂點查詢 O(logN) 直線是否相交查詢 O(logN)
UVa 137 10321
兩個簡單多邊形的交集、聯集、差集
求出所有線段交點的演算法,略作修改。O(NlogN + K)。
Polygon Kernel(Under Construction!)
程度★★ 難度★
簡單多邊形的核
在多邊形內部,可以看到整個多邊形的地點們,稱做「核」。核可能是一個點、一條線段、或一塊凸的區域。
有核的多邊形稱做星狀多邊形(star-shaped polygon)。
所有邊的半平面交,即是核。O(NlogN)。【待補文字】
ICPC 2512 UVa 588
Polygon Visibility(Under Construction!)
程度★ 難度★★★
UVa 746