數學基礎:向量叉乘
詳情點擊數學基礎之向量點乘與叉乘
判斷線段相交
常用的方法是通過向量叉乘來判斷,這種方法不需要算出直線方程(再判斷交點有否),在代碼實現上比較簡便。
用這種方法判別線段是否相交一般分為兩步:
1. 快速排斥實驗
2. 跨立實驗
Part1:快速排斥實驗
首先判斷兩條線段在$x$以及$y$坐標的投影是否有重合。也就是判斷一個線段中$x$較大的端點是否小於另一個線段中$x$較小的段點,若是,則說明兩個線段必然沒有交點,同理判斷下$y$。
//如果四條判斷有一個為真,則代表兩線段必不可交,否則應該進行第二步判斷。 max(C.x,D.x)<min(A.x,B.x) || max(C.y,D.y)<min(A.y,B.y) || max(A.x,B.x)<min(C.x,D.x) || max(A.y,B.y)<min(C.y,C.y)
顯然,上圖通不過快速排斥實驗。
Part2:跨立實驗
向量間的順逆時針關系
計算向量叉乘是與直線和線段相關算法的核心部分。
設向量$\vec P = (x_1, y_1)$,$\vec Q = (x_2, y_2)$,則向量叉乘定義為:$\vec P \times \vec Q = x_1 * y_2 - x_2 * y_1$,其結果是一個向量,且為向量$\vec P$, $\vec Q$所在平面的法向量。顯然有性質$\vec P \times \vec Q = - (\vec Q \times \vec P)$和$\vec P \times (-\vec Q) = - (\vec P \times \vec Q)$。
叉乘的一個非常重要性質是可以通過它的符號判斷兩向量相互之間的順逆時針關系:
- 若$\vec P \times \vec Q > 0$, 則$\vec P$在$\vec Q$的順時針方向。
- 若$\vec P \times \vec Q < 0$, 則$\vec P$在$\vec Q$的逆時針方向。
- 若$\vec P \times \vec Q = 0$, 則$\vec P$與$\vec Q$共線,但可能同向也可能反向。
通過叉乘來判斷線段相交
如果兩線段相交那么就意味着它們互相跨立,即如上圖點A和B分別在線段CD兩側,點C和D分別在線AB兩側。
判斷A點與B點是否在線段DC的兩側,即向量$\vec {AD}$與向量$\vec {BD}$分別在向量$\vec {CD}$的兩端,也就是其叉積是異號的,即($\vec {AD}$ $\times$ $\vec {CD}$) ∗ ($\vec {BD}$ $\times$ $\vec {CD}$) < 0。同時也要證明C點與D點在線段AB的兩端,兩個同時滿足,則表示線段相交。
然后我們來看看臨界情況,也就是上式恰好等於 0 的情況下:
當出現如上圖所示的情況的時候,($\vec {AD}$ $\times$ $\vec {CD}$) * ($\vec {BD}$ $\times$ $\vec {CD}$) = 0,顯然,這種情況是相交的。只要將等號直接補上即可。
再接得想一想,如果沒有第一步的快速排斥實驗,僅判斷第二步,會出現什么問題?
當出現如上所示的情況的時候,叉積都為 0, 可以通過跨立實驗,但是兩個線段並沒有交點。不過還好,這種情況在第一步快速排斥已經被排除了。
代碼實現
struct Line { double x1; double y1; double x2; double y2; }; bool intersection(const Line &l1, const Line &l2) { //快速排斥實驗 if ((l1.x1 > l1.x2 ? l1.x1 : l1.x2) < (l2.x1 < l2.x2 ? l2.x1 : l2.x2) || (l1.y1 > l1.y2 ? l1.y1 : l1.y2) < (l2.y1 < l2.y2 ? l2.y1 : l2.y2) || (l2.x1 > l2.x2 ? l2.x1 : l2.x2) < (l1.x1 < l1.x2 ? l1.x1 : l1.x2) || (l2.y1 > l2.y2 ? l2.y1 : l2.y2) < (l1.y1 < l1.y2 ? l1.y1 : l1.y2)) { return false; } //跨立實驗 if ((((l1.x1 - l2.x1)*(l2.y2 - l2.y1) - (l1.y1 - l2.y1)*(l2.x2 - l2.x1))* ((l1.x2 - l2.x1)*(l2.y2 - l2.y1) - (l1.y2 - l2.y1)*(l2.x2 - l2.x1))) > 0 || (((l2.x1 - l1.x1)*(l1.y2 - l1.y1) - (l2.y1 - l1.y1)*(l1.x2 - l1.x1))* ((l2.x2 - l1.x1)*(l1.y2 - l1.y1) - (l2.y2 - l1.y1)*(l1.x2 - l1.x1))) > 0) { return false; } return true; }
參考資料:
https://blog.csdn.net/qq826309057/article/details/70942061