找到相对于任意原点的两点之间的顺时针角度
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可以在第一象限的任何地方。角度必须为顺时针且在 0-360(或 0 到 360-1)范围内。
我正在 C/C++ 中执行此操作。
编辑:为每个请求添加代码。这有点不同,因为我陷入了 概念 并且需要对此进行澄清。如果点 x,y 位于 50,50 和 P 之间,则此函数应该 return。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;
}
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;
由于 chux 已经回答了你的问题并且你接受了他的回答,这里是从数学和编程的角度来看待它的另一种方法。这可以应用于任何三个不同的相对点。
数学 - 这将在二维中完成 space,但可以应用于任何维度 space.
- 点将以小写形式捐赠,而向量将以大写形式提供。
- 点积将表示为
*
- 矢量的长度与其大小相同。
- 点将与其轴分量 (x,y) 一起显示
- 向量将与其向量分量 (i,j) 一起显示
- Theta 是两个向量之间的角度,Phi 是外角
> **抽象** - 向量的规则和方程式
> 我们有三个点 `a(x,y)`、`b(x,y)` 和 `c(x,y)`
> 从这些我们可以构造两个向量 `A` 和 `B`
> `A = ca` 和 `B = cb`
> `A = `
> `B = `
> 现在我们已经定义了两个向量 `A` 和 `B` 让我们找到 `Theta`。
> 为了找到它们之间的 `cos(angle)` 我们需要知道两者
> `A` 和 `B` 的大小以及它们之间的 `Dot Product`。
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.
现在唯一剩下的就是将这些等式应用到您选择的编程语言中。
注意 - 这只会找到两个向量之间的角度 Theta,从 360 中减去 Theta 得到 Phi 将得到这两个向量周围的外角。这不包含或暗示角度本身的任何旋转方向。这个不区分顺时针还是逆时针。用户必须自己计算。这只是在 3 个点或 2 个向量上使用基本属性和操作来找到它们之间的角度,这只是整个问题的一个步骤。如果您参考上面的证明,其中内角为 45 度,外角为 315 度;如果将点 b 更改为 (7,5) 而不是点 b 在矢量 A 上反射,则输出将是完全相同的值:两个矢量之间的角度为 45 度,外角为 315 度。这不知道你在哪个方向旋转;除非你考虑使用和携带余弦函数的符号,如果它是 - 或 + 那么它可能会有所不同,但请记住三角余弦在不同的象限中也是 + 和 -。
编程 - 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
主要版本
#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
此解决方案使用向量来解决您的问题。它依赖于这样一个事实,给定两个向量 u 和 v,它们之间的最小角度的余弦是 (uv / |u||v|), 其中 uv 是点积u 和 v。求从u到v的最小角度的方向是正还是负,即逆时针还是顺时针,三乘积用来。三个向量 u、v 和 w 的三重积是 (wu Xv),可以解释为由三个向量定义的平行六面体的有符号体积。由于叉积(以及三重积)仅针对 R^3 中的向量定义,因此此处使用的向量是 3 向量,感兴趣的点位于XY 平面。形成平行六面体的第三个向量位于正 Z 方向,因此三重积的正结果表示 u 和 v[= 之间的最小角度46=]有逆时针意义。
函数smallest_angle()
returns 两个向量之间的最小角度,以弧度为单位。 clockwise_angle()
函数 returns 从第一个向量 u 到第二个向量 v 的顺时针弧度角。函数 angle_about_c()
returns 从线段 CA 到 CB 的顺时针弧度角。请注意,点 A、B 和 C 被视为向量。
#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
我在第一象限中有两个点 A(X,Y) 和 B(P,Q)。还有一个点C(L,M)。如何在顺时针方向找到 CA 和 CB 之间的角度?
我搜索了很多,所有的解决方案都使用了 atan2() 但它找到了原点相对于 x 轴的角度。
C和A可以假设是固定的。 B可以在第一象限的任何地方。角度必须为顺时针且在 0-360(或 0 到 360-1)范围内。
我正在 C/C++ 中执行此操作。
编辑:为每个请求添加代码。这有点不同,因为我陷入了 概念 并且需要对此进行澄清。如果点 x,y 位于 50,50 和 P 之间,则此函数应该 return。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;
}
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;
由于 chux 已经回答了你的问题并且你接受了他的回答,这里是从数学和编程的角度来看待它的另一种方法。这可以应用于任何三个不同的相对点。
数学 - 这将在二维中完成 space,但可以应用于任何维度 space.
- 点将以小写形式捐赠,而向量将以大写形式提供。
- 点积将表示为
*
- 矢量的长度与其大小相同。
- 点将与其轴分量 (x,y) 一起显示
- 向量将与其向量分量 (i,j) 一起显示
- Theta 是两个向量之间的角度,Phi 是外角
> **抽象** - 向量的规则和方程式 > 我们有三个点 `a(x,y)`、`b(x,y)` 和 `c(x,y)` > 从这些我们可以构造两个向量 `A` 和 `B` > `A = ca` 和 `B = cb` > `A = ` > `B = ` > 现在我们已经定义了两个向量 `A` 和 `B` 让我们找到 `Theta`。 > 为了找到它们之间的 `cos(angle)` 我们需要知道两者 > `A` 和 `B` 的大小以及它们之间的 `Dot Product`。
Magnitude
orLength
of a vector issqrt( (i^2) + (j^2) )
Dot Product
isA*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 theclockwise angle
that you were asking for in the first diagram on the left
whereTheta
is the angle between any two vectors.
现在唯一剩下的就是将这些等式应用到您选择的编程语言中。
注意 - 这只会找到两个向量之间的角度 Theta,从 360 中减去 Theta 得到 Phi 将得到这两个向量周围的外角。这不包含或暗示角度本身的任何旋转方向。这个不区分顺时针还是逆时针。用户必须自己计算。这只是在 3 个点或 2 个向量上使用基本属性和操作来找到它们之间的角度,这只是整个问题的一个步骤。如果您参考上面的证明,其中内角为 45 度,外角为 315 度;如果将点 b 更改为 (7,5) 而不是点 b 在矢量 A 上反射,则输出将是完全相同的值:两个矢量之间的角度为 45 度,外角为 315 度。这不知道你在哪个方向旋转;除非你考虑使用和携带余弦函数的符号,如果它是 - 或 + 那么它可能会有所不同,但请记住三角余弦在不同的象限中也是 + 和 -。
编程 - 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
主要版本
#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
此解决方案使用向量来解决您的问题。它依赖于这样一个事实,给定两个向量 u 和 v,它们之间的最小角度的余弦是 (uv / |u||v|), 其中 uv 是点积u 和 v。求从u到v的最小角度的方向是正还是负,即逆时针还是顺时针,三乘积用来。三个向量 u、v 和 w 的三重积是 (wu Xv),可以解释为由三个向量定义的平行六面体的有符号体积。由于叉积(以及三重积)仅针对 R^3 中的向量定义,因此此处使用的向量是 3 向量,感兴趣的点位于XY 平面。形成平行六面体的第三个向量位于正 Z 方向,因此三重积的正结果表示 u 和 v[= 之间的最小角度46=]有逆时针意义。
函数smallest_angle()
returns 两个向量之间的最小角度,以弧度为单位。 clockwise_angle()
函数 returns 从第一个向量 u 到第二个向量 v 的顺时针弧度角。函数 angle_about_c()
returns 从线段 CA 到 CB 的顺时针弧度角。请注意,点 A、B 和 C 被视为向量。
#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