如何将 atan2() 映射到 0-360 度

发布于 2024-08-02 07:43:25 字数 458 浏览 3 评论 0原文

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);

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

ゝ杯具 2024-08-09 07:43:25

使用 Modulo 的解决方案

捕获所有情况的简单解决方案。

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

说明

正数:1 到 180

如果将 1 到 180 之间的任何正数除以 360,您将得到与输入的数字完全相同的数。这里的 Mod 只是确保这些正数数字作为相同的值返回。

负数:-180 到 -1

此处使用 mod 将返回 180 到 359 度范围内的值。

特殊情况:0 和 360

使用 mod 意味着返回 0,这使其成为安全的 0-359 度解决方案。

Solution using Modulo

A simple solution that catches all cases.

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

Explanation

Positive: 1 to 180

If you mod any positive number between 1 and 180 by 360, you will get the exact same number you put in. Mod here just ensures these positive numbers are returned as the same value.

Negative: -180 to -1

Using mod here will return values in the range of 180 and 359 degrees.

Special cases: 0 and 360

Using mod means that 0 is returned, making this a safe 0-359 degrees solution.

开始看清了 2024-08-09 07:43:25
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
(x > 0 ? x : (2*PI + x)) * 360 / (2*PI)
堇色安年 2024-08-09 07:43:25

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

Add 360° if the answer from atan2 is less than 0°.

ㄟ。诗瑗 2024-08-09 07:43:25

或者,如果您不喜欢分支,请将两个参数取反并将答案添加 180°。

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

Or if you don't like branching, negate the two parameters and add 180° to the answer.

(Adding 180° to the return value puts it nicely in the 0-360 range, but flips the angle. Negating both input parameters flips it back.)

抚笙 2024-08-09 07:43:25

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

@erikkallen is close but not quite right.

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

This should work in C++: (depending on how fmod is implemented, it may be faster or slower than the conditional expression)

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

Alternatively you could do this:

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

since (x,y) and (-x,-y) differ in angles by 180 degrees.

卸妝后依然美 2024-08-09 07:43:25

我有 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;
}

I have 2 solutions that seem to work for all combinations of positive and negative x and y.

1) Abuse atan2()

According to the docs atan2 takes parameters y and x in that order. However if you reverse them you can do the following:

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

2) Use atan2() correctly and convert afterwards

double degrees = std::atan2(y, x) * 180 / M_PI;
if (degrees > 90)
{
    degrees = 450 - degrees;
}
else
{
    degrees = 90 - degrees;
}
愛上了 2024-08-09 07:43:25

@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

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

@Jason S: your "fmod" variant will not work on a standards-compliant implementation. The C standard is explicit and clear (7.12.10.1, "the fmod functions"):

if y is nonzero, the result has the same sign as x

thus,

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

is actually just a verbose rewriting of:

atan2(y,x)/M_PI*180

Your third suggestion, however, is spot on.

您的好友蓝忘机已上羡 2024-08-09 07:43:25

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

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

Here's some javascript. Just input x and y values.

var angle = (Math.atan2(x,y) * (180/Math.PI) + 360) % 360;
旧时光的容颜 2024-08-09 07:43:25

这就是我通常做的事情:

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

This is what I normally do:

float rads = atan2(y, x);
if (y < 0) rads = M_PI*2.f + rads;
float degrees = rads*180.f/M_PI;
当梦初醒 2024-08-09 07:43:25

另一种解决方案是使用 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);
    }

An alternative solution is to use the mod () function defined as:

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

Then, with the following function, the angle between ini(x,y) and end(x,y) points is obtained. The angle is expressed in degrees normalized to [0, 360] deg. and North referencing 360 deg.

    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);
    }
妳是的陽光 2024-08-09 07:43:25
angle = Math.atan2(x,y)*180/Math.PI;

我制定了一个公式,将定向角度设置为 0 到 360

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

I have made a Formula for orienting angle into 0 to 360

angle + Math.ceil( -angle / 360 ) * 360;
贪了杯 2024-08-09 07:43:25
double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

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

double degree = fmodf((atan2(x, y) * (180.0 / M_PI)) + 360, 360);

This will return degree from 0°-360° counter-clockwise, 0° is at 3 o'clock.

乙白 2024-08-09 07:43:25

值范围为 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)))

A formula to have the range of values from 0 to 360 degrees.

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)))
怂人 2024-08-09 07:43:25

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)

The R packages geosphere will calculate bearingRhumb, which is a constant bearing line given an origin point and easting/northing. The easting and northing must be in a matrix or vector. The origin point for a wind rose is 0,0. The following code seems to readily resolve the issue:

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)
烟沫凡尘 2024-08-09 07:43:25
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 度

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 deg becomes (-1 + 360) = 359 deg
-179 deg becomes (-179 + 360) = 181 deg

小情绪 2024-08-09 07:43:25

对于您的应用程序,我怀疑您不需要精确的度数,而更喜欢更近似的罗盘角度,例如 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];
}

For your application I suspect you don't need exact degrees and would prefer a more approximate compass angle, eg 1 of 16 directions? If so then this code avoids atan issues and indeed avoids floating point altogether. It was written for a video game so uses 8 bit and 16 bit integers:

/*

                                           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];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文