2009-09-17 9 views
5

Ho un mazzo di vettori normale alle superfici di finestre in un software di modellazione 3D. Progettato per il piano XY, mi piacerebbe sapere in quale direzione si trovano ad affrontare, tradotto alla 8 bussola coordinate (Nord, Nord-Est, Est, Sud-Est, Sud, Sud-Ovest, Ovest e Nord-Ovest).Come "scattare" un vettore direzionale (2D) su una bussola (N, NE, E, SE, S, SW, W, NW)?

I vettori di lavorare in questo modo:

asse
  • la X rappresenta Est-Ovest (con Est essere positivo)
  • l'asse y rappresenta Nord-Sud (con il Nord essere positivo)
  • così
    • (0, 1) == Nord
    • (1, 0) == Oriente
    • (0, -1) == Sud
    • (-1,0) == Ovest

Dato un vettore (x, y) Sto cercando il più vicino degli 8 coordinate bussola. Qualche idea su come farlo elegantemente?

+0

qualche idea su come etichettarlo correttamente? sentiti libero ... –

+1

No, nella mia risposta (1, 0) è est e sta andando in senso antiorario, poiché sto facendo le cose matematicamente qui. Ciò è in contrasto con l'uso standard degli angoli nella navigazione (0 = nord, in senso orario). Puoi adattarlo aggiungendo un offset appropriato al +8 e usando a - davanti a atan2. – starblue

+0

Grazie per avermi segnalato starblue, mi sto confondendo con i miei schizzi qui - la tua risposta è azzeccata! –

risposta

7

Questo funziona in Java, calcolando un valore di 0 ... 7 per otto direzioni:

import static java.lang.Math.*;  

int compass = (((int) round(atan2(y, x)/(2 * PI/8))) + 8) % 8; 

Le mappe di risultato alla bussola come segue:

0 => E 
1 => NE 
2 => N 
3 => NW 
4 => W 
5 => SW 
6 => S 
7 => SE 
6

Probabilmente chiamerei semplicemente atan2() per calcolare l'angolo di direzione ("imbardata"), quindi utilizzare una sequenza di if: s o alcune matematiche per "scattare" su un multiplo di 90 gradi.

+0

grazie, sembra promettente, lo controlleremo dopo pranzo! –

4

c'è bisogno di fare una funzione atan.

se lo fai: y/x otterrai la pendenza della linea. A giudicare dal numero che ottieni puoi determinare l'angolo/ottante.

per xe positivi (x> 0)

  • (y/x)> 2.4 - => 90 gradi (nord)
  • 2.4> (y/x)> 0,4 ​​- => 45 gradi (ovest)
  • 0,4> (y/x)> -0.4 -> = 0 gradi (ovest)
  • -0.4> (y/x)> -2.4 - => -45 gradi (sud-ovest)
  • -2.4> (y/x) - => 90 gradi (sud)

e un elenco analogo per quelli del x negativi

ed infine i casi eccezione:

  • (x == 0 & & y> 0) -> = -90 gradi (sud)
  • (x == 0 & & y < 0) -> = 90 gradi (sud)
01.235.164,106174 millions

addendum: Riporto solo questo metodo per il calcolo di un'Atan è un no go (ad esempio in un sistema embedded))

Ho dovuto scavare un po '. Ecco una routine altamente ottimizzata che uso (usata nei giochi mobili).

ingresso: x1, y1 = punto di inizio della vettore x2, y2 = punto finale del vettore uscita (0-7) = 0 = nord, 1 = nord-ovest, 2 = ovest, ... ecc

int CalcDir(int x1, int y1, int x2, int y2) 
{ 
     int dx = x2 - x1, dy = y2 - y1; 
     int adx = (dx<0)?-dx:dx, ady = (dy<0)?-dy:dy, r; 
     r=(dy>0?4:0)+(dx>0?2:0)+(adx>ady?1:0); 
     r=(int []){2,3,1,0,5,4,6,7}[r]; 
     return r; 
} 

void CalcDirTest(){ 
     int t = CalcDir(0, 0, 10, 1); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, 9, 10); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, -1, 10); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, -10, 9); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, -10, -1); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, -9, -10); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, 1, -10); 
     printf("t = %d",t); 
     t = CalcDir(0, 0, 10, -9); 
     printf("t = %d",t); 
} 

Ciò comporterà il seguente output:

t = 7 
t = 6 
t = 5 
t = 4 
t = 3 
t = 2 
t = 1 
t = 0 

(I vettori per il test potrebbe sembrare stranamente scelto, ma li ho ottimizzato tutti un po 'per essere univocamente a un'ottante e non al confine esatto)

+0

Non vedo come la routine ottimizzata finale possa funzionare in quanto il valore r può facilmente superare la dimensione dell'array anonimo. – GregM

+0

Ben avvistato. Corretto il bug – Toad

3

Questo non usa atan2 e fa nella peggiore delle ipotesi 4 confronti e 2 prodotti per chiamata. Confrontando xey nei 4 blocchi interni (l'ho modificato solo nel primo blocco), può essere ridotto a esattamente 4 confronti e 1 prodotto per chiamata.

int compass(double x,double y) 
{ 
    double t = 0.392699082; // tan(M_PI/8.0); 

    if (x>=0) 
    { 
    if (y>=0) 
    { 
     if (x>y) { if (y<t*x) return E_COMPASS; } 
     else { if (x<t*y) return N_COMPASS; } 
     return NE_COMPASS; 
    } 
    else 
    { 
     if (-y<t*x) return E_COMPASS; 
     if (x<-t*y) return S_COMPASS; 
     return SE_COMPASS; 
    } 
    } 
    else 
    { 
    if (y>=0) 
    { 
     if (y<-t*x) return W_COMPASS; 
     if (-x<t*y) return N_COMPASS; 
     return NW_COMPASS; 
    } 
    else 
    { 
     if (-y<-t*x) return W_COMPASS; 
     if (-x<-t*y) return S_COMPASS; 
     return SW_COMPASS; 
    } 
    } 
    return E_COMPASS; 
}