Situation
I've been hitting my head on this problem for a while now. Basically, I have an angle r (radians), that I want to change to a desired angle wr (radians) with a set speed dr (radians / second), during each step of a simulation. Thus I need that for each step, I have a new angle nr, where nr = r + dr * dt and dt is the delta time since the last step.
Angles are in the space [-pi, pi] and the delta angle is determined by the shortest way to turn. For each step, the delta angle is added to the current rotation, wrapped to [-pi, pi] and stored as the new angle.
Since I haven't got infinite precision in my steps, I will obviously - virtually - never hit the desired angle straight on, thus I need to find when the desired rotation has been reached - and crossed - and then stop rotating and set the angle to the desired angle. In pseudocode:
if rotationSpeed!= 0
angle = WrapRotation(angle + rotationSpeed * deltaTime)
if desiredAngle has been reached or crossed
angle = desiredAngle
rotationSpeed = 0
Problem
In almost all circumstances, this is easy to do, but when you have an angle high or low enough (close to -pi or pi) and the new angle takes you across the "border", so to speak, things get complicated. All of my attempts have failed to cover all possible situations on current, previous and desired rotation. So I'm asking you if you happen to know a solution for this?
If needed, I've attached my code (C#) below:
// Sets the desired rotation and rotation speed in radians/second:
public void SetRotation(float rotation, float speed)
{
desiredRotation = MathHelper.WrapAngle(rotation);
if (desiredRotation != this.rotation)
{
// Determine the shortest way to turn (indicated by the sign of speed)
float a = desiredRotation - this.rotation;
if (a > Math.PI) a -= 2 * (float)Math.PI;
if (a < -Math.PI) a += 2 * (float)Math.PI;
desiredRotationSpeed = a < 0 ? -speed : speed;
}
}
// Update is called per each step. Takes in the amount of seconds since the last call.
public void Update(float seconds)
{
/* Other stuff */
if (desiredRotationSpeed != 0)
{
float delta = desiredRotationSpeed * seconds;
rotation = MathHelper.WrapAngle(rotation + delta);
if( /* This is the part I'm struggling with. */ )
{
desiredRotationSpeed = 0f;
rotation = desiredRotation;
}
}
/* Other stuff */
}
Under normal circumstances (non-cylic behaviour) the following works:
if (Math.Abs(value - desiredValue) < Math.Abs(deltaValue))
/* Desired has been reached. Do stuff! */
So to clarify. I want to find when the desired angle has been hit (and surpassed due to the precision), so that I can set my current angle to the desired, and stop rotating.
Thanks a lot for your help! I have a feeling that this really has a simple solution! :3