32

我正在研究一个简单的旋转例程,它将对象旋转规范化在 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;
            }
        }
    }
4

9 回答 9

55

使用模算术:

this.orientation += degrees;

this.orientation = this.orientation % 360;

if (this.orientation < 0)
{
    this.orientation += 360;
}
于 2009-10-27T02:06:06.713 回答
41

这是一个标准化到任何范围的方法。用于在 [-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 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
}

对于整数

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

所以基本相同,但没有地板。我个人使用的版本是适用于所有数字类型的通用版本,它还使用了重新定义的地板,在整数类型的情况下不做任何事情。

于 2010-01-07T16:49:54.447 回答
22

这可以简化为以下内容。

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

C# 遵循与 C 和 C++ 相同的规则,并且angle % 360会为您提供介于-359和之间359的任何整数的值。然后第二行是确保它在包容范围0内。359

如果你想变得“聪明”,你可以把它归结为一行:

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

这将在所有情况下保持积极,但对于保存一行代码来说这是一个令人讨厌的黑客行为,所以我不会这样做,但我解释一下。

degrees % 360你会得到一个介于-359359之间的数字。添加360会将范围修改为介于1和之间719。如果orientation已经是正数,添加这将保证它仍然是,并且最终% 360将它带回范围0通过359.

至少,您可以简化代码,因为sifwhiles 可以组合。例如,这两行中条件的结果总是相同的,因此您不需要环绕if

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

因此,为此,您可以这样做:

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 进行轮换时,这一点很重要(相信我,他们会这样做),然后发现他们必须在您的代码逐渐消失时吃早午餐 :-)

于 2009-10-27T02:08:54.040 回答
16

我更喜欢避免循环、条件、任意偏移(3600)和Math.____()调用:

var degrees = -123;
degrees = (degrees % 360 + 360) % 360;
// degrees: 237
于 2017-02-22T14:32:26.210 回答
11

重新定向圆形值的公式,即保持角度在 0 到 359 之间是:

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

转换角度方向的通用公式可以是:

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

其中 shift 的值表示循环移位,例如我想要 -179 到 180 的值,那么它可以表示为:angle + Math.ceil( (-angle-179) / 360 ) * 360

于 2015-02-04T08:09:34.000 回答
9

我在 AS3 中快速模拟了这个,但应该可以工作(你可能需要+=角度)

private Number clampAngle(Number angle)
{
    return (angle % 360) + (angle < 0 ? 360 : 0);
}
于 2009-10-27T02:23:18.660 回答
1

我建议为标准化角度制作单独的功能 - 这是一个更清洁的解决方案。

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

小提琴证明此类功能按预期工作:https ://dotnetfiddle.net/Vh4CUa

然后你可以像这里一样使用它:

public void Rotate(int degrees){
    orientation = NormalizeEulerAngle(orientation + degrees);
}
于 2020-03-07T09:35:49.933 回答
-1

添加 360 度的任意倍数,您可能的输入值可能在其之间(使其高于零),然后将剩余的部分与 % 一起取,如下所示

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

上述情况可以将输入角度降至 -3600。您可以添加任意高的数字(360 的倍数),这会使输入值首先变为正数。

通常在动画期间,您之前的帧/步值可能已经被上一步标准化,所以您只需添加 360 就可以了:

normalized_angle = (angle+360) %360;
于 2014-02-09T02:10:12.633 回答
-3

将角度(度)标准化为区间 [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;
}
于 2018-09-01T17:32:27.943 回答