私は、オブジェクトの回転を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;
}
}
}
モジュロ演算を使用します。
this.orientation += degrees;
this.orientation = this.orientation % 360;
if (this.orientation < 0)
{
this.orientation += 360;
}
これは、任意の範囲に正規化するものです。 [-180,180]、[0,180]、または[0,360]間の正規化に役立ちます。
(C++にもありますが)
//任意の数値を任意の範囲に正規化します //範囲が最小値を下回るか最大値を超えると折り返されると想定して double normalise(const double value、const double start、 const double end) { const double width = end-start; // const double offsetValue = value-start; // 0を基準とした値 return(offsetValue-(floor(offsetValue/width)* width))+ start; // +リセットを開始して元の開始に戻す範囲 }
Intの場合
//任意の数値を任意の範囲に正規化します //範囲が最小値を下回るか最大値を超えると折り返されると想定して int normalise(const int value、const int start、 const int end) { const int width = end-start; // const int offsetValue = value-start; // 0を基準にした値 return(offsetValue-((offsetValue/width)* width))+ start; // +リセットして、元の範囲の開始点に戻る }
基本的には同じですが、床はありません。私が個人的に使用しているバージョンは、すべての数値タイプで機能する汎用バージョンであり、整数タイプの場合は何もしない再定義されたフロアも使用しています。
これは次のように簡略化できます。
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
の間の値を提供します。2行目は0〜359の範囲にあることを確認します包括的。
シフティになりたい場合は、1行に減らすことができます。
this.orientation = (this.orientation + (degrees % 360) + 360) % 360;
これはすべての状況下でそれをポジティブに保ちますが、それはコードの1行を保存するための厄介なハックなので、私はそれをしませんが、私はwill説明します。
degrees % 360
からは、-359
から359
までの数字が表示されます。 360
を追加すると、範囲が1
から719
の範囲に変更されます。 orientation
がすでに正の値である場合、これを追加するとそれがまだ有効であることを保証し、最後の% 360
は0
から359
の範囲に戻します。
最小限の最小では、if
sとwhile
sを組み合わせることができるため、コードを簡略化できます。たとえば、次の2行の条件の結果:
if (this.orientation < 0)
while (this.orientation < 0)
is always同じなので、周囲のif
は必要ありません。
したがって、そのためには、次のようにします。
public void Rotate (int degrees) {
this.orientation += degrees;
while (this.orientation < 0) this.orientation += 360;
while (this.orientation > 359) this.orientation -= 360;
}
しかし、ループを回避するため、モジュラスバージョンではstillを使用します。これは、ユーザーがローテーションに360,000,000,000を入力すると(そしてwillこれを実行して、私を信じて)、コードがすり減るときに早めの昼食を取る必要があることがわかります:-)
円形の値の向きを変更するための式、つまり角度を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
私はこれをAS3ですばやくモックアップしましたが、動作するはずです(角度に+=
が必要になる場合があります)。
private Number clampAngle(Number angle)
{
return (angle % 360) + (angle < 0 ? 360 : 0);
}
ループ、条件、任意のオフセット(3600)、およびMath.____()
の呼び出しは避けたい:
var degrees = -123;
degrees = (degrees % 360 + 360) % 360;
// degrees: 237
角度(度)を間隔[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;
}
次のように、可能な入力値が可能な360度の倍数を追加し(ゼロより上にするため)、残りを%で取ります。
angle = 382;
normalized_angle = (angle+3600) %360;
//result = 22
上記のケースでは、入力角度を-3600まで下げることができます。入力値を最初に正にする、非常に高い任意の数(360の倍数)を追加できます。
通常、アニメーション中は、前のフレーム/ステップの値はおそらく前のステップですでに正規化されているため、360を追加するだけで十分です。
normalized_angle = (angle+360) %360;
角度を正規化するために別の関数を作成することをお勧めします。これはよりクリーンなソリューションです。
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);
}