web-dev-qa-db-fra.com

Comment calculer l'angle entre une ligne et l'axe horizontal?

Dans un langage de programmation (Python, C #, etc.), je dois déterminer comment calculer l'angle entre une ligne et l'axe horizontal?

Je pense qu'une image décrit le mieux ce que je veux:

no words can describe this

Donné (P1x, P1y) et (P2x, P2y) quel est le meilleur moyen de calculer cet angle? L'origine est en haut et seul le quadrant positif est utilisé.

243
orlp

Commencez par trouver la différence entre le point de départ et le point de fin (ici, il s’agit davantage d’un segment de ligne dirigé, et non d’une "ligne", car les lignes s’étendent à l’infini et ne commencent pas à un point particulier).

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

Calculez ensuite l’angle (qui va de l’axe X positif en P1 à l’axe Y positif en P1).

angleInDegrees = arctan(deltaY / deltaX) * 180 / PI

Mais arctan n'est peut-être pas idéal, car diviser les différences de cette manière effacera la distinction nécessaire pour distinguer le quadrant dans lequel l'angle est situé (voir ci-dessous). Utilisez plutôt le code suivant si votre langue comprend une fonction atan2:

angleInDegrees = atan2(deltaY, deltaX) * 180 / PI

EDIT (22 février 2017): En général, cependant, appeler atan2(deltaY,deltaX) uniquement pour obtenir le bon angle pour cos et sin peut être inélégant. Dans ces cas, vous pouvez souvent effectuer les opérations suivantes:

  1. Traiter (deltaX, deltaY) comme un vecteur.
  2. Normalisez ce vecteur en un vecteur unitaire. Pour ce faire, divisez deltaX et deltaY par la longueur du vecteur (sqrt(deltaX*deltaX+deltaY*deltaY)), sauf si la longueur est 0.
  3. Après cela, deltaX sera maintenant le cosinus de l'angle entre le vecteur et l'axe horizontal (dans la direction allant du X positif à l'axe Y positif à P1).
  4. Et deltaY sera maintenant le sinus de cet angle.
  5. Si la longueur du vecteur est 0, il n'y aura pas d'angle entre elle et l'axe horizontal (il n'aura donc pas de sinus ni de cosinus significatifs).

EDIT (28 février 2017): Même sans normalisation (deltaX, deltaY):

  • Le signe de deltaX vous dira si le cosinus décrit à l'étape 3 est positif ou négatif.
  • Le signe de deltaY vous dira si le sinus décrit à l'étape 4 est positif ou négatif.
  • Les signes de deltaX et deltaY vous indiqueront le quadrant dans lequel se trouve l'angle, par rapport à l'axe des X positif à P1:
    • +deltaX, +deltaY: 0 à 90 degrés.
    • -deltaX, +deltaY: 90 à 180 degrés.
    • -deltaX, -deltaY: 180 à 270 degrés (-180 à -90 degrés).
    • +deltaX, -deltaY: 270 à 360 degrés (-90 à 0 degrés).

Une implémentation en Python utilisant radians (fournie le 19 juillet 2015 par Eric Leschinski, qui a révisé ma réponse):

from math import *
def angle_trunc(a):
    while a < 0.0:
        a += pi * 2
    return a

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark):
    deltaY = y_landmark - y_orig
    deltaX = x_landmark - x_orig
    return angle_trunc(atan2(deltaY, deltaX))

angle = getAngleBetweenPoints(5, 2, 1,4)
assert angle >= 0, "angle must be >= 0"
angle = getAngleBetweenPoints(1, 1, 2, 1)
assert angle == 0, "expecting angle to be 0"
angle = getAngleBetweenPoints(2, 1, 1, 1)
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 3)
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 0)
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(1, 1, 2, 2)
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -2, -2)
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -1, 2)
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)

Tous les tests réussissent. Voir https://en.wikipedia.org/wiki/Unit_circle

379
Peter O.

Désolé, mais je suis à peu près sûr que la réponse de Peter est fausse. Notez que l'axe y descend en bas de la page (commun dans les graphiques). En tant que tel, le calcul de deltaY doit être inversé, sinon vous obtenez une mauvaise réponse.

Considérer:

System.out.println (Math.toDegrees(Math.atan2(1,1)));
System.out.println (Math.toDegrees(Math.atan2(-1,1)));
System.out.println (Math.toDegrees(Math.atan2(1,-1)));
System.out.println (Math.toDegrees(Math.atan2(-1,-1)));

donne

45.0
-45.0
135.0
-135.0

Donc, si dans l'exemple ci-dessus, P1 est (1,1) et P2 (2,2) [parce que Y augmente la page], le code ci-dessus donnera 45,0 degrés pour l'exemple présenté, ce qui est faux. Changer l'ordre du calcul deltaY et cela fonctionne correctement.

49
user1641082

J'ai trouvé une solution en Python qui fonctionne bien!

from math import atan2,degrees

def GetAngleOfLineBetweenTwoPoints(p1, p2):
    return degrees(atan2(p2 - p1, 1))

print GetAngleOfLineBetweenTwoPoints(1,3)
1
dctremblay

En considérant la question exacte, en nous plaçant dans un système de coordonnées "spécial" où un axe positif signifie un mouvement VERS LE BAS (comme un écran ou une vue d'interface), vous devez adapter cette fonction de la manière suivante et inverser les coordonnées Y:

Exemple dans Swift 2.0

func angle_between_two_points(pa:CGPoint,pb:CGPoint)->Double{
    let deltaY:Double = (Double(-pb.y) - Double(-pa.y))
    let deltaX:Double = (Double(pb.x) - Double(pa.x))
    var a = atan2(deltaY,deltaX)
    while a < 0.0 {
        a = a + M_PI*2
    }
    return a
}

Cette fonction donne une réponse correcte à la question. La réponse est exprimée en radians. L’utilisation, pour afficher les angles en degrés, est la suivante: 

let p1 = CGPoint(x: 1.5, y: 2) //estimated coords of p1 in question
let p2 = CGPoint(x: 2, y : 3) //estimated coords of p2 in question

print(angle_between_two_points(p1, pb: p2) / (M_PI/180))
//returns 296.56
1
philippe
deltaY = Math.Abs(P2.y - P1.y);
deltaX = Math.Abs(P2.x - P1.x);

angleInDegrees = Math.atan2(deltaY, deltaX) * 180 / PI

if(p2.y > p1.y) // Second point is lower than first, angle goes down (180-360)
{
  if(p2.x < p1.x)//Second point is to the left of first (180-270)
    angleInDegrees += 180;
  else (270-360)
    angleInDegrees += 270;
}
else if (p2.x < p1.x) //Second point is top left of first (90-180)
  angleInDegrees += 90;
0
mamashare

Une formule pour un angle de 0 à 2pi.

Il y a x = x2-x1 et y = y2-y1.La formule fonctionne pour

toute valeur de x et y. Pour x = y = 0, le résultat est indéfini.

f(x,y)=pi()-pi()/2*(1+sign(x))*(1-sign(y^2))

     -pi()/4*(2+sign(x))*sign(y)

     -sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
0
theodore panagos

Basé sur la référence "Peter O" .. Voici la version Java

private static final float angleBetweenPoints(PointF a, PointF b) {
float deltaY = b.y - a.y;
float deltaX = b.x - a.x;
return (float) (Math.atan2(deltaY, deltaX)); }
0
Venkateswara Rao

fonction matlab:

function [lineAngle] = getLineAngle(x1, y1, x2, y2) 
    deltaY = y2 - y1;
    deltaX = x2 - x1;

    lineAngle = rad2deg(atan2(deltaY, deltaX));

    if deltaY < 0
        lineAngle = lineAngle + 360;
    end
end
0
Benas