Calculate angle (clockwise) between two points

Question:

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?

Asked By: Eric

||

Answers:

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.

Answered By: awmo

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.

Answered By: ali_m

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.

Answered By: Chris St Pierre

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
Answered By: Colin Basnett

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?

Answered By: Antoine

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.

Answered By: theodore panagos

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
Answered By: ishahak

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)
    }
}
Answered By: degr
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.