确定角度是否位于其他2个角度之间

时间:2012-07-10 03:29:17

标签: c++ math trigonometry

我试图弄清楚角度是否位于其他两个角度之间。我一直在尝试创建一个简单的函数来执行此操作,但我的技术都不适用于所有可能的角度值。

你能否帮助我编辑我的功能,以正确判断角度是否位于其他两个角度之间?

enter image description here

在上图中;我使用绿点作为中心点,然后确定每条线与绿点的角度。然后我计算黑点到绿点的角度。我试图检查黑点的角度是否 BETWEEN 两条线的角度。

注意:在我的情况下;据说角度(targetAngle)位于2个其他角度之间,如果两个角度之差<1。 180度和targetAngle位于由这两个角度构成的空腔中。

下面的代码应该可以工作,但是这些代码失败了(它们位于角度之间):
- is_angle_between(150,190,110)
- is_angle_between(3,41,345)

bool is_angle_between(int target, int angle1, int angle2) 
{  
  int rAngle1 = ((iTarget - iAngle1) % 360 + 360) % 360;  
  int rAngle2 = ((iAngle2 - iAngle1) % 360 + 360) % 360;  
  return (0 <= rAngle1 && rAngle1 <= rAngle2);  
}  

// Example usage  
is_angle_between(3, 41, 345);  

我尝试的另一种技术也不起作用:

int is_angle_between(int target, int angle1, int angle2)
{
  int dif1  = angle1-angle2;
  int dif2  = angle2-angle1;
  int uDif1 = convert_to_positive_angle( dif1 ); // for eg; convert -15 to 345
  int uDif2 = convert_to_positive_angle( dif2 );

  if (uDif1 <= uDif2) {
    if (dif1 < 0) {
      return (target <= angle1 && target >= angle2);
    }
    else return (in_between_numbers(iTarget, iAngle1, iAngle2));
  }
  else {
    if (dif2 < 0) {
      return (target <= angle1 && target >= angle2);
    }
    else return (in_between_numbers(iTarget, iAngle1, iAngle2));
  }

  return -1;
}

11 个答案:

答案 0 :(得分:10)

bool is_angle_between(int target, int angle1, int angle2) 
{
  // make the angle from angle1 to angle2 to be <= 180 degrees
  int rAngle = ((angle2 - angle1) % 360 + 360) % 360;
  if (rAngle >= 180)
    std::swap(angle1, angle2);

  // check if it passes through zero
  if (angle1 <= angle2)
    return target >= angle1 && target <= angle2;
  else
    return target >= angle1 || target <= angle2;
}  

答案 1 :(得分:6)

受关于Intervals in modular arithmetic的帖子的启发:

static bool is_angle_between(int x, int a, int b) {
    b = modN(b - a);
    x = modN(x - a);

    if (b < 180) {
        return x < b;
    } else {
        return b < x;
    }
}

其中(在检查角度的情况下)modN()将实现为

// modN(x) is assumed to calculate Euclidean (=non-negative) x % N.
static int modN(int x) {
    const int N = 360;
    int m = x % N;
    if (m < 0) {
        m += N;
    } 
    return m;
}

答案 2 :(得分:3)

void normalize( float& angle ) 
{
    while ( angle < -180 ) angle += 360;
    while ( angle >  180 ) angle -= 360;
}

bool isWithinRange( float testAngle, float a, float b )
{
    a -= testAngle;
    b -= testAngle;
    normalize( a );
    normalize( b );
    if ( a * b >= 0 )
        return false;
    return fabs( a - b ) < 180;
}

答案 3 :(得分:2)

如果angle2始终为0,而​​angle1始终在0到180之间,那么这很容易:

return angle1 < 180 && 0 < target && target < angle1;

如果我正确地阅读了这些要求。

但到达那里并不难。

int reduced1 = (angle1 - angle2 + 360) % 360; // and imagine reduced2 = 0
if (180 < reduced1) { angle2 = angle1; reduced1 = 360 - reduced1; } // swap if backwards
int reducedTarget = (target - angle2 + 360) % 360;
return reduced1 < 180 && 0 < reducedTarget && reducedTarget < reduced1;

答案 4 :(得分:1)

我之前通过比较角度来做到这一点。

enter image description here

在上面的草图中,当且仅当

时,AD才会介于AB和AC之间
angle BAD + angle CAD == angle BAC

由于浮点不准确,我首先将它们四舍五入后比较5个小数位。

因此,归结为在两个向量pq之间使用角度算法,其简单地说就是:

double a = p.DotProduct(q);
double b = p.Length() * q.Length();
return acos(a / b); // radians

我将把矢量DotProduct和Length计算作为谷歌搜索练习。而你只需从另一个终端中减去一个终端的坐标即可得到向量。

当然,您应首先检查AB和AC是并联还是反并联。

答案 5 :(得分:1)

角度A和B之间的角度T,总有两个答案:真和假。

我们需要指明我们的意思,在这种情况下,我们正在寻找标准化的小扫描角度以及我们的角度是否在这些值之间。给定任意两个角度,它们之间存在反射角,是反射角内T的归一化值?

如果我们旋转A和B和T使得T = 0并将A和B标准化为+-半周期(180°或2PI)。那么我们的答案是A和B是否有不同的符号,并且是在彼此的半圆周内。

如果我们从测试中减去角度,则加180°(因此A相对于T + 180)。然后我们通过360修改为我们提供[-360°,360°]之间的范围,我们再添加360°和mod(注意,你也可以检查它是否为负数,如果是则增加360),给我们一个值,即一定是[0°,360°]。我们减去180°给出一个介于[-180°,180°]之间的值,相对于T + 180°-180°又名T.T所以T现在是角度零,所有角度都在标准化范围内。现在我们检查以确保角度有一个符号变化,并且它们相差不超过180°,我们有答案。

因为问题在C ++中提出:

bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
    int a_adjust = ((((a - test + 180)) % 360) + 360) % 360 - 180;
    int b_adjust = ((((b - test + 180)) % 360) + 360) % 360 - 180;
    return ((a_adjust ^ b_adjust) < 0) && ((a_adjust - b_adjust) < 180) && ((a_adjust - b_adjust) > -180);
}

我们也可以做一些技巧来简化代码并避免任何不需要的模运算(参见下面的评论)。标准化将角度α移动到相对于角度t的范围[-180°,180°]。

int normalized(int a, int test) {
    int n = a - test + 180;
    if ((n > 360) || (n < -360)) n %= 360;
    return (n > 0)? n - 180: n + 180;
}

bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
    int a_adjust = normalized(a,test);
    int b_adjust = normalized(b,test);
    return ((a_adjust ^ b_adjust) < 0) &&
            ((a_adjust > b_adjust)? a_adjust-b_adjust: b_adjust-a_adjust) < 180;
}

此外,如果我们可以确定范围是[0,360],我们可以使用更简单的if语句

bool isAngleBetweenNormalizedSmallSweepRange(int test, int a, int b) {
    int dA = a - test + 180;
    if (dA > 360) {
        dA -= 360;
    }
    int a_adjust = (dA > 0) ? dA - 180 : dA + 180;
    int dB = b - test + 180;
    if (dB > 360) {
        dB -= 360;
    }
    int b_adjust = (dB > 0) ? dB - 180 : dB + 180;
    return ((a_adjust ^ b_adjust) < 0)
            && ((a_adjust > b_adjust) ? a_adjust - b_adjust : b_adjust - a_adjust) < 180;
}

JS Fiddle test of the code

答案 6 :(得分:0)

我从this主题发现了这句话:

  

如果点P在三角形ABC内,则

     

区域PAB +区域PBC +区域PAC =区域ABC

     

请注意,如果P位于AB,BC或CA的边缘,则上述保持。但   有效地,PAB,PBC,PAC之一是0(所以请确保你   检查一下。)

     

如果P在外面,则上述等式不成立......

     

如何确定区域?你有两种选择:1)苍鹭定理,   涉及sqrt,慢2)更优选的方式是交叉产品   (或有效地,绝对值的一半(向下的总和)   产品减去上涨产品的总和))

     

例如,如果A =(x1,y1)B =(x2,y2),则C =(x3,y3)Area =   ABS(X1 * Y2 + X2 * Y3 + X3 * Y1-X1 * Y3-X3 * Y2-X2 * Y1)/ 2

     

你也可能要小心浮点错误......   而不是检查严格的不等式,检查abs(b-a)

希望这会有所帮助

答案 7 :(得分:0)

使用与您的问题类似的功能,我运用以下方法:

    public static bool IsInsideRange(double testAngle, double startAngle, double endAngle)
    {
        var a1 = System.Math.Abs(AngleBetween(startAngle, testAngle));
        var a2 = System.Math.Abs(AngleBetween(testAngle, endAngle));
        var a3 = System.Math.Abs(AngleBetween(startAngle, endAngle));
        return a1 + a2 == a3;
    }

    public static double AngleBetween(double start, double end)
    {
        return (end - start) % 360;
    }

答案 8 :(得分:0)

我知道这篇文章很老,但似乎没有一个公认的答案,我发现以下方法非常可靠。虽然它可能超过你的需要。它支持大于180度的角度范围(以及大于360度和负角度)。它还支持小数精度。

该方法使用此normalize()辅助函数将角度转换为正确的空间:

float normalize( float degrees )
{
  //-- Converts the specified angle to an angle between 0 and 360 degrees
  float circleCount = (degrees / 360.0f);
  degrees -= (int)circleCount * 360;
  if( 0.0f > degrees )
  {
    degrees += 360.0f;
  }
  return degrees;
}

以下是解决方案:

bool isWithinRange( float start, float end, float angle )
{
  if( fabsf( end - start ) >= 360.0f )
  {
    //-- Ranges greater or equal to 360 degrees cover everything
    return true;
  }

  //-- Put our angle between 0 and 360 degrees
  float degrees = normalize( angle );

  //-- Resolve degree value for the start angle; make sure it's
  //   smaller than our angle.
  float startDegrees = normalize( start );
  if( startDegrees > degrees )
  {
    startDegrees -= 360.0f;
  }

  //-- Resolve degree value for the end angle to be within the
  //   same 360 degree range as the start angle and make sure it
  //   comes after the start angle.
  float endDegrees = normalize( end );
  if( endDegrees < startDegrees )
  {
    endDegrees += 360.0f;
  }
  else if( (endDegrees - startDegrees) >= 360.0f )
  {
    endDegrees -= 360.0f;
  }

  //-- All that remains is to validate that our angle is between
  //   the start and the end.
  if( (degrees < startDegrees) || (degrees > endDegrees) )
  {
    return false;
  }

  return true;
}

希望这有助于某人。

答案 9 :(得分:0)

如果您有角度$$ a $和$ b $,并且不想看到角度x是否在这些角度之间。

您可以计算a->xa->b之间的角度。 如果∠a->x小于∠a->b,则x必须介于ab之间。

角度之间的距离ab

function distanceBetweenAngles(a, b) {
    distance = b - a;
    if (a > b) {
       distance += 2*pi;
    }
    return distance;
}

然后你可以做

// Checks if angle 'x' is between angle 'a' and 'b'
function isAngleBetween(x, a, b) {
    return distanceBetweenAngles(a, b) >= distanceBetweenAngles(a, x);
}

这假设你正在使用Radians而不是Degrees。它删除了许多不必要的代码。

答案 10 :(得分:0)

如果你们有时间,请检查一下:

bool AngleIsBetween(int firstAngle, int secondAngle, int targetAngle)
{        
    while (firstAngle >= 360)
    firstAngle -= 360;

    while (secondAngle >= 360)
    secondAngle -= 360;

    while (targetAngle >= 360)
    targetAngle -=360;

    while (firstAngle < 0)
    firstAngle += 360;

    while (secondAngle < 0)
    secondAngle += 360;

    while (targetAngle < 0)
    targetAngle +=360;              

    int temp = secondAngle;

    if (firstAngle > secondAngle)
    {
    secondAngle = firstAngle;
    firstAngle = temp;       
    }

    if ((secondAngle - firstAngle) > 180)
    {
    temp = secondAngle - 360;
    secondAngle = firstAngle;
    firstAngle = temp;
    }

    return ((targetAngle >= firstAngle) && (targetAngle <= secondAngle)); 
}

如果需要,请将参数更改为浮动。