120

atan2(y, x)在 180° 处有不连续性,在顺时针方向切换到 -180°..0°。

如何将值范围映射到 0°..360°?

这是我的代码:

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

startPoint给定and endPoint,两个 XY 点结构,我正在计算滑动触摸事件的方向。该代码适用于 iPhone,但任何支持的语言atan2f()都可以。

4

16 回答 16

114

使用模数的解决方案

一个简单的解决方案,可以捕获所有情况。

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 度解决方案。

于 2014-09-08T13:06:30.210 回答
72
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
于 2009-08-21T10:20:23.473 回答
47

如果 atan2 的答案小于 0°,只需添加 360°。

于 2009-08-21T10:17:48.160 回答
38

或者,如果您不喜欢分支,只需否定这两个参数并将答案添加 180°。

(将 180° 添加到返回值使其很好地处于 0-360 范围内,但会翻转角度。否定两个输入参数会将其翻转回来。)

于 2009-08-21T12:08:07.530 回答
22

@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 度。

于 2009-08-21T19:14:43.823 回答
13

我有 2 个解决方案似乎适用于正负 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;
}
于 2014-08-20T06:35:48.140 回答
8

@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

但是,您的第三个建议是正确的。

于 2009-08-21T20:37:00.833 回答
5

这是我通常做的:

float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;
于 2017-02-06T14:59:33.410 回答
5

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

var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
于 2020-04-21T02:24:53.137 回答
3

另一种解决方案是使用mod () 函数定义为:

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

然后,通过以下函数,获得了ini(x,y)end(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);
    }
于 2016-02-03T21:47:59.290 回答
2
angle = Math.atan2(x,y)*180/Math.PI;

我已经制定了一个将角度定向到 0 到 360 的公式

angle + Math.ceil( -angle / 360 ) * 360;
于 2015-02-04T07:58:00.530 回答
2

值范围为 0 到 360 度的公式。

f(x,y)=180-90*(1+sign(x))* (1-sign(y^2))-45*(2+sign(x))*sign(y)

     -(180/pi())*sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
于 2018-11-25T13:18:30.453 回答
1

R包geosphere将计算bearingRhumb,它是给定原点和东/北的恒定方位线。东移和北移必须在矩阵或向量中。风玫瑰的原点是 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)
于 2016-05-19T14:32:57.240 回答
1
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

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

于 2016-10-09T06:49:23.577 回答
1
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 度

于 2017-02-03T03:56:28.357 回答
0

对于您的应用程序,我怀疑您不需要精确的度数,而是更喜欢更近似的罗盘角度,例如 16 个方向中的 1 个?如果是这样,那么此代码避免了 atan 问题,并且确实完全避免了浮点。它是为视频游戏编写的,因此使用 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];
}
于 2021-08-24T19:49:29.250 回答