我怎样才能找到一个点周围两个角之间的最小差别呢?

给定一个二维圆,在 -PI-> PI的范围内有两个角,它们之间的最小角度值是多少?

考虑到 PI 和-PI 之间的差别不是2 PI 而是0。

举个例子:

想象一个圆,有两条线从中心出来,这两条线之间有两个角度,它们在内部的角度,也就是 较小的角度,和它们在外部的角度,也就是更大的角度。

两个角度加起来是一个完整的圆。假设每个角度都能在一定范围内适合,考虑到翻转,较小的角度值是多少

136181 次浏览

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

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

请注意,%操作符在所有语言中的行为并不相同,特别是当涉及到负值时,因此如果移植一些符号调整可能是必要的。

X 是目标角,y 是源角或起始角:

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

它返回带符号的 delta 角度。注意,根据 API 的不同,atan2()函数的参数顺序可能不同。

我勇敢地接受挑战,提供一个经过签名的答案:

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

这给出了任意角度的符号角度:

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

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

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

不需要计算三角函数,用 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);

让 dif = 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

没有罪恶,没有因为,没有晒黑,... ... 只有几何! ! !

对于 UnityEngine 用户,简单的方法就是使用 Mathf 三角形

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;

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

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

double deltaOrientation = angle1 - angle2;
double delta =  remainder(deltaOrientation, 2*M_PI);

我非常喜欢 彼得 · 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));
}

需要注意的一点是,我故意翻转了这个符号: 逆时针方向的三角形是负的,而顺时针方向的三角形是正的