判断某点是否在三角形内
判斷某點(diǎn)是否在三角形內(nèi)
這個(gè)問(wèn)題碰到過(guò)好幾次了,不僅是筆試的時(shí)候,還有在工作上,所以這里做個(gè)小總結(jié)。
1、通過(guò)第三方庫(kù)函數(shù)
首先介紹最簡(jiǎn)單的方法,直接調(diào)用已有的函數(shù)。采用python的matplotlib庫(kù),里面的Path.contains_points,
該函數(shù)十分強(qiáng)大,可以根據(jù)任意幾個(gè)點(diǎn)所組成的多邊形,計(jì)算新輸入的點(diǎn)是否在該多邊形內(nèi),當(dāng)然三角形就是其中的一種情況了。
該函數(shù)使用示例如下:
>>>from matplotlib import path >>>p = path.Path([(0, 0), (1, 0), (1, 1)]) >>>p.contains_points([(0.1, 0.1)]) array([ True], dtype=bool)其中我們用(0, 0)、(0, 1)、(1, 1)構(gòu)建了一個(gè)三角形,當(dāng)新輸入一點(diǎn)(0.1, 0.1)的時(shí)候,返回值為True,說(shuō)明該點(diǎn)位于三角形內(nèi)。該函數(shù)還支持輸入多個(gè)點(diǎn)進(jìn)行判決,得到一個(gè)bool型的array數(shù)組。
看到array自然就能聯(lián)想到,它也支持以numpy.array格式輸入的點(diǎn),但是一定要注意,以此種格式輸入時(shí),一定要加上reshape(n, 2),即輸入必須為一個(gè)n行2列的數(shù)組。
2、內(nèi)角和等于360°
這種方法最好理解,即對(duì)于△ABC內(nèi)的某一點(diǎn)P,連接PA、PB、PC得到三條線段,當(dāng)且僅當(dāng)三條線段組成的三個(gè)夾角和為360°的時(shí)候,該點(diǎn)才位于三角形內(nèi)部。但此種方法的計(jì)算涉及到平方根、反正(余)弦,效率十分低下。
類似的還有,P與ABC三個(gè)頂點(diǎn)組成的三個(gè)三角形,面積之和等于△ABC的面積,同樣可以證明點(diǎn)P位于三角形內(nèi)部,但效率也很低。
3、Same Side Technique
這種方法是我從別處看來(lái)的,覺(jué)得很新穎,想看英文原版的可以直接點(diǎn)這里。下面我解釋一下該方法的思想,首先看下圖:
如果某點(diǎn)位于△ABC內(nèi)部,那么該點(diǎn)一定位于AB的下邊,AC的右邊,BC的左邊。要用數(shù)學(xué)的方法來(lái)表示,可以采用向量積,向量積有個(gè)很重要的右手定則,忘記了的自行復(fù)習(xí)一下。下面接著看下一張圖:
對(duì)于任意的一點(diǎn)P, AB→×AP→,其結(jié)果根據(jù)右手定則,應(yīng)為指向屏幕外,而AB→×AP′→的結(jié)果則是指向屏幕里。根據(jù)這一特性,我們只要找到某一點(diǎn)P,滿足AB→×AP→與AB→×AC→具有同一方向、AC→×AP→與AC→×AB→具有同一方向、BC→×BP→與BC→×BA→具有同一方向,則點(diǎn)P位于△ABC內(nèi)。
這種方法涉及到的計(jì)算是向量運(yùn)算,沒(méi)有平方根、反余弦這些,效率比第2種方法高,代碼部分可以參考原文,下面著重介紹最后一種方法。
4、Barycentric Coordinates
最后一種方法的效率最高,但是理解起來(lái)需要的步驟多一點(diǎn),我們先看下面這張圖
AP→可表示為:
其中u、v為常量系數(shù),從圖中我們即可得知,當(dāng)u>0且v>0時(shí),P的位置才有可能正確,那是不是u、v可以無(wú)限大呢?當(dāng)然不是,當(dāng)u+v>1時(shí),P就會(huì)超出三角形的范圍,而當(dāng)u+v=1時(shí),點(diǎn)P剛好位于BC上,這里稍微證明一下。
當(dāng)點(diǎn)P在BC上時(shí),也就是 BP→、PC→同向,將前面 AP→的表示方式代入,那么這兩個(gè)向量可以分別表示為:
BP→=AP→?AB→=(u?1)?AB→+v?AC→ PC→=AC→?AP→=(1?v)?AC→?u?AB→
由于兩者同向,那么就存在一個(gè) λ( λ>0),使得 BP→=λPC→,代入上面兩式,整理一下,就得到了下面的式子:
(u?1+λu)?AB→=(λ?λv?v)?AC→
上式要成立,當(dāng)且僅當(dāng)左右兩邊的系數(shù)都為0。由此得到兩個(gè)方程,消去 λ就可以得到u+v=1了。有了這一點(diǎn),為什么三角形內(nèi)部點(diǎn)的u、v之和小于1,外部點(diǎn)的大于1,請(qǐng)讀者自行理解,這里不再展開了。
根據(jù)模型,得到公式
由此我們的模型就得到了:對(duì)于任意給定的一點(diǎn)P,判斷其是否位于△ABC的內(nèi)部,就是要計(jì)算其相對(duì)于△ABC的u、v值,只要這兩個(gè)值滿足u>0、 v>0、 u+v<1,那么可以確定點(diǎn)P位于三角形內(nèi)。這樣只要得到u、v的表達(dá)式,在代碼里面就能直接使用了。該表達(dá)式的推導(dǎo)還是有點(diǎn)麻煩的,我看原文(最底下)中,作者是用軟件算出來(lái)的,想挑戰(zhàn)一下的朋友也可以自行推導(dǎo),這里我直接給出最終的代碼(參考的是stackoverflow上面的第三個(gè)回答):
Area = 0.5 *(-p1y*p2x + p0y*(-p1x + p2x) + p0x*(p1y - p2y) + p1x*p2y);u = 1/(2*Area)*(p0y*p2x - p0x*p2y + (p2y - p0y)*px + (p0x - p2x)*py); v = 1/(2*Area)*(p0x*p1y - p0y*p1x + (p0y - p1y)*px + (p1x - p0x)*py);其中p0、p1、p2分別是三角形的三個(gè)頂點(diǎn),p表示待判斷的點(diǎn),后面跟著x、y表示頂點(diǎn)的橫、縱坐標(biāo)。只要滿足u>0、v>0、u+v<1,就說(shuō)明p位于p0、p1、p2組成的三角形內(nèi)部,這幾個(gè)公式的計(jì)算量對(duì)計(jì)算機(jī)而言是相當(dāng)小了。
PS
第一次寫技術(shù)類博客,竟然是兩個(gè)月以后了,拖延癥晚期啊。而且剛開始寫,也不知道側(cè)重點(diǎn)在哪,因?yàn)橛械臅r(shí)候看下解決方案就好,有的時(shí)候可能還想了解一下原理,還是慢慢摸索吧。最后,這篇文章還是費(fèi)了不少時(shí)間的,由此我想到了網(wǎng)上那些大神的博客,衷心為他們點(diǎn)贊!
總結(jié)
以上是生活随笔為你收集整理的判断某点是否在三角形内的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: STM32的三种Boot模式的差异
- 下一篇: 拒绝垃圾专业化学:选择正确的专业远比多考