Normalise orientation between 0 and 360

前端 未结 9 1603
不知归路
不知归路 2021-01-30 10:58

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 a

相关标签:
9条回答
  • 2021-01-30 11:34

    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);
    }
    
    0 讨论(0)
  • 2021-01-30 11:34

    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;
    }
    
    0 讨论(0)
  • 2021-01-30 11:36

    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 :-)

    0 讨论(0)
  • 2021-01-30 11:38

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

    var degrees = -123;
    degrees = (degrees % 360 + 360) % 360;
    // degrees: 237
    
    0 讨论(0)
  • 2021-01-30 11:39

    Use modulo arithmetic:

    this.orientation += degrees;
    
    this.orientation = this.orientation % 360;
    
    if (this.orientation < 0)
    {
        this.orientation += 360;
    }
    
    0 讨论(0)
  • 2021-01-30 11:42

    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.

    0 讨论(0)
提交回复
热议问题