How to map atan2() to degrees 0-360

atan2(y, x) has that discontinuity at 180° where it switches to -180°..0° going clockwise.

How do I map the range of values to 0°..360°?

here is my code:

CGSize deltaPoint = CGSizeMake(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
float swipeBearing = atan2f(deltaPoint.height, deltaPoint.width);

I'm calculating the direction of a swiping touch event given the startPoint and endPoint, both XY point structs. The code is for the iPhone but any language that supports atan2f() will do.

168696 次浏览

如果 atan2的答案小于0 ° ,就加360 ° 。

(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)

或者,如果你不喜欢分支,取消这两个参数,并添加180 ° 的答案。

(在返回值上加180 ° ,使其在0-360的范围内,但角度发生了翻转。否定两个输入参数会使它倒退。)

@ Erikkallen 很接近,但并不完全正确。

theta_rad = atan2(y,x);
theta_deg = (theta_rad/M_PI*180) + (theta_rad > 0 ? 0 : 360);

这在 C + + 中应该可以工作: (取决于如何实现 fmod,它可能比条件表达式更快或更慢)

theta_deg = fmod(atan2(y,x)/M_PI*180,360);

或者你可以这样做:

theta_deg = atan2(-y,-x)/M_PI*180 + 180;

因为(x,y)和(- x,-y)的角度相差180度。

@ Jason S: 您的“ fmod”变体将无法在符合标准的实现上工作。C 标准是明确和清晰的(7.12.10.1,“ fmod 函数”) :

如果 y 为非零,则结果与 x 的符号相同

因此,

fmod(atan2(y,x)/M_PI*180,360)

实际上只是一个冗长的重写:

atan2(y,x)/M_PI*180

然而,你的第三个建议是正确的。

我有两个解,似乎适用于所有正负 x 和 y 的组合。

1)虐待 atan2()

根据文献 atan2的参数 y 和 x 的顺序。然而,如果你把它们倒过来,你可以做到以下几点:

double radians = std::atan2(x, y);
double degrees = radians * 180 / M_PI;
if (radians < 0)
{
degrees += 360;
}

2)正确使用 atan2()并在事后进行转换

double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
degrees = 450 - degrees;
}
else
{
degrees = 90 - degrees;
}

使用模的解决方案

一个捕捉所有情况的简单解决方案。

degrees = (degrees + 360) % 360;  // +360 for implementations where mod returns negative numbers

解释

阳性: 1到180

如果你用360修改任何1到180之间的正数,你会得到和你输入的一模一样的数字。这里的 Mod 只是确保这些正数作为相同的值返回。

阴性: -180比 -1

在这里使用 mod 将返回180度和359度范围内的值。

特殊情况: 0和360

使用 mod 意味着返回0,因此这是一个安全的0-359度解决方案。

angle = Math.atan2(x,y)*180/Math.PI;

我已经做了一个公式来定位角度为0到360

angle + Math.ceil( -angle / 360 ) * 360;

另一种解决方案是使用定义为:

function mod(a, b) {return a - Math.floor (a / b) * b;}

然后,通过下面的函数,得到 Ini (x,y)结束(x,y)点之间的夹角。角度以归一化为[0,360]度的度表示。北纬360度。

    function angleInDegrees(ini, end) {
var radian = Math.atan2((end.y - ini.y), (end.x - ini.x));//radian [-PI,PI]
return mod(radian * 180 / Math.PI + 90, 360);
}

R 包地圈将计算方位 Rhumb,这是一条给定原点和东/北的恒定方位线。向东和向北必须在一个矩阵或向量中。风玫瑰的起点是0,0。下列守则似乎很容易解决这个问题:

windE<-wind$uasE
windN<-wind$vasN
wind_matrix<-cbind(windE, windN)
wind$wind_dir<-bearingRhumb(c(0,0), wind_matrix)
wind$wind_dir<-round(wind$wind_dir, 0)
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

这将返回度从0 °-360 ° 逆时针,0 ° 是在3点钟。

theta_rad = Math.Atan2(y,x);
if(theta_rad < 0)
theta_rad = theta_rad + 2 * Math.PI;    //if neg., add 2 PI to it
theta_deg = (theta_rad/M_PI*180) ;        //convert from radian to degree


//or
theta_rad = Math.Atan2(y,x);
theta_rad = (theta_rad < 0) ? theta_rad + 2 * Math.PI : theta_rad;
theta_deg = (theta_rad/M_PI*180) ;

-1度变成(-1 + 360) = 359度
-179度变为(-179 + 360) = 181度

我通常都是这么做的:

float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;

一个值范围从0到360度的公式。

F (x,y) = 180-90 * (1 + 符号(x)) * (1-符号(y ^ 2))-45 * (2 + 符号(x)) * 符号(y)

     -(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))

这是一些 javascript,只需要输入 x 和 y 值。

var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;

对于你的应用程序,我怀疑你不需要精确的度数,并会更喜欢一个更接近的罗盘角度,例如16个方向的1?如果是这样,那么这段代码避免了一个问题,实际上完全避免了浮点数。它是为一个视频游戏编写的,所以使用8位和16位整数:

/*


349.75d         11.25d, tan=0.2034523
\             /
\   Sector  /
\    0    /  22.5d tan = ?2 - 1
15      |      1   33.75
|         /   45d, tan = 1
14           |            2 _56.25
|             /  67.5d, tan = 1 + ?2
13              |               3
|                __ 78.75
|
12---------------+----------------4 90d tan = infty
|                __ 101.25
|
11              |               5
|
10           |            6
|
9       |      7
8






*/


// use signs to map sectors:
static const int8_t map[4][5] = {  /* +n means n >= 0, -n means n < 0 */
/* 0: +x +y */ {0, 1, 2, 3, 4},
/* 1: +x -y */ {8, 7, 6, 5, 4},
/* 2: -x +y */ {0, 15, 14, 13, 12},
/* 3: -x -y */ {8, 9, 10, 11, 12}
};


int8_t sector(int8_t x, int8_t y) { // x,y signed in range -128:127, result 0:15 from north, clockwise.
int16_t tangent; // 16 bits
int8_t quadrant = 0;
if (x > 0) x = -x; else quadrant |= 2; // make both negative avoids issue with negating -128
if (y > 0) y = -y; else quadrant |= 1;
if (y != 0) {
// The primary cost of this algorithm is five 16-bit multiplies.
tangent = (int16_t)x*32;   // worst case y = 1, tangent = 255*32 so fits in 2 bytes.
/*
determine base sector using abs(x)/abs(y).
in segment:
0 if         0 <= x/y < tan 11.25   -- centered around 0     N
1 if tan 11.25 <= x/y < tan 33.75   --                 22.5  NxNE
2 if tan 33.75 <= x/y < tan 56.25   --                 45    NE
3 if tan 56.25 <= x/y < tan 78.75   --                 67.5  ExNE
4 if tan 78.75 <= x/y < tan 90      --                 90    E
*/
if (tangent > y*6  ) return map[quadrant][0]; // tan(11.25)*32
if (tangent > y*21 ) return map[quadrant][1]; // tan(33.75)*32
if (tangent > y*47 ) return map[quadrant][2]; // tan(56.25)*32
if (tangent > y*160) return map[quadrant][3]; // tan(78.75)*32
// last case is the potentially infinite tan(90) but we don't need to check that limit.
}
return map[quadrant][4];
}