Vector Product

程度★ 難度★

內積與外積

電腦做運算時,會有浮點數誤差的問題。為避免浮點數誤差的問題,用電腦計算幾何問題時,會採用不同於一般數學運算時所用的公式和定理。

內積(inner product、dot product)、外積(outer product、cross product)這兩個運算只用了加法和乘法,而不包括除法,故能有效的避免除法所產生的浮點數誤差。內積與外積有許多很有用的特性。大部分的幾何問題,都可以用內積與外積來計算答案。

此處僅作簡單介紹,以下都是用二維空間當作範例。

資料結構

內積與外積是向量運算,所以得設計一個向量的資料結構。

向量資料結構擁有一個座標,並擁有一支內積函式與一支外積函式。

兩個向量做內積的結果是一個純量。兩個向量做外積的結果為一個向量,然而我們通常只會用到純量部份,所以讓外積函式的回傳值為純量。

內積、外積跟長度的關係

內積後取絕對值,求得的是投影量,再除以投影標的的單位向量,則得到投影長度。

外積後取絕對值,求得的是平行四邊形的面積量,再除以底的單位向量,則得到高。

內積、外積跟角度的關係

注意到acos與asin的回傳值,回傳的結果是弳度量(radian)而非度度量(grade),而且回傳值的範圍也不同。一般都以內積與acos求得介於0˚到180˚之間的夾角大小。

內積與向量夾角

利用內積的性質,可以粗略判斷夾角大小:內積大於0時,兩向量夾角小於90˚;等於0時,夾角等於90˚;小於零時,夾角大於90˚且小於180˚。

外積與向量旋轉

外積大於0時,兩向量前後順序為逆時針順序(在180˚之內);等於0時,兩向量平行,也就是指夾角等於0˚或180˚;小於0時,兩向量前後順序為順時針順序(在180˚之內)。

舉例來說,要判斷多邊形凹凸,就沿著多邊形外圍繞一圈,看看每一條邊是不是折向同方向即可。

Distance

程度★ 難度★★

點到原點距離

以下簡單介紹二維座標平面計算距離的方式。

開根號相當耗費時間。有時候做一些幾何計算時,會將數學式子簡化到不必開根號,以節省計算時間。因此,設計不開根號的程式碼,有時候也是會有用途的。

點到點距離

畢氏定理、勾股定理。

點到線距離

用數學計算時常會用ax+by=c來表示直線,但是在電腦中通常會直接以相異兩點表示直線,而不會去計算abc。這種情況下可以運用外積來計算點到線距離。

外積可求出v1 v2組成的平行四邊形面積,然後除以v2的長度,便是垂直距離。外積可能會有負值,請記得取絕對值,才不會得到負的距離。

點到線段距離

點到線段的最短距離,有時候是點到線上的垂直距離,有時候卻是點到線段端點的距離。

由圖片可觀察到,點到線段的距離,和三點共線、點在線上這些因素無關,所以這裡將空間劃分為垂直距離區和端點距離區兩塊,用內積進行判斷。這只是一種劃分方式,各位也可以自行發明適合的劃分方式。

線段到線距離

判斷線段的兩個端點是不是在線的同側。如果在同側則計算點到線距離;如果在不同側或者有任一點在線上,則距離為零。用外積判斷是否同側。

線段到線段距離

這個問題似乎沒有更簡單的方式:如果線段有相交,則距離為零;如果不相交,則窮舉所有的端點到線段距離。點到線段的距離已經在先前的段落提過了,現在剩下線段相交尚待解決。

線段相交,也就是一條線段被另一條線段分為兩邊。所以我們只要檢查線段的兩頭,是不是位於另一條線段的兩側即可。

考慮線段平行的特殊情況。兩線段平行但不共線時,恰好可以直接用端點到線段的距離求出答案。至於兩線段共線時,不管兩線段有沒有相交,恰好也可以直接用端點到線段的距離求出答案。結論就是:只要兩線段平行或共線,便可以直接用端點到線段的距離求出答案,不用理會這兩條線段是否相交。

各位可以整合「線段相交」與「點到線段距離」的程式碼,避免重複計算相同的向量,以節省時間。

線段到線段距離

線段相交,可以想像成是兩條交錯的四邊形對角線。換句話說,就是將線段的端點安排成四邊形的頂點,讓四邊形的對角線成為原來的兩條線段。如此一來,只要用一個四邊形,便可代表這兩條線段。

凸四邊形的對角線,都會相交;凹四邊形、交叉四邊形的對角線,不會相交──於是判斷線段相交,可以轉化做判斷凸四邊形。要判斷凸多邊形,只要順著多邊形的外圍繞一圈,看看是否一直往同側轉彎即可。判斷轉彎得利用外積:順時針轉彎外積得正值,逆時針得負值。

另外,外積等於零則表示線段的端點產生三點共線,分析各種情況後,發現此時可以直接用端點到線段的距離求出答案。

各位可以整合「線段相交」與「點到線段距離」的程式碼,避免重複計算相同的向量,以節省時間。

線到線距離

兩線相交距離為零,兩線平行距離為l1上任取一點到l2的距離。用外積判斷平行。

UVa 152 10514 10709

Intersection

程度★ 難度★★

Intersection

人類比電腦擅長判斷相交。人類可以追著線條移動,快速找到交點;人類也有很強的空間感,能夠迅速劃分地理位置,看一眼就能區隔出成堆的線段。但是電腦卻做不到這些,電腦只會算數字、分條件。

判斷相交原本是極容易的事情,主角改為電腦之後,卻變成極複雜的事情了。下面介紹二維座標平面上判斷相交的方式、計算交點的方式。

線段與點,判斷相交

三角不等式,開根號後又相加,會有點誤差,一般沒人用。

比較妥當的方式,是先用外積判斷點與線段是否共線,再看看點是否在線段端點之間。

兩共線線段,判斷相交

判斷四個端點,是否位在另一條線段上面。偵測點與線段相交,一共四次。

兩線段,判斷相交

http://www.csie.nctu.edu.tw/~sctsai/adprog/notes/CompGeo.ppt

看看線段的兩端點是否位於另一線段的兩側,如此才會相交。

兩共線線段交點

兩線段重疊,交點無限多;兩線段端點相接觸,交點恰為一點;兩線段不相交,交點不存在。

如果兩共線線段要有剛好一個交點,那麼交點就一定是線段端點。只要先檢查線段是否重疊,再檢查端點是否相同就可以了。

兩線交點

可參考:http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/,這是數學公式解,是用聯立多項式推導出來的。

兩線段交點

UVa 191 273 378 527 866 10902 10907

Intersection

程度★ 難度★★

線段與點,判斷相交

還有一種方式是用座標大小關係判斷。

更簡潔的方式,是判斷點是否在線段之外。如此就不必擔心線段的兩個端點誰大誰小了。

實作時要小心兩條線段恰好是水平線、垂直線的情況,也要小心線段斜率小於零的情況。

兩共線線段,判斷相交

判斷四個端點,是否位在另一條線段上面。偵測點與線段相交,一共四次。

更簡潔的方式,是判斷兩條線段是否沒有相交。對一條線段而言,只要另一條線段的兩個端點都在相同外側,就是沒有相交。

實作時要小心兩條線段恰好是水平線、垂直線的情況。

兩線交點

這裡介紹一個利用外積來計算交點的方法,其實等同於數學公式解。請見下圖:

原則上,就是以p1為基準點,p34為平行四邊形的底,然後利用兩個平行四邊形的高的比例,便能求出p1到p2與p1到交點的距離比例。

平行四邊形的面積可用外積運算求出,所以這個方法相當方便。各位可依照下圖所列舉的各種情況(補上線段的延長線,使之為線),驗證此方法是有效的,並試著整理出例外情況。

實作程式碼時,要注意外積的順序,外積的順序不同會產生出正負號的差異。

兩線段交點

線段與線段的交點。跟上一段所述相同,只是要小心判斷兩個平行四邊形的高的比例是不是為負值、或者超過1,此即表示線段p34超出了線段p12的範圍,不會相交。至於範圍是指p1和p2兩點,以平行p34的方向所畫出的邊界線作為範圍,如下圖所示:

另外,除了以p1為基準點,還要再以p3為基準點再算一遍,才能確定兩線段到底有沒有相交。

至於兩共線線段求交點的情況,前面章節已提過,這裡就不予討論。大家可以自己揣摩看看。

小細節就略過,程式碼如下:

由於p13與p31只差一負號,故可以用p13代替p31。另外c1與c3也只差一負號,故可以用c1代替c3。於是程式碼可再精簡。