1. 程式人生 > >【演算法】計算機圖形學的一些經典小題:判斷點在多邊形內,隨機生成三角形內的點,判斷兩個矩形是否相交等

【演算法】計算機圖形學的一些經典小題:判斷點在多邊形內,隨機生成三角形內的點,判斷兩個矩形是否相交等

前幾天面試的時候被問到了,如何隨機在三角形內生成點,我按照我的想法回答了一遍,但覺得回答的不夠好。最後面試官說了一個最優的方法。覺得不錯,順帶總結一下最近看到的一些關於計算機圖形學方面的經典小題,知乎上看到的還有Leetcode上的

1.判斷一個點是否在多邊形內

首先先說一下輸入的內容,多邊形的頂點是一個數組輸入進來,其中每個相鄰點之間對應著多邊形上有邊相連

POINT p1 = ptPolygon[i];
POINT p2 = ptPolygon[(i + 1) % nCount];

即上面兩個點之間存在邊相連

判斷是否在凸多邊形內部

這個方法我記得是在程式設計之美上看到的,即計算該點與多邊形點之間向量的叉積,如果叉積同向則說明在內部,如果有反向則說明在外部。
這裡寫圖片描述


比如上面這個圖,分別計算PA,PB 的叉積 PB,PC 的叉積 PC,PA的叉積
如果其都是同向的,則說明其在內部,如果不是同向的,則說明在外部。
這個演算法複雜度是O(n)的,n是多邊的頂點數

判斷是否在任意多邊形內部

一個經典的演算法是射線法

  • 從該點P出發向任意方向射出一條射線, 如果與多邊形的邊相交的數量為奇數個,則說明其在內部,如果相交個數為偶數個則在內部
  • 一般為了計算方便就選擇從P點向x軸向右方向上的射線,而判斷相交只用求出交點的橫座標看起是否大於P.x的座標就好

2.隨機生成三角形內的點

這個是被面試官問到的題目,我的做法是先將三角形的外圍用一個矩形給括起來,然後對矩形內的點進行隨機取樣,取樣出來的點用上面1中的方法判斷是否在三角形內。如果不在則繼續取樣。
面試官對此不是很滿意,後來我想出來可以通過左邊軸變換的方式進行,但是回答的也不好
最後面試官說了自己的方法:
這裡寫圖片描述


網上找了一個圖將就著看吧
其實這個方法也是座標軸轉換的思想,我之前想到的已經比較接近了,只不過沒有往向量這塊去考慮。
即由之前的xy座標系轉換為以三角形的兩條邊為基底的座標系,如上圖所示轉換為OA,OB作為座標的表示,所以OP=tOA+kOB如果0<t,k<1的話其在OA,OB組成的平行四邊形內,判斷P點是否在三角形內只用判斷t+k<1即可,如果在三角形內則取樣成功,如果不在三角形內則可以再次取樣,還有更精妙的演算法,就是將超出的點重新對映到這個三角形內即可將其變為OP=(1t)OA+(1k)OB即可,推導非常簡單,可以自己嘗試一下,思路是如果超出三角形的話,可以將其看成O點對應平行四邊形的頂點C點進行生成的落在Δ
OAB
內的的點,而C點本身可以看成OA+OB得到

一個非常容易的易錯點

這道題一個非常經典的易錯思路是:比如上面的方法先隨機生成t,然後再在(0,1-t)的區間內生成k點。
因為這種不隨機:
比如假設t生成的結果非常接近於1,如果按照原始的方法則會出現大概率出現取樣超過原來三角形的情況,而在這種取樣的方式之下,必然會取樣成功。

3.判斷一個圓形和矩形是否相交

這道題是今天知乎上看到的:
怎樣判斷平面上一個矩形和一個圓形是否有重疊?
具體的演算法也非常簡單易懂,但是非常的巧妙。
我總結一下巧妙的地方在於:

  1. 先通過一個變換,將圓形變到了矩形的右上角,
  2. 通過向量,外加取最大值來巧妙的求得了圓形到矩形的最短距離【所以這個題目的簡化版本可以用來求解圓形到矩形的最短距離】

4.判斷兩個矩形是否相交併求相交部分面積

Leetcode上有這道題:
223. Rectangle Area
我一開始的思路是,類比與上面那道題,將第二個矩形換到第一個矩形右上角,然後判斷以第一個矩形右頂點為座標原點時,第二個矩形的左下角是否落在了第三象限。這樣用來判斷是否相交還是不錯的,但是求面積就不行了,比如如下的圖
這裡寫圖片描述

然後翻Leecode的discuss,底下最高票答案給的方法一時沒看明白

int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) {
    int left = max(A,E), right = max(min(C,G), left);
    int bottom = max(B,F), top = max(min(D,H), bottom);
    return (C-A)*(D-B) - (right-left)*(top-bottom) + (G-E)*(H-F);
}

後來查到了另外一個人的部落格,我覺得它講的挺明白的: 判斷兩矩形是否相交
這道題本質上相當於求兩個矩形相交區域的橫縱座標

int left = max(A,E);
int bottom = max(B,F);

這個是求得相交區域左下角的座標的。如果不相交也是沒有問題的
這個可以這麼看,即兩個矩形的左邊和下面兩條邊做直線延長,然後得到的相交區域一定是一個矩形,然後上面求得的座標就是這個矩形的右上角
同理,右邊和上面四條邊所在直線相交點的左下角為:

int right = min(C,G);
int top = min(D,H);

在判斷一下以上兩個座標點的相對位置,如果第一個在第二個左下角的話,則能夠構成矩形,如果不是這種情況則不能構成矩形。