19

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

你能帮我编辑我的函数以正确确定一个角度是否位于其他两个角度之间吗?

在此处输入图像描述

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

注意:就我而言;如果 2 个角度之间的差异 < 180 度并且 targetAngle 位于由这 2 个角度形成的空腔中,则称一个角度(targetAngle)位于其他 2 个角度之间。

以下代码应该可以工作,但对于这些(确实位于角度之间)失败:
- 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;
}
4

11 回答 11

11
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;
}  
于 2012-07-10T11:13:35.177 回答
9

灵感来自关于模算术中的区间的帖子:

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;
}
于 2015-11-24T11:29:46.800 回答
4
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;
}
于 2014-05-08T18:42:48.193 回答
2

如果角度 2 始终为 0,角度 1 始终介于 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;
于 2012-07-10T04:17:30.263 回答
1

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

在此处输入图像描述

在上面的草图中,当且仅当矢量 AD 将在 AB 和 AC 之间

angle BAD + angle CAD == angle BAC

由于浮点数不准确,我先将它们四舍五入到小数点后 5 位,然后比较这些值。

所以归结为在两个向量之间有一个角度算法,p简单q地说如下:

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

我将把向量 DotProduct 和 Length 计算留作 google 搜索练习。您只需通过从另一个终端减去一个终端的坐标即可获得向量。

您当然应该首先检查 AB 和 AC 是平行还是反平行。

于 2012-07-10T16:36:19.420 回答
1

这里所有的热门答案都是错误的。因此,我觉得我有必要发布一个答案。

我只是重新发布我在此处发布的答案的一部分:https ://stackoverflow.com/a/42424631/2642059该答案还涉及您已经知道哪个角度是左侧和右侧的情况反射角。但是您还需要确定角度的哪一侧是哪一侧。


如果这些陈述中的任何一个为真,第一个找到最左边的角度angle1是你的最左边的角度:

  1. angle1 <= angle2 && angle2 - angle1 <= PI
  2. angle1 > angle2 && angle1 - angle2 >= PI

为简单起见,假设你最左边的角度是l,你最右边的角度是r,你试图找出g是否在它们之间。

这里的问题是看起来。我们正在寻找基本上 3 个积极的案例:

  1. l ≤ g ≤ r
  2. l ≤ g ∧ r < l
  3. g ≤ r ∧ r < l

由于您正在计算角度的左侧和右侧,您会注意到这里有一个优化机会同时执行这两个过程。您的函数将如下所示:

if(angle1 <= angle2) {
    if(angle2 - angle1 <= PI) {
        return angle1 <= target && target <= angle2;
    } else {
        return angle2 <= target || target <= angle1;
    }
} else {
    if(angle1 - angle2 <= PI) {
        return angle2 <= target && target <= angle1;
    } else {
        return angle1 <= target || target <= angle2;
    }
}

或者,如果您需要它,您可以扩展到这种噩梦般的情况:

angle1 <= angle2 ?
(angle2 - angle1 <= PI && angle1 <= target && target <= angle2) || (angle2 - angle1 > PI && (angle2 <= target || target <= angle1)) :
(angle1 - angle2 <= PI && angle2 <= target && target <= angle1) || (angle1 - angle2 > PI && (angle1 <= target || target <= angle2))

请注意,所有这些数学都假定您的输入是弧度并且在 [0 : 2π] 范围内。

Live Example

于 2017-02-23T21:41:52.837 回答
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°,得到一个相对于 T+180°-180° 又名 T 的 [-180°,180°] 之间的值。所以 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);
}

我们还可以做一些技巧来简化代码并避免任何不需要的模运算(请参阅下面的注释)。Normalize 会将角度 a 移动到相对于角度 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 测试代码

于 2016-07-19T10:33:14.973 回答
0

我从这个线程中找到了这个引用:

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

面积 PAB+面积 PBC +面积 PAC=面积 ABC

请注意,如果 P 在 AB、BC 或 CA 的边缘,则上述成立。但实际上,区域 PAB、PBC、PAC 之一是 0(所以只要确保你检查)。

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

如何确定面积?您有两个选择:1)Heron 定理,涉及 sqrt,较慢 2)更优选的方式是叉积(或有效地,绝对值的一半(向下积的总和减去向上积的总和))

例如,如果 A=(x1,y1) B=(x2,y2), C=(x3,y3) 面积= abs(x1*y2+x2*y3+x3*y1-x1*y3-x3*y2- x2*y1)/2

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

希望这会有所帮助

于 2012-07-10T03:48:06.360 回答
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;
    }
于 2014-02-16T22:35:33.730 回答
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;
}

希望这可以帮助某人。

于 2014-03-08T07:53:18.573 回答
0

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

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

到角之间的距离,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);
}

这假设您使用的是弧度,而不是度数。它删除了很多不必要的代码。

于 2016-02-09T18:49:18.990 回答