角度を正規化する

オイラー角でよく使うけど忘れがちな計算のまとめ。

0~360°に正規化する

480°→120°
-10°→350°

負数にも対応しており、例えば-10の場合は戻り値は350となる。

// 指定された角度を 0 ~ 2π の間の値に狭める(単位:ラジアン)
function float RepeatAngle(float angle;)
{
    float result = angle % (PI * 2);
    if(result < 0) result += PI * 2;
    return result;
}

float angle = -10;
angle = RepeatAngle(radians(angle));
printf('angle:' + sprintf('%g', degrees(angle)) + '\n');
// 350

-180~180°に正規化する

190°→-170°
-320°→40°

360°をまたいだ角度の引き算をする場合に有効。たとえば、30°から350°を引く場合に答えを-320°ではなく、40°に正規化したい。

// 指定された角度を π ~ -π の間の値に狭める(単位:ラジアン)
function float WrapAngle(float angle;)
{
    float result = (angle + PI) % (PI * 2);
    if(result < 0) result += PI * 2;
    result -= PI;
    return result;
}

float angle = WrapAngle(radians(30 - 350));
printf('angle:' + sprintf('%g', degrees(angle)) + '\n');
// 40

180°で折り返しながら360°で循環させるため、180°足してから0~360°の範囲に収め、再度180°を引いて元の角度に戻している。

ベクトルの成す角を0~360°に正規化する

XZ平面にてベクトル同士の成す角度を内積とアークコサインで計算すると、0~180°に収まるが、これを0~360°にしたい場合、外積の符号を使う。ポイントを時計回りの順でソートしたい場合などに便利。

X軸方向のベクトル(1,0,0)に対する各ポイントの角度。

//
// XZ平面で0~360°に正規化する
// Run Over: Detail
//

float crossY(vector v0; vector v1;)
{
    return(v0.z * v1.x - v1.z * v0.x);
}

for(int i = 0; i < npoints(0); i++)
{
    vector pos = point(0, "P", i);
    float angle = acos(dot(normalize(pos), set(1,0,0)));
    
    // 外積のY成分で比較
    float y = crossY(normalize(pos), set(1,0,0));
    // 負の場合は補角を取る
    if(y < 0)
        angle = 2*PI - angle;
    
    setpointattrib(0, "__angle", i, degrees(angle));
}

タイトルとURLをコピーしました