1
\$\begingroup\$

Not sure if this belongs here or on the Math Exchange site; it's probably a simple math concept that I'm missing.

I have a 2D sprite with a rotation (let's call it A), and I want to slowly rotate it until it's at a new rotation (B). Depending on where A is, I'll rotate right or left, which ever gets me to B faster. A is constantly changing as it rotates towards B, and B is also able to change.

I'm currently taking the difference of B - A, and rotating left (CCW) if it's positive, and rotating right (CW) if it's negative. This almost works, but breaks when the target crosses the boundary at 0/ and changes signs.

In the first image, you'll see how my current logic works as expected:

B - A ≈ 5.76 - 5.23 = 0.53 = turn CCW.

enter image description here

But, if B continues to rotate left, it will rotate past and result in the sprite turning CW, which is slower than turning CCW:

B - A ≈ 0.17 - 5.50 = -5.33 = turn CW.

enter image description here

Any ideas would be appreciated, thanks!

NOTES:

  • I'm using Bevy + Rust, but this seems like a generalized math question so not sure if that's important
  • I've tried this with angles ranging from [-π, π] (instead of [0, 2π]), but it has the same issue when the angle crosses the boundary between π and -π.
\$\endgroup\$
1

2 Answers 2

0
\$\begingroup\$

The maximum amount you should ever rotate is half a turn, since if you're rotating more than that, it would have been faster to go the other way. You should check your final result to see if it its magnitude is greater than Pi. If it is, your "shortest path" is greater than half a rotation, indicating that you have a modulo issue and need to add 2*Pi to B.

In your example, a result of -5.33 is almost one full turn, which obviously isn't the fastest way to reach a heading. Since 5.33 is greater than pi, add 2*Pi to the result, yielding a value of 0.95, which is the correct minimum distance to turn counterclockwise.

\$\endgroup\$
0
\$\begingroup\$

Finding the minimum rotation from an orientation A to a target orientation B is equivalent to finding the signed angle between A and B, then applying the rotation.

This excellent answer on Stack Overflow addresses this very problem by providing a working formula in modular arithmetic.

When working with angles, we call a signed angle a number where:

  • Its value is the smallest of the two angles formed by a source vector with direction A and a target vector with direction B. Its absolute value is always 0-180.
  • Its sign is the rotation direction (CCW if positive, CW if negative) to accomplish the smallest rotation possible.

By combining the above and the modulo operator, such a function returns a value between -180 and 179. A simple Python implementation would look like this:

def signed_angle(source_angle, target_angle):
    a = target_angle - source_angle
    a = (a + 180) % 360 - 180
    return a

Sample execution of the above:

>>> signed_angle(0,90)
90
>>> signed_angle(72,254)
-178
>>> signed_angle(135,-217)
82

Additional math considerations are included in the linked answer by its original author. Also, this answer assumed angles were expressed in degrees, but converting it to work with radians is trivial.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .