1.2D空間的直線相交
在二維空間中,利用兩個直線方程y = kx + b我們可以直接計算出交點,但是這種方法麻煩了些,並且套用到三維空間用公式就更麻煩了,接下來介紹的是如何利用向量叉乘求出直線交點。並且由於利用叉乘最后可以的到一個比例值,這個值的大小還可以判斷四個點所得到的兩個線段是延長線相交還是線段相交。
2.向量叉乘
三維空間中,兩個向量叉乘得到的是一個垂直於兩向量組成的平面的向量,方向可利用右手螺旋法則獲取,這一點百度谷歌一搜一大把,不細說了大小可由下面的公式得到,注意和點乘的區別。
向量叉乘的幾何意義是得到一個三角形的有向面積,如下圖所示,向量OA和OB叉乘的到的向量大小的二分之一就等於三角形OAB的面積
有了以上基礎,我們就可以開始計算三維空間中的直線交點了
3.三維空間中的兩直線交點
下圖CE和AB是平行線且長度相等。
首先確定兩條直線是否平行,利用向量點乘結果是否等於0來判斷,等於0垂直,等於1則平行。
接着我們需要確定兩條直線在一個平面內,否則無論如何也無法相交,這個用向量叉乘來判斷,即判斷向量CA和向量AB叉乘得到的向量是否垂直於向量CD。
然后明確一個目標,在四個點ABCD已知的情況下,求交點O我們只需要知道CO/CD就可以了。通過觀察發現,三角形ACD的面積比三角形CDE的面積等於線段CO和CD的比值,我們來證明一下,步驟很簡單。
證明 :S三角形ACD/S三角形CDE = AO比AB
三角形AFO和三角形EGC相似
AF / EG = AO/CE;
CE = AB 所以等式成立
問題轉化成要計算兩個三角形的面積,那么我們只需要 向量AB,CD和CA就可以了,開始寫代碼
3.代碼實現
利用叉乘求交點少了很多if else的判斷,並且可以做到二維和三維的通用,傳遞參數的時候只要將所有點和向量y軸的值設為0就可以當作二維來使用了。
利用代碼中得到的比例值num2的大小還可以判斷是延長線相交還是線段相交
注意下述方法所傳入的參數是兩個點和兩個方向,可以改寫成傳入四個點。
bool BeamSimu::line_intersect_3d(const QVector3D &line1P1, const QVector3D &line1P2, const QVector3D &line2P1, const QVector3D &line2P2, QVector3D &intersectP) { /* 設兩條線段的端點分別為 (x1,y1,z1), (x2,y2,z2) 和 (x3,y3,z3), (x4,y4,z4) 那么第一條線段方程為U(t) x=x1+(x2-x1)t y=y1+(y2-y1)t z=z1+(z2-z1)t 0<=t<=1 同樣,第二條線段方程為V(t) x=x3+(x4-x3)t y=y3+(y4-y3)t z=z3+(z4-z3)t 0<=t<=1 我們的問題就成為是否存在t1,t2,使得U(t1)=V(t2) 也就是求t1,t2,使得 x1+(x2-x1)t1=x3+(x4-x3)t2 y1+(y2-y1)t1=y3+(y4-y3)t2 z1+(z2-z1)t1=z3+(z4-z3)t2 可以通過前面兩條方程求出t1,t2,然后帶入第三條方程進行檢驗解是否符合。此外還要求0<=t1,t2<=1,否則還是不相交 */
// 方法一 // float x1 = line1P1.x(); // float y1 = line1P1.y(); // float z1 = line1P1.z(); // float x2 = line1P2.x(); // float y2 = line1P2.y(); // float z2 = line1P2.z(); // float x3 = line2P1.x(); // float y3 = line2P1.y(); // float z3 = line2P1.z(); // float x4 = line2P2.x(); // float y4 = line2P2.y(); // float z4 = line2P2.z(); // float t1 = 0,t2 = 0; // t2 = ( (z3-z1)/(z2-z1) - (x3-x1)/(x2-x1) ) / ( (x4-x3)/(x2-x1) - (z4-z3)/(z2-z1) ); // t1 = ( z3-z1 + (z4-z3)*t2) / (z2-z1); // if (t1 <= 1 && t2 <= 1) { // intersectP.setX(x1+(x2-x1)*t1); // intersectP.setY(y1+(y2-y1)*t1); // intersectP.setZ(z1+(z2-z1)*t1); // return true; // } // else // return false; // 方法二 /// 判斷線與線之間的相交 /// </summary> /// <param name="intersection">交點</param> /// <param name="p1">直線1上一點</param> /// <param name="v1">直線1方向</param> /// <param name="p2">直線2上一點</param> /// <param name="v2">直線2方向</param> /// <returns>是否相交</returns> { QVector3D v1 = line1P2 - line1P1; QVector3D v2 = line2P2 - line2P1; if (QVector3D::dotProduct(v1, v2) == 1) { // 兩線平行 return false; } QVector3D startPointSeg = line2P1 - line1P1; QVector3D vecS1 = QVector3D::crossProduct(v1, v2); // 有向面積1 QVector3D vecS2 = QVector3D::crossProduct(startPointSeg, v2); // 有向面積2 float num = QVector3D::dotProduct(startPointSeg, vecS1); // 判斷兩這直線是否共面 if (num >= 1E-05f || num <= -1E-05f) { return false; } // 有向面積比值,利用點乘是因為結果可能是正數或者負數
if (qFuzzyIsNull(vecS1.lengthSquared())) {
return false;
} float num2 = QVector3D::dotProduct(vecS2, vecS1) / vecS1.lengthSquared(); if (num2 > 1 || num < 0) { return false;//num2的大小還可以判斷是延長線相交還是線段相交 } intersectP = line1P1 + v1 * num2; return true; } }
原文鏈接:https://blog.csdn.net/xdedzl/article/details/86009147