Reputation: 99
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
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
Upvotes: 3
Views: 6068
Reputation: 141
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;
}
}
Upvotes: 0
Reputation: 75545
Note that I am assuming your angles are between 0 and 359, rather than having negatives.
There are two separate problems here.
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? 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);
}
}
}
Upvotes: 5
Reputation: 564
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))
Upvotes: 3