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.

``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``