Encontrar la dirección del viaje en un mundo con bordes envueltos

20

Necesito encontrar la dirección de la distancia más corta desde un punto en mi mundo 2D a otro punto donde se envuelven los bordes (como asteroides, etc.). Sé cómo encontrar la distancia más corta, pero estoy luchando por encontrar en qué dirección está.

La distancia más corta viene dada por:

int rows = MapY;
int cols = MapX;

int d1 = abs(S.Y - T.Y);
int d2 = abs(S.X - T.X);
int dr = min(d1, rows-d1);
int dc = min(d2, cols-d2);

double dist = sqrt((double)(dr*dr + dc*dc));

Ejemplo del mundo

                   :         
                   :  T    
                   :         
    :--------------:---------
    :              :
    :           S  :
    :              :
    :              :
    :  T           :
    :              :
    :--------------:

En el diagrama, los bordes se muestran con: y -. También he mostrado una repetición envuelta del mundo en la parte superior derecha. Quiero encontrar la dirección en grados de S a T. Entonces, la distancia más corta es a la repetición superior derecha de T. pero, ¿cómo calculo la dirección en grados de S a la repetida T en la parte superior derecha?

Conozco las posiciones de S y T, pero supongo que necesito encontrar la posición de la T repetida, sin embargo, hay más de 1.

El sistema de coordenadas del mundo comienza en 0,0 en la esquina superior izquierda y 0 grados para la dirección podría comenzar en el oeste.

Parece que esto no debería ser demasiado difícil, pero no he podido encontrar una solución. Espero que alguien pueda ayudar? Cualquier sitio web sería apreciado.

loca
fuente
¿Cuáles son las coordenadas para la T en la esquina superior derecha?
Nunca he visto un juego con envoltura diagonal. Por lo general, tiene una envoltura para cada dirección (N, E, S, W).
55
Cualquier juego que tenga envoltura horizontal y vertical tiene envoltura diagonal por defecto.
Piense en cada coordenada como viviendo en un círculo, y calcule la menor de las dos distancias posibles para cada coordenada individualmente.
Kerrek SB
1
@crazy: Busque "torus" en Wikipedia ...
Kerrek SB

Respuestas:

8

Tendrá que ajustar un poco su algoritmo para calcular el ángulo: actualmente solo registra la diferencia absoluta en la posición, pero necesita la diferencia relativa (es decir, puede ser positiva o negativa dependiendo del posicionamiento).

int dx = T.X - S.X; // difference in position
int dy = T.Y - S.Y;

if (dx > MapX / 2) // if distance is bigger than half map width, then looping must be closer
    dx = (dx - MapX) * -1; // reduce distance by map width, reverse 
else if (dx < -MapX / 2) // handle the case that dx is negative
    dx = (dx + MapX) * -1;

//Do the same for dy
if (dy > MapY / 2)
    dy = (dy - MapY) * -1;
else if (dy < -MapY / 2)
    dy = (dy + MapY) * -1;

double dist = sqrt(dy*dy+dx*dx); // same as before
double angle = atan2(dy,dx) * 180 / PI; // provides angle in degrees
Toomai
fuente
1
Necesita algo de trabajo en los signos de dx y dy ya que el código se romperá si TX es menor que SX o TY es menor que XY Aparte de eso, esta es la mejor solución en mi humilde opinión.
Scott Chamberlain el
En ese momento lo arreglaré.
1
Todavía tengo algunos errores sobre cuál será el signo de dx y dy cuando todo esté dicho y hecho, ¿te importa si edito?
Scott Chamberlain
¿Por qué es esta la respuesta aceptada? Ni siquiera funciona . Supongamos que MapXes 100, T.Xes 90 y S.Xes 10. dxclaramente debería ser 20, ¡pero este algoritmo devolverá 30!
Sam Hocevar
Ugh, esto es lo que sucede cuando no tienes la oportunidad de probar el código antes de publicarlo. Arreglará. Si alguien encuentra otro error con esto, probablemente lo elimine antes de que muchas personas se engañen.
Toomai
11

En un mundo así, hay un número infinito de caminos de S a T. Denotemos las coordenadas de T por (Tx, Ty), las coordenadas de S por (Sx, Sy)y el tamaño del mundo por (Wx, Wy). Las coordenadas envueltas de T son (Tx + i * Wx, Ty + j * Wy), donde iy json enteros, es decir, elementos del conjunto {..., -2, -1, 0, 1, 2, ...}. Los vectores que conectan S a T son (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy). Para un (i, j)par dado , la distancia es la longitud del vector sqrt(Dx * Dx + Dy * Dy), y la dirección en radianes es atan(Dy / Dx). El camino más corto es uno de los 9 caminos, donde iy jestán en {-1, 0, 1}: ingrese la descripción de la imagen aquí

Los valores iy jpara la ruta más corta se pueden determinar directamente:

int i = Sx - Tx > Wx / 2 ? 1 : Sx - Tx < -Wx / 2 ? -1 : 0;
int j = Sy - Ty > Wy / 2 ? 1 : Sy - Ty < -Wy / 2 ? -1 : 0;

¡Gracias, @IlmariKaronen, @SamHocevar y @romkyns por su ayuda!

Comunidad
fuente
1
Puede hacerlo mejor que eso: si abs(Tx-Sx) < Wx/2, entonces i=0es óptimo; de lo contrario, la opción óptima es i=-1o i=1, dependiendo del signo de Tx-Sx. Lo mismo vale para Ty-Syy j.
Ilmari Karonen
1
Esta respuesta es increíblemente complicada para un problema tan simple. No es necesario utilizar la búsqueda lineal cuando el valor mínimo se puede calcular directamente.
sam hocevar
Bonita foto, pero el algoritmo sugerido no merece ninguno de los votos positivos que recibió esta respuesta.
RomanSt
5

Calcule un vector de dirección posible, incluso si no es el más corto, luego ajuste su coordenada X para que esté en el [-MapX/2,MapX/2]rango, y lo mismo para Y:

int DirX = (T.X - S.X + 3 * MapX / 2) % MapX) - MapX / 2;
int DirY = (T.Y - S.Y + 3 * MapY / 2) % MapY) - MapY / 2;

¡Eso es! También obtienes la distancia sin más cálculos:

double dist = sqrt((double)(DirX*DirX + DirY*DirY));
sam hocevar
fuente
¡Gracias! Versión GLSL:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01
0

Supongo que hay varias maneras de hacer esto. Aquí hay 2 que puedo pensar en la parte superior de mi cabeza:

# 1: maneje los estuches manualmente

Hay exactamente 10 casos que pueden suceder:

  • Está en el mismo mosaico que S
  • Está en cualquiera de los 8 mosaicos circundantes.
  • No se encuentra en absoluto.

Sin embargo, para cada uno de los mosaicos circundantes, son permutaciones de diferentes cálculos para el componente de distancia X o Y. Debido a que es un número finito de casos, puede simplemente codificar cómo calcularlos y encontrar la distancia más corta entre todos ellos.

Aquí hay una ilustración de 2 casos para encontrar dx. El caso 1, donde Testá en el mismo mosaico que S, dx es justo S.x - T.x. Para los mosaicos a la derecha, dxse calculará como TileWidth - S.x + T.x.

               :         
               :  T    
               :         
:--------------:---------
:              :
:           S  :
:  |--------|--:--|
:dx=(S.x-T.x) dx=(TileWidth-S.x+T.x)
:  T           :
:              :
:--------------:

Como una pequeña optimización, encuentre la distancia mínima antes de sacar una raíz cuadrada. Luego te ahorras hasta 7 sqrtllamadas.

# 2: Resumen las coordenadas

Si necesita hacer algo más espacialmente "fluido", como un algoritmo de búsqueda de ruta, simplemente abstraiga las coordenadas para que su algoritmo de búsqueda de ruta ni siquiera se dé cuenta de que el mundo está hecho de mosaicos repetitivos. El algoritmo de búsqueda de ruta podría ir infinitamente en cualquier dirección teóricamente (bueno, bueno, estarás limitado por límites numéricos, pero obtienes el punto).

Para el cálculo de distancia simple, no te molestes en hacer esto.

diez cuatro
fuente
¡Idea inteligente sobre comparar el valor de la distancia al cuadrado antes de tomar el sqrt!
Scott Chamberlain
Ah, ya veo, @Kol tiene una respuesta similar con una explicación más matemática, gracias, esto me da algo con lo que trabajar
Comparar la distancia al cuadrado puede ser más inteligente que tomar el sqrt, pero usar la distancia de Manhattan es aún más inteligente ya que no requiere multiplicación en absoluto.
sam hocevar
0

No te molestes con las "9 direcciones". La razón es que hay 5 casos degenerados entre esos 9: "recto norte", "recto oeste", "recto sur", "recto este" e "idéntico". Por ejemplo, el norte recto es degenerado porque representa el caso donde el noroeste y el noreste se unen y producen el mismo resultado.

Por lo tanto, tiene 4 direcciones para calcular y puede elegir el mínimo.

MSalters
fuente
No creo que esto sea correcto, o te he entendido mal por completo. Uno de los dos.
-1

Gracias por todas las respuestas al final, utilicé Toomai editado por Scott Chamberlain. También tuve que hacer algunos cambios debido al hecho de que mi sistema de coordenadas comienza con y en la parte superior izquierda y aumenta a medida que avanza (básicamente invertido en comparación con las coordenadas de gráfico normales para y).

He publicado en caso de que alguien más encuentre esta página y tenga el mismo sistema invertido.

  int dx = T.X - S.X; // difference in position
int dy = S.Y - T.Y;

if (dx > MapX / 2) // if distance is bigger than half map width, then looping must be closer
    dx = (dx - (MapX / 2)) * -1; // reduce distance by half map width, reverse 
else if (dx < -MapX / 2) // handle the case that dx is negative
    dx = (dx + (MapX / 2)) * -1;

//Do the same for dy
if (dy > MapY / 2)
    dy = (MapY - dy)) * -1;
else if (dy < -MapY / 2)
    dy = (dy + MapY);

double angle = atan2(dy,dx) * 180 / PI; // provides angle in degrees

angle = 180 - angle; //convert to 360 deg
loca
fuente
Este código es ligeramente mejor que el de Toomai pero tampoco funciona.
Sam Hocevar
1
Además, debe comprender por qué tuvo que hacer estos cambios. Es no porque su sistema de coordenadas se inicia con yen la parte superior. Se debe a que el comportamiento deseado es supuestamente envolver las coordenadas en el borde mundial, mientras que el código que reutilizó reflejó las coordenadas en cada límite.
sam hocevar