How to map atan2() to degrees 0-360

こ雲淡風輕ζ 提交于 2019-12-17 05:42:50

问题


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.

Thanks for your help guys, with both the general solution and code.

Update: I made erikkallen's answer into a function with nice long variable names so I'll comprehend it 6 months from now. Maybe it will help some other iPhone noob.

float PointPairToBearingDegrees(CGPoint startingPoint, CGPoint endingPoint)
{
    CGPoint originPoint = CGPointMake(endingPoint.x - startingPoint.x, endingPoint.y - startingPoint.y); // get origin point to origin by subtracting end from start
    float bearingRadians = atan2f(originPoint.y, originPoint.x); // get bearing in radians
    float bearingDegrees = bearingRadians * (180.0 / M_PI); // convert to degrees
    bearingDegrees = (bearingDegrees > 0.0 ? bearingDegrees : (360.0 + bearingDegrees)); // correct discontinuity
    return bearingDegrees;
}

回答1:


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



回答2:


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.




回答3:


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




回答4:


Or if you don't like branching, just 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.)




回答5:


@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.




回答6:


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



回答7:


@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.




回答8:


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;



回答9:


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;



回答10:


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



回答11:


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)



回答12:


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




回答13:


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.




回答14:


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


来源:https://stackoverflow.com/questions/1311049/how-to-map-atan2-to-degrees-0-360

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!