La plus petite différence entre 2 angles


137

Étant donné 2 angles dans la plage -PI -> PI autour d'une coordonnée, quelle est la valeur du plus petit des 2 angles entre eux?

Tenant compte du fait que la différence entre PI et -PI n'est pas de 2 PI mais de zéro.

Exemple:

Imaginez un cercle, avec 2 lignes sortant du centre, il y a 2 angles entre ces lignes, l'angle qu'elles forment à l'intérieur, c'est-à-dire le plus petit angle , et l'angle qu'elles forment à l'extérieur, c'est-à-dire le plus grand angle. Les deux angles lorsqu'ils sont additionnés forment un cercle complet. Étant donné que chaque angle peut tenir dans une certaine plage, quelle est la valeur des angles les plus petits, en tenant compte du survol


2
J'ai lu 3 fois avant de comprendre ce que vous vouliez dire. Veuillez ajouter un exemple, ou expliquer mieux ...
Kobi

Imaginez un cercle, avec 2 lignes partant du centre, il y a 2 angles entre ces lignes, l'angle qu'elles forment à l'intérieur, c'est-à-dire le plus petit angle, et l'angle qu'elles forment à l'extérieur, c'est-à-dire le plus grand angle. Les deux angles lorsqu'ils sont additionnés forment un cercle complet. Étant donné que chaque angle peut tenir dans une certaine plage, quelle est la valeur des angles les plus petits, en tenant compte du survol
Tom J Nowell


2
@JimG. ce n'est pas la même question, dans cette question l'angle P1 utilisé dans l'autre question serait la réponse incorrecte, ce serait l'autre angle plus petit. De plus, il n'y a aucune garantie que l'angle soit avec l'axe horizontal
Tom J Nowell

Réponses:


193

Cela donne un angle signé pour tous les angles:

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

Attention dans de nombreux langages, l' moduloopération renvoie une valeur avec le même signe que le dividende (comme C, C ++, C #, JavaScript, liste complète ici ). Cela nécessite une modfonction personnalisée comme celle-ci:

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

Ou alors:

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

Si les angles sont compris entre [-180, 180], cela fonctionne également:

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

De manière plus verbeuse:

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

Plus simple et a plus de sens, lire à haute voix, bien que effectivement la même chose, d'abord bti calcule l'angle, la deuxième partie s'assure que c'est toujours le plus petit des 2 angles possibles
Tom J Nowell

1
bien que l'on puisse vouloir faire un% 360, par exemple si j'avais l'angle 0 et l'angle cible 721, la bonne réponse serait 1, la réponse donnée par ce qui précède serait 361
Tom J Nowell

1
Un équivalent plus concis, bien que potentiellement plus coûteux, du deuxième énoncé de cette dernière approche, est a -= 360*sgn(a)*(abs(a) > 180). (À bien y penser, si vous avez des implémentations sans branche de sgnet abs, alors cette caractéristique pourrait en fait commencer à compenser le besoin de deux multiplications.)
mmirate

1
L'exemple «Angle signé pour n'importe quel angle» semble fonctionner dans la plupart des scénarios, à une exception près. Dans le scénario double targetA = 2; double sourceA = 359;'a' sera égal à -357,0 au lieu de 3,0
Stevoisiak

3
En C ++, vous pouvez utiliser std :: fmod (a, 360) ou fmod (a, 360) pour utiliser modulo en virgule flottante.
Joeppie

145

x est l'angle cible. y est la source ou l'angle de départ:

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

Il renvoie l'angle delta signé. Notez que selon votre API, l'ordre des paramètres de la fonction atan2 () peut être différent.


13
x-yvous donne la différence d'angle, mais elle peut être hors des limites souhaitées. Pensez à cet angle définissant un point sur le cercle unité. Les coordonnées de ce point sont (cos(x-y), sin(x-y)). atan2renvoie l'angle pour ce point (qui équivaut à x-y) sauf que sa plage est [-PI, PI].
Max


2
une solution simple en une ligne et résolue pour moi (pas la réponse choisie;)). mais l'inverse de tan est un processus coûteux.
Mohan Kumar

2
Pour moi, la solution la plus élégante. Dommage que cela puisse coûter cher en calcul.
focs le

Pour moi aussi la solution la plus élégante! J'ai parfaitement résolu mon problème (je voulais avoir une formule qui me donne l' angle de virage signé qui est le plus petit des deux directions / angles de virage possibles).
Jürgen Brauer

41

Si vos deux angles sont x et y, l'un des angles entre eux est abs (x - y). L'autre angle est (2 * PI) - abs (x - y). Ainsi, la valeur du plus petit des 2 angles est:

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

Cela vous donne la valeur absolue de l'angle et suppose que les entrées sont normalisées (c'est-à-dire: dans la plage [0, 2π)).

Si vous souhaitez conserver le signe (c'est-à-dire la direction) de l'angle et accepter également des angles en dehors de la plage, [0, 2π)vous pouvez généraliser ce qui précède. Voici le code Python pour la version généralisée:

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

Notez que l' %opérateur ne se comporte pas de la même manière dans toutes les langues, en particulier lorsque des valeurs négatives sont impliquées, donc en cas de portage, des ajustements de signe peuvent être nécessaires.


1
@bradgonesurfing C'est / était vrai, mais pour être juste, vos tests ont vérifié des choses qui n'étaient pas spécifiées dans la question d'origine, en particulier les entrées non normalisées et la préservation des signes. La deuxième version de la réponse modifiée devrait réussir vos tests.
Laurence Gonsalves

La deuxième version ne fonctionne pas non plus pour moi. Essayez 350 et 0 par exemple. Il devrait renvoyer -10 mais renvoie -350
kjyv

@kjyv Je ne peux pas reproduire le comportement que vous décrivez. Pouvez-vous publier le code exact?
Laurence Gonsalves

Ah, je suis désolé. J'ai testé à nouveau exactement votre version avec rad et degrés en python et cela a bien fonctionné. Cela a dû être une erreur dans ma traduction en C # (ne l'avez plus).
kjyv

2
Notez qu'à partir de Python 3, vous pouvez en fait utiliser tau de manière native! Écrivez juste from math import tau.
mhartl

8

Je relève le défi de fournir la réponse signée:

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 réponse est une fonction Python au fait. Désolé, j'étais en mode Python pendant un moment. J'espère que ça va.
David Jones

Je vais brancher la nouvelle formule dans mon code à l'étage et voir ce qu'il en advient! (merci ^ _ ^)
Tom J Nowell

1
Je suis presque sûr que la réponse de PeterB est également correcte. Et diaboliquement hackish. :)
David Jones

4
Mais celui-ci ne contient pas de fonctions trigonométriques :)
nornagon

Quelle est la formule équivalente pour java? si les angles sont en degrés.?
Soley

6

Pour les utilisateurs d'UnityEngine, le moyen le plus simple consiste simplement à utiliser Mathf.DeltaAngle .


1
N'a pas de sortie signée tho
kjyv


2

Un code efficace en C ++ qui fonctionne pour n'importe quel angle et dans les deux: radians et degrés est:

inline double getAbsoluteDiff2Angles(const double x, const double y, const double c)
{
    // c can be PI (for radians) or 180.0 (for degrees);
    return c - fabs(fmod(fabs(x - y), 2*c) - c);
}

-1

Il n'est pas nécessaire de calculer des fonctions trigonométriques. Le code simple en langage C est:

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

soit dif = a - b, en radians

dif = difangrad(a,b);

soit dif = a - b, en degrés

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

Pas de péché, pas de cos, pas de bronzage, .... seulement la géométrie !!!!


7
Punaise! Puisque vous #define PIV2 comme "M_PI + M_PI", pas "(M_PI + M_PI)", la ligne se arg = arg - PIV2;développe arg = arg - M_PI + M_PIet ne fait rien.
canton7
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.