2009-12-10 24 views
96

Dato 2 angoli nell'intervallo -PI -> PI attorno a una coordinata, qual è il valore del più piccolo dei 2 angoli tra di loro?La più piccola differenza tra 2 angoli

Tenendo conto che la differenza tra PI e -PI non è 2 PI ma zero.

Esempio:

immaginate un cerchio, con 2 linee che esce dal centro, ci sono 2 angoli tra queste linee, l'angolo fanno all'interno alias l'angolo minore, e l'angolo fanno fuori, alias l'angolo più grande. Entrambi gli angoli quando aggiunti fanno un cerchio completo. Dato che ogni angolo può rientrare in un determinato intervallo, qual è il valore di angoli più piccoli, tenendo conto del rollover

+2

Ho letto 3 volte prima di aver capito cosa intendevi. Per favore aggiungi un esempio, o spiega meglio ... – Kobi

+0

Immagina un cerchio, con 2 linee che escono dal centro, ci sono 2 angoli tra quelle linee, l'angolo che fanno all'interno, ovvero l'angolo più piccolo, e l'angolo che fanno all'esterno, ovvero l'angolo più grande. Entrambi gli angoli quando aggiunti fanno un cerchio completo. Dato che ogni angolo può rientrare in un determinato intervallo, qual è il valore degli angoli più piccoli, tenendo conto del rollover –

+0

Possibile duplicato di [Come calcolare l'angolo tra una linea e l'asse orizzontale?] (Https://stackoverflow.com)/questions/7586063/how-to-calcolare-the-angle-between-a-line-and-the-axis-axis) –

risposta

135

Questo dà un angolo firmato per eventuali angoli:

a = targetA - sourceA 
a = (a + 180) % 360 - 180 

Attenzione in molte lingue l'operazione modulo restituisce un valore con il stesso segno del dividendo (come C, C++, C#, JavaScript, full list here).Ciò richiede un costume mod funzione in questo modo:

mod = (a, n) -> a - floor(a/n) * n 

O almeno così:

mod = (a, n) -> (a % n + n) % n 

Se gli angoli sono entro [-180, 180] questo funziona anche:

a = targetA - sourceA 
a += (a>180) ? -360 : (a<-180) ? 360 : 0 

In un altro modo verboso:

a = targetA - sourceA 
a -= 360 if a > 180 
a += 360 if a < -180 
+0

Più semplice e ha più senso leggere ad alta voce, anche se effettivamente la stessa cosa, prima bti calcola l'angolo, la seconda parte si assicura sempre la più piccola delle 2 possibili angolazioni –

+1

sebbene si possa voler fare un% 360, ad es. se avessi l'angolo 0 e l'angolo di mira 721, la risposta corretta sarebbe 1, la risposta data da quanto sopra sarebbe 361 –

+0

Sì, è vero e deliberato, ma sicuramente vale la pena segnalarlo. Nel mio esempio ho già ottenuto 'targetA' e' sourceA' da 'atan2', quindi i loro angoli assoluti non sono mai maggiori di 360. – bennedich

30

Se i tuoi due angoli sono x e y, allora uno degli angoli tra loro è abs (x - y). L'altro angolo è (2 * PI) - abs (x - y). Quindi il valore del più piccolo dei 2 angoli è:

min((2 * PI) - abs(x - y), abs(x - y)) 

Questo dà il valore assoluto dell'angolo, e assume gli ingressi sono normalizzati (ossia: nell'intervallo [0, 2π)).

Se si desidera conservare il segno (ovvero: direzione) dell'angolo e accettare anche angoli al di fuori dell'intervallo [0, 2π) è possibile generalizzare quanto sopra. Ecco il codice Python per la versione generalizzata:

PI = math.pi 
TAU = 2*PI 
def smallestSignedAngleBetween(x, y): 
    a = (x - y) % TAU 
    b = (y - x) % TAU 
    return -a if a < b else b 

Nota che l'operatore % non si comporta lo stesso in tutte le lingue, in particolare quando sono coinvolti valori negativi, per cui se il porting alcune regolazioni segno può essere necessario.

+0

Errore nella suite di test https://gist.github.com/bradphelan/7fe21ad8ebfcb43696b8 – bradgonesurfing

+1

@bradgonesurfing Questo è/era vero, ma per essere corretti i test sono stati controllati che non sono stati specificati nella domanda originale, in particolare input non normalizzati e conservazione del segno. La seconda versione nella risposta modificata dovrebbe superare i test. –

105

x è l'angolo di destinazione. y è la sorgente o l'angolo iniziale:

atan2(sin(x-y), cos(x-y)) 

Restituisce l'angolo delta firmato. Si noti che a seconda dell'API l'ordine dei parametri per la funzione atan2() potrebbe essere diverso.

+3

funziona. perché? – ericsoco

+8

'x-y' ti dà la differenza di angolo, ma potrebbe essere fuori dai limiti desiderati. Pensa a questo angolo definendo un punto sul cerchio unitario. Le coordinate di quel punto sono '(cos (x-y), sin (x-y))'. 'atan2' restituisce l'angolo per quel punto (che è equivalente a' x-y') eccetto il suo intervallo è [-PI, PI]. – Max

+1

Passa alla suite di test https://gist.github.com/bradphelan/7fe21ad8ebfcb43696b8 – bradgonesurfing

7

prendo la parola per la sfida di fornire la risposta firmato:

def f(x,y): 
    import math 
    return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs) 
+1

Ah ... la risposta è una funzione Python a proposito. Scusa, ero in modalità Python per un momento. Spero che vada bene. –

+0

Inserirò la nuova formula nel mio codice di sopra e vedrò cosa ne sarà!(grazie)^_ ^) –

+1

Sono sicuro che anche la risposta di PeterB è corretta. E malvagiamente hackish. :) –

5

Ari (Al contrario di algoritmica) Soluzione thmetical:

angle = Pi - abs(abs(a1 - a2) - Pi); 
+0

Questo fallisce nella suite di test https://gist.github.com/bradphelan/7fe21ad8ebfcb43696b8 – bradgonesurfing

2

Non v'è alcuna necessità di calcolare le funzioni trigonometriche. Il semplice codice in linguaggio C è:

#include <math.h> 
#define PIV2 M_PI+M_PI 
#define C360 360.0000000000000000000 
double difangrad(double x, double y) 
{ 
double arg; 

arg = fmod(y-x, PIV2); 
if (arg < 0) arg = arg + PIV2; 
if (arg > M_PI) arg = arg - PIV2; 

return (-arg); 
} 
double difangdeg(double x, double y) 
{ 
double arg; 
arg = fmod(y-x, C360); 
if (arg < 0) arg = arg + C360; 
if (arg > 180) arg = arg - C360; 
return (-arg); 
} 

diamo dif = a - b, in radianti

dif = difangrad(a,b); 

lasciare dif = a - b, in gradi

dif = difangdeg(a,b); 

difangdeg(180.000000 , -180.000000) = 0.000000 
difangdeg(-180.000000 , 180.000000) = -0.000000 
difangdeg(359.000000 , 1.000000) = -2.000000 
difangdeg(1.000000 , 359.000000) = 2.000000 

Nessun peccato, no cos, no tan, .... solo geometria !!!!

+4

Bug! Dal momento che #define PIV2 come" M_PI + M_PI " , non "(M_PI + M_PI) ", la riga' arg = arg - PIV2; 'si espande in' arg = arg - M_PI + M_PI', e quindi non fa nulla. – canton7

4

Per gli utenti di UnityEngine, il modo più semplice è quello di utilizzare Mathf.DeltaAngle.

Problemi correlati