Trovare la direzione di viaggio in un mondo con bordi avvolti


20

Devo trovare la direzione della distanza più breve da un punto del mio mondo 2D a un altro punto in cui i bordi sono avvolti (come asteroidi, ecc.). So come trovare la distanza più breve ma faccio fatica a trovare in quale direzione si trova.

La distanza più breve è data da:

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

Esempio del mondo

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

Nel diagramma i bordi sono indicati con: e -. Ho mostrato anche una ripetizione avvolta del mondo in alto a destra. Voglio trovare la direzione in gradi da S a T. Quindi la distanza più breve è la ripetizione in alto a destra di T. ma come faccio a calcolare la direzione in gradi da S alla T ripetuta in alto a destra?

Conosco le posizioni di S e T, ma suppongo di dover trovare la posizione della T ripetuta, tuttavia più di 1.

Il sistema di coordinate dei mondi inizia da 0,0 in alto a sinistra e 0 gradi per la direzione potrebbe iniziare a ovest.

Sembra che questo non dovrebbe essere troppo difficile ma non sono stato in grado di trovare una soluzione. Spero che qualcuno possa aiutare? Tutti i siti Web sarebbero apprezzati.


Quali sono le coordinate per la T in alto a destra?

Non ho mai visto un gioco con involucro diagonale. Di solito hai un avvolgimento per ogni direzione (N, E, S, W).

5
Per impostazione predefinita, ogni gioco che ha un involucro orizzontale e verticale ha un involucro diagonale.

Pensa a ciascuna coordinata come a vivere su un cerchio e scopri la più breve delle due possibili distanze per ciascuna coordinata individualmente.
Kerrek SB,

1
@crazy: cerca "torus" su Wikipedia ...
Kerrek SB,

Risposte:


8

Dovrai modificare un po 'il tuo algoritmo per calcolare l'angolo - attualmente registra solo la differenza assoluta di posizione, ma hai bisogno della differenza relativa (cioè può essere positiva o negativa a seconda del posizionamento).

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

1
È necessario lavorare sui segni di dx e dy poiché il codice così com'è si interromperà se TX è inferiore a SX o TY è inferiore a XY A parte questo, questa è la soluzione IMHO migliore.
Scott Chamberlain,

In quel momento lo aggiusterò.

1
Hai ancora qualche errore su quale sarà il segno di dx e dy quando tutto sarà detto e fatto, ti dispiace se modifico?
Scott Chamberlain,

Perché questa è la risposta accettata ?? Non funziona nemmeno . Supponiamo che MapXsia 100, T.X90 e S.X10. dxdovrebbe essere chiaramente 20, ma questo algoritmo restituirà 30!
Sam Hocevar,

Questo è ciò che accade quando non hai l'opportunità di testare il codice prima di pubblicarlo. Risolverà. Se qualcuno trova un altro errore con questo, probabilmente lo cancellerò prima che troppe persone vengano fuorviate.
Toomai,

11

In un tale mondo ci sono infiniti percorsi da S a T. Indichiamo le coordinate di T di (Tx, Ty), le coordinate di S di (Sx, Sy)e la dimensione del mondo di (Wx, Wy). Le coordinate avvolte di T sono (Tx + i * Wx, Ty + j * Wy), dove ie jsono numeri interi, ovvero elementi dell'insieme {..., -2, -1, 0, 1, 2, ...}. I vettori che collegano S a T sono (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy). Per una data (i, j)coppia, la distanza è la lunghezza del vettore sqrt(Dx * Dx + Dy * Dy)e la direzione in radianti è atan(Dy / Dx). Il percorso più breve è uno dei 9 percorsi, dove ie jsono in {-1, 0, 1}: inserisci qui la descrizione dell'immagine

I valori ie jper il percorso più breve possono essere determinati direttamente:

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;

Grazie, @IlmariKaronen, @SamHocevar e @romkyns per il vostro aiuto!


1
Puoi fare di meglio: se abs(Tx-Sx) < Wx/2, allora i=0è ottimale; altrimenti la scelta ottimale è i=-1o i=1, a seconda del segno di Tx-Sx. Lo stesso vale per Ty-Sye j.
Ilmari Karonen,

1
Questa risposta è incredibilmente complicata per un problema così semplice. Non è necessario utilizzare la ricerca lineare quando il valore minimo può essere calcolato direttamente.
sam hocevar,

Bella foto, ma l'algoritmo suggerito non merita nessuno dei voti che questa risposta ha ricevuto.
Roman,

5

Calcola un possibile vettore di direzione, anche se non è il più corto, quindi avvolgi la sua coordinata X in modo che sia [-MapX/2,MapX/2]compresa nell'intervallo e lo stesso per 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;

Questo è tutto! Ottieni anche la distanza senza ulteriori calcoli:

double dist = sqrt((double)(DirX*DirX + DirY*DirY));

Grazie! Versione GLSL:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01

0

Immagino che ci siano molti modi per farlo. Qui ci sono 2 che mi viene in mente dalla parte superiore della mia testa:

# 1: gestire le custodie manualmente

Esistono esattamente 10 casi che possono accadere:

  • È nella stessa piastrella di S
  • È in una delle 8 tessere circostanti
  • Non si trova affatto.

Per ciascuna delle piastrelle circostanti, tuttavia, sono permutazioni di diversi calcoli per il componente della distanza X o Y. Poiché si tratta di un numero limitato di casi, è possibile programmare semplicemente come calcolarli e trovare la distanza più breve tra tutti.

Ecco un'illustrazione di 2 casi per la ricerca dx. Caso 1, dove si Ttrova nella stessa tessera di S, dx è giusto S.x - T.x. Per le tessere a destra, dxverrà calcolato come TileWidth - S.x + T.x.

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

Come piccola ottimizzazione, trova la distanza minima prima di prendere una radice quadrata. Quindi ti risparmi fino a 7 sqrtchiamate.

# 2: astrarre le coordinate

Se hai bisogno di fare qualcosa di più "fluido" nello spazio, come un algoritmo di ricerca del percorso, basta astrarre le coordinate in modo che il tuo algoritmo di ricerca del percorso non si accorga nemmeno che il mondo è fatto di tessere ripetute. L'algoritmo di ricerca del percorso potrebbe andare infinitamente in qualsiasi direzione teoricamente (ok beh, sarai limitato da limiti numerici, ma otterrai il punto).

Per un semplice calcolo della distanza, non preoccuparti di farlo.


Idea intelligente sul confronto del valore della distanza quadrata prima di prendere il sqrt!
Scott Chamberlain,

Ah, vedo, @Kol ha una risposta simile con una spiegazione più matematica, grazie questo mi dà qualcosa con cui lavorare

Confrontare la distanza quadrata può essere più intelligente che prendere la sqrt, ma usare la distanza di Manhattan è ancora più intelligente poiché non richiede alcuna moltiplicazione.
sam hocevar,

0

Non preoccuparti delle "9 direzioni". La ragione è che ci sono 5 casi degenerati tra quelli 9: "dritto nord", "stretto ovest", "dritto sud", "dritto est" e "identico". Ad esempio, il nord diritto è degenerato perché rappresenta il caso in cui il nordovest e il nordest si uniscono e producono lo stesso risultato.

Pertanto, hai 4 direzioni da calcolare e puoi semplicemente scegliere il minimo.


Non penso sia giusto o ti ho completamente frainteso. Uno dei due.

-1

Grazie per tutte le risposte alla fine ho usato Toomai a cura di Scott Chamberlain. Ho anche apportato alcune modifiche a causa del fatto che il mio sistema di coordinate inizia con y in alto a sinistra e aumenta man mano che ci si sposta verso il basso (sostanzialmente invertito rispetto alle normali coordinate grafiche per y).

Ho pubblicato nel caso in cui qualcun altro trovi questa pagina e abbia lo stesso sistema inverso.

  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

Questo codice è leggermente migliore di quello di Toomai ma non funziona neanche.
Sam Hocevar,

1
Inoltre, devi capire perché hai dovuto apportare queste modifiche. E ' non è perché il vostro coordinare sistema si avvia con yin alto. È perché il comportamento desiderato dovrebbe presumibilmente avvolgere le coordinate ai margini del mondo, mentre il codice che hai riutilizzato rispecchiava le coordinate su ciascun confine.
sam hocevar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.