Нормализация ориентации между 0 и 360 - PullRequest
21 голосов
/ 27 октября 2009

Я работаю над простой процедурой поворота, которая нормализует поворот объектов между 0 и 360 градусами. Мой код C #, кажется, работает, но я не совсем доволен этим. Может ли кто-нибудь улучшить приведенный ниже код, сделав его более надежным?

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;
            }
        }
    }

Ответы [ 8 ]

43 голосов
/ 27 октября 2009

Использовать арифметику по модулю:

this.orientation += degrees;

this.orientation = this.orientation % 360;

if (this.orientation < 0)
{
    this.orientation += 360;
}
29 голосов
/ 07 января 2010

Это тот, который нормализуется к любому диапазону. Полезно для нормализации между [-180,180], [0,180] или [0,360].

(хотя в C ++)

//Normalizes any number to an arbitrary range 
//by assuming the range wraps around when going below min or above max 
double normalise( 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
}

Для целых

//Normalizes any number to an arbitrary range 
//by assuming the range wraps around when going below min or above max 
int normalise( 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
}

Так что в основном то же самое, но без пола. Я лично использую версию, которая работает для всех числовых типов, а также использует переопределенный пол, который ничего не делает в случае целочисленных типов.

18 голосов
/ 27 октября 2009

Это можно упростить до следующего.

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

C # следует тем же правилам, что и C и C ++, и i % 360 даст вам значение от -359 до 359 для любого целого числа, а вторая строка должна убедиться, что оно находится в диапазоне от 0 до 359 включительно.

Если вы хотите быть бегущим, вы можете поместить его в одну строку:

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

, который сохранит положительный результат при любых условиях, но это неприятный хак для сохранения одной строки кода, поэтому я бы не стал этого делать, но я объясню .

С degrees % 360 вы получите число от -359 до 359. Добавление 360 изменит диапазон между 1 и 719. Если orientation уже положительно, добавление этого гарантирует, что оно все еще остается, и окончательное значение % 360 вернет его в диапазон от 0 до 359.

При минимальном минимальном вы можете упростить свой код, поскольку if s и while s можно комбинировать. Например, результат условий в этих двух строках:

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

равно всегда одно и то же, следовательно, вам не нужно окружающее if.

Итак, для этого вы можете сделать:

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

но я бы все же выбрал версию модуля, поскольку она избегает циклов. Это будет важно, когда пользователь вводит 360 000 000 000 для ротации (и они будут делать это, поверьте мне), а затем обнаруживает, что ему нужно рано пообедать, пока ваш код размалывает: -)

9 голосов
/ 04 февраля 2015

формула для переориентации круговых значений, т. Е. Для поддержания угла между 0 и 359:

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

Обобщенная формула для сдвига угла ориентации может быть:

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

, в котором значение смещения представляет круговое смещение, например, если я хочу значения от -179 до 180, то оно может быть представлено как: угол + Math.ceil ((-angle-179) / 360) * 360

8 голосов
/ 27 октября 2009

Я довольно быстро смоделировал это в AS3, но должно работать (вам может понадобиться += на углу)

private Number clampAngle(Number angle)
{
    return (angle % 360) + (angle < 0 ? 360 : 0);
}
4 голосов
/ 22 февраля 2017

Я предпочитаю избегать циклов, условных выражений, произвольных смещений (3600) и Math.____() вызовов:

var degrees = -123;
degrees = (degrees % 360 + 360) % 360;
// degrees: 237
0 голосов
/ 01 сентября 2018

Функция, которая пригодится при нормализации углов (градусов) в интервале [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 голосов
/ 09 февраля 2014

Добавьте любое кратное 360 градусов, между которыми могут быть ваши возможные входные значения (чтобы оно было выше нуля), и просто возьмите оставшееся с%, как это

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

Приведенный выше случай может уменьшить входные углы до -3600. Вы можете добавить любое число (кратное 360) безумно высокого значения, которое сначала сделает входное значение положительным.

Обычно во время анимации ваше предыдущее значение кадра / шага, вероятно, уже нормализуется к предыдущему шагу, поэтому вам будет полезно просто добавить 360:

normalized_angle = (angle+360) %360;
...