Encontrar el punto de intersecci贸n de dos l铆neas (y segmentos de l铆nea)

Introducci贸n



Muy a menudo, al desarrollar juegos, es necesario encontrar el punto de intersecci贸n de l铆neas rectas, segmentos, rayos, etc. C贸mo implementar esto de la manera m谩s simple posible, en este art铆culo.



imagen



Los m茅todos populares y su cr铆tica



Quiz谩s muchos recuerden una forma del 谩lgebra escolar: hacer ecuaciones de dos l铆neas rectas, equiparar sus lados derechos, encontrar x y sustituirla en la ecuaci贸n de una l铆nea recta para encontrar y ( m谩s detalles aqu铆 ).



Sin embargo, este m茅todo se vuelve bastante engorroso a la hora de escribir c贸digo (quiz谩s por eso est谩s leyendo este art铆culo ahora), adem谩s, no es universal: si una de las rectas es paralela al eje Y, obtendremos una divisi贸n por error cero al calcular la pendiente, y tenemos que registre un c贸digo para este caso; Si dos l铆neas son paralelas, tambi茅n debe modificar el procesamiento de este caso. Este c贸digo se vuelve largo y feo.



En busca de una soluci贸n m谩s elegante a este problema, me encontr茅 con algunas formas muy interesantes basadas en la multiplicaci贸n de vectores (habr.com/ru/post/267037 ) y rayos castinging ( ru.wikipedia.org/wiki/Ray_casting#Concept ). Pero en mi opini贸n, son innecesariamente complejos computacionalmente. Por tanto, presento a su atenci贸n (y cr铆tica) mi m茅todo.



Mi manera



Tarea



Se dan las coordenadas de dos segmentos. Debe averiguar si los segmentos se cruzan y, de ser as铆, en qu茅 punto. Para ello, escribiremos una funci贸n.



Decisi贸n



imagen



Leyenda para evitar malentendidos: a - vector a, a (y) - proyecci贸n del vector a sobre el eje Y, a {x1, y1} - vector a, especificado por las coordenadas x1, y1.



Representemos nuestros segmentos en forma de dos vectores: a {x2-x1; y2-y1} y b {x3-x4; y3-x4}. Tenga en cuenta que el vector b est谩 en la direcci贸n opuesta a la esperada. Introduzcamos un vector c {x3-x1; y3-y1}. Tenga en cuenta que a * k + b * n = c, donde k, n son algunos coeficientes. As铆, obtenemos un sistema de ecuaciones:



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

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

Nuestra tarea se reduce a encontrar estos coeficientes (a decir verdad, basta con encontrar solo uno de ellos).



Propongo multiplicar ambos lados de la ecuaci贸n inferior por q = -a (x) / a (y). Entonces, despu茅s de sumar dos ecuaciones, inmediatamente nos deshacemos de k. Encontrar n se reduce a resolver una ecuaci贸n lineal ordinaria. Es importante tener en cuenta que n puede no tener una soluci贸n.



El lector atento notar谩 que cuando a (y) = 0, obtenemos un error. Escribamos la ramificaci贸n en la etapa de encontrar a (y). Este caso es a煤n m谩s simple, porque inmediatamente obtenemos una ecuaci贸n con una inc贸gnita.



Recomiendo intentar imprimir n usted mismo, para que quede m谩s claro de qu茅 proviene el c贸digo a continuaci贸n.



Sabiendo n, puedes encontrar el punto de intersecci贸n, para esto restamos el vector b * n de la coordenada del punto (x3, y3)



Poniendo todo junto



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;
}


Esta funci贸n toma las coordenadas de los v茅rtices y devuelve el valor 1 si las l铆neas rectas de los segmentos (es decir, las l铆neas rectas) se cruzan, de lo contrario 0. Si necesita las coordenadas de los v茅rtices, puede tomarlas de la matriz de puntos [].



Importante: al introducir dos l铆neas coincidentes, el algoritmo no muestra ninguna intersecci贸n. El algoritmo encuentra el punto de intersecci贸n de las l铆neas en las que se encuentran los segmentos de l铆nea, por lo que el punto puede estar fuera del segmento (que deber谩 verificar en su c贸digo).



Apliquemos la funci贸n:



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;
}


Ep铆logo



Aunque no encontr茅 este m茅todo en el proceso de buscar en Google mi problema y desarrollar el algoritmo yo mismo, no pretendo ser completamente original (y correcto). 隆Bienvenidos a los comentarios!



All Articles