Ответ 1
Использовать арифметику по модулю:
this.orientation += degrees;
this.orientation = this.orientation % 360;
if (this.orientation < 0)
{
this.orientation += 360;
}
Я работаю над простой процедурой поворота, которая нормализует вращение объектов между 0 и 360 градусами. Мой код на С#, кажется, работает, но я не совсем этому доволен. Может ли кто-нибудь улучшить код ниже, сделав его немного более надежным?
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;
}
}
}
Использовать арифметику по модулю:
this.orientation += degrees;
this.orientation = this.orientation % 360;
if (this.orientation < 0)
{
this.orientation += 360;
}
Это нормализуется к любому диапазону. Полезно для нормализации между [-180, 180], [0,180] или [0,360].
(это в С++ хотя)
//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 }
Для ints
//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 }
Так что в принципе то же самое, но без пола. Версия, которую я лично использую, является общей, которая работает для всех числовых типов, а также использует переопределенный пол, который ничего не делает в случае интегральных типов.
Это можно упростить до следующего.
public void Rotate (int degrees) {
this.orientation = (this.orientation + degrees) % 360;
if (this.orientation < 0) this.orientation += 360;
}
С# следует тем же правилам, что и 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
и while
. Например, результат условий в этих двух строках:
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 для вращения (и они сделают это, поверьте мне), а затем найдите, что они должны взять ранний обед, пока ваш код не мелькает: -)
Я как бы быстро высмеял это в AS3, но должен работать (вам может понадобиться +=
по углу)
private Number clampAngle(Number angle)
{
return (angle % 360) + (angle < 0 ? 360 : 0);
}
формула для переориентации круговых значений, чтобы удерживать угол между 0 и 359:
angle + Math.ceil( -angle / 360 ) * 360
обобщенная формула для ориентации угла сдвига может быть:
angle + Math.ceil( (-angle+shift) / 360 ) * 360
в котором значение сдвига представляет собой циклический сдвиг, например, я хочу, чтобы значения от -179 до 180 затем были представлены как: угол + Math.ceil((-angle-179)/360) * 360
Я предпочитаю избегать циклов, условных выражений, произвольных смещений (3600) и вызовов Math.____()
:
var degrees = -123;
degrees = (degrees % 360 + 360) % 360;
// degrees: 237
Добавьте любое кратное на 360 градусов, между которыми могут быть ваши возможные входные значения (чтобы взять их выше нуля), и просто возьмите оставшиеся с%, как это
angle = 382;
normalized_angle = (angle+3600) %360;
//result = 22
Приведенный выше пример может принимать углы ввода до -3600. Вы можете добавить любое число (кратное 360), безумно высокое, что сделало бы входное значение положительным первым.
Обычно во время анимации ваше предыдущее значение frame/step, вероятно, уже было бы нормализовано предыдущим шагом, поэтому вам будет полезно просто добавить 360:
normalized_angle = (angle+360) %360;