3

for example my current angle position is 170, I click on the object to rotate it to -170 degrees, here the problem is that after 178, 179, 180, the next number is -180, -179, -178 and so on...

even tough numerically 170 degrees is far from -170 degrees, but visually they look near, an object is rotating the longest way in order to reach that number, for example:

if(currentAngle < targetAngle)
{
   currentAngle += 1;

}

if(currentAngle > targetAngle)
{
  currentAngle -= 1;
}

this way I can reach the target angle, but again how to transpass this barrier between 180 and -180, maybe there are a formula for this?

update:

    onclick() {
                        double angle = Math.atan2(dy, dx);
                        targetAngle = (int)Math.toDegrees(angle); //180 to //-179
    }

onFrame() {

 //here happens the animation

      if(currentAngle < targetAngle)
      {

          currentAngle +=1;

      }

      if(currentAngle > targetAngle)
      {
          currentAngle -= 1;
      }
}

now what if I'am currently on -179 angle degree, and I clicked on 160 angle degree, it should rotate to left to reach that angle as fast as posible, but in my case it is rotating to the right(which is longer), and thats because it is limited from -180 to 179, thus how to break the limits and go from 179 to -180, -181, -182...if you understand what I mean

enter image description here

1) my click handler:

onClick() { 

double angle = Math.atan2(dy, dx);
                    angle = (int)Math.toDegrees(angle);
                    Log.d("test", "angler:" + angle);
                    if(angle < 0)
                    angle += 360;
}

so here I convert the degrees to positive using angle += 360, then:

2) my onFrame handler:

onFrame() {

       if(currentAngle != angle)
        {

            if(angle < currentAngle)
            {
                currentAngle -= 5;
            }
            else
            {
                currentAngle += 5;
            }
          int diff = Math.abs(angle - currentAngle);
            if(diff <= 5)
            {
                currentAngle = angle; // if its near we put it exact to that angle
            }
            invalidate(); //update the view
        }


}

thats all I have

merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • 3
    You only need the answer for one programming language. – Stephen C Mar 06 '15 at 23:15
  • @StephenC, no, cause this encounters in many programming languages, i don't even know to which programming languages this may refer, that why I've tagged it with java, c# and actionscript – Человек и PHP Mar 06 '15 at 23:17
  • 1
    In that case, you want an algorithm; i.e. a solution in no specific language. – Stephen C Mar 06 '15 at 23:20
  • 1
    You compute the distance between the current angle and the target angle both ways and choose the smaller one. – Adrian McCarthy Mar 06 '15 at 23:42
  • @AdrianMcCarthy, sorry for my noobiness cause i don't know how to do it :), but can you checkout my code update? – Человек и PHP Mar 06 '15 at 23:48
  • 1
    @user4642850 - Enough of the "I'm a noob" stuff. This simply requires you to >>think about it<< so that you understand what you are doing. If you don't want to think, programming is not for you ... – Stephen C Mar 07 '15 at 00:08
  • It appears that you are trying to do something involving rendering. Please show the full code what what you are trying to accomplish, and explain the desired behavior by referring to that code. – merlin2011 Mar 07 '15 at 10:21
  • @merlin2011 please give me 1 like to have 10 reputation, cause I can't post pictures that required 10 reputation, to show what I want to say :D – Человек и PHP Mar 07 '15 at 11:18
  • You can try, but I think you'll need to post the source code that generates the picture also. – merlin2011 Mar 07 '15 at 11:21
  • @merlin2011, thanks, updated image + code – Человек и PHP Mar 07 '15 at 11:48
  • This looks much better, but where is the rest of the page that your code lives in? Or is this part of a framework that somebody else provided? – merlin2011 Mar 07 '15 at 11:49
  • updated, the other code I have doesn't make sense, to most relevant of things I've posted, second day can't figure out how to do it :D, maybe you've got some toughts about this, I appreciate any help – Человек и PHP Mar 07 '15 at 11:51

3 Answers3

4

Note that I am assuming your angles are between 0 and 359, rather than having negatives.

There are two separate problems here.

  1. Given a currentAngle and a targetAngle, how does one determine the direction of rotation which will result in completing the rotation in the shortest number of frames?
  2. How do we handle the boundary condition where the angle crosses from 359 to 0/360?

Solution

Problem 1 is mostly addressed in @ellitron's answer, but you have to separate out the pieces a little bit to determine the direction to move your currentAngle. Problem 2 requires a mod by 360 which handles negative numbers after each update to currentAngle, and this answer gives us a nice trick for that.

if (currentAngle - targetAngle == 0) return;

if (Math.abs(currentAngle - targetAngle) < 180) {
    // Rotate current directly towards target.
    if (currentAngle < targetAngle) currentAngle++;
    else currentAngle--;
} else {
    // Rotate the other direction towards target.
    if (currentAngle < targetAngle) currentAngle--;
    else currentAngle++;
}
currentAngle = ((currentAngle % 360) + 360) % 360;

For future reference, here is how one might test this code outside of a rendering environment. We can simply pass in two command line arguments and determine from the output whether we rotated the right way or not.

public class Angle {
public static void main(String[] args) {
    int currentAngle = Integer.parseInt(args[0]);
    int targetAngle = Integer.parseInt(args[1]);

    while (currentAngle - targetAngle != 0) {
        if (Math.abs(currentAngle - targetAngle) < 180) {
            // Rotate current directly towards target.
            if (currentAngle < targetAngle) currentAngle++;
            else currentAngle--;
        } else {
            // Rotate the other direction towards target.
            if (currentAngle < targetAngle) currentAngle--;
            else currentAngle++;
        }
        currentAngle = ((currentAngle % 360) + 360) % 360;
        System.out.printf("CurrentAngle = %d, targetAngle = %d\n", 
            currentAngle, targetAngle);
    }

}
}
Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • awesome man, thank you very much, I will study this code, also it works even without currentAngle %= 360 – Человек и PHP Mar 07 '15 at 12:24
  • so the most important piece of code is: if (Math.abs(currentAngle - angleR) < 180) - of course 180 is the middle degree, and we can see if it is far enough or close to rotate that or either way, perfect :D – Человек и PHP Mar 07 '15 at 12:39
3

As you have defined currentAngle in your question, its values in ascending order are:

0,1,2,...,180,-179,-178,...,0

Which means that for you, -179 is greater than 179, and therefore arithmetic comparison will not work for you. First you must convert these numbers to a range that looks like:

0,1,2,...,180,181,182,...,359

Which you can do with the following formula:

if(angle < 0)
    angle += 360

Now you can find the difference between the two angles (say angle1 and angle2) like this:

abs(angle1 - angle2)

or if you want to cross over 0, then do this:

360 - abs(angle1 - angle2)

To give you the shortest distance between these two angles, you would take the minimum like this:

min(abs(angle1 - angle2), 360 - abs(angle1 - angle2))
Jonathan Ellithorpe
  • 564
  • 1
  • 4
  • 17
  • oh this is more close to what I want :), I've added an update code for example, but now I'll try to use this answer in my code... – Человек и PHP Mar 06 '15 at 23:46
  • Oh it worked for the left side that is where -180 ends and 179 begins, but in right side that is 0 end, 1 begin it don't work, thus how can make it for the both sides? – Человек и PHP Mar 07 '15 at 00:06
  • @user4642850 If I understand what you are trying to do correctly, it did not work because you need to take the minimum of these two calculated angles (edited). – Jonathan Ellithorpe Mar 07 '15 at 00:23
  • the last formula is ok(thanks very useful) I use it to stop when the current angle is near to the target, but the issue is: now I can transpass the 179 to -180, -179, -178 and so on, but in the other(right) side where is 3, 2, 1, 0, -1, -2 ,-3 can't, thus it is the same as it was for the left side, I've tried to put if(angle > 0) angle -= 360, it don't work as it works for: if(angle < 0) angle += 360, why? (, sorry maybe I'am missing something – Человек и PHP Mar 07 '15 at 01:12
0

I had the same problem for rotation animation. From 1 to -1 it goes through the large arc, with length 358 but the small arc is with length 2. So does the opposite. But imagine it gets values 0, 100, 200, 300, 0, 100, 200, 300, and so on... in order animation to be smooth it has to go through values 0, 100, 200, 300, 360, 460, 560, 660, .. so the angle will rise if it turns only clockwise.

Check out this algorithm:

class RotAngle
{
    double oldAngle = 0; //old angle modulo 360
    int rot = 0; //this is current spin count (times 360 degrees)

    public double Rotate(double angle)
    {
        double currAngle = ((angle % 360) + 360) % 360;
        //if you enter only positive angles (angle % 360) will do
        //((angle % 360) + 360) % 360 is needed for negative angles

        double diff_2 = 2 * (oldAngle - currAngle); 

        /* mathematically is equal to: old + 360 - current < current - old */
        /* so closer is one rotation less */
        if (diff_2 < -360) rot--;

        /* opposite: 360 + current - old < old - current -> one rotation more is closer */
        if (diff_2 > 360) rot++;

        oldAngle = currAngle;

        return rot * 360 + currAngle;
    }
}
altair
  • 103
  • 1
  • 6