簡體   English   中英

查找相對於任意原點的兩點之間的順時針角度

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

我在第一象限中有兩個點A(X,Y)和B(P,Q)。 還有一點C(L,M)。 如何找到CA和CB之間沿順時針方向的角度?

我進行了很多搜索,所有解決方案都使用了atan2(),但是它找到了相對於x軸的原點夾角。

可以假定C和A是固定的。 B可以在第一象限的任何位置

可以假定C和A是固定的。 B可以在第一象限的任何位置。 角度必須為順時針且在0-360(或0到360-1)范圍內。

我在C / C ++中執行此操作。

編輯:根據請求添加代碼。 這有點不同,因為我陷入了一個概念 ,需要對其進行澄清。 如果點x,y位於50,50和P之間,則該函數應該返回。P是相對於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;

 }

如何找到CA和CB之間沿順時針方向的角度?

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

chux已經回答了您的問題,並且您接受了他的回答,因此這也是從數學和編程角度看待它的另一種方法。 這可以應用於任何三個不同的相對點。


數學 -這將在2D空間中完成,但可以應用於任何尺寸空間。

  • 點將以小寫字母捐贈,矢量將是大寫字母。
  • 點積將以*表示
  • 向量的長度與其大小相同。
  • 點將顯示其軸分量(x,y)
  • 向量將顯示為其向量分量(i,j)
  • Theta將是兩個向量之間的夾角,而Phi將是外角

抽象 -向量的規則和方程式
我們有三個點a(x,y)b(x,y)c(x,y)
從這些我們可以構造兩個向量A<i,j>B<i,j>

A = caB = cb
A = <ax - cx, ay - cy>
B = <bx - cx, by - cy>

現在我們定義了兩個向量AB讓我們找到Theta 為了找到它們之間的cos(angle) ,我們需要知道AB的大小以及它們之間的Dot Product

向量的MagnitudeLengthsqrt( (i^2) + (j^2) )
Dot ProductA*B = (Ai x Bi) + (Aj x Bj)
Cos(Angle) = (A*B) / (magA * magB)
Theta = acos( Cos(Angle) )
Phi = 360 - Theta


證明 -一個點為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是您在左側第一張圖中要求的clockwise angle
其中Theta是任意兩個向量之間的角度。


現在剩下的唯一事情就是將這些等式應用於您選擇的編程語言。

注意 -這只會找到兩個向量之間的角度Theta,然后從360中減去Theta來找到Phi,這將為您提供這兩個向量周圍的外角。 這並不包含或暗示角度本身的任何旋轉方向。 這不能區分順時針還是逆時針。 用戶將不得不自己計算。 這只是使用3個點或2個向量的基本屬性和運算來找到它們之間的角度,這只是整個問題的一個步驟。 如果參考上面的證明,其中內角為45度,外角為315; 如果將點b更改為(7,5),而不是將點b反射到向量A上,則輸出將是完全相同的值:兩個向量之間的夾角為45度,外角為315。 這不知道您要朝哪個方向旋轉; 除非您考慮使用並攜帶余弦函數的符號-或+,否則可能會有所不同,但是從Trig余弦中記住,在不同象限中也為+和-。


編程-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;
}

產量

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

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

產量

Theta = 45
Phi = 315

這樣可以從左側拳頭圖中的3個點找到順時針角度。 至於右圖,只需找到角度即可,而不是從360中減去它。使用的類如下。


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

此解決方案使用向量來解決您的問題。 它依賴於以下事實:在給定兩個向量uv的情況下 ,它們之間最小角度的余弦為( uv / | u || v |),其中uvuv的點積。 為了找出從uv的最小角度的感覺是正還是負,即逆時針還是順時針,使用了三乘積。 三個向量uvw的三乘積為( wu X v ),可以解釋為由三個向量定義的平行六面體的有符號體積。 由於叉積(以及三乘積)僅針對R ^ 3中的矢量定義,因此此處使用的矢量是3個矢量,感興趣點位於XY平面中。 形成平行六面體的第三個矢量位於Z軸正方向,因此三乘積的正結果表明uv之間的最小角度具有逆時針方向。

函數smallest_angle()返回弧度的兩個向量之間的最小角度。 clockwise_angle()函數將弧度從第一向量u返回到第二向量v,以弧度為單位。 函數angle_about_c()從線段CACB返回以弧度為單位的順時針角度。 注意,將點ABC視為向量。

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

程序輸出:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM