给定一个2D圆形,其围绕坐标轴有两个角度,范围为-PI
-> PI
,求它们之间的最小角度值是多少?
需要考虑到PI和-PI之间的差值不是2PI而是零。
举个例子:
想象一个圆,中心有两条线,它们之间有2个角度,内部的角度被称为较小的角度,外部的角度被称为较大的角度。
两个角度加起来总共是一个完整的圆。鉴于每个角度都可以适合某个特定范围内,考虑到翻转,较小的角度值是多少。
给定一个2D圆形,其围绕坐标轴有两个角度,范围为-PI
-> PI
,求它们之间的最小角度值是多少?
需要考虑到PI和-PI之间的差值不是2PI而是零。
举个例子:
想象一个圆,中心有两条线,它们之间有2个角度,内部的角度被称为较小的角度,外部的角度被称为较大的角度。
两个角度加起来总共是一个完整的圆。鉴于每个角度都可以适合某个特定范围内,考虑到翻转,较小的角度值是多少。
这个可以给出任意角度的有符号角:
a = targetA - sourceA
a = (a + 180) % 360 - 180
请注意,在许多编程语言中,取模
运算返回与被除数相同符号的值(例如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
a -= 360*sgn(a)*(abs(a) > 180)
。(想一想,如果您有无分支的 sgn
和 abs
实现,那么这个特性实际上可能开始弥补需要两个乘法的缺点。) - mmiratedouble targetA = 2; double sourceA = 359;
中,“a”的值将等于-357.0而不是3.0。 - Stevoisiak%
运算符像余数一样(保留符号),那么您可以简单地添加额外的 360 而不是定义一个模数函数:
a = (a + 540) % 360 - 180
如上所述,这仅适用于相差不超过 360 度的角度,这种情况可能经常出现。否则:
a = ((a % 360) + 540) % 360 - 180
- RedMattx是目标角度,y是源角度或起始角度:
atan2(sin(x-y), cos(x-y))
它返回带符号的角度差。请注意,根据您的API,atan2()函数的参数顺序可能会有所不同。
x-y
给出的是角度差,但它可能超出了所需范围。将这个角度视为单位圆上的一个点,该点的坐标为 (cos(x-y), sin(x-y))
。atan2
返回该点的角度(等同于 x-y
),但其范围为 [-PI, PI]。 - Max如果你的两个角度是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
请注意,在涉及负值时,%
运算符在不同的语言中的行为可能不同,因此如果需要移植代码,则可能需要进行一些符号调整。
from math import tau
。 - mhartl一个在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;
在一些其他编程语言中,负数的取模为正,可以省略内部绝对值。
我挑战自己来提供带有符号的答案:
def f(x,y):
import math
return min(y-x, y-x+2*math.pi, y-x-2*math.pi, key=abs)
math
模块。 - astroide算术(相对于算法)解法:
angle = Pi - abs(abs(a1 - a2) - Pi);
我非常喜欢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));
}
需要注意的一点是,我故意反转了符号:逆时针增加的角度是负数,顺时针增加的角度是正数。
没有必要计算三角函数。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 = difangrad(a,b);
计算 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
没有正弦,没有余弦,没有正切,……只有几何!!!
arg = arg - PIV2;
会扩展为arg = arg - M_PI + M_PI
,因此没有任何作用。 - canton7