31

I'm working on a simple rotate routine which normalizes an objects rotation between 0 and 360 degrees. My C# code seems to be working but I'm not entirely happy with it. Can anyone improve on the code below making it a bit more robust?

public void Rotate(int degrees)
    {
        this.orientation += degrees;

        if (this.orientation < 0)
        {
            while (this.orientation < 0)
            {
                this.orientation += 360;
            }
        }
        else if (this.orientation >= 360)
        {
            while (this.orientation >= 360)
            {
                this.orientation -= 360;
            }
        }
    }
Damjan Pavlica
  • 21,431
  • 6
  • 55
  • 65
JuniorDeveloper
  • 351
  • 1
  • 4
  • 5
  • Just for anybody else who stumbles upon these magic solutions. I have tested several of these for true 0-360 values out. Using a gyro that can give values in the +/- many thousands range enzuguri's solution achieves this !! Saad Ahmed and QBziZ's solution yield -10 for an input of -10; not the 350 I would expect. Hope this is helpful JC – jc508 Oct 27 '19 at 07:04

9 Answers9

53

Use modulo arithmetic:

this.orientation += degrees;

this.orientation = this.orientation % 360;

if (this.orientation < 0)
{
    this.orientation += 360;
}
tvanfosson
  • 490,224
  • 93
  • 683
  • 780
  • Can you get a negative result from modulous? Floating point issue? – Swanny Oct 27 '09 at 02:31
  • C gives you negatives, I'm assuming C# does the same. And, since degrees is an int, you're limited to integer rotations. It's not explicitly spelled out that orientation is an int but it would be strange if it wasn't. – paxdiablo Oct 27 '09 at 02:35
  • 1
    Oh. So you can. -1 % 360 is negative 1. I expected 359. Silly me. You learn something every day. – Swanny Oct 27 '09 at 02:39
  • 1
    (angle+3600) %360 (it allows 10 whole negative revolutions as input) – sergio Feb 09 '14 at 02:20
35

This is one that normalizes to any range. Useful for normalizing between [-180,180], [0,180] or [0,360].

( it's in C++ though )

// Normalizes any number to an arbitrary range 
// by assuming the range wraps around when going below min or above max 
double normalize( const double value, const double start, const double end ) 
{
  const double width       = end - start   ;   // 
  const double offsetValue = value - start ;   // value relative to 0

  return ( offsetValue - ( floor( offsetValue / width ) * width ) ) + start ;
  // + start to reset back to start of original range
}

For ints

// Normalizes any number to an arbitrary range 
// by assuming the range wraps around when going below min or above max 
int normalize( const int value, const int start, const int end ) 
{
  const int width       = end - start   ;   // 
  const int offsetValue = value - start ;   // value relative to 0

  return ( offsetValue - ( ( offsetValue / width ) * width ) ) + start ;
  // + start to reset back to start of original range
}

So basically the same but without the floor. The version I personally use is a generic one that works for all numeric types and it also uses a redefined floor that does nothing in case of integral types.

ceztko
  • 13,391
  • 2
  • 44
  • 64
QBziZ
  • 2,888
  • 20
  • 24
  • 3
    This function is amazing, solved the "normalize angle problem" once and for all! :) leads to, if you just want to normalize in [0; 2*PI[ an angle: angle -= Math.Floor(angle/(2*Math.PI))*2*Math.PI; – Exceptyon May 11 '13 at 13:04
  • The function with the doubles is not handling very small figures well. For instance enter normalise( -1.0E-15, 0.0, 360.0), the outcome will be 360.0. the outcome should be smaller than 360 imo. – Richy Jun 26 '20 at 05:44
  • What you say is true but very trivial. You should not confuse floating point arithmetic with actual analytical mathematics. Floating point on the most basic level is : here’s a finite bunch of numbers, with them, you have to try to get to the analytical solution with the least amount of error. In a bit more detail, floating point numbers are rational numbers with a finite set of numerators and denominators. It all depends on your application domain because no function will ever be perfect for the real world. In my case, such small numbers can be considered as such if the range is that big. – QBziZ Jun 27 '20 at 09:58
21

This can be simplified to the following.

public void Rotate (int degrees) {
    this.orientation = (this.orientation + degrees) % 360;
    if (this.orientation < 0) this.orientation += 360;
}

C# follows the same rules as C and C++ and i % 360 will give you a value between -359 and 359 for any integer, then the second line is to ensure it's in the range 0 through 359 inclusive.

If you wanted to be shifty, you could get it down to one line:

    this.orientation = (this.orientation + (degrees % 360) + 360) % 360;

which would keep it positive under all conditions but that's a nasty hack for saving one line of code, so I wouldn't do it, but I will explain it.

From degrees % 360 you will get a number between -359 and 359. Adding 360 will modify the range to between 1 and 719. If orientation is already positive, adding this will guarantee it still is, and the final % 360 will bring it back to the range 0 through 359.

At a bare minimum, you could simplify your code since the ifs and whiles can be combined. For example, the result of the conditions in these two lines:

if (this.orientation < 0)
while (this.orientation < 0)

is always the same, hence you don't need the surrounding if.

So, to that end, you could do:

public void Rotate (int degrees) {
    this.orientation += degrees;
    while (this.orientation <   0) this.orientation += 360;
    while (this.orientation > 359) this.orientation -= 360;
}

but I'd still go for the modulus version since it avoids loops. This will be important when a user enters 360,000,000,000 for the rotation (and they will do this, believe me) and then find they have to take an early lunch while your code grinds away :-)

paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841
  • 1
    I´d say `this.orientation = (this.orientation + degrees + 360) % 360;` is easier to read. – jensgram Oct 27 '09 at 12:38
  • 2
    @jensgram, the problem with that one is that it can *still* give you a number in the range -359 through -1 if, for example, orientation is 0 and degrees is -719, giving -359. You have to normalize degrees first then force it to positive. – paxdiablo Oct 27 '09 at 13:21
  • @paxdiablo, you're right! I didn't anticipate `degrees` to be outside +/-359. – jensgram Oct 27 '09 at 14:28
14

I prefer to avoid loops, conditionals, arbitrary offsets (3600), and Math.____() calls:

var degrees = -123;
degrees = (degrees % 360 + 360) % 360;
// degrees: 237
Ronnie Overby
  • 41,852
  • 68
  • 257
  • 338
11

formula for re-orienting circular values i.e to keep angle between 0 and 359 is:

angle + Math.ceil( -angle / 360 ) * 360

generalized formula for shifting angle orientation can be:

angle + Math.ceil( (-angle+shift) / 360 ) * 360

in which value of shift represent circular shift for e.g I want values in -179 to 180 then it can be represented as: angle + Math.ceil( (-angle-179) / 360 ) * 360

Saad Ahmed
  • 1,069
  • 9
  • 9
  • This is the simplest answer that works on fractions and negative numbers. Works also with arbitrary large numbers without loops. – Danon Jun 14 '16 at 17:27
9

I sort of quickly mocked this up in AS3, but should work (you may need += on the angle)

private Number clampAngle(Number angle)
{
    return (angle % 360) + (angle < 0 ? 360 : 0);
}
enzuguri
  • 788
  • 4
  • 9
1

I'd recommend making separate function for normalizing angle - it's a cleaner solution.

public static float NormalizeEulerAngle(float angle){
    var normalized = angle % 360;
    if(normalized < 0)
        normalized += 360;
    return normalized;
}

Fiddle proving that such function works as intended: https://dotnetfiddle.net/Vh4CUa

And then you can use it like here:

public void Rotate(int degrees){
    orientation = NormalizeEulerAngle(orientation + degrees);
}
Kris Krej
  • 59
  • 4
-1

Add any multiple of 360 degrees between which your possible input values could be (to take it above zero), and just take the remaining with %, like this

angle = 382;
normalized_angle = (angle+3600) %360;
//result = 22

The case above can take input angles down to -3600. You can add any number (multiple of 360) crazily high that would make the input value positive first.

Usually during an animation, your previous frame/step value would probably be already normalized by the previous step, so you'll be good to go by just adding 360:

normalized_angle = (angle+360) %360;
sergio
  • 877
  • 1
  • 11
  • 14
-3

Function that comes handy when normalizing angles (degrees) into interval [0, 360> :

float normalize_angle(float angle)
{
    float k = angle;

    while(k < 0.0)
        k += 360.0;
    while(k >= 360.0)
        k -= 360.0;
    return k;
}
Emil
  • 1
  • 2