How to calculate the angle between a line and the horizontal axis? How to calculate the angle between a line and the horizontal axis? python python

How to calculate the angle between a line and the horizontal axis?


First find the difference between the start point and the end point (here, this is more of a directed line segment, not a "line", since lines extend infinitely and don't start at a particular point).

deltaY = P2_y - P1_ydeltaX = P2_x - P1_x

Then calculate the angle (which runs from the positive X axis at P1 to the positive Y axis at P1).

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

But arctan may not be ideal, because dividing the differences this way will erase the distinction needed to distinguish which quadrant the angle is in (see below). Use the following instead if your language includes an atan2 function:

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

EDIT (Feb. 22, 2017): In general, however, calling atan2(deltaY,deltaX) just to get the proper angle for cos and sin may be inelegant. In those cases, you can often do the following instead:

  1. Treat (deltaX, deltaY) as a vector.
  2. Normalize that vector to a unit vector. To do so, divide deltaX and deltaY by the vector's length (sqrt(deltaX*deltaX+deltaY*deltaY)), unless the length is 0.
  3. After that, deltaX will now be the cosine of the angle between the vector and the horizontal axis (in the direction from the positive X to the positive Y axis at P1).
  4. And deltaY will now be the sine of that angle.
  5. If the vector's length is 0, it won't have an angle between it and the horizontal axis (so it won't have a meaningful sine and cosine).

EDIT (Feb. 28, 2017): Even without normalizing (deltaX, deltaY):

  • The sign of deltaX will tell you whether the cosine described in step 3 is positive or negative.
  • The sign of deltaY will tell you whether the sine described in step 4 is positive or negative.
  • The signs of deltaX and deltaY will tell you which quadrant the angle is in, in relation to the positive X axis at P1:
    • +deltaX, +deltaY: 0 to 90 degrees.
    • -deltaX, +deltaY: 90 to 180 degrees.
    • -deltaX, -deltaY: 180 to 270 degrees (-180 to -90 degrees).
    • +deltaX, -deltaY: 270 to 360 degrees (-90 to 0 degrees).

An implementation in Python using radians (provided on July 19, 2015 by Eric Leschinski, who edited my answer):

from math import *def angle_trunc(a):    while a < 0.0:        a += pi * 2    return adef 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)

All tests pass. See https://en.wikipedia.org/wiki/Unit_circle


Sorry, but I'm pretty sure Peter's answer is wrong. Note that the y axis goes down the page (common in graphics). As such the deltaY calculation has to be reversed, or you get the wrong answer.

Consider:

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

gives

45.0-45.0135.0-135.0

So if in the example above, P1 is (1,1) and P2 is (2,2) [because Y increases down the page], the code above will give 45.0 degrees for the example shown, which is wrong. Change the order of the deltaY calculation and it works properly.


import mathfrom collections import namedtuplePoint = namedtuple("Point", ["x", "y"])def get_angle(p1: Point, p2: Point) -> float:    """Get the angle of this line with the horizontal axis."""    dx = p2.x - p1.x    dy = p2.y - p1.y    theta = math.atan2(dy, dx)    angle = math.degrees(theta)  # angle is in (-180, 180]    if angle < 0:        angle = 360 + angle    return angle

Testing

For testing I let hypothesis generate test cases.

enter image description here

import hypothesis.strategies as sfrom hypothesis import given@given(s.floats(min_value=0.0, max_value=360.0))def test_angle(angle: float):    epsilon = 0.0001    x = math.cos(math.radians(angle))    y = math.sin(math.radians(angle))    p1 = Point(0, 0)    p2 = Point(x, y)    assert abs(get_angle(p1, p2) - angle) < epsilon


matomo