35

注意:この質問は、単純さ、ラジアン、度、さまざまなゼロ方位のために度で示します。問題は基本的に同じです。

回転補間の背後にあるコードについて誰かが何かアイデアを持っていますか?線形補間関数が与えられた場合:Lerp(from、to、amount)、ここで、amountは0 ... 1であり、fromとtoの間の値をamountで返します。この同じ関数を0度から360度の間の回転補間にどのように適用できますか?度が0と360の外に戻されるべきではないことを考えると。

度のこの単位円を考えると:

単位円

ここで、from=45およびto=315の場合、アルゴリズムは角度への最短経路をたどる必要があります。つまり、ゼロを通過して360に到達し、次に315に到達する必要があります。90、180、270から315までは到達しません。

これを達成するための良い方法はありますか?それとも、if()ブロックの恐ろしい混乱になるのでしょうか?私はこれを行うためのいくつかのよく理解された標準的な方法を逃していますか?どんな助けでもいただければ幸いです。

4

10 に答える 10

42

私はこれが2歳であることを知っていますが、最近同じ問題を探していて、ここにifsが投稿されていないとエレガントな解決策が見当たらないので、ここに行きます:

    shortest_angle=((((end - start) % 360) + 540) % 360) - 180;
    return shortest_angle * amount;

それでおしまい

ps:もちろん、%はモジュロを意味し、shortest_angleは補間角度全体を保持する変数です。

于 2013-01-24T10:14:52.490 に答える
13

申し訳ありませんが、少し複雑でした。より簡潔なバージョンを次に示します。

    public static float LerpDegrees(float start, float end, float amount)
    {
        float difference = Math.Abs(end - start);
        if (difference > 180)
        {
            // We need to add on to one of the values.
            if (end > start)
            {
                // We'll add it on to start...
                start += 360;
            }
            else
            {
                // Add it on to end.
                end += 360;
            }
        }

        // Interpolate it.
        float value = (start + ((end - start) * amount));

        // Wrap it..
        float rangeZero = 360;

        if (value >= 0 && value <= 360)
            return value;

        return (value % rangeZero);
    }

より最適化されたバージョンを入手した人はいますか?

于 2010-04-25T15:29:49.493 に答える
9

より良いアプローチは、sin と cos を補間することだと思います。なぜなら、それらは複数定義されている形に悩まされないからです。w = "量" とすると、w = 0 は角度 A、w = 1 は角度 B となります。

CS = (1-w)*cos(A) + w*cos(B);
SN = (1-w)*sin(A) + w*sin(B);
C = atan2(SN,CS);

必要に応じて、ラジアンと度に変換する必要があります。ブランチも調整する必要があります。atan2 の場合、C は -pi から pi の範囲に戻ります。0 から 2pi が必要な場合は、C に pi を追加するだけです。

于 2015-05-08T17:07:38.380 に答える
3

注意: C# コードを使用

頭の中をぐるぐるぐるぐる回った結果、ここにたどり着きました。基本的には0-360ラッピングはギリギリで行うことが前提です。0 ~ 360 の範囲外の値を内部的に処理し、関数から値が要求された時点で 0 ~ 360 の範囲内にラップします。

始点と終点を選択した時点で、次の操作を実行します。

float difference = Math.Abs(end - start);
if (difference > 180)
{
    // We need to add on to one of the values.
    if (end > start)
    {
        // We'll add it on to start...
        start += 360;
    }
    else
    {
        // Add it on to end.
        end += 360;
    }
}

これにより、実際の開始値と終了値が得られますが、これは 0 ~ 360 の範囲外である可能性があります...

値が 0 から 360 の間であることを確認するためのラップ関数があります...

public static float Wrap(float value, float lower, float upper)
{
    float rangeZero = upper - lower;

    if (value >= lower && value <= upper)
        return value;

    return (value % rangeZero) + lower;
}

次に、関数から現在の値を要求する時点で:

return Wrap(Lerp(start, end, amount), 0, 360);

これは、問題に対する最適な解決策ではないことはほぼ確実ですが、一貫して機能しているように見えます。誰かがこれを行うためのより最適な方法を持っているなら、それは素晴らしいことです.

于 2010-04-25T15:16:42.773 に答える
2

質問への回答をよりよく説明するために、回答を書き直したいと思いました。数式には EXCEL を使用し、単位には学位を使用しています。

簡単にするために、Bは 2 つの値の大きい方で、 は 2 つの値Aの小さい方です。後でソリューションでMAX()と をそれぞれ使用できます。MIN()

パート 1 - 行く方法は?

最初にやりたいことは、時計回りまたは反時計回りのどちらの方向に計算を実行するかを決定することです。IF()そのためにステートメントを使用します。

IF( (B-A)<=180, (Clockwise_Formula), (AntiClockwise_Formula) )

上記の式は、 から反時計回り ( から からB時計回りにA進むのと同じ) が 180 度以下かどうかをチェックします。そうでない場合は、反対方向に移動する方が短くなります。AB

これを確認するには: 90 - 45 = 45 (180 以下) は IF ステートメントを TRUE にするため、時計回りの方向は短くなりますが、315 - 45 = 270 (180 より大きい) は if ステートメントを作成します。 FALSE の場合、反時計回りの式は短くなります。

パート 2 - 時計回りの式

ここで、とNの間の時間を時計回りまたは反時計回りに補間します。時計回りの式は比較的単純です。AB

Clockwise_Formula: ((B-A)/N*S)+A

Sは 1 から始まり N-1 で終わる補間回数のカウントです ( の場合、S = N答えは になりますB)

例: A= 90、B= 270、N= 4

S=1:     ((270-90)/4*1)+90 = 135
S=2:     ((270-90)/4*2)+90 = 180
S=3:     ((270-90)/4*3)+90 = 225

パート 3 - 反時計回りの式

反時計回りの式は、360 度の角度を反時計回りに交差する必要があるため、もう少し複雑になります。私が考えることができる最も簡単な方法は、 に 360 を追加し、関数Aを使用して答えを 360 で変調することです。MOD(FORMULA,VALUE)

が最小の数になったため、式を入れ替える必要もありますA。(少し混乱するかもしれませんが、うまくいきます!)BB

(Unmodulated) AntiClockwise_Formula: (((A+360)-B)/N*S)+B

例: A= 60、B= 300、N= 4

S=1:     (((60+360)-300)/4*1)+300 = 330
S=2:     (((60+360)-300)/4*2)+300 = 360
S=3:     (((60+360)-300)/4*3)+300 = 390

パート 4 - 回答を 0 ~ 360 に制限する

答えが 360 よりも大きくなることがあります (ただし、常にではありません)。ここで、Anticlockwise_formula をMOD()関数でラップします。

AntiClockwise_Formula: MOD((((A+360)-B)/N*S)+B,360)

パート 3 で使用した例を変更すると、次のようになります。

S=1:     330
S=2:     0
S=3:     30

パート 5 - すべてをまとめる

パート 1 ~ 4 のすべての要素を組み合わせると、答えは次のようになります。

IF((B-A)<=180,((B-A)/N*S)+A,MOD((((A+360)-B)/N*S)+B,360))

どこ:

A= 2 つの値の小さい方 (A を MIN() に置き換えることができます)

B= 2 つの値のうち大きい方 (B を MAX() に置き換えることができます)

N= 実行する補間の数 (例: 2 は半分、3 は 3 分の 1 など)

S= 最大 N-1 までの増分カウント (説明についてはパート 2 を参照)

于 2014-09-06T08:38:08.343 に答える
0

角度を処理するための私の好ましい方法は、1回転あたり2の累乗である単位を使用することです。たとえば、16ビットの符号付き整数を使用して-180〜 + 180度を表す場合、(from-to)/num_stepsを使用して補間を行うことができます。360から0に移動した時点でバイナリ値がオーバーフローするため、角度の加算と減算は常に機能します。

あなたの場合におそらくやりたいことは、360を法とする数学です。したがって、角度の差は(from-to)%360として計算されます。他のSOの質問で対処されたものには、まだいくつかの兆候の問題があります。

于 2010-04-25T14:23:26.927 に答える
0

user151496 の回答の修正 (元は度単位であり、間違った出力が得られました):

 def interp_angle(theta_1, theta_2, ratio):
    shortest_angle = ((((theta_2 - theta_1) % (np.pi*2)) + np.pi) % (np.pi*2)) - np.pi
    return (theta_1 + shortest_angle * ratio) % (np.pi*2)

テスト: で実行

theta1, theta2 = 0, 0.5
print('Average of {:.4g}pi rad and {:.4g}pi rad = {:.4g}pi rad'.format(theta1, theta2, interp_angle(theta1*np.pi, theta2*np.pi, 0.5)/np.pi))
theta1, theta2 = 0, 0.99
print('Average of {:.4g}pi rad and {:.4g}pi rad = {:.4g}pi rad'.format(theta1, theta2, interp_angle(theta1*np.pi, theta2*np.pi, 0.5)/np.pi))
theta1, theta2 = 0, 1.01
print('Average of {:.4g}pi rad and {:.4g}pi rad = {:.4g}pi rad'.format(theta1, theta2, interp_angle(theta1*np.pi, theta2*np.pi, 0.5)/np.pi))
theta1, theta2 = 0.1, -0.1
print('Average of {:.4g}pi rad and {:.4g}pi rad = {:.4g}pi rad'.format(theta1, theta2, interp_angle(theta1*np.pi, theta2*np.pi, 0.5)/np.pi))
theta1, theta2 = 0.1, 2-0.1
print('Average of {:.4g}pi rad and {:.4g}pi rad = {:.4g}pi rad'.format(theta1, theta2, interp_angle(theta1*np.pi, theta2*np.pi, 0.5)/np.pi))

私に与えます:

Average of 0pi rad and 0.5pi rad = 0.25pi rad
Average of 0pi rad and 0.99pi rad = 0.495pi rad
Average of 0pi rad and 1.01pi rad = 1.505pi rad
Average of 0.1pi rad and -0.1pi rad = 0pi rad
Average of 0.1pi rad and 1.9pi rad = 0pi rad
于 2019-11-28T01:02:44.787 に答える