views:

246

answers:

4

Hello, NB: I'll present this question in degrees purely for simplicity, radians, degrees, different zero-bearing, the problem is essentially the same.

Does anyone have any ideas on the code behind rotational interpolation? Given a linear interpolation function: Lerp(from, to, amount), where amount is 0...1 which returns a value between from and to, by amount. How could I apply this same function to a rotational interpolation between 0 and 360 degrees? Given that degrees should not be returned outside 0 and 360.

Given this unit circle for degrees:

Unit Circle

where from = 45 and to = 315, the algorithm should take the shortest path to the angle, i.e. it should go through zero, to 360 and then to 315 - and not all the way round 90, 180, 270 to 315.

Is there a nice way to achieve this? Or is it going to just be a horrid mess of if() blocks? Am I missing some well understood standard way of doing this? Any help would be appreciated.

+2  A: 

My preferred way to deal with angle is to use units that are a power of 2 per revolution. For exanple, it you use 16 bit signed integers to represent -180 to +180 degrees, you can simply take (from-to)/num_steps to do your interpolation. Adding and subtracting angles always works, as the binary values overflow right at the point where you go from 360 to 0.

What you probably want to do in your case is math modulo 360. So angle differences are computed as (from-to)%360. There are still some sign issues with that which have been addressed in other SO questions.

phkahler
+1  A: 

NB: using C# code

After some crazy rummaging around in my brain, here's what I've come up with. Basically the premise is to perform the 0-360 wrapping at the last minute. Deal internally with values outside 0-360 and then wrap them inside 0-360 at the point a value is requested from the function.

At the point where you pick a start an an end point, you perform the following:

        float difference = Math.Abs(end - start);
        if (difference > 180)
        {
            // We need to add on to one of the values.
            if (end > start)
            {
                // We'll add it on to start...
                start += 360;
            }
            else
            {
                // Add it on to end.
                end += 360;
            }
        }

This gives you the actual start and end values, which may be outside 0-360...

We have a wrap function to ensure a value is between 0 and 360...

public static float Wrap(float value, float lower, float upper)
{
    float rangeZero = upper - lower;

    if (value >= lower && value <= upper)
        return value;

    return (value % rangeZero) + lower;
}

Then at the point you request the current value from the function:

return Wrap(Lerp(start, end, amount), 0, 360);

This is almost certainly not the most optimal solution to the problem, however it does appear to work consistently. If anyone has any more optimal way to do this that would be great.

Rob
+1  A: 

Sorry, that was a bit convoluted, here's a more concise version:

    public static float LerpDegrees(float start, float end, float amount)
    {
        float difference = Math.Abs(end - start);
        if (difference > 180)
        {
            // We need to add on to one of the values.
            if (end > start)
            {
                // We'll add it on to start...
                start += 360;
            }
            else
            {
                // Add it on to end.
                end += 360;
            }
        }

        // Interpolate it.
        float value = (start + ((end - start) * amount));

        // Wrap it..
        float rangeZero = 360;

        if (value >= 0 && value <= 360)
            return value;

        return (value % rangeZero);
    }

Anyone got a more optimised version?

Rob
A: 

Thank you very much, Rob!

Michael