29

I have been not using math for a long time and this should be a simple problem to solve.

Suppose I have two points A: (1, 0) and B: (1, -1).

I want to use a program (Python or whatever programming language) to calculate the clockwise angle between A, origin (0, 0) and B. It will be something like this:

angle_clockwise(point1, point2)

Note that the order of the parameters matters. Since the angle calculation will be clockwise:

  • If I call angle_clockwise(A, B), it returns 45.
  • If I call angle_clockwise(B, A), it returns 315.

In other words, the algorithm is like this:

  1. Draw a line (line 1) between the first point param with (0, 0).
  2. Draw a line (line 2) between the second point param with (0, 0).
  3. Revolve line 1 around (0, 0) clockwise until it overlaps line 2.
  4. The angular distance line 1 traveled will be the returned angle.

Is there any way to code this problem?

4
  • 3
    Read en.wikipedia.org/wiki/Atan2 and note atan2 is in docs.python.org/2/library/math.html
    – Paul
    Commented Jul 30, 2015 at 23:15
  • the question is asking about the code to implement atan2,but not the concept of atan2,why suggest to see more about atan2?
    – ggrr
    Commented Jul 31, 2015 at 1:40
  • 1
    "If I call angle_clockwise(B, A), it returns 335" - surely you mean 315 (360 - 45)?
    – ali_m
    Commented Jul 31, 2015 at 2:19
  • 1
    Whoops... Yes I mean 315. Now you see how long I haven't been using math :D
    – Eric
    Commented Jul 31, 2015 at 15:18

8 Answers 8

40

Numpy's arctan2(y, x) will compute the counterclockwise angle (a value in radians between -π and π) between the origin and the point (x, y).

You could do this for your points A and B, then subtract the second angle from the first to get the signed clockwise angular difference. This difference will be between -2π and 2π, so in order to get a positive angle between 0 and 2π you could then take the modulo against 2π. Finally you can convert radians to degrees using np.rad2deg.

import numpy as np

def angle_between(p1, p2):
    ang1 = np.arctan2(*p1[::-1])
    ang2 = np.arctan2(*p2[::-1])
    return np.rad2deg((ang1 - ang2) % (2 * np.pi))

For example:

A = (1, 0)
B = (1, -1)

print(angle_between(A, B))
# 45.

print(angle_between(B, A))
# 315.

If you don't want to use numpy, you could use math.atan2 in place of np.arctan2, and use math.degrees (or just multiply by 180 / math.pi) in order to convert from radians to degrees. One advantage of the numpy version is that you can also pass two (2, ...) arrays for p1 and p2 in order to compute the angles between multiple pairs of points in a vectorized way.

5
  • arctan2(y,x) computes the counterclockwise angle (in readians) between the x-axis and the vector (x,y). To define an angle, you need three points or two vectors, not just two points.
    – Spirko
    Commented Feb 11, 2018 at 16:35
  • 2
    @Spirko OP is asking for the clockwise angle between the vector from the origin to point A, and the vector from the origin to point B. We have three points and two vectors, so the angle is well-defined.
    – ali_m
    Commented Feb 11, 2018 at 19:16
  • "This will be between -π and π" This is not true - the angle will be be between -2π and 2π
    – Eric
    Commented May 7, 2018 at 0:00
  • 2
    @Eric You're right - that only refers to the output of np.arctan2 and not the difference of two such angles. I've updated the wording to clarify this.
    – ali_m
    Commented May 8, 2018 at 18:59
  • you are amazing. Commented May 14, 2018 at 12:41
17

Use the inner product and the determinant of the two vectors. This is really what you should understand if you want to understand how this works. You'll need to know/read about vector math to understand.

See: https://en.wikipedia.org/wiki/Dot_product and https://en.wikipedia.org/wiki/Determinant

from math import acos
from math import sqrt
from math import pi

def length(v):
    return sqrt(v[0]**2+v[1]**2)
def dot_product(v,w):
   return v[0]*w[0]+v[1]*w[1]
def determinant(v,w):
   return v[0]*w[1]-v[1]*w[0]
def inner_angle(v,w):
   cosx=dot_product(v,w)/(length(v)*length(w))
   rad=acos(cosx) # in radians
   return rad*180/pi # returns degrees
def angle_clockwise(A, B):
    inner=inner_angle(A,B)
    det = determinant(A,B)
    if det<0: #this is a property of the det. If the det < 0 then B is clockwise of A
        return inner
    else: # if the det > 0 then A is immediately clockwise of B
        return 360-inner

In the determinant computation, you're concatenating the two vectors to form a 2 x 2 matrix, for which you're computing the determinant.

2
  • 1
    How would this generalize to n-dims?
    – norok2
    Commented Jul 30, 2018 at 13:20
  • Does this handle division by zero? Commented May 23, 2022 at 12:01
8

Here's a solution that doesn't require cmath.

import math

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

v1 = Vector(0, 1)
v2 = Vector(0, -1)

v1_theta = math.atan2(v1.y, v1.x)
v2_theta = math.atan2(v2.y, v2.x)

r = (v2_theta - v1_theta) * (180.0 / math.pi)

if r < 0:
    r += 360.0

print r
3
  • Better not use such a poor approximation for pi
    – 317070
    Commented Feb 12, 2017 at 22:13
  • 1
    @317070 Fixed value of pi.
    – user2209008
    Commented Feb 15, 2017 at 17:41
  • can use r % 360 at the end. In Python the modulus works as it should be with negatives :) Commented Mar 26, 2018 at 20:55
6

A verified 0° to 360° solution

It is an old thread, but for me the other solutions didn't work well, so I implemented my own version.

My function will return a number between 0 and 360 (excluding 360) for two points on the screen (i.e. 'y' starts at the top and increasing towards the bottom), where results are as in a compass, 0° at the top, increasing clockwise:

def angle_between_points(p1, p2):
    d1 = p2[0] - p1[0]
    d2 = p2[1] - p1[1]
    if d1 == 0:
        if d2 == 0:  # same points?
            deg = 0
        else:
            deg = 0 if p1[1] > p2[1] else 180
    elif d2 == 0:
        deg = 90 if p1[0] < p2[0] else 270
    else:
        deg = math.atan(d2 / d1) / pi * 180
        lowering = p1[1] < p2[1]
        if (lowering and deg < 0) or (not lowering and deg > 0):
            deg += 270
        else:
            deg += 90
    return deg
2

Check out the cmath python library.

>>> import cmath
>>> a_phase = cmath.phase(complex(1,0))
>>> b_phase = cmath.phase(complex(1,-1))
>>> (a_phase - b_phase) * 180 / cmath.pi
45.0
>>> (b_phase - a_phase) * 180 / cmath.pi
-45.0

You can check if a number is less than 0 and add 360 to it if you want all positive angles, too.

1

Chris St Pierre: when using your function with:

A = (x=1, y=0)
B = (x=0, y=1)

This is supposed to be a 90 degree angle from A to B. Your function will return 270.

Is there an error in how you process the sign of the det or am I missing something?

1
  • This does not answer the question. Once you receive enough reputation, you can comment on questions. In the meantime, please attempt to answer questions that do not require clarification from its author. Commented Feb 13, 2018 at 14:34
0

A formula that calculates an angle clockwise, and is used in surveying:

f(E,N)=pi()-pi()/2*(1+sign(N))* (1-sign(E^2))-pi()/4*(2+sign(N))*sign(E)

     -sign(N*E)*atan((abs(N)-abs(E))/(abs(N)+abs(E)))

The formula gives angles from 0 to 2pi,start from the North and

is working for any value of N and E. (N=N2-N1 and E=E2-E1)

For N=E=0 the result is undefined.

0

in radians, clockwise, from 0 to PI * 2

static angle(center:Coord, p1:Coord, p2:Coord) {
    var a1 = Math.atan2(p1.y - center.y, p1.x - center.x);
    var a2 = Math.atan2(p2.y - center.y, p2.x -center.x);
    a1 = a1 > 0 ? a1 : Math.PI * 2 + a1;//make angle from 0 to PI * 2
    a2 = a2 > 0 ? a2 : Math.PI * 2 + a2;
    if(a1 > a2) {
        return a1 - a2;
    } else {
        return Math.PI * 2 - (a2 - a1)
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.