Introduction
The nature of angles has caused me grief in many of my creative efforts. An angle of 350 degrees can sometimes be considered more than and sometimes less than an angle of say 10 degrees!
Recently, I needed to increment an angle by a variable amount and then stop it in the desired direction. While this should be a trivial problem, something in my brain prevents me from finding a simple solution.
This tip shows one solution to the problem in JavaScript, but the algorithm should be easily understood and transferred to any language.
Background
I needed to code to have a little animated sprite run to one end of the screen and to then gradually turn around and run back again. He would then do the same at the other end of the screen.
In my JavaScript code below, we see the animation update routine:
var manTargetDirection = 0;
var manDirection = 0;
function runningManUpdate(img, dt) {
if(img.vel.x > 0) {
if(img.pos.x > (window.innerWidth*3/4)) {
manTargetDirection = 180;
}
} else {
if(img.pos.x < (window.innerWidth/4)) {
manTargetDirection = 0;
}
}
if(manDirection != manTargetDirection) {
manDirection = rotateTo(manDirection,20*dt,manTargetDirection);
}
var cos = Math.cos(manDirection*Math.PI/180.0);
img.scale = new Point(2.0*cos,2.0);
img.vel = new Point(img.speed*cos,0);
}
Nothing deep and meaningful here.
For the function rotateTo
, I had wrongly thought the following would do the trick:
function rotateTo(a_old,delta,target) {
var a_new = a_old + delta;
if ((a_old < target) && (a_new > target))
return target;
else
return a_new;
}
This works well if a_old
is numerically less that the target
angle. But what if the target is 0 degrees? Then a_old
can never be less than the target and so the little running man goes around and around and around... Other strange things can happen - all because the silly angle keeps coming back to zero.
First Working Generic Solution
After much agony, I have come up with this:
function rotateTo(a_old,delta,target) {
var a_new = a_old + delta;
var t2;
if(delta > 0) {
if((target > a_old) && (target <= a_new))
return target;
t2 = target + 360;
if((t2 > a_old) && (t2 <= a_new))
return target;
} else {
if((target < a_old) && (target >= a_new))
return target;
t2 = target - 360;
if((t2 < a_old) && (t2 >= a_new))
return target;
}
return a_new % 360;
}
This solution works for both positive and negative rotation directions. I would very much like to see a simpler solution - but no lambdas please. I would like to think of this as a beginners' article.
Using the Code
The rotcheck.html code has a simple form to check the operation of the function rotateTo()
. As you probably know, it should open in any modern browser and can be edited with any text editor. There has to be a more elegant and simple solution to this. I would even be interested to see how it could be done as a PID control loop - those pesky angles going to 2PI and then being zero again must cause problems even there.