Comment mapper atan2 () en degrés 0-360


108

atan2(y, x) a cette discontinuité à 180 ° où il passe à -180 ° ..0 ° dans le sens des aiguilles d'une montre.

Comment mapper la plage de valeurs à 0 ° ..360 °?

voici mon code:

CGSize deltaPoint = CGSizeMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
float swipeBearing = atan2f(deltaPoint.height, deltaPoint.width);

Je calcule la direction d'un événement tactile de balayage étant donné le startPointet endPoint, les deux structures de point XY. Le code est pour l'iPhone mais toute langue prise en charge atan2f()fera l'affaire.


Remarque: La méthode de mise à jour publiée ne renvoie pas zéro degré, mais des valeurs juste au-dessus de 0 à 360,0.
chux - Réintégrer Monica le

2
> [Comment obtenir un angle à partir de 2 positions] [1] [1]: stackoverflow.com/questions/9457988/…
MAnoj Sarnaik

Cette fonction fonctionne très bien, cependant l'angle de calcul de "BearingDegrees" est inversé. par exemple, 45 degrés correspondraient généralement au 1er quadrant, mais ceci dans le 4ème quadrant. 135 degrés seraient généralement dans le 2ème quadrant mais cette fonction le renvoie dans le 3ème quadrant. Je peux simplement prendre la valeur de retour de la fonction x et l'annuler à partir de 360 ​​pour obtenir la valeur d'angle correcte, mais je suis curieux de savoir pourquoi cela se produit en premier lieu?
goelv

Réponses:


66
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)

6
Veut probablement aussi x> = 0 pour le cas x = 0.
bpw1621

13
Pour ceux qui ne sont pas à l'aise avec cette notation, et sans la conversion en degrés intégrée: if (x> 0) {radians = x;} else {radians = 2 * PI + x;} donc nous ajoutons simplement 2PI au résultat si il est inférieur à 0.
David Doria

1
Ou (x >= 0 ? x : (2*PI + x)) * 180/PIcomme dans(x < 0 ? 2*PI + x : x) * 180/PI
user3342816

97

Solution utilisant Modulo

Une solution simple qui répond à tous les cas.

degrees = (degrees + 360) % 360;  // +360 for implementations where mod returns negative numbers

Explication

Positif: 1 à 180

Si vous modifiez un nombre positif entre 1 et 180 par 360, vous obtiendrez exactement le même nombre que vous avez entré. Mod ici garantit simplement que ces nombres positifs sont retournés avec la même valeur.

Négatif: -180 à -1

L'utilisation de mod ici renverra des valeurs comprises entre 180 et 359 degrés.

Cas particuliers: 0 et 360

L'utilisation de mod signifie que 0 est renvoyé, ce qui en fait une solution sûre de 0 à 359 degrés.


3
awesome solutions :)
Qadir Hussain

5
Je ne pense pas qu'il soit nécessaire d'ajouter 360. -1% 360 est toujours 359 :)
pleasemorebacon

11
Je ne pense pas que ce soit correct dans toutes les langues. En Javascript-1 % 360 = -1
Startec le

Pas non plus une approche viable à Java
Hulk

1
@pleasemorebacon Incorrect. Dans certaines langues, -1% 360 vaut -1.
Pharap

40

Ajoutez simplement 360 ° si la réponse de atan2 est inférieure à 0 °.


6
Ce qui équivaut à "ajouter simplement 2 * PI" si vous vivez un de ces jours.
Chris O

33

Ou si vous n'aimez pas le branchement, annulez simplement les deux paramètres et ajoutez 180 ° à la réponse.

(L'ajout de 180 ° à la valeur de retour le place bien dans la plage 0-360, mais inverse l'angle. La négation des deux paramètres d'entrée le retourne.)


2
Merci, c'est exactement ce que je cherchais.
Jeremy Herrman

2
Je préfère modifier mon code pour utiliser des angles dénormalisés (<0,> = 360) mais il semble toujours y avoir quelqu'un visant cette fausse sensation "optimisée"; c'est pourquoi je voulais ajouter ceci. (Ou était-ce parce que c'était le moyen le plus rapide de contourner un code de débogage temporaire que j'ai utilisé? Hmm)
aib

1
Certainement pas simple à grok, car je peux être d'accord après plus de 2 ans. Donc: Ajouter 180 ° à la valeur de retour le met bien dans la plage 0-360, mais inverse l'angle. Annuler les deux paramètres d'entrée le retourne.
aib

Cela peut avoir des problèmes lorsque $ x = 0 $ et $ y> 0 $ iirc
Trinidad

22

@erikkallen est proche mais pas tout à fait raison.

theta_rad = atan2(y,x);
theta_deg = (theta_rad/M_PI*180) + (theta_rad > 0 ? 0 : 360);

Cela devrait fonctionner en C ++: (selon la façon dont fmod est implémenté, il peut être plus rapide ou plus lent que l'expression conditionnelle)

theta_deg = fmod(atan2(y,x)/M_PI*180,360);

Vous pouvez également faire ceci:

theta_deg = atan2(-y,-x)/M_PI*180 + 180;

puisque (x, y) et (-x, -y) diffèrent en angles de 180 degrés.


si je vous ai bien compris dans Fortran, c'est atan2 (-y, -x) * 180 / PI + 180. Est-ce exact?
gansub

désolé je ne connais pas FORTRAN. Mais vos calculs semblent corrects.
Jason S

11

J'ai 2 solutions qui semblent fonctionner pour toutes les combinaisons de x et y positifs et négatifs.

1) Abus atan2 ()

Selon la documentation, atan2 prend les paramètres y et x dans cet ordre. Cependant, si vous les inversez, vous pouvez effectuer les opérations suivantes:

double radians = std::atan2(x, y);
double degrees = radians * 180 / M_PI;
if (radians < 0)
{
    degrees += 360; 
}

2) Utiliser correctement atan2 () et convertir ensuite

double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
    degrees = 450 - degrees;
}
else
{
    degrees = 90 - degrees;
}

Vous monsieur, êtes un sauveur de vie. Je viens d'utiliser cette approche dans Unity et fonctionne comme un charme.
porfiriopartida

7

@Jason S: votre variante "fmod" ne fonctionnera pas sur une implémentation conforme aux standards. Le standard C est explicite et clair (7.12.10.1, «les fonctions fmod»):

si y est différent de zéro, le résultat a le même signe que x

Donc,

fmod(atan2(y,x)/M_PI*180,360)

est en fait juste une réécriture verbeuse de:

atan2(y,x)/M_PI*180

Votre troisième suggestion, cependant, est juste.


5

C'est ce que je fais normalement:

float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;

2
angle = Math.atan2(x,y)*180/Math.PI;

J'ai fait une formule pour orienter l'angle de 0 à 360

angle + Math.ceil( -angle / 360 ) * 360;

2

Une solution alternative consiste à utiliser la fonction mod () définie comme:

function mod(a, b) {return a - Math.floor (a / b) * b;}

Ensuite, avec la fonction suivante, l'angle entre les points ini (x, y) et fin (x, y) est obtenu. L'angle est exprimé en degrés normalisés à [0, 360] deg. et Nord référençant 360 degrés.

    function angleInDegrees(ini, end) {
        var radian = Math.atan2((end.y - ini.y), (end.x - ini.x));//radian [-PI,PI]
        return mod(radian * 180 / Math.PI + 90, 360);
    }

1

La géosphère des packages R calculera le relèvementRhumb, qui est une ligne de relèvement constante étant donné un point d'origine et une direction est / nord. L'est et le nord doivent être dans une matrice ou un vecteur. Le point d'origine d'une rose des vents est 0,0. Le code suivant semble résoudre facilement le problème:

windE<-wind$uasE
windN<-wind$vasN
wind_matrix<-cbind(windE, windN)
wind$wind_dir<-bearingRhumb(c(0,0), wind_matrix)
wind$wind_dir<-round(wind$wind_dir, 0)

1
theta_rad = Math.Atan2(y,x);
if(theta_rad < 0)
  theta_rad = theta_rad + 2 * Math.PI;    //if neg., add 2 PI to it
theta_deg = (theta_rad/M_PI*180) ;        //convert from radian to degree

//or
theta_rad = Math.Atan2(y,x);
theta_rad = (theta_rad < 0) ? theta_rad + 2 * Math.PI : theta_rad;
theta_deg = (theta_rad/M_PI*180) ;

-1 deg devient (-1 + 360) = 359 deg
-179 deg devient (-179 + 360) = 181 deg


Quoi Math.PI? Est-ce la même chose que M_PI?
Pang

1
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

Cela retournera le degré de 0 ° -360 ° dans le sens antihoraire, 0 ° est à 3 heures.


1

Une formule pour avoir la plage de valeurs de 0 à 360 degrés.

f (x, y) = 180-90 * (1 + signe (x)) * (1-signe (y ^ 2)) - 45 * (2 + signe (x)) * signe (y)

     -(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))

Pouvez-vous expliquer en quoi cela se rapporte à la question?
Klaus Gütter

1

Voici quelques javascript. Entrez simplement les valeurs x et y.

var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.