web-dev-qa-db-fra.com

Un moyen facile de garder les angles entre -179 et 180 degrés

Existe-t-il un moyen simple de convertir un angle (en degrés) compris entre -179 et 180? Je suis sûr que je pourrais utiliser mod (%) et quelques déclarations if, mais ça devient moche:


//Make angle between 0 and 360
angle%=360;

//Make angle between -179 and 180
if (angle>180) angle-=360;

Il semble juste qu'il devrait y avoir une opération mathématique simple qui fera les deux déclarations en même temps. Il se peut que je doive simplement créer une méthode statique pour la conversion pour le moment.

32
User1

Je suis un peu en retard à la fête, je sais, mais ...

La plupart de ces réponses sont inutiles, car elles essaient d'être intelligentes et concises et ne s'occupent pas des cas Edge.

C'est un peu plus verbeux, mais si vous voulez que cela fonctionne, inscrivez-vous dans la logique pour que cela fonctionne. N'essayez pas d'être intelligent.

 int normalizeAngle (int angle) 
 {
 int newAngle = angle; 
 while (newAngle <= -180) newAngle + = 360; 
 while (newAngle> 180) newAngle - = 360; 
 return newAngle; 
} 

Cela fonctionne et est raisonnablement propre et simple, sans essayer d'être sophistiqué. Notez que seulement zéro ou l’une des boucles while peut être exécutée.

16
Platinum Azure
// reduce the angle  
angle =  angle % 360; 

// force it to be the positive remainder, so that 0 <= angle < 360  
angle = (angle + 360) % 360;  

// force into the minimum absolute value residue class, so that -180 < angle <= 180  
if (angle > 180)  
    angle -= 360;  
75
James K Polk

Essayez ceci à la place! 

atan2(sin(angle), cos(angle))

atan2 a une plage de [-π, π). Cela tire parti du fait que tan θ = sin θ/cos θ, et que atan2 est suffisamment intelligent pour savoir dans quel quadrant θ se trouve. 

Puisque vous voulez des degrés, vous voudrez convertir votre angle de et en radians: 

atan2(sin(angle * PI/180.0), cos(angle * PI/180.0)) * 180.0/PI

Mise à jour Mon exemple précédent était parfaitement légitime mais limitait la plage à ± 90 °. La plage de atan2 correspond à la valeur souhaitée de -179 ° à 180 °. Conservé ci-dessous. 


Essaye ça: 

asin(sin(angle)))

Le domaine de sin est la ligne réelle, la plage est [-1, 1]. Le domaine de asin est [-1, 1] et la plage est [-PI/2, PI/2]. Puisque asin est l'inverse de sin, votre entrée n'est pas modifiée (beaucoup, il y a une certaine dérive parce que vous utilisez des nombres à virgule flottante). Ainsi, vous récupérez votre valeur d'entrée et vous obtenez la plage souhaitée comme un effet secondaire de la plage restreinte de l'arcsine. 

Puisque vous voulez des degrés, vous voudrez convertir votre angle de et en radians: 

asin(sin(angle * PI/180.0)) * 180.0/PI

(Avertissement: les fonctions de déclenchement sont plusieurs fois plus lentes que les simples opérations de division et de soustraction, même si elles sont effectuées dans une FPU!)

15
Seth

Pas aussi intelligent, mais pas si.

angle = (angle + 179)% 360 - 179;

Mais je ne sais pas comment Java gère modulo pour les nombres négatifs. Cela ne fonctionne que si -1 modulo 360 est égal à 359.

METTRE &AGRAVE; JOUR

Il suffit de cocher la documentation et a % b renvoie une valeur comprise entre -(|b| - 1) et +(|b| - 1) et donc le code est cassé Pour prendre en compte les valeurs négatives renvoyées par l'opérateur modulo, il faut utiliser ce qui suit.

angle = ((angle + 179) % 360 + 360) % 360 - 179;

Mais ... non ... jamais ... Utilisez quelque chose de similaire à votre solution initiale, mais fixé pour des valeurs inférieures à -179.

9
Daniel Brückner

Cela fonctionne avec les nombres décimaux et négatifs et ne nécessite pas de boucles, ni de fonctions trigonométriques:

angle - = Math.floor (angle/360 + 0,5) * 360

Le résultat est dans l'intervalle [-180, 180). Pour (-180, 180] intervalle, vous pouvez utiliser ceci à la place:

angle - = Math.ceil (angle/360 - 0,5) * 360

7
Astronomino

Je sais que les années ont passé, mais quand même. 

Cette solution ne contient aucune boucle, aucune soustraction, aucun modulo (permet de normaliser l'intervalle en radians). Fonctionne pour toute entrée, y compris les valeurs négatives, les grandes valeurs, les cas Edge.

double normalizedAngle = angle - (ceil((angle + M_PI)/(2*M_PI))-1)*2*M_PI;  // (-Pi;Pi]:
double normalizedAngle = angle - (ceil((angle + 180)/360)-1)*360;           // (-180;180]:

double normalizedAngle = angle - (floor((angle + M_PI)/(2*M_PI)))*2*M_PI;  // [-Pi;Pi):
double normalizedAngle = angle - (floor((angle + 180)/360))*360;           // [-180;180):
3
alexburtnik

Peut-être pas utile, mais j'ai toujours aimé utiliser des angles non-degrés.

Une plage d'angle comprise entre 0 et 255 peut être conservée dans les limites à l'aide d'opérations au niveau du bit, ou pour une variable à un seul octet, un simple dépassement autorisé.

Un angle compris entre -128 et 127 n’est pas aussi simple avec les opérations au niveau des bits, mais là encore, pour une variable à un octet, vous pouvez le laisser déborder.

Je pensais que c'était une excellente idée pour les jeux, où vous utilisiez probablement une table de correspondance pour les angles. Ces jours-ci, pas si bien - les angles sont utilisés différemment et flottent quand même.

Encore - peut-être mérite une mention.

3
Steve314

Un moyen court qui gère les nombres négatifs est

double mod = x - Math.floor((x + 179.0) / 360) * 360;

Cast au goût.

BTW: Il semble que les angles entre (180.0, 181.0) ne sont pas définis. La plage ne devrait-elle pas être (-180, 180] (exclusif, inclusif]

3
Peter Lawrey

Voici une solution entière uniquement:

int normalize(int angle)
{
    angle %= 360;
    int fix = angle / 180; // Integer division!!
    return (fix) ? angle - (360 * (fix)) : angle;
}

Parfois, être malin est simplement plus amusant, Platinum Azure.

2
Cory B

Eh bien, encore une solution, celle-ci ne comportant qu'une division et aucune boucle.

static double normalizeAngle(double angle)
{
    angle %= 360.0; // [0..360) if angle is positive, (-360..0] if negative
    if (angle > 180.0) // was positive
        return angle - 360.0; // was (180..360) => returning (-180..0)
    if (angle <= -180.0) // was negative
        return angle + 360.0; // was (-360..180] => returning (0..180]
    return angle; // (-180..180]
}
2
Vlad

J'ai fait une formule pour l'orientation des valeurs circulaires

maintenir l'angle entre 0 et 359 est:

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

mais garder entre -179 et 180 formule peut être:

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

cela donnera son décalage d'orientation sur -179 en conservant l'angle réel intact

formule généralisée pour le changement d'orientation d'angle peut être:

angle + Math.ceil( (-angle+shift) / 360 ) * 360
2
Saad Ahmed
int angle = -394;

// shortest
angle %= 360;
angle = angle < -170 ? angle + 360 : (angle > 180 ? angle - 380 : angle);

// cleanest
angle %= 360;
if (angle < -179) angle += 360;
else if (angle > 180) angle -= 360;
1
Matthew Whited

Voici ma contribution. Il semble fonctionner pour tous les angles sans problèmes de bord. C'est rapide. Il peut faire n180 [360000359] = -1 presque instantanément. Notez que la fonction Sign permet de sélectionner le chemin logique approprié et d’utiliser le même code pour différents angles.

Ratch

n180[a_] := 
 If[Abs[Mod[a, If[Sign[a] == 0, 360, Sign[a] 360]]] <= 180, 
  Mod[a, If[Sign[a] == 0, 360, Sign[a] 360]], 
  Mod[a, If[Sign[a] == 0, 360, -Sign[a] 360]]]
0
Ratch

Que diriez-vous 

(angle % 360) - 179

Cela retournera en réalité des résultats différents de ceux de l'approche naïve présentée dans la question, mais conservera l'angle entre les limites spécifiées. (Je suppose que cela pourrait rendre la réponse fausse, mais je vais le laisser ici au cas où cela résoudrait le problème similaire d'une autre personne).

0
pkaeding