Trouver la direction du voyage dans un monde aux bords enveloppés

20

J'ai besoin de trouver la direction de distance la plus courte d'un point dans mon monde 2D à un autre point où les bords sont enveloppés (comme des astéroïdes, etc.). Je sais comment trouver la distance la plus courte, mais j'ai du mal à trouver la direction dans laquelle elle se trouve.

La distance la plus courte est donnée par:

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

Exemple du monde

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

Dans le diagramme, les bords sont représentés par: et -. J'ai également montré une répétition enveloppée du monde en haut à droite. Je veux trouver la direction en degrés de S à T. Donc, la distance la plus courte est vers la répétition en haut à droite de T. mais comment puis-je calculer la direction en degrés de S vers le T répété en haut à droite?

Je connais les positions de S et de T mais je suppose que je dois trouver la position du T répété mais il y en a plus de 1.

Le système de coordonnées des mondes commence à 0,0 en haut à gauche et 0 degrés pour la direction pourrait commencer à l'ouest.

Il semble que cela ne devrait pas être trop difficile, mais je n'ai pas pu trouver de solution. J'espère que quelqu'un peut aider? Tous les sites Web seraient appréciés.

fou
la source
Quelles sont les coordonnées du T en haut à droite?
Je n'ai jamais vu de jeu avec un habillage diagonal. Habituellement, vous avez une enveloppe pour chaque direction (N, E, S, W).
5
Tout jeu qui a à la fois un habillage horizontal et vertical a un habillage diagonal par défaut.
Considérez chaque coordonnée comme vivant sur un cercle et déterminez la plus courte des deux distances possibles pour chaque coordonnée individuellement.
Kerrek SB
1
@crazy: Recherchez "torus" sur Wikipédia ...
Kerrek SB

Réponses:

8

Vous devrez modifier un peu votre algorithme pour calculer l'angle - actuellement, vous enregistrez uniquement la différence absolue de position, mais vous avez besoin de la différence relative (c'est-à-dire qu'elle peut être positive ou négative selon le positionnement).

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
la source
1
Vous avez besoin d'un peu de travail sur les signes de dx et dy car le code tel qu'il se cassera si TX est inférieur à SX ou TY est inférieur à XY Autre que cela, c'est la meilleure solution à mon humble avis.
Scott Chamberlain,
Alors je vais arranger ça.
1
Vous avez encore quelques erreurs sur ce que sera le signe de dx et dy quand tout sera dit et fait, ça vous dérange si je modifie?
Scott Chamberlain
Pourquoi est-ce la réponse acceptée ?? Ça ne marche même pas . Supposons que MapX100, T.X90 et S.X10. dxsoient clairement 20, mais cet algorithme renverra 30!
sam hocevar
C'est ce qui arrive quand vous n'avez pas la possibilité de tester le code avant de le publier. Réparera. Si quelqu'un trouve une autre erreur avec cela, je vais probablement le supprimer avant que trop de gens ne se trompent.
Toomai
11

Dans un tel monde, il existe un nombre infini de chemins de S à T. Notons les coordonnées de T par (Tx, Ty), les coordonnées de S par (Sx, Sy)et la taille du monde par (Wx, Wy). Les coordonnées enveloppées de T sont (Tx + i * Wx, Ty + j * Wy), où iet jsont des entiers, c'est-à-dire des éléments de l'ensemble {..., -2, -1, 0, 1, 2, ...}. Les vecteurs reliant S à T le sont (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy). Pour une (i, j)paire donnée , la distance est la longueur du vecteur sqrt(Dx * Dx + Dy * Dy), et la direction en radians est atan(Dy / Dx). Le chemin le plus court est l' un des 9 chemins, où iet jsont {-1, 0, 1}: entrez la description de l'image ici

Les valeurs iet jpour le chemin le plus court peuvent être déterminées directement:

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;

Merci, @IlmariKaronen, @SamHocevar et @romkyns pour votre aide!

Communauté
la source
1
Vous pouvez faire mieux que cela: si abs(Tx-Sx) < Wx/2, alors i=0est optimal; sinon le choix optimal est i=-1ou i=1, selon le signe de Tx-Sx. Il en va de même pour Ty-Syet j.
Ilmari Karonen
1
Cette réponse est incroyablement compliquée pour un problème aussi simple. Il n'est pas nécessaire d'utiliser la recherche linéaire lorsque la valeur minimale peut être calculée directement.
sam hocevar
Belle photo, mais l'algorithme suggéré ne mérite aucun des votes positifs que cette réponse a reçus.
RomanSt
5

Calculez un vecteur de direction possible, même s'il n'est pas le plus court, puis enveloppez sa coordonnée X pour qu'elle soit dans la [-MapX/2,MapX/2]plage, et même pour 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;

C'est ça! Vous obtenez également la distance sans autres calculs:

double dist = sqrt((double)(DirX*DirX + DirY*DirY));
sam hocevar
la source
Merci! Version GLSL:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01
0

Je suppose qu'il y a plusieurs façons de procéder. Voici 2 que je peux penser du haut de ma tête:

# 1: Gérer les étuis manuellement

Il y a exactement 10 cas qui peuvent se produire:

  • C'est dans la même tuile que S
  • C'est dans l'une des 8 tuiles environnantes
  • Ce n'est pas trouvé du tout.

Pour chacune des tuiles environnantes, ce sont des permutations de différents calculs pour la composante de distance X ou Y. Comme il s'agit d'un nombre fini de cas, vous pouvez simplement coder en dur comment les calculer et trouver la distance la plus courte entre tous.

Voici une illustration de 2 cas à trouver dx. Cas 1, où Test dans la même tuile que S, dx est juste S.x - T.x. Pour les tuiles à droite, dxsera calculé comme TileWidth - S.x + T.x.

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

Pour une petite optimisation, trouvez la distance minimale avant de prendre une racine carrée. Ensuite, vous vous enregistrez jusqu'à 7 sqrtappels.

# 2: Résumé des coordonnées

Si vous avez besoin de faire quelque chose de plus fluide "spatialement", comme un algorithme de recherche de chemin, il suffit d'abstraire les coordonnées pour que votre algorithme de recherche de chemin ne réalise même pas que le monde est fait de tuiles répétitives. Théoriquement, l'algorithme de recherche de chemin peut aller à l'infini dans n'importe quelle direction (bon, vous serez limité par des limites numériques, mais vous obtenez le point).

Pour un calcul de distance simple, ne vous embêtez pas à le faire.

dix quatre
la source
Idée intelligente sur la comparaison de la valeur de la distance au carré avant de prendre le sqrt!
Scott Chamberlain,
Ah je vois, @Kol a une réponse similaire avec une explication plus mathématique, merci cela me donne quelque chose avec quoi travailler
Comparer la distance au carré peut être plus intelligent que de prendre le sqrt, mais utiliser la distance de Manhattan est encore plus intelligent car il ne nécessite aucune multiplication.
sam hocevar
0

Ne vous embêtez pas avec les "9 directions". La raison en est qu'il y a 5 cas dégénérés parmi ces 9: "nord droit", "ouest droit", "sud droit", "est est" et "identique". Par exemple, le nord droit est dégénéré car il représente le cas où le nord-ouest et le nord-est se rejoignent et produisent le même résultat.

Ainsi, vous avez 4 directions à calculer et vous pouvez simplement choisir le minimum.

MSalters
la source
Je ne pense pas que ce soit juste, ou je vous ai complètement mal compris. Un des deux.
-1

Merci pour toutes les réponses à la fin, j'ai utilisé Toomai édité par Scott Chamberlain. J'ai également dû faire quelques changements car mon système de coordonnées commence par y en haut à gauche et augmente à mesure que vous descendez (essentiellement inversé par rapport aux coordonnées normales du graphique pour y).

J'ai posté au cas où quelqu'un d'autre trouverait cette page et aurait le même système y inversé.

  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
fou
la source
Ce code est légèrement meilleur que celui de Toomai mais ne fonctionne pas non plus.
sam hocevar
1
Vous devez également comprendre pourquoi vous avez dû effectuer ces modifications. Ce n'est pas parce que votre système de coordonnées commence par yen haut. C'est parce que le comportement souhaité est censé envelopper les coordonnées à la limite du monde, tandis que le code que vous avez réutilisé reflétait les coordonnées à chaque frontière.
sam hocevar