查找两条线(和线段)的交点

介绍



通常,在开发游戏时,必须找到直线,线段,光线等的交点。本文中如何以最简单的方式实现这一点。



图片



流行方法及其批评



也许许多人会记住从学校代数开始的一种方法-制作两条直线的方程式,将它们的右手边等价,找到x,然后将其代入直线方程式以找到y(更多详细信息在这里)。



但是,此方法在编写代码时变得非常麻烦(也许这就是为什么现在阅读本文),而且,它不是通用的:如果其中一条直线平行于Y轴,则在计算斜率时将得到除以零的误差,因此必须为这种情况注册代码;如果两条线是平行的,那么您也需要修改这种情况。这样的代码又长又丑。



为了找到更优雅的解决方案,我偶然发现了一些基于矢量乘法的有趣方法(habr.com/ru/post/267037)和射线投射(ru.wikipedia.org/wiki/Ray_casting#Concept)。但是我认为它们在计算上是不合理的复杂。因此,我向您介绍(和批评)我的方法。



我的方式



任务



给出了两个线段的坐标。您需要找出线段是否相交,如果相交,则在什么时候相交。为此,我们将编写一个函数。



决断



图片



避免误解的图例:a-向量a,(y)-向量a在Y轴上的投影,{x1,y1}-向量a,由坐标x1,y1指定。



让我们以两个向量的形式表示我们的细分:{x2-x1; y2-y1}和b {x3-x4; y3-x4}。请注意,向量b的方向与预期相反。让我们介绍一个向量c {x3-x1; y3-y1}。注意a * k + b * n = c,其中k,n是一些系数。因此,我们获得了一个方程组:



a(x)* k + b(x)* n = c(x)

a(y)* k + b(y)* n = c(y)

我们的任务被简化为寻找这些系数(说实话,只找到其中​​之一就足够了)。



我建议将下式的两边乘以q = -a(x)/ a(y)。因此,在添加了两个方程式之后,我们立即摆脱了k。将n简化为求解普通线性方程。重要的是要注意,n可能没有解。



细心的读者会注意到,当(y)= 0时,我们会得到一个错误。让我们在找到(y)的阶段编写分支。这种情况甚至更简单,因为我们立即得到了一个未知数的方程。



我建议您尝试自行打印n,这样可以更清楚地了解下面代码中的内容。



知道n,就可以找到交点,为此,我们从点(x3,y3)的坐标中减去向量b * n



把它放在一起



float dot[2];  //  

bool cross(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
    float n;
    if (y2 - y1 != 0) {  // a(y)
        float q = (x2 - x1) / (y1 - y2);   
        float sn = (x3 - x4) + (y3 - y4) * q; if (!sn) { return 0; }  // c(x) + c(y)*q
        float fn = (x3 - x1) + (y3 - y1) * q;   // b(x) + b(y)*q
        n = fn / sn;
    }
    else {
        if (!(y3 - y4)) { return 0; }  // b(y)
        n = (y3 - y1) / (y3 - y4);   // c(y)/b(y)
    }
    dot[0] = x3 + (x4 - x3) * n;  // x3 + (-b(x))*n
    dot[1] = y3 + (y4 - y3) * n;  // y3 +(-b(y))*n
    return 1;
}


如果线段的直线(即直线)相交,则此函数获取顶点的坐标并返回值1;否则,返回0。如果需要顶点的坐标,则可以从点[]数组中获取它们。



重要提示:当引入两条重合线时,该算法不会显示任何交集。该算法找到线段所在的线的交点,因此该点可能在线段之外(您必须在代码中检入)。



让我们应用函数:



int main() {
    if (cross(1,1,7,2, 7,3,5,6)) {
        std::cout << dot[0] << " " << dot[1] << std::endl;
    }
    else {
        std::cout<<"Not cross!"<<std::endl;
    }
    return 0;
}


后记



尽管我在搜索问题过程中没有遇到过这种方法,而是自己开发了算法,但我并不假装自己是完全原创的(而且是正确的)。所以欢迎发表评论!



All Articles