¿Cómo determino si dos líneas se cruzan o no, y si lo hacen, en qué punto x, y?
geometry
line-intersection
ReyNestor
fuente
fuente
Respuestas:
Hay un buen enfoque para este problema que utiliza productos cruzados de vectores. Defina el producto cruzado vectorial bidimensional v × w como v x w y - v y w x .
Suponga que los dos segmentos de línea van de p a p + r y de q a q + s . Entonces, cualquier punto en la primera línea es representable como p + t r (para un parámetro escalar t ) y cualquier punto en la segunda línea como q + u s (para un parámetro escalar u ).
Las dos líneas se cruzan si podemos encontrar t y u tal que:
Cruza ambos lados con s , obteniendo
Y como s × s = 0, esto significa
Y por lo tanto, resolviendo para t :
Del mismo modo, podemos resolver por usted :
Para reducir el número de pasos de cálculo, es conveniente reescribir esto de la siguiente manera (recordando que s × r = - r × s ):
Ahora hay cuatro casos:
Si r × s = 0 y ( q - p ) × r = 0, entonces las dos líneas son colineales.
En este caso, exprese los puntos finales del segundo segmento ( q y q + s ) en términos de la ecuación del primer segmento de línea ( p + t r ):
Si el intervalo entre t 0 y t 1 se cruza con el intervalo [0, 1], entonces los segmentos de línea son colineales y se superponen; de lo contrario son colineales y disjuntos.
Observe que si s y r punto en direcciones opuestas, a continuación, s · r <0 y así el intervalo a ser comprobados es [ t 1 , t 0 ] en lugar de [ t 0 , t 1 ].
Si r × s = 0 y ( q - p ) × r ≠ 0, entonces las dos líneas son paralelas y no se cruzan.
Si r × s ≠ 0 y 0 ≤ t ≤ 1 y 0 ≤ u ≤ 1, los dos segmentos de línea se encuentran en el punto p + t r = q + u s .
De lo contrario, los dos segmentos de línea no son paralelos pero no se intersecan.
Crédito: este método es la especialización bidimensional del algoritmo de intersección de líneas 3D del artículo "Intersección de dos líneas en tres espacios" de Ronald Goldman, publicado en Graphics Gems , página 304. En tres dimensiones, el caso habitual es que las líneas son asimétricas (ni paralelas ni intersectadas) en cuyo caso el método proporciona los puntos de aproximación más cercana de las dos líneas.
fuente
/ (r × s)
, pero(r × s)
es un vector, ¿verdad? Un vector(0, 0, rx * sy - ry * sx)
. Y el lado izquierdo es similarmente un vector paralelo al eje z. Entonces ... ¿acabo de dividir el componente z por el otro componente z? ¿Es la fórmula para t realmente|(q − p) × s| / |(r × s)|
?FWIW, la siguiente función (en C) detecta las intersecciones de línea y determina el punto de intersección. Se basa en un algoritmo en "Los trucos de los gurús de programación de juegos de Windows de Andre LeMothe ". No es diferente a algunos de los algoritmos en otras respuestas (por ejemplo, Gareth). LeMothe luego usa la Regla de Cramer (no me pregunte) para resolver las ecuaciones por sí mismas.
Puedo dar fe de que funciona en mi débil clon de asteroides, y parece tratar correctamente los casos límite descritos en otras respuestas por Elemental, Dan y Wodzu. También es probablemente más rápido que el código publicado por KingNestor porque es todo multiplicación y división, ¡sin raíces cuadradas!
Supongo que hay algún potencial para dividir por cero allí, aunque no ha sido un problema en mi caso. Suficientemente fácil de modificar para evitar el choque de todos modos.
Por cierto, debo decir que en el libro de LeMothe, aunque aparentemente él tiene el algoritmo correcto, el ejemplo concreto muestra enchufes en los números incorrectos y hace cálculos incorrectos. Por ejemplo:
Eso me confundió por horas . :(
fuente
s
yt
directamente, probar la relación entre los dos numeradores y el denominador. Solo si se confirma que las líneas se cruzan, realmente necesita calcular el valor det
(pero nos
).El problema se reduce a esta pregunta: ¿se cruzan dos líneas de A a B y de C a D? Luego puedes preguntarlo cuatro veces (entre la línea y cada uno de los cuatro lados del rectángulo).
Aquí está la matemática vectorial para hacerlo. Supongo que la línea de A a B es la línea en cuestión y la línea de C a D es una de las líneas rectangulares. Mi notación es que
Ax
es la "coordenada x de A" yCy
es la "coordenada y de C". Y "*
" significa producto de punto, por ejemploA*B = Ax*Bx + Ay*By
.Este
h
número es la clave. Sih
está entre0
y1
, las líneas se cruzan, de lo contrario no lo hacen. SiF*P
es cero, por supuesto, no puede hacer el cálculo, pero en este caso las líneas son paralelas y, por lo tanto, solo se cruzan en los casos obvios.El punto exacto de intersección es
C + F*h
.Más diversión:
Si
h
es exactamente0
o1
las líneas se tocan en un punto final. Puede considerar esto como una "intersección" o no como mejor le parezca.Específicamente,
h
es cuánto tienes que multiplicar la longitud de la línea para tocar exactamente la otra línea.Por lo tanto, si
h<0
, significa que la línea del rectángulo está "detrás" de la línea dada (con "dirección" siendo "de A a B"), y sih>1
la línea del rectángulo está "delante" de la línea dada.Derivación:
A y C son vectores que apuntan al inicio de la línea; E y F son los vectores de los extremos de A y C que forman la línea.
Para cualquiera de las dos líneas no paralelas en el plano, debe haber exactamente un par de escalares
g
yh
para que esta ecuación sea válida:¿Por qué? Debido a que dos líneas no paralelas deben cruzarse, lo que significa que puede escalar ambas líneas en cierta cantidad cada una y tocarse entre sí.
(¡ Al principio, esto parece una ecuación única con dos incógnitas! Pero no lo es cuando se considera que se trata de una ecuación vectorial 2D, lo que significa que esto es realmente un par de ecuaciones en
x
yy
).Tenemos que eliminar una de estas variables. Una manera fácil es hacer que el
E
término sea cero. Para hacer eso, tome el producto de puntos de ambos lados de la ecuación usando un vector que punteará a cero con E. Ese vector que llaméP
anteriormente, e hice la transformación obvia de E.Ahora tienes:
fuente
A + E*g = C + F*h
Las dos líneas se cruzan si y solo si la solución a esa ecuación (suponiendo que no sean paralelas) tiene ambas,g
yh
entre 0 y 1 (in o exclusivo, dependiendo de si cuenta tocar en un punto final).He tratado de implementar el algoritmo descrito tan elegantemente por Jason arriba; Desafortunadamente, mientras trabajaba a través de las matemáticas en la depuración, encontré muchos casos para los que no funciona.
Por ejemplo, considere los puntos A (10,10) B (20,20) C (10,1) D (1,10) da h = .5 y, sin embargo, queda claro al examinar que estos segmentos no están cerca de cada uno otro.
Graficar esto deja en claro que el criterio 0 <h <1 solo indica que el punto de intercepción estaría en CD si existiera, pero no le dice a uno si ese punto está en AB. Para asegurarse de que haya un punto de cruce, debe hacer el cálculo simétrico para la variable gy el requisito de intercepción es: 0 <g <1 Y 0 <h <1
fuente
Aquí hay una mejora en la respuesta de Gavin. La solución de marcp es similar también, pero tampoco pospone la división.
Esto en realidad resulta ser una aplicación práctica de la respuesta de Gareth Rees también, porque el equivalente del producto cruzado en 2D es el producto perp-dot, que es de lo que este código usa tres. Cambiar a 3D y usar el producto cruzado, interpolando tanto syt al final, da como resultado los dos puntos más cercanos entre las líneas en 3D. De todos modos, la solución 2D:
Básicamente, pospone la división hasta el último momento y mueve la mayoría de las pruebas hasta antes de que se realicen ciertos cálculos, agregando así salidas anticipadas. Finalmente, también evita la división por el caso cero que ocurre cuando las líneas son paralelas.
También es posible que desee considerar el uso de una prueba epsilon en lugar de la comparación contra cero. Las líneas que están extremadamente cercanas al paralelo pueden producir resultados que están ligeramente desviados. Esto no es un error, es una limitación con las matemáticas de coma flotante.
fuente
s32_y
lugar de algo que describe cómo espoint2YDifference
?Pregunta C: ¿Cómo detecta si dos segmentos de línea se cruzan o no?
He buscado el mismo tema y no estaba contento con las respuestas. Así que escribí un artículo que explica muy detalladamente cómo verificar si dos segmentos de línea se cruzan con muchas imágenes. Hay un código Java completo (y probado).
Aquí está el artículo, recortado en las partes más importantes:
El algoritmo, que verifica si el segmento de línea a se cruza con el segmento de línea b, se ve así:
¿Qué son los cuadros delimitadores? Aquí hay dos cuadros delimitadores de dos segmentos de línea:
Si ambos cuadros delimitadores tienen una intersección, mueve el segmento de línea a para que un punto esté en (0 | 0). Ahora tiene una línea a través del origen definido por a. Ahora mueva el segmento de línea b de la misma manera y verifique si los nuevos puntos del segmento de línea b están en diferentes lados de la línea a. Si este es el caso, verifíquelo al revés. Si este es también el caso, los segmentos de línea se cruzan. Si no, no se cruzan.
Pregunta A: ¿Dónde se cruzan dos segmentos de línea?
Usted sabe que dos segmentos de línea a y b se cruzan. Si no lo sabe, verifíquelo con las herramientas que le di en la "Pregunta C".
Ahora puede pasar por algunos casos y obtener la solución con matemáticas de séptimo grado (vea el código y el ejemplo interactivo ).
Pregunta B: ¿Cómo detecta si dos líneas se cruzan o no?
Digamos que su punto
A = (x1, y1)
, puntoB = (x2, y2)
,C = (x_3, y_3)
,D = (x_4, y_4)
. Su primera línea está definida por AB (con A! = B), y su segunda línea por CD (con C! = D).Pregunta D: ¿Dónde se cruzan dos líneas?
Verifique con la pregunta B si se cruzan en absoluto.
Las líneas ayb están definidas por dos puntos para cada línea. Básicamente, puede aplicar la misma lógica que se utilizó en la pregunta A.
fuente
La respuesta una vez aceptada aquí es incorrecta (desde entonces no se ha aceptado, ¡así que hurra!). No elimina correctamente todas las no intersecciones. Trivialmente puede parecer que funciona pero puede fallar, especialmente en el caso de que 0 y 1 se consideren válidos para h.
Considere el siguiente caso:
Líneas en (4,1) - (5,1) y (0,0) - (0,2)
Estas son líneas perpendiculares que claramente no se superponen.
A = (4,1)
B = (5,1)
C = (0,0)
D = (0,2)
E = (5,1) - (4,1) = (- 1,0)
F = (0,2) - (0,0) = (0, -2)
P = (0,1)
h = ((4,1) - (0,0)) punto (0,1) / ((0 , -2) punto (0,1)) = 0
De acuerdo con la respuesta anterior, estos dos segmentos de línea se encuentran en un punto final (valores de 0 y 1). Ese punto final sería:
(0,0) + (0, -2) * 0 = (0,0)
Entonces, aparentemente los dos segmentos de línea se encuentran en (0,0), que está en la línea CD, pero no en la línea AB. Entonces, ¿qué va mal? La respuesta es que los valores de 0 y 1 no son válidos y solo a veces SUCEDEN para predecir correctamente la intersección del punto final. Cuando la extensión de una línea (pero no la otra) se encuentra con el segmento de línea, el algoritmo predice una intersección de segmentos de línea, pero esto no es correcto. Me imagino que al probar comenzando con AB vs CD y luego también probando con CD vs AB, este problema se eliminaría. Solo si ambos caen entre 0 y 1 inclusive, se puede decir que se cruzan.
Recomiendo usar el método de producto cruzado vectorial si debe predecir puntos finales.
-Dan
fuente
Versión de Python de la respuesta de iMalc:
fuente
denom = float(...)
Encontrar la intersección correcta de dos segmentos de línea es una tarea no trivial con muchos casos de borde. Aquí hay una solución bien documentada, funcional y probada en Java.
En esencia, hay tres cosas que pueden suceder al encontrar la intersección de dos segmentos de línea:
Los segmentos no se cruzan
Hay un punto de intersección único
La intersección es otro segmento.
NOTA : En el código, supongo que un segmento de línea (x1, y1), (x2, y2) con x1 = x2 e y1 = y2 es un segmento de línea válido. Matemáticamente hablando, un segmento de línea consta de puntos distintos, pero estoy permitiendo que los segmentos sean puntos en esta implementación para completar.
El código está tomado de mi repositorio de github
Aquí hay un ejemplo de uso simple:
fuente
Solo quería mencionar que se puede encontrar una buena explicación y una solución explícita en la serie Numeric Recipes. Tengo la tercera edición y la respuesta está en la página 1117, sección 21.4. Otra solución con una nomenclatura diferente se puede encontrar en un documento de Marina Gavrilova Prueba de intersección de sección de línea confiable . Su solución es, en mi opinión, un poco más simple.
Mi implementación está abajo:
fuente
Hay muchas soluciones disponibles anteriormente, pero creo que la solución a continuación es bastante simple y fácil de entender.
Dos segmentos Vector AB y Vector CD se cruzan si y solo si
Más específicamente, ayb están en el lado opuesto del segmento CD si y solo si exactamente uno de los dos triples a, c, d y b, c, d está en el sentido contrario a las agujas del reloj.
Aquí CCW representa en sentido antihorario que devuelve verdadero / falso según la orientación de los puntos.
Fuente: http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf Página 2
fuente
CCW
define la prueba? Con el signo del producto exterior?C y Objetivo-C
Basado en la respuesta de Gareth Rees
Muchas de las funciones y estructuras son privadas, pero es bastante fácil saber qué está pasando. Esto es público en este repositorio https://github.com/hfossli/AGGeometryKit/
fuente
Esto está funcionando bien para mí. Tomado de aquí .
fuente
Intenté algunas de estas respuestas, pero no me funcionaron (lo siento muchachos); Después de buscar más en la red, encontré esto .
Con una pequeña modificación a su código, ahora tengo esta función que devolverá el punto de intersección o, si no se encuentra ninguna intersección, devolverá -1, -1.
fuente
Parece haber cierto interés en la respuesta de Gavin para la cual Cortijon propuso una versión de JavaScript en los comentarios e iMalc proporcionó una versión con un poco menos de cálculos . Algunos han señalado las deficiencias con varias propuestas de código y otros han comentado sobre la eficacia de algunas propuestas de código.
El algoritmo proporcionado por iMalc a través de la respuesta de Gavin es el que estoy usando actualmente en un proyecto de JavaScript y solo quería proporcionar una versión limpia aquí si puede ayudar a alguien.
fuente
t = dx2 * dy3 - dx3 * dy2;
...t/d
entra.crossProduct = (line1XDifference * line2YDifference) - (line2XDifference * line1YDifference)
yscaledResult = crossProduct / dotProduct
?p1x, p1y
etc. están destinados a describir puntos por sus valores x e y, por lo quep1x
es una abreviatura depoint1x
, del mismo modod1x
, en mi mente es una abreviatura de la letra griegadeltaX
o podría decirsedifferenceInX
. (más)Creo que hay una solución mucho más simple para este problema. Se me ocurrió otra idea hoy y parece funcionar bien (al menos en 2D por ahora). Todo lo que tiene que hacer es calcular la intersección entre dos líneas, luego verificar si el punto de intersección calculado está dentro de los cuadros de límite de ambos segmentos de línea. Si es así, los segmentos de línea se cruzan. Eso es.
EDITAR:
Así es como calculo la intersección (ya no sé dónde encontré este fragmento de código)
viene de
y esta es mi clase (simplificada para el propósito de la respuesta) BoundingBox:
fuente
Esta solución puede ayudar
fuente
Porté la respuesta anterior de Kris a JavaScript. Después de probar numerosas respuestas diferentes, proporcionó los puntos correctos. Pensé que me estaba volviendo loco porque no estaba obteniendo los puntos que necesitaba.
fuente
Lo intenté de muchas maneras y luego decidí escribir el mío. Asi que aqui esta:
Basado en estas dos fórmulas: (las simplifiqué de la ecuación de líneas y otras fórmulas)
fuente
Esto se basa en la respuesta de Gareth Ree. También devuelve la superposición de los segmentos de línea si lo hacen. Codificado en C ++, V es una clase de vector simple. Donde el producto cruzado de dos vectores en 2D devuelve un solo escalar. Fue probado y aprobado por el sistema de prueba automática de mi escuela.
fuente
Aquí hay una implementación básica de un segmento de línea en C #, con el código de detección de intersección correspondiente. Requiere una estructura de vector / punto 2D llamada
Vector2f
, aunque puede reemplazarla con cualquier otro tipo que tenga propiedades X / Y. También puede reemplazarfloat
condouble
si eso se adapta mejor a sus necesidades.Este código se usa en mi biblioteca de física .NET, Boing .
fuente
Un programa C ++ para verificar si dos segmentos de línea dados se cruzan
fuente
Basado en la respuesta de @Gareth Rees, versión para Python:
fuente
Si cada lado del rectángulo es un segmento de línea, y la porción dibujada por el usuario es un segmento de línea, entonces solo debe verificar la intersección del segmento dibujado por el usuario con los cuatro segmentos de línea lateral. Este debería ser un ejercicio bastante simple dados los puntos de inicio y finalización de cada segmento.
fuente
Basado en la respuesta de t3chb0t:
fuente
Leí estos algoritmos del libro "geometría de vista múltiple"
siguiente texto usando
'como signo de transposición
* como producto punteado
x como producto cruzado, cuando se usa como operador
1. definición de línea
un punto x_vec = (x, y) 'se encuentra en la línea ax + by + c = 0
denotamos L = (a, b, c) ', el punto como (x, y, 1)' como coordenadas homogéneas
la ecuación lineal se puede escribir como
(x, y, 1) (a, b, c) '= 0 o x' * L = 0
2. intersección de líneas
tenemos dos líneas L1 = (a1, b1, c1) ', L2 = (a2, b2, c2)'
suponga que x es un punto, un vector yx = L1 x L2 (producto cruzado L1 L2).
tenga cuidado, x es siempre un punto 2D, lea las coordenadas homogéneas si está confundido acerca de (L1xL2) es un vector de tres elementos, y x es una coordenadas 2D.
según triple producto, sabemos que
L1 * (L1 x L2) = 0, y L2 * (L1 x L2) = 0, debido al coplano L1, L2
sustituimos (L1xL2) con el vector x, entonces tenemos L1 * x = 0, L2 * x = 0, lo que significa que x está en L1 y L2, x es el punto de intersección.
tenga cuidado, aquí x es coordenadas homogéneas, si el último elemento de x es cero, significa que L1 y L2 son paralelas.
fuente
Muchas respuestas han incluido todos los cálculos en una sola función. Si necesita calcular las pendientes de la línea, las intersecciones con el eje y las intersecciones con el eje x para usar en otra parte de su código, realizará esos cálculos de forma redundante. He separado las funciones respectivas, he usado nombres de variables obvios y he comentado mi código para que sea más fácil de seguir. Necesitaba saber si las líneas se cruzan infinitamente más allá de sus puntos finales, así que en JavaScript:
http://jsfiddle.net/skibulk/evmqq00u/
fuente