求三维空间中两直线(或线段)的交点

 

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

 

posted @ 2020-02-19 10:03  狂奔~  阅读(19172)  评论(0编辑  收藏  举报