2
\$\begingroup\$

The following code will find the shortest rotation (in radians) (from pi to -pi) that I need to apply to from to leave me with to.

Scalar rotationBetween(Scalar from, Scalar to)
{
    Scalar fromMod = std::signbit(from) ? 
        pi * 2 + std::fmod(from, pi * 2) : std::fmod(from, pi * 2);
    Scalar toMod = std::signbit(to) ?
        pi * 2 + std::fmod(to, pi * 2) : std::fmod(to, pi * 2);

    Scalar rotation = toMod - fromMod;

    if(rotation > pi)
        return rotation - 2 * pi;
    else if(rotation < -pi)
        return 2 * pi + rotation;
    else
        return rotation;
}

Scalar is a c++ float.

Can I simplify this? I feel like this is far too much logic to do something so simple.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ std::min(std::max(from, to) - std::min(from,to), std::min(from,to) + 2*pi - std::max(from,to));? \$\endgroup\$
    – Chaosed0
    Commented Jun 25, 2014 at 18:35

2 Answers 2

4
\$\begingroup\$

To get a value inside [-pi,pi], you can add pi, do fmod, and subtract pi again. Here is one way that works. Unfortunately you still need to somehow test whether from - to is positive or negative before calling fmod:

Scalar rotationBetween(Scalar from, Scalar to)
{
    return (from > to) ? -pi + std::fmod(from - to + pi, pi * 2)
                       :  pi - std::fmod(to - from + pi, pi * 2);
}

If you are absolutely sure that from and to are in [-2pi,2pi], then this simpler version will work:

Scalar rotationBetween(Scalar from, Scalar to)
{
    std::fmod(from - to + 5 * pi, pi * 2) - pi;
}

Finally, if you just want short code and don’t care about performance, this can be interesting:

Scalar rotationBetween(Scalar from, Scalar to)
{
    return 2 * std::atan(std::tan((from - to) / 2);
}
\$\endgroup\$
0
\$\begingroup\$

I think the following should do the trick.

(Edit: as pointed out in the comments, this gives the unsigned rotation).

Scalar rotationBetween(Scalar from, Scalar to)
{
   //Calculates the two possibilities, and take the smallest
   Scalar difference = std::min(
      std::fmod(from - to, pi * 2),
      std::fmod(to - from, pi * 2)); 

   //takes the above that is in the range 0 to 2*pi 
   //and brings it in the range -pi to pi
   if (difference > pi) 
      difference -= 2 * pi;

   return difference;
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ This cannot be correct. It returns the same value for rotationBetween(a,b) and rotationBetween(b,a). \$\endgroup\$ Commented Jun 26, 2014 at 6:53
  • \$\begingroup\$ @sam But the min angle between a an b is the same angle between b and a. Look at a clock. Is the big hand / little hand different from the little hand and the big??? \$\endgroup\$ Commented Jun 26, 2014 at 7:19
  • 1
    \$\begingroup\$ They are the same in absolute value, but the sign matters, too. rotationBetween(0,1) should return 1 but it is expected that rotationBetween(1,0) returns -1. \$\endgroup\$ Commented Jun 26, 2014 at 7:28
  • \$\begingroup\$ Oh yes, that makes it trickier. \$\endgroup\$ Commented Jun 26, 2014 at 12:02

You must log in to answer this question.

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