Find clockwise angle between two points with respect to an arbitrary origin

6.3k views Asked by At

I have two points A(X,Y) and B(P,Q) in the first quadrant. There's another point C(L,M). How do I find angle between CA and CB in clockwise direction?

I searched a lot and all the solution used atan2() but it finds the angle from origin with respect to x axis.

C and A can be assumed fixed. And B is can be anywhere in the first quadrant

C and A can be assumed fixed. And B is can be anywhere in the first quadrant. The angle must be clockwise and within range 0-360 (Or 0 to 360-1).

I am doing this in C/C++.

Edit : Adding code per request. This is a bit different, because I got stuck at a concept and needed clarification regarding it. This function ought to return if the point x,y lies between 50,50 and P. P is the angle with respect to CA.

bool isInsideAngle(long double x,long double y, long double p)
{   
    if((atan2(y,x) >= atan2(50,100)) && (atan2(y,x) <= (p * PI / 50)))
    {
        // cout<<"YES!";
        //   cout<<" atan2(y,x) = " <<atan2(y,x)*180/PI<<endl;
        //   cout<<" atan2(50,50) = " <<atan2(50,100)*180/PI<<endl;
        //   cout<<" (p * PI / 50) = "<<(p * PI / 50)*180/PI<<endl;
        return true;
    }

    else
        return false;

 }
3

There are 3 answers

12
chux - Reinstate Monica On BEST ANSWER

How do I find angle between CA and CB in clockwise direction?

// The atan2 functions return arctan y/x in the interval [−π , +π] radians
double Dir_C_to_A = atan2(Ay - Cy, Ax - Cx);
double Dir_C_to_B = atan2(By - Cy, Bx - Cx);
double Angle_ACB = Dir_C_to_A - Dir_C_to_B;

// Handle wrap around
const double Pi = acos(-1);  // or use some π constant
if (Angle_ACB > Pi) Angle_ACB -= 2*Pi;
else if (Angle_ACB < -Pi) Angle_ACB += 2*Pi;

// Answer is in the range of [-pi...pi]
return Angle_ACB;
4
ad absurdum On

This solution uses vectors to solve your problem. It relies on the fact that, given two vectors u and v, the cosine of the smallest angle between them is (uv / |u||v|), where uv is the dot product of u and v. To find whether the sense of the smallest angle from u to v is positive or negative, i.e., counter-clockwise or clockwise, the triple-product is used. The triple product of three vectors u, v, and w is (wuXv), and can be interpreted as the signed volume of the parallelepiped defined by the three vectors. Since the cross-product (and hence the triple-product) is only defined for vectors in R^3, the vectors used here are 3-vectors, with the points of interest lying in the XY-plane. The third vector forming the parallelepiped lies in the positive Z-direction, so that a positive result for the triple-product indicates that the smallest angle between u and v has a counter-clockwise sense.

The function smallest_angle() returns the smallest angle between two vectors in radians. The clockwise_angle() function returns the clockwise angle in radians from the first vector u to the second vector v. The function angle_about_c() returns the clockwise angle in radians from the line segment CA to CB. Note that the points A, B, and C are taken to be vectors.

#include <stdio.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

struct Vector {
    double i;
    double j;
    double k;
};

double magnitude(struct Vector u);
struct Vector vect_diff(struct Vector u, struct Vector v);
double dot_product(struct Vector u, struct Vector v);
struct Vector cross_product(struct Vector u, struct Vector v);
double triple_product(struct Vector u, struct Vector v, struct Vector w);
double smallest_angle(struct Vector u, struct Vector v);
double clockwise_angle(struct Vector u, struct Vector v);
double angle_about_c(struct Vector a, struct Vector b, struct Vector c);

int main(void)
{
    struct Vector vect_a = { .i = 1, .j = 1 };
    struct Vector vect_b = { .i = 0, .j = 1 };

    printf("Smallest angle:  %f rad\n", smallest_angle(vect_a, vect_b));
    printf("Clockwise angle: %f rad\n", clockwise_angle(vect_a, vect_b));

    struct Vector point_a  = { .i = 3, .j = 3 };
    struct Vector point_b1 = { .i = 4, .j = 2 };
    struct Vector point_b2 = { .i = 2, .j = 2 };
    struct Vector point_c  = { .i = 3, .j = 1 };

    printf("Clockwise angle from CA to CB1: %f rad\n",
           angle_about_c(point_a, point_b1, point_c));
    printf("Clockwise angle from CA to CB2: %f rad\n",
           angle_about_c(point_a, point_b2, point_c));

    return 0;
}

double magnitude(struct Vector u)
{
    return sqrt(dot_product(u, u));
}

struct Vector vect_diff(struct Vector u, struct Vector v)
{
    return (struct Vector) { .i = u.i - v.i,
                             .j = u.j - v.j,
                             .k = u.k - v.k };
}

double dot_product(struct Vector u, struct Vector v)
{
    return (u.i * v.i) + (u.j * v.j) + (u.k * v.k);
}

struct Vector cross_product(struct Vector u, struct Vector v)
{
    return (struct Vector) { .i = (u.j * v.k) - (u.k * v.j),
                             .j = (u.k * v.i) - (u.i * v.k),
                             .k = (u.i * v.j) - (u.j * v.i) };
}

double triple_product(struct Vector u, struct Vector v, struct Vector w)
{
    return dot_product(u, cross_product(v, w));
}

double smallest_angle(struct Vector u, struct Vector v)
{
    return acos(dot_product(u, v) / (magnitude(u) * magnitude(v)));
}

double clockwise_angle(struct Vector u, struct Vector v)
{
    double angle_s = smallest_angle(u, v);

    if (triple_product((struct Vector) { 0, 0, 1 }, u, v) > 0) {
        angle_s = 2 * M_PI - angle_s;
    }

    return angle_s;
}

double angle_about_c(struct Vector a, struct Vector b, struct Vector c)
{
    return clockwise_angle(vect_diff(a, c), vect_diff(b, c));
}

Program output:

Smallest angle:  0.785398 rad
Clockwise angle: 5.497787 rad
Clockwise angle from CA to CB1: 0.785398 rad
Clockwise angle from CA to CB2: 5.497787 rad
5
Francis Cugler On

As chux already answered your question and you accepted his answer, here is an alternative way to look at it both from the math & programming perspectives. This can be applied to any three distinctive relative points.


Math - This will be done in 2D space, but can be applied to any dimensional space.

  • Points will be donated by lower case where vectors will be uppercase.
  • The Dot Product will be denoted by *
  • A length of a vector is the same as its magnitude.
  • Points will be shown with their axis components (x,y)
  • Vectors will be shown with their vector components (i,j)
  • Theta will be the angle between two vectors & Phi will be the outside angle

> **Abstraction** - Rules & Equations of Vectors > We have three points `a(x,y)`, `b(x,y)` & `c(x,y)` > From These we can construct two vectors `A` & `B` > `A = ca` and `B = cb` > `A = ` > `B = ` > Now that we have defined two vectors `A` & `B` lets find `Theta`. > In order to find the `cos(angle)` between them we need to know both > the magnitudes of `A` & `B` and the `Dot Product` between them.

Magnitude or Length of a vector is sqrt( (i^2) + (j^2) )
Dot Product is A*B = (Ai x Bi) + (Aj x Bj)
Cos(Angle) = (A*B) / (magA * magB)
Theta = acos( Cos(Angle) )
Phi = 360 - Theta


Proof - An example with points being a(5,7), b(3,5) & c(5,3)

A = < 5-5, 7-3 > = < 0, 4 >
B = < 3-5, 5-3 > = < -2, 2 >

magA = sqrt( 0^2 + 4^2 ) = 4
magB = sqrt( (-2)^2 + 2^2 ) = 2sqrt(2)

cosAngle = A*B / (magA * magB)
cosAngle = (0*-2 + 4*2) = 8 / ( 4 x 2sqrt(2) ) = 0.7071067812

Theta = acos( cosAngle ) = 45 degrees
Phi = 360 - 45 = 315

Phi is the clockwise angle that you were asking for in the first diagram on the left
where Theta is the angle between any two vectors.


The only thing that is left is to now apply these equations to your programming language of choice.

Note - This will only find the Angle Theta between two vectors and subtracting Theta from 360 to find Phi will give you the exterior angle around those two vectors. This does not incorporate or imply any direction of rotation of the angles themselves. This does not distinguish between clockwise or counterclockwise. The user would have to calculate that for themselves. This is just using basic properties and operations on 3 points or 2 vectors to find the angle between them which is only a single step of the full problem. If you refer to the proof above where the interior angle is 45 degrees and the exterior angle is 315; if you change point b to be (7,5) instead where point b is reflected over vector A then the output will be the exact same values: 45 degrees for the angle between the two vectors and 315 for the exterior angle. This does not know which direction you are rotating in; unless if you considered using and carrying about the sign of the cosine function if it is - or + then it might make a difference, but remembering from Trig cosine is + and - in different quadrants as well.


Programming - C++

main.cpp

#include <iostream>
#include "Vector2.h"

int main() {   
    // We can assume that points and vectors are similar except that points
    // Don't have a direction where vectors do.
    Vector2 pointA( 5, 7 );
    Vector2 pointB( 3, 5 );
    Vector2 pointC( 5, 3 );

    Vector2 vec1 = pointA - pointC;
    Vector2 vec2 = pointB - pointC;

    // This is the actual angle
    float ThetaA = vec1.getAngle( vec2, false, false );     

    // Other Option
    float ThetaB = vec1.getCosAngle( vec2, false );
    ThetaB = acos( ThetaB );
    ThetaB = Math::radian2Degree( ThetaB );
    
    // Same as other option above which this is already being done in getAngle()
    float ThetaC = Math::radian2Degree( acos( vec1.getCosAngle( vec2, false ) ) );

    std::cout << "ThetaA = " << ThetaA << std::endl;
    std::cout << "ThetaB = " << ThetaB << std::endl;
    std::cout << "ThetaC = " << ThetaC << std::endl;

    float Phi = 360.0f - ThetaA;
    std::cout << "Phi = " << Phi << std::endl;
 
    return 0;
}

Output

ThetaA = 45
ThetaB = 45
ThetaC = 45
Phi = 315

Short Version of Main

#include <iostream>
#include "Vector2.h" 

int main() {
    Vector2 pointA( 5, 7 );
    Vector2 pointB( 3, 5 );
    Vector2 pointC( 5, 3 );

    Vector2 vec1 = pointA - pointC;
    Vector2 vec2 = pointB - pointC;

    float angle = vec1.getAngle( vec2, false, false );
    float clockwiseAngle = 360 - angle;
  
    std::cout << "Theta " << angle << std::endl;
    std::cout << "Phi " << clockwiwseAngle << std::endl;
   
    return 0;
}

Output

Theta = 45
Phi = 315

This is how you can find the clockwise angle from the 3 points in the fist diagram on the left. As for the diagram on the right just find the angle without subtracting it from 360. The used classes are below.


Vector2.h

#ifndef VECTOR2_H
#define VECTOR2_H

#include "GeneralMath.h"

class Vector2 { 

public:
    union {
        float m_f2[2];
        struct {
            float m_fX;
            float m_fY;
        };  
    };

    // Constructors
    inline Vector2();
    inline Vector2( float x, float y );
    inline Vector2( float *pfv );

    // Destructor
    ~Vector2(){}

    // Operators
    inline Vector2  operator+( const Vector2 &v2 ) const;
    inline Vector2  operator+() const;
    inline Vector2& operator+=( const Vector2 &v2 );
    inline Vector2  operator-( const Vector2 &v2 ) const;
    inline Vector2  operator-() const;
    inline Vector2& operator-=( const Vector2 &v2 );
    inline Vector2  operator*( const float &fValue ) const;
    inline Vector2& operator*=( const float &fValue );
    inline Vector2  operator/( const float &fValue ) const;
    inline Vector2& operator/=( const float &fValue );  

    // Functions
    inline void     normalize();
    inline void     zero();
    inline bool     isZero() const;
    inline float    dot( const Vector2 v2 ) const;
    inline float    length2() const;
    inline float    length() const;
    inline float    getCosAngle( const Vector2 &v2, const bool bNormalized = false );
    inline float    getAngle( const Vector2 &v2, const bool bNormalized = false, bool bRadians = true );

    // Pre Multiple Vector By A Scalar
    inline friend Vector2 Vector2::operator*( const float &fValue, const Vector2 v2 ) {         
        return Vector2( fValue*v2.m_fX, fValue*v2.m_fY );    
    } // operator*  

    // Pre Divide Vector By A Scalar
    inline friend Vector2 Vector2::operator/( const float &fValue, const Vector2 v2 ) {
        Vector2 vec2;
        if ( Math::isZero( v2.m_fX ) ) {
            vec2.m_fX = 0.0f;
        } else {
            vec2.m_fX = fValue / v2.m_fX;
        }

        if ( Math::isZero( v2.m_fY ) ) {
            vec2.m_fY = 0.0f;
        } else {
            vec2.m_fY = fValue / v2.m_fY;
        }

        return vec2;    
    } // operator/

}; // Vector2

inline Vector2::Vector2() : m_fX( 0.0f ), m_fY( 0.0f ) {
} // Vector2

inline Vector2::Vector2( float x, float y ) : m_fX( x ), m_fY( y ) {
} // Vector2

inline Vector2::Vector2( float *pfv ) {
    m_fX = pfv[0];
    m_fY = pfv[1];
} // Vector2

// Unary + Operator
inline Vector2 Vector2::operator+() const {
    return *this;
} // operator+

// Binary + Take This Vector And Add Another Vector To It
inline Vector2 Vector2::operator+( const Vector2 &v2 ) const {
    return Vector2( m_fX + v2.m_fX, m_fY + v2.m_fY );
} // operator+

// Add Two Vectors Together
inline Vector2 &Vector2::operator+=( const Vector2 &v2 ) {
    m_fX += v2.m_fX;
    m_fY += v2.m_fY;
    return *this;
} // operator+=

// Unary - Operator: Negate Each Value
inline Vector2 Vector2::operator-() const {
    return Vector2( -m_fX, -m_fY );
} // operator-

// Unary - Take This Vector And Subtract Another Vector From It
inline Vector2 Vector2::operator-( const Vector2 &v2 ) const {
    return Vector2( m_fX - v2.m_fX, m_fY - v2.m_fY );
} // operator-

// Subtract Two Vectors From Each Other
inline Vector2 &Vector2::operator-=( const Vector2 &v2 ) {
    m_fX -= v2.m_fX;
    m_fY -= v2.m_fY;
    return *this;
} // operator-=

// Post Multiply Vector By A Scalar
inline Vector2 Vector2::operator*( const float &fValue ) const {
    return Vector2( m_fX * fValue, m_fY * fValue );
} // operator*

// Multiply This Vector By A Scalar
inline Vector2& Vector2::operator*=( const float &fValue ) {    
    m_fX *= fValue;
    m_fY *= fValue;    
    return *this;    
} // operator*

// Post Divide Vector By A Scalar
inline Vector2 Vector2::operator/( const float &fValue ) const {    
    Vector2 vec2;
    if ( Math::isZero( fValue ) ) {
        vec2.m_fX = 0.0f;
        vec2.m_fY = 0.0f;
    } else {
        float fValue_Inv = 1/fValue;
        vec2.m_fX = vec2.m_fX * fValue_Inv;
        vec2.m_fY = vec2.m_fY * fValue_Inv;
    }    
    return vec2;
} // operator/

// Divide This Vector By A Scalar Value
inline Vector2& Vector2::operator/=( const float &fValue ) {    
    if ( Math::isZero( fValue ) ) {
        m_fX = 0.0f;
        m_fY = 0.0f;
    } else {
        float fValue_Inv = 1/fValue;
        m_fX *= fValue_Inv;
        m_fY *= fValue_Inv;
    }    
    return *this;
} // operator/=

// Make The Length Of This Vector Equal To One
inline void Vector2::normalize() {    
    float fMag;
    fMag = sqrt( m_fX * m_fX + m_fY * m_fY );
    if ( fMag <= Math::ZERO ) {
        m_fX = 0.0f;
        m_fY = 0.0f;
        return;
    }

    fMag = 1/fMag;
    m_fX *= fMag;
    m_fY *= fMag;    
} // normalize

// Return True if Vector Is ( 0, 0 )
inline bool Vector2::isZero() const {
    if ( Math::isZero( m_fX ) && Math::isZero( m_fY ) ) {
        return true;
    } else {
        return false;
    }
} // isZero

// Set Vector To ( 0, 0 )
inline void Vector2::zero() {
    m_fX = 0.0f;
    m_fY = 0.0f;
} // zero

// Return The Length Of This Vector
inline float Vector2::length() const {
    return sqrtf( m_fX * m_fX + m_fY * m_fY );
} // length

// Return The Length Of This Vector
inline float Vector2::length2() const {
    return ( m_fX * m_fX + m_fY * m_fY );
} // length2

// Return The Dot Product Between THIS Vector And Another Vector
inline float Vector2::dot( const Vector2 v2 ) const {
    return ( m_fX * v2.m_fX + m_fY * v2.m_fY );
} // dot

// Returns The cos(Angle) Value Between THIS Vector And Vector v2.
// This Is Less Expensive Than Using GetAngle()
inline float Vector2::getCosAngle( const Vector2 &v2, const bool bNormalized ) {    
    // A . B = |A||B|cos(angle)
    // -> cos-1((A.B)/(|A||B|))

    float fMagA = length();
    if ( fMagA <= Math::ZERO ) {
        // This (A) Is An Invalid Vector
        return 0;
    }

    float fValue = 0.0f;

    if ( bNormalized ) {
        // v2 Already Normalized
        fValue = dot(v2) / fMagA;
    } else {
        // v2 Not Normalized
        float fMagB = v2.length();
        if ( fMagB <= Math::ZERO ) {
            // B Is An Invalid Vector
            return 0;
        }
        fValue = dot(v2) / ( fMagA * fMagB );
    }

    // Correct Value Due To Rounding Problems
    Math::constrain( -1.0f, 1.0f, fValue );

    return fValue;

} // getCosAngle

// Returns The Angle Between THIS Vector And Vector v2 In RADIANS
inline float Vector2::getAngle( const Vector2 &v2, const bool bNormalized, bool bRadians ) {    
    // A . B = |A||B|cos(angle)
    // -> cos-1((A.B)/(|A||B|))

    if ( bRadians ) {
        return acos( getCosAngle( v2, bNormalized ) );
    } else {
        // Convert To Degrees
        return Math::radian2Degree( acos( getCosAngle( v2, bNormalized ) ) );
    }    
} // GetAngle

#endif // VECTOR2_H

GeneralMath.h

#ifndef GENERALMATH_H
#define GENERALMATH_H

#include <math.h>

class Math {
public:
    
    static const float PI;
    static const float PI_HALVES;
    static const float PI_THIRDS;
    static const float PI_FOURTHS;
    static const float PI_SIXTHS;
    static const float PI_2;
    static const float PI_INVx180;
    static const float PI_DIV180;
    static const float PI_INV;
    static const float ZERO;

    Math();

    inline static bool  isZero( float fValue );
    inline static float sign( float fValue );

    inline static int   randomRange( int iMin, int iMax );
    inline static float randomRange( float fMin, float fMax );
    
    inline static float degree2Radian( float fDegrees );
    inline static float radian2Degree( float fRadians );
    inline static float correctAngle( float fAngle, bool bDegrees, float fAngleStart = 0.0f );
    inline static float mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX );

    template<class T>
    inline static void constrain( T min, T max, T &value );

    template<class T>
    inline static void swap( T &value1, T &value2 );

}; // Math

// Convert Angle In Degrees To Radians
inline float Math::degree2Radian( float fDegrees ) {
    return fDegrees * PI_DIV180;
} // degree2Radian

// Convert Angle In Radians To Degrees
inline float Math::radian2Degree( float fRadians ) {
    return fRadians * PI_INVx180;
} // radian2Degree

// Returns An Angle Value That Is Alway Between fAngleStart And fAngleStart + 360
// If Radians Are Used, Then Range Is fAngleStart To fAngleStart + 2PI
inline float Math::correctAngle( float fAngle, bool bDegrees, float fAngleStart ) {    
    if ( bDegrees ) {
        // Using Degrees
        if ( fAngle < fAngleStart ) {
            while ( fAngle < fAngleStart ) {
                fAngle += 360.0f;
            }
        } else if ( fAngle >= (fAngleStart + 360.0f) ) {
            while ( fAngle >= (fAngleStart + 360.0f) ) {
                fAngle -= 360.0f;
            }
        }
        return fAngle;
    } else {
        // Using Radians
        if ( fAngle < fAngleStart ) {
            while ( fAngle < fAngleStart ) {
                fAngle += Math::PI_2;
            }
        } else if ( fAngle >= (fAngleStart + Math::PI_2) ) {
            while ( fAngle >= (fAngleStart + Math::PI_2) ) {
                fAngle -= Math::PI_2;
            }
        }
        return fAngle;
    }    
} // correctAngle

// Tests If Input Value Is Close To Zero
inline bool Math::isZero( float fValue ) {
    if ( (fValue > -ZERO) && (fValue < ZERO) ) {
        return true;
    }
    return false;
} // isZero

// Returns 1 If Value Is Positive, -1 If Value Is Negative Or 0 Otherwise
inline float Math::Sign( float fValue ) {    
    if ( fValue > 0 ) {
        return 1.0f;
    } else if ( fValue < 0 ) {
        return -1.0f;
    }
    return 0;
} // sign

// Return A Random Number Between iMin And iMax Where iMin < iMax
inline int Math::randomRange( int iMin, int iMax ) {    
    if ( iMax < iMin ) {
        swap( iMax, iMin );
    }    
    return (iMin + ((iMax - iMin +1) * rand()) / (RAND_MAX+1) );
    
} // randomRange

// Return A Random Number Between fMin And fMax Where fMin < fMax
inline float Math::randomRange( float fMin, float fMax ) {
    if ( fMax < fMin ) {
        swap( fMax, fMin );
    }    
    return (fMin + (rand()/(float)RAND_MAX)*(fMax-fMin));    
} // randomRange

// Returns The fValueY That Corresponds To A Point On The Line Going From Min To Max
inline float Math::mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX ) {    
    if ( fValueX >= fMaxX ) {
        return fMaxY;
    } else if ( fValueX <= fMinX ) {
        return fMinY;
    } else {
        float fM = (fMaxY - fMinY) / (fMaxX - fMinX);
        float fB = fMaxY - fM * fMaxX;

        return (fM*fValueX + fB);
    }    
} // mapValue

// Constrain a Value To Be Between T min & T max
template<class T>
inline void Math::constrain( T min, T max, T &value ) {    
    if ( value < min ) {
        value = min;
        return;
    }

    if ( value > max ) {
        value = max;
    }    
} // constrain

// Swap Two Values
template<class T>
inline void Math::Swap( T &value1, T &value2 ) {    
    T temp;

    temp   = value1;
    value1 = value2;
    value2 = temp;    
} // swap

#endif // GENERALMATH_H

GeneralMath.cpp

#include "GeneralMath.h"

const float Math::PI            = 4.0f  * atan(1.0f); // tan(pi/4) = 1 or acos(-1)
const float Math::PI_HALVES     = 0.50f * Math::PI;
const float Math::PI_THIRDS     = Math::PI * 0.3333333333333f;
const float Math::PI_FOURTHS    = 0.25f * Math::PI;
const float Math::PI_SIXTHS     = Math::PI * 0.6666666666667f;
const float Math::PI_2          = 2.00f * Math::PI;
const float Math::PI_DIV180     = Math::PI / 180.0f;
const float Math::PI_INVx180    = 180.0f / Math::PI;
const float Math::PI_INV        = 1.0f / Math::PI;
const float Math::ZERO          = (float)1e-7;

Math::Math() {
} // Math