Ich möchte den Winkel im Uhrzeigersinn zwischen 2 Vektoren (2D, 3D) herausfinden.
Der klassische Weg mit dem Punktprodukt gibt mir den Innenwinkel (0-180 Grad) und ich muss einige if-Anweisungen verwenden, um zu bestimmen, ob das Ergebnis der Winkel ist, den ich brauche, oder dessen Komplement.
Kennen Sie eine direkte Methode zur Berechnung des Winkels im Uhrzeigersinn?
std::atan2()
?Antworten:
2D-Fall
So wie das Punktprodukt proportional zum Kosinus des Winkels ist, ist die Determinante proportional zu seinem Sinus. Sie können den Winkel also folgendermaßen berechnen:
dot = x1*x2 + y1*y2 # dot product between [x1, y1] and [x2, y2] det = x1*y2 - y1*x2 # determinant angle = atan2(det, dot) # atan2(y, x) or atan2(sin, cos)
Die Ausrichtung dieses Winkels entspricht der des Koordinatensystems. In einem linkshändigen Koordinatensystem , dh x zeigt nach rechts und y nach unten, wie es für Computergrafiken üblich ist, bedeutet dies, dass Sie ein positives Vorzeichen für Winkel im Uhrzeigersinn erhalten. Wenn die Ausrichtung des Koordinatensystems mit y nach oben mathematisch ist, erhalten Sie Winkel gegen den Uhrzeigersinn, wie es in der Mathematik üblich ist. Durch Ändern der Reihenfolge der Eingaben wird das Vorzeichen geändert. Wenn Sie mit den Vorzeichen nicht zufrieden sind, tauschen Sie einfach die Eingaben aus.
3D-Fall
In 3D definieren zwei beliebig platzierte Vektoren ihre eigene Rotationsachse senkrecht zu beiden. Diese Drehachse hat keine feste Ausrichtung, was bedeutet, dass Sie die Richtung des Drehwinkels auch nicht eindeutig festlegen können. Eine übliche Konvention besteht darin, Winkel immer positiv sein zu lassen und die Achse so auszurichten, dass sie zu einem positiven Winkel passt. In diesem Fall reicht das Punktprodukt der normalisierten Vektoren aus, um Winkel zu berechnen.
dot = x1*x2 + y1*y2 + z1*z2 #between [x1, y1, z1] and [x2, y2, z2] lenSq1 = x1*x1 + y1*y1 + z1*z1 lenSq2 = x2*x2 + y2*y2 + z2*z2 angle = acos(dot/sqrt(lenSq1 * lenSq2))
Ebene in 3D eingebettet
Ein Sonderfall ist der Fall, in dem Ihre Vektoren nicht willkürlich platziert werden, sondern in einer Ebene mit einem bekannten Normalenvektor n liegen . Dann liegt die Rotationsachse ebenfalls in Richtung n , und die Ausrichtung von n legt eine Ausrichtung für diese Achse fest. In diesem Fall können Sie die obige 2D-Berechnung, einschließlich n, in die Determinante anpassen , um ihre Größe 3 × 3 zu erhalten.
dot = x1*x2 + y1*y2 + z1*z2 det = x1*y2*zn + x2*yn*z1 + xn*y1*z2 - z1*y2*xn - z2*yn*x1 - zn*y1*x2 angle = atan2(det, dot)
Eine Bedingung dafür ist, dass der normale Vektor n eine Längeneinheit hat. Wenn nicht, müssen Sie es normalisieren.
Als dreifaches Produkt
Diese Determinante könnte auch als dreifaches Produkt ausgedrückt werden , wie @Excrubulent in einer vorgeschlagenen Bearbeitung hervorhob .
Dies ist möglicherweise in einigen APIs einfacher zu implementieren und bietet eine andere Perspektive auf das, was hier vor sich geht: Das Kreuzprodukt ist proportional zum Sinus des Winkels und liegt senkrecht zur Ebene, also ein Vielfaches von n . Das Punktprodukt misst daher grundsätzlich die Länge dieses Vektors, jedoch mit dem richtigen Vorzeichen.
quelle
qAtan2(y, x)
(aus dem Qt-Framework) ist, aber wenn jemand das gleiche Problem wie ich hat, könnte dies helfen.atan2
normalerweise im Bereich [-180 °, 180 °]. Um [0 °, 360 °] ohne Fallunterscheidung kann man ersetzenatan2(y,x)
mitatan2(-y,-x) + 180°
.Um den Winkel zu berechnen, müssen Sie nur den
atan2(v1.s_cross(v2), v1.dot(v2))
2D-Fall aufrufen . Wos_cross
ist das skalare Analogon der Kreuzproduktion (signierter Bereich des Parallelogramms)? Für 2D-Fälle wäre das Keilproduktion. Für 3D-Fälle müssen Sie die Drehung im Uhrzeigersinn definieren, da von einer Seite der Ebene im Uhrzeigersinn eine Richtung und von der anderen Seite der Ebene eine andere Richtung ist =)Bearbeiten: Dies ist der Winkel gegen den Uhrzeigersinn, der Winkel im Uhrzeigersinn ist genau entgegengesetzt
quelle
atan2f
die y-Koordinate das erste Argument ist, also sollte es so seinangle = atan2f(v2.y, v2.x) - atan2f(v1.y, v1.x)
.cross
unddot
durch die explizite Formel im 2D-Fall alle Zweifel an dercross
Rückgabe eines 3D-Vektors beseitigen (aber das ist nur ein Vorschlag, den Sie ignorieren können). - Ansonsten gefällt mir diese Lösung, weil sie nur einenatan2f
Funktionsaufruf erfordert .Diese Antwort ist dieselbe wie die von MvG, erklärt sie jedoch anders (sie ist das Ergebnis meiner Bemühungen, zu verstehen, warum die Lösung von MvG funktioniert). Ich poste es mit der Möglichkeit, dass andere es hilfreich finden.
Der Winkel gegen den Uhrzeigersinn
theta
vonx
bisy
in Bezug auf den Blickwinkel ihrer gegebenen Normalenn
(||n|| = 1
) ist gegeben durchwobei
||x||
die Größe von bezeichnetx
.Schritt (1) folgt mit der Feststellung, dass
und so
was gleich ist
wenn
||n|| = 1
.Schritt (2) folgt aus der Definition von
atan2
, wobei zu beachten istatan2(cy, cx) = atan2(y,x)
, woc
ein Skalar ist. Schritt (3) folgt aus der Definition vonatan2
. Schritt (4) folgt aus den geometrischen Definitionen voncos
undsin
.quelle
Mit dem Skalarprodukt (Punktprodukt) zweier Vektoren können Sie den Cosinus des Winkels zwischen ihnen ermitteln. Um die 'Richtung' des Winkels zu erhalten, sollten Sie auch das Kreuzprodukt berechnen, mit dem Sie überprüfen können (über die z-Koordinate), ob der Winkel im Uhrzeigersinn ist oder nicht (dh ob Sie ihn aus 360 Grad extrahieren oder nicht).
quelle
Für eine 2D-Methode können Sie das Kosinusgesetz und die Richtungsmethode verwenden.
Berechnung des Winkels des Segments P3: P1 im Uhrzeigersinn zum Segment P3: P2.
double d = direction(x3, y3, x2, y2, x1, y1); // c int d1d3 = distanceSqEucl(x1, y1, x3, y3); // b int d2d3 = distanceSqEucl(x2, y2, x3, y3); // a int d1d2 = distanceSqEucl(x1, y1, x2, y2); //cosine A = (b^2 + c^2 - a^2)/2bc double cosA = (d1d3 + d2d3 - d1d2) / (2 * Math.sqrt(d1d3 * d2d3)); double angleA = Math.acos(cosA); if (d > 0) { angleA = 2.*Math.PI - angleA; } This has the same number of transcendental
Operationen als obige Vorschläge und nur eine weitere Gleitkommaoperation.
Die verwendeten Methoden sind:
public int distanceSqEucl(int x1, int y1, int x2, int y2) { int diffX = x1 - x2; int diffY = y1 - y2; return (diffX * diffX + diffY * diffY); } public int direction(int x1, int y1, int x2, int y2, int x3, int y3) { int d = ((x2 - x1)*(y3 - y1)) - ((y2 - y1)*(x3 - x1)); return d; }
quelle
Wenn Sie mit "direktem Weg" das Vermeiden der
if
Aussage meinen , dann glaube ich nicht, dass es eine wirklich allgemeine Lösung gibt.Wenn Ihr spezifisches Problem jedoch den Verlust einer gewissen Genauigkeit bei der Winkeldiskretisierung zulässt und Sie einige Zeit bei der Typkonvertierung verlieren können, können Sie den zulässigen Bereich des Phi-Winkels [-pi, pi) auf den zulässigen Bereich eines vorzeichenbehafteten Ganzzahltyps abbilden . Dann würden Sie die Komplementarität kostenlos erhalten. Allerdings habe ich diesen Trick in der Praxis nicht wirklich angewendet. Höchstwahrscheinlich würden die Kosten für Float-to-Integer- und Integer-to-Float-Konvertierungen den Nutzen der Direktheit überwiegen. Es ist besser, Ihre Prioritäten beim Schreiben von autovektorisierbarem oder parallelisierbarem Code festzulegen, wenn diese Winkelberechnung häufig durchgeführt wird.
Wenn Ihre Problemdetails so sind, dass es ein definitiv wahrscheinlicheres Ergebnis für die Winkelrichtung gibt, können Sie die integrierten Funktionen des Compilers verwenden, um diese Informationen an den Compiler weiterzuleiten und so die Verzweigung effizienter zu optimieren. Zum Beispiel im Fall von gcc ist das eine
__builtin_expect
Funktion. Es ist etwas praktischer, wenn Sie es in solchelikely
undunlikely
Makros einwickeln (wie im Linux-Kernel):#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)
quelle
Eine Formel für den Winkel im Uhrzeigersinn, 2D-Fall, zwischen 2 Vektoren, xa, ya und xb, yb.
Winkel (vec.a-vec, b) = pi () / 2 * ((1 + Zeichen (ya)) * (1-Zeichen (xa ^ 2)) - (1 + Zeichen (yb)) * (1- Zeichen (xb ^ 2)))
+pi()/4*((2+sign(ya))*sign(xa)-(2+sign(yb))*sign(xb)) +sign(xa*ya)*atan((abs(ya)-abs(xa))/(abs(ya)+abs(xa))) -sign(xb*yb)*atan((abs(yb)-abs(xb))/(abs(yb)+abs(xb)))
quelle
Kopieren Sie diese einfach und fügen Sie sie ein.
angle = (acos((v1.x * v2.x + v1.y * v2.y)/((sqrt(v1.x*v1.x + v1.y*v1.y) * sqrt(v2.x*v2.x + v2.y*v2.y))))/pi*180);
Bitte ;-)
quelle