En primer lugar, en el caso de los rectángulos alineados a los ejes, la respuesta de Kevin Reid es la mejor y el algoritmo es el más rápido.
Segundo, para formas simples, use velocidades relativas (como se ve a continuación) y el teorema del eje de separación para la detección de colisiones. Se le dirá si ocurre una colisión en el caso de movimiento lineal (sin rotación). Y si hay rotación, necesita un pequeño paso de tiempo para que sea preciso. Ahora, para responder la pregunta:
¿Cómo saber en el caso general si dos formas convexas se cruzan?
Le daré un algoritmo que funciona para todas las formas convexas y no solo para hexágonos.
Supongamos que X e Y son dos formas convexas. Se cruzan si y solo si tienen un punto en común, es decir, hay un punto x ∈ X y un punto y ∈ Y tal que x = y . Si considera el espacio como un espacio vectorial, esto equivale a decir x - y = 0 . Y ahora llegamos a este negocio de Minkowski:
La suma de Minkowski de X y Y es el conjunto de todos los x + y para x ∈ X y y ∈ Y .
Un ejemplo para X e Y
X, Y y su suma Minkowski, X + Y
Supongamos que (-Y) es el conjunto de todos -y para y ∈ Y , dado el párrafo anterior, X e Y se cruzan si y solo si X + (-Y) contiene 0 , es decir, el origen .
Comentario lateral: ¿por qué escribo X + (-Y) en lugar de X - Y ? Bueno, porque en matemáticas, hay una operación llamada la diferencia de Minkowski de A y B, que a veces se escribe X - Y, pero no tiene nada que ver con el conjunto de todos x - y para x ∈ X e y ∈ Y (el verdadero Minkowski La diferencia es un poco más compleja).
Por lo tanto, nos gustaría calcular la suma de Minkowski de X e -Y y determinar si contiene el origen. El origen no es especial en comparación con cualquier otro punto, por lo que para encontrar si el origen está dentro de un determinado dominio, utilizamos un algoritmo que podría decirnos si algún punto dado pertenece a ese dominio.
La suma de X e Y de Minkowski tiene una propiedad genial, que es que si X e Y son convexos, entonces X + Y también lo es. Y descubrir si un punto pertenece a un conjunto convexo es mucho más fácil que si ese conjunto no fuera (se sabe que es) convexo.
No podemos calcular todas las x - y para x ∈ X e y ∈ Y porque hay una infinidad de tales puntos x e y , así que con suerte, dado que X , Y e X + Y son convexos, podemos usar los puntos "más externos" que definen las formas X e Y , que son sus vértices, y obtendremos los puntos más externos de X + Y , y también algunos más.
Estos puntos adicionales están "rodeados" por los más externos de X + Y para que no contribuyan a definir la forma convexa recién obtenida. Decimos que no definen el " casco convexo " del conjunto de puntos. Entonces, lo que hacemos es deshacernos de ellos en preparación para el algoritmo final que nos dice si el origen está dentro del casco convexo.
El casco convexo de X + Y. Hemos eliminado los vértices "internos".
Por lo tanto, obtenemos
Un primer algoritmo ingenuo
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
Los bucles obviamente tienen complejidad O (mn) , donde m y n son el número de vértices de cada forma. El minkoswki
conjunto contiene elementos mn como máximo. El convexHull
algoritmo tiene una complejidad que depende del algoritmo que utilizó , y puede apuntar a O (k log (k)) donde k es el tamaño del conjunto de puntos, por lo que en nuestro caso obtenemos O (mn log (mn) ) . El contains
algoritmo tiene una complejidad que es lineal con el número de aristas (en 2D) o caras (en 3D) del casco convexo, por lo que realmente depende de sus formas iniciales, pero no será mayor que O (mn) .
Te dejaré googlear el contains
algoritmo para formas convexas, es bastante común. Puedo ponerlo aquí si tengo tiempo.
Pero lo que estamos haciendo es la detección de colisiones, por lo que podemos optimizarlo mucho
Originalmente teníamos dos cuerpos A y B moviéndose sin rotación durante un paso de tiempo dt (por lo que puedo ver mirando sus fotos). Llamemos a v A y v B las velocidades respectivas de A y B , que son constantes durante nuestro paso de tiempo de duración dt . Obtenemos lo siguiente:
y, como señala en sus imágenes, estos cuerpos barren áreas (o volúmenes, en 3D) a medida que se mueven:
y terminan como A ' y B' después del paso de tiempo.
Para aplicar nuestro algoritmo ingenuo aquí, solo tendríamos que calcular los volúmenes barridos. Pero no estamos haciendo esto.
En el marco de referencia de B , B no se mueve (¡duh!). Y A tiene una cierta velocidad con respecto a B que obtienes calculando v A - v B (puedes hacer lo contrario, calcular la velocidad relativa de B en el marco de referencia de A ).
De izquierda a derecha: velocidades en el marco de referencia base; velocidades relativas; calcular velocidades relativas.
Al considerar a B como inmóvil en su propio marco de referencia, solo tiene que calcular el volumen que A recorre a medida que se mueve durante dt con su velocidad relativa v A - v B .
Esto disminuye el número de vértices que se utilizarán en el cálculo de la suma de Minkowski (a veces en gran medida).
Otra posible optimización es en el punto donde calcula el volumen barrido por uno de los cuerpos, digamos A. No tiene que traducir todos los vértices que conforman A. Solo aquellos que pertenecen a bordes (caras en 3D) cuyos "cara" normal exterior la dirección del barrido. Seguramente ya lo habrás notado cuando calculaste las áreas barridas para los cuadrados. Puede saber si una normal es hacia la dirección de barrido utilizando su producto de punto con la dirección de barrido, que tiene que ser positivo.
La última optimización, que no tiene nada que ver con su pregunta sobre las intersecciones, es realmente útil en nuestro caso. Utiliza esas velocidades relativas que mencionamos y el llamado método de eje de separación. Seguramente ya lo sabes.
Suponga que conoce los radios de A y B con respecto a sus centros de masa (es decir, la distancia entre el centro de masa y el vértice más alejado de él), así:
Una colisión puede ocurrir sólo si es posible que el círculo de delimitación de A se encuentran la de B . Vemos aquí que no lo hará, y la manera de decirle a la computadora que consiste en calcular la distancia de C B a I como en la siguiente imagen y asegurarse de que es más grande que la suma de los radios de A y B . Si es más grande, no hay colisión. Si es más pequeño, entonces colisión.
Esto no funciona muy bien con formas que son bastante largas, pero en el caso de cuadrados u otras formas similares, es una muy buena heurística para descartar colisión .
El teorema del eje de separación aplicado a B y el volumen barrido por ASin embargo, el le dice si ocurre la colisión. La complejidad del algoritmo asociado es lineal con la suma de los números de vértices de cada forma convexa, pero es menos mágico cuando llega el momento de manejar la colisión.
Nuestro nuevo y mejor algoritmo que usa intersecciones para ayudar a detectar colisiones, pero aún no es tan bueno como el teorema del eje de separación para saber si ocurre una colisión
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}