如何找到围绕一个点的两个角度之间的最小差异?

问题描述 投票:0回答:12

给定一个 2D 圆,其围绕坐标的 2 个角度在

-PI
->
PI
范围内,它们之间的最小角度的值是多少?

考虑到 PI 和 -PI 之间的差异不是 2 PI 而是零。

示例:

想象一个圆,有2条线从中心伸出,这些线之间有2个角度,它们在内部形成的角度又称为较小的角度,它们在外部形成的角度又称为较大的角度。

两个角相加就是一个完整的圆。考虑到每个角度都可以在一定范围内,考虑到翻转,较小的角度值是多少

language-agnostic geometry angle
12个回答
275
投票

这给出了任何角度的带符号角度:

a = targetA - sourceA
a = (a + 180) % 360 - 180

请注意,在许多语言中,

modulo
运算会返回一个与被除数具有相同符号的值(例如 C、C++、C#、JavaScript,此处有完整列表)。这需要一个自定义
mod
函数,如下所示:

mod = (a, n) -> a - floor(a/n) * n

或者这样:

mod = (a, n) -> (a % n + n) % n

如果角度在 [-180, 180] 范围内,这也有效:

a = targetA - sourceA
a += (a>180) ? -360 : (a<-180) ? 360 : 0

更详细地说:

a = targetA - sourceA
a -= 360 if a > 180
a += 360 if a < -180

185
投票

x 是目标角度。 y 是源或起始角度:

atan2(sin(x-y), cos(x-y))

它返回带符号的增量角度。请注意,根据您的 API,atan2() 函数的参数顺序可能会有所不同。


60
投票

如果两个角度是 x 和 y,那么它们之间的角度之一是 abs(x - y)。另一个角度是 (2 * PI) - abs(x - y)。所以 2 个角度中最小的值为:

min((2 * PI) - abs(x - y), abs(x - y))

这将为您提供角度的绝对值,并且假设输入已标准化(即:在

[0, 2π)
范围内)。

如果您想保留角度的符号(即:方向)并接受范围之外的角度

[0, 2π)
,您可以概括上述内容。以下是通用版本的 Python 代码:

PI = math.pi
TAU = 2*PI
def smallestSignedAngleBetween(x, y):
    a = (x - y) % TAU
    b = (y - x) % TAU
    return -a if a < b else b

请注意,

%
运算符在所有语言中的行为并不相同,特别是在涉及负值时,因此如果移植,可能需要进行一些符号调整。


15
投票

适用于任何角度且适用于弧度和度数的高效 C++ 代码是:

inline double getAbsoluteDiff2Angles(const double x, const double y, const double c)
{
    // c can be PI (for radians) or 180.0 (for degrees);
    return c - fabs(fmod(fabs(x - y), 2*c) - c);
}

在这里查看它的工作原理: https://www.desmos.com/calculator/sbgxyfchjr

对于带符号的角度:

return fmod(fabs(x - y) + c, 2*c) - c;

在其他一些负数模为正的编程语言中,可以消除内部的abs。


11
投票

我迎接挑战,提供签名答案:

def f(x,y):
  import math
  return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)

6
投票

对于UnityEngine用户来说,最简单的方法就是使用Mathf.DeltaAngle


5
投票

算术(相对于算法)解决方案:

angle = Pi - abs(abs(a1 - a2) - Pi);

2
投票

我绝对喜欢上面Peter B的答案,但是如果您需要一种产生相同结果的极其简单的方法,那就是:

function absAngle(a) {
  // this yields correct counter-clock-wise numbers, like 350deg for -370
  return (360 + (a % 360)) % 360;
}

function angleDelta(a, b) {
  // https://gamedev.stackexchange.com/a/4472
  let delta = Math.abs(absAngle(a) - absAngle(b));
  let sign = absAngle(a) > absAngle(b) || delta >= 180 ? -1 : 1;
  return (180 - Math.abs(delta - 180)) * sign;
}

// sample output
for (let angle = -370; angle <= 370; angle+=20) {
  let testAngle = 10;
  console.log(testAngle, "->", angle, "=", angleDelta(testAngle, angle));
}

需要注意的一件事是我故意颠倒了符号:逆时针增量为负,顺时针增量为正


0
投票

老屁股线程但是......我在Desmos中遇到了同样的问题。这是我的实现,如果对任何人有用的话:

LaTex 的截图: https://i.stack.imgur.com/6RzDA.png

参数 v1 和 v2 是向量。插入类似点。 如:v1 = (0,0), v2 = (0,1).

我在想可能有某种方法可以通过复数乘法来优化它,但我只是把它留在那里,因为我只是使用 Desmos 来拼凑一个模型。


0
投票

比较两个向量的 Lua 代码,给出:

function angleComparing(x1, y1, x2, y2)
    local a1 = math.atan2(y1, x1)
    local a2 = math.atan2(y2, x2)
    local diff = (a2 - a1 + math.pi)%(2*math.pi) - math.pi
    return diff -- returns values in range [-pi, pi]
end

用途:

for i = 360, -360, -180 do
    for j = -120, 120, 120 do
        local a1 = math.rad (i)
        local a2 = math.rad (j)
        local dy1, dx1 = math.sin (a1), math.cos (a1)
        local dy2, dx2 = math.sin (a2), math.cos (a2)
        local dif = angleComparing(dx1, dy1, dx2, dy2)
        print (i, j, string.format('%.1f', math.deg (dif)))
    end
end

结果(角度1,角度2,差值):

360     -120    120.0
360     0       0.0
360     120     -120.0
180     -120    -60.0
180     0       -180.0
180     120     60.0
0       -120    120.0
0       0       0.0
0       120     -120.0
-180    -120    -60.0
-180    0       -180.0
-180    120     60.0
-360    -120    120.0
-360    0       0.0
-360    120     -120.0

-1
投票

无需计算三角函数。 C语言的简单代码是:

#include <math.h>
#define PIV2 M_PI+M_PI
#define C360 360.0000000000000000000
double difangrad(double x, double y)
{
double arg;

arg = fmod(y-x, PIV2);
if (arg < 0 )  arg  = arg + PIV2;
if (arg > M_PI) arg  = arg - PIV2;

return (-arg);
}
double difangdeg(double x, double y)
{
double arg;
arg = fmod(y-x, C360);
if (arg < 0 )  arg  = arg + C360;
if (arg > 180) arg  = arg - C360;
return (-arg);
}

令 dif = a - b ,以弧度表示

dif = difangrad(a,b);

令 diff = a - b ,以度为单位

dif = difangdeg(a,b);

difangdeg(180.000000 , -180.000000) = 0.000000
difangdeg(-180.000000 , 180.000000) = -0.000000
difangdeg(359.000000 , 1.000000) = -2.000000
difangdeg(1.000000 , 359.000000) = 2.000000

没有 sin,没有 cos,没有 tan,....只有几何!!!!


-1
投票

一个简单的方法,我在C++中使用的是:

double deltaOrientation = angle1 - angle2;
double delta =  remainder(deltaOrientation, 2*M_PI);
© www.soinside.com 2019 - 2024. All rights reserved.