web-dev-qa-db-ja.com

同じ平面内の同じ原点を持つ2つの3Dベクトル間の符号付き角度

必要なのは、同じ3D平面内にあり、同じOriginを持つ2つのベクトルVaとVb間の符号付き回転角です。

  1. 両方のベクトルを含む平面は任意であり、XYまたはその他の基本平面と平行ではありません
  2. Vn-平面法線
  3. 法線と両方のベクトルは、同じ原点O = {0、0、0}を持ちます。
  4. Va-Vnで左回転を測定するための参照

角度は、平面がXY平面の場合、VaはそのX軸単位ベクトルを表すように測定する必要があります。

VaをX軸として使用し、VbとVnの外積をY軸として使用し、atan2()などの2dメソッドを使用して、一種の座標空間変換を実行する必要があると思います。何か案は?フォーミュラ?

46

2つのベクトルの外積を使用して、2つのベクトルによって形成される平面の法線を取得します。次に、その方向と元の平面法線との間の内積をチェックして、同じ方向を向いているかどうかを確認します。

angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
cross = crossProduct(Va, Vb);
if (dotProduct(Vn, cross) < 0) { // Or > 0
  angle = -angle;
}
58
msell

私が現在使用しているソリューションは、ここにはありません。平面の法線が正規化されていると仮定すると(_|Vn| == 1_)、符号付き角度は次のようになります。

atan2((Vb x Va) . Vn, Va . Vb)

範囲[-PI、+ PI](または利用可能なatan2実装が返すもの)の角度を返します。

_._とxはそれぞれドットとクロス積です。

明示的な分岐および除算/ベクトル長の計算は必要ありません。右回転には左回転ではなく_Va x Vb_を使用します

これが機能する理由の説明:_beta == alpha_または_beta == 360° - alpha_を使用して、アルファをベクトル間の直接の角度(0°から180°)、ベータを探している角度(0°から360°)にします

_Va . Vb == |Va| * |Vb| * cos(alpha)    (by definition) 
        == |Va| * |Vb| * cos(beta)     (cos(alpha) == cos(-alpha) == cos(360° - alpha)


Va x Vb == |Va| * |Vb| * sin(alpha) * n1  
    (by definition; n1 is a unit vector perpendicular to Va and Vb with 
     orientation matching the right-hand rule)

Therefore (again assuming Vn is normalized):
   n1 . Vn == 1 when beta < 180
   n1 . Vn == -1 when beta > 180

==>  (Va x Vb) . Vn == |Va| * |Vb| * sin(beta)
_

最後に

_tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)
_
33
Adrian Leonhard

これは2つの手順で実行できます。

  1. 2つのベクトル間の角度を決定する

    theta = acos(Va、Vbのドット積)。 Vaと仮定すると、Vbは正規化されます。これにより、2つのベクトル間の最小角度が得られます

  2. 角度の符号を決定する

    ベクトルV3 = Va、Vbの外積を求めます。 (順序は重要です)

    (V3の内積、Vn)が負の場合、シータは負です。それ以外の場合、シータは正です。

13
Parag

ドット積 を使用して、サインアップする角度を取得できます。角度の符号を取得するには、Vn * (Va x Vb)の符号を取ります。 XY平面の特殊なケースでは、これはただVa_x*Vb_y - Va_y*Vb_x

7
Stephen Canon

1つのベクトルを他のベクトルに交差させ、正規化して単位ベクトルを取得します。

2つのベクトル間の角度のサインは、2つのベクトルの大きさで割った外積の大きさに等しくなります。

http://mathworld.wolfram.com/CrossProduct.html

2
duffymo

Thetaをベクトル間の角度とします。 C = Vaとクロス積Vbとします。それから

sin theta = length(C)/(length(Va)* length(Vb))

シータが正か負かを判断するには、CがVaとVbに垂直であり、 右手の法則 によって決定される方向を指していることに注意してください。したがって、特にCはVnと平行です。あなたの場合、CがVnと同じ方向を指している場合、左回転が必要なため、シータは負になります。おそらく、VnとCが同じ方向を向いているかどうかをすばやくチェックする最も簡単な計算方法は、ドット積を取得することです。正の場合、それらは同じ方向を指します。

これはすべて、 外積 の基本プロパティから得られます。

1
David Norman

アドバンストカスタマーは次のソリューションを提供しました(元は質問の編集)。

SOLUTION:

sina = |Va x Vb| / ( |Va| * |Vb| )
cosa = (Va . Vb) / ( |Va| * |Vb| )

angle = atan2( sina, cosa )

sign = Vn . ( Va x Vb )
if(sign<0)
{
    angle=-angle
}
1
Peter O.

Vxがx軸であり、法線Vnが与えられた場合、クロス積でy軸を取得でき、ベクトルVbをVxおよびVyに投影できます(ドット積により、Vbの投影の長さを取得できます) VxおよびVy)、平面上の(x、y)座標が与えられると、atan2(y、x)を使用して範囲[-pi、+ pi]の角度を取得できます。

1
LittleSweet

これは、2Dまたは3Dの2つのベクトルu、v間の符号付き角度を計算するMatlabコードです。コードは自明です。符号の規則により、ixとiy([1,0,0]、[0,1,0])またはiyとiz([0,1,0]、[0、 0,1])

function thetaDEG = angDist2Vecs(u,v)

if length(u)==3
    %3D, can use cross to resolve sign
    uMod = sqrt(sum(u.^2));
    vMod = sqrt(sum(v.^2));
    uvPr = sum(u.*v);
    costheta = min(uvPr/uMod/vMod,1);

    thetaDEG = acos(costheta)*180/pi;

    %resolve sign
    cp=(cross(u,v));
    idxM=find(abs(cp)==max(abs(cp)));
    s=sign(cp(idxM(1)));
    if s < 0
        thetaDEG = -thetaDEG;
    end
elseif length(u)==2
    %2D use atan2
    thetaDEG = (atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
else
    error('u,v must be 2D or 3D vectors');
end
0
Massimo