1
\$\begingroup\$

I'm trying to find the orientation for an entity, as an angle in [0º-360º). I've searched for an answer and tried to implement something of my own, but it always fails on the edge cases. For a better understanding of what I am trying to achieve I've created the following image.

representation

EDIT: It is worth mentioning that I do the conversions to and from radians where needed. Additionally, this is used for an internal simulation (meaning this will not be rendered on screen).

What is known: the center point coordinates which is always at (0,0) and the position of the red entity (x,y)

What needs to be computed: the orientation of the red entity such as it faces the center point (0, 0) in degrees 0-359

In my particular case I have to support negative coordinates. The degrees start at 0 which is at the top and goes CW.

What I tried

I've tried using atan2(y, x) which I almost got working, but it fails at some edge cases as I will describe.

When the entity is in the first quadrant I get, let's say 45 degrees, but this is not good in my case, because if the entity is in the first quadrant and it's facing the center the angle should be between 270 and 180. So I did some mapping and it seems to be working.

When the entity is in the second quadrant I get, let's say -45 degrees, which again is not good because I need it to be in range of 0-359. So I check if the degree is less than 0, add 360 and mod by 360. The result I get is good since it's between 270 and 359.

I could go on about the rest of the quadrants, but then I get another issue: when the x or y coordinates for the entity are 0 (only one, not both).

I could write some if cases to adapt the result accordingly, but I figured there must be a smarter solution to this and a faster one, since this needs to be computed 60 times a second.

I've also tried some custom math, using triangles, but ultimately I have to manually do some checks to get the correct answer.

I don't know if this is of any value for the answer, but I'm trying to implement this in Java/Kotlin, though I'm not looking for someone to just give me the magic piece of code that I can just paste and have it working, I'm looking to understand how it can be done better.

Is there a better way to achieve the result I want here?


The solution that worked perfectly for me is the following

angle = -(toDegrees(atan2(y, x)) + 90) % 360

As far as I can see it covers the edge cases.

\$\endgroup\$

3 Answers 3

1
\$\begingroup\$

We have - at least - three issues to consider:

  1. atan2(x, y) gives you the angle from the origin to the given point (the position of the red entity on this case). What you want is the opposite direction.

  2. However, the angle is from the positive x axis (0 = on the positive x axis), but given your picture you are measuring from the y axis (0 = on the positive y axis).

  3. Futhermore, the angle given by atan2(x, y) is given as a positive rotation, and that means counterclockwise. Yet, your picture indicates you want clockwise angle.

Let us be clear, measuring from the x axis, counterclockwise, is the convention.


I said at least three issues because there is another one to consider. You depict the positive y axis on the top. That is what we do in math, however... well, for screens the y axis increases downwards because of historical reasons. In fact, the origin is in the top left corner.

I do not know if you want to take this into account, of you already are doing it (or whatever software you are using takes care of it for you).


So, what transformation on the orientation will take it from the atan2 representation to the one you want...

Add a quarter turn (it is plus half turn to fix issue 1, and minus a quarter turn to fix issue 3), and change the sign of the resulting angle (to fix issue 2). Of course mod by a whole turn.

angle = -(atan2(x, y) + quater_turn) mod whole_turn

If you also need to take the position from another point:

angle = -(atan2(x - x_origin, y - y_origin) + quater_turn) mod whole_turn

If you also need to flip the vertical axis because of screen coordinates:

angle = -(atan2(x - x_origin, y_origin - y) + quater_turn) mod whole_turn

Oh, and add a whole turn if it is negative

if (angle < 0) angle += whole_turn

Note: Libaries often work in radians, however you use degrees. I speak in turns to keep it agnostic. Do your angle conversions as needed.


As Quentin points out we can do this by just flipping the parameters of atan2.

angle = atan2(y, x)

If it isn't clear that is the same, consider that we needed to do a counterclockwise quarter rotation, and change the sign. We do the rotation by doing this atan(-y, x) (if you wish, you can confirm by trigonometry), and then change the sign. We should not need to mod by a whole turn here. However, we still need to add a whole turn if the result is negative.


<rant> It bothers me that you say 0-359. I know you mean degrees because you mention it later, I will ignore that you do not specify it here... however 359.5º is a valid angle isn't it? And it is not in the range 0º-359º. I think you mean [0º-360º). Well, if you don't, I hope that means that you take the integer part of the angle once you have it degrees. </rant>

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Can't you do the whole rotate-and-flip just by swapping around atan2's coordinates? \$\endgroup\$
    – Quentin
    Jul 30, 2018 at 8:01
  • \$\begingroup\$ @Quentin didn't even cross my mind. Yes, that should be better, I will add that. \$\endgroup\$
    – Theraot
    Jul 30, 2018 at 8:03
  • \$\begingroup\$ Thank you for your answer! I'll try and make the changes you suggested and I will accept it right after that. I've also edited the question for more clarity (regarding the rant section of your answer and the concerns about rendering on screen). \$\endgroup\$
    – Paul
    Jul 30, 2018 at 9:24
0
\$\begingroup\$

Let's say (xi,yi) are the normalized coordinates of the red entity (length = 1), then:

to compute the angle:

if xi > 0 then angle = acos( yi ); // acos in degrees

if xi < 0 then angle = 360 - acos( yi ); acos in degrees

\$\endgroup\$
0
\$\begingroup\$

A formula for a clockwise angle from 0 to 360 degrees:

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

      -180/pi()*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))
\$\endgroup\$
3
  • \$\begingroup\$ It would help to label the variables and explain how the formula works, breaking down each piece if possible. \$\endgroup\$
    – Cobertos
    Dec 4, 2018 at 18:49
  • \$\begingroup\$ You can use excel ,to see how the formula works. \$\endgroup\$ Jan 14, 2019 at 6:01
  • \$\begingroup\$ You can use excel to see how the formula works. In A1 and B1 put the coordinates x and y. In the cell C1 put the formula: =180-90*(1+SIGN(B1))*(1-SIGN(A1^2))- 45*(2+SIGN(B1))*SIGN(A1)-180/PI()*SIGN(B1*A1)*ATAN((ABS(B1)-ABS(A1))/(ABS(B1)+ABS(A1))) \$\endgroup\$ Jan 14, 2019 at 6:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .