Calculate the real difference between two angles, keeping the correct sign
1 April 2009When you build some animations with WPF, Surface or JavaFX you sometimes need to know how evolve an angle. For example, you have the new angle (orientation) of an object and you have store before the last measure of this orientation : how to calculate the evolution ?
A first solution
"This is simple" will you say, just do this :
double difference = secondAngle - firstAngle;
But this snippet leads to errors. This is because the orientation value given is relative to a certain initial value, and restart to 0° if you pass the 360°. Lets give you an example: if the object was oriented at 358° and the user turns it of 5° you will obtain a difference of (3-358=) -355° where you actually wants to find 5....
A better solution
A solution I propose is to consider that the methods is called enough frequently that if the object rotate to the left or to the right it has not enough time to rotate more than 180° (half a tour).
Based on this, we consider that the direction of the rotation is given by the "shortest way". If it shorter to turn to the left to go to the new angle, then we select the angle sign which tells we have turned to the left. It may be easiest to understand by looking atentivly to the image above.
An image is maybe better than word :
We then have this method in C#:
private double calculateDifferenceBetweenAngles(double firstAngle, double secondAngle) { double difference = secondAngle - firstAngle; while (difference < -180) difference += 360; while (difference > 180) difference -= 360; return difference; }
A case of use
When do use it ? For example when you build a carousel: the user can click on a specific item and the carousel rotate so it is in front of you. You then need to have the correct angle. I found no other way to do it.
Does someone have a better way to proceed ?
- By JonathanANTOINE@falsemail.com
- - MultiTouch
- - Tags :
Comments
Thanks. it was very useful.
@LEM : You are welcome !
Wouldn't if statements make more sense than while loops?
Thanks for the tip.
@[Dan] : The angles can ve really more than 360 and it will then works with this code.
Also I don't think using a while instead of an if cause a lot of perf problems...
++
You saved my day
Yep. We use this where I work (and in my homebrew). We call it "Normalize180", and there's a variant called "Normalize360", which does the same thing, but in the range 0f<a<360f.
You could also divide the difference by 180.0f, then take only the fractional part of that (behind the '.' ) and multiply it by 180, making sure to maintain the sign.
i.e.
float NormalizedAngle = diff / 180.0f;
if(diff > 0){
return (NormalizedAngle - Math.Floor(NormalizedAngle)) * 180.0f;
} else {
return (( NormalizedAngle - ( Math.Floor(NormalizedAngle) + 1.0f ) * 180.0f );//Errr... not sure about this bit - have to deal with negative case. Might be a better way to get the fractional part than number minus floor(number)
}
That would probably be less cycles than a while loop, but typical input numbers are unlikely to be high, so your approach is probably fine in most cases.
I was wondering two things: why do you use while instead of if, is that C#? and, perhaps, could you do
double difference = mod( secondAngle - firstAngle + 180, 360 ) - 180;
I suppose it depends on the compiler or deeper knowledge of the computer to know which is faster.
@tk :I simply do it to have the right result : the angle can be more than 360° (suppose you have done 2 turns)....
Your solution may works too...
@tk : That's so elegant - thank you, just what I needed :o)
@tk is superior as the sign can interpret the direction of rotation. Jon's method doesn't work in some cases, say 10 degrees to 350 reports 20 degree turn, not -20 degree...
@Rich
350 - 10 = 340 > 180 => 340 - 360 = -20
I use:
differenceAngle = (targetAngle - currentAngle) % 360.
this gives you the angle of the target relative to current angle, and is positive for a clockwise rotation.
A snippet of code taken from my archive in Blitz3D that was used to turn a missile towards the player
It eliminates the need to use mod
Will work with angles of any size
Returns an angle in the range -180 to 180
(
diffangle = (actualangle - destangle) + 180;
diffangle = (diffangle / 360.0)
diffangle = ((diffangle - Floor( diffangle )) * 360.0) - 180;
)
!Notes: The Floor command takes the float number and rounds it down to the nearest whole number!
try this..
If you only want to know the absolute difference then you can use
DEL = PI - ABS(PI - ABS(A - B)) in radians.
DEL = 180 - ABS(180- ABS(A - B)) in degrees.
Perfect ! just what i was looking for. Tested and works successfully !
Cool !
Thanks, this helped me out big time!