Je veux trouver l’angle dans le sens des aiguilles d’une montre entre 2 vecteurs (2D, 3D).
La méthode classique avec le produit scalaire me donne l’angle interne (0-180 degrés) et j’ai besoin d’utiliser des instructions if pour déterminer si le résultat est l’angle dont j’ai besoin ou son complément.
Connaissez-vous un moyen direct de calculer l’angle dans le sens des aiguilles d’une montre?
Tout comme le produit scalaire est proportionnel au cosinus de l’angle, le déterminant est proportionnel à son sinus. Vous pouvez donc calculer l’angle comme ceci:
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)
L’orientation de cet angle correspond à celle du système de coordonnées. Dans un système de coordonnées gaucher , c.-à-d. X pointant vers la droite et y vers le bas, comme cela est courant pour les graphiques informatiques, cela signifie que vous obtenez un signe positif pour les angles dans le sens des aiguilles d’une montre. Si l’orientation du système de coordonnées est mathématique avec y up, vous obtenez des angles dans le sens inverse des aiguilles d’une montre, comme c’est la convention en mathématiques. Changer l’ordre des entrées changera le signe, donc si vous n’êtes pas satisfait des signes, remplacez les entrées.
En 3D, deux vecteurs placés arbitrairement définissent leur propre axe de rotation, perpendiculaire aux deux. Cet axe de rotation n’a pas d’orientation fixe, ce qui signifie que vous ne pouvez pas non plus fixer la direction de l’angle de rotation de manière unique. Une convention courante consiste à laisser les angles toujours positifs et à orienter l’axe de manière à ce qu’il corresponde à un angle positif. Dans ce cas, le produit scalaire des vecteurs normalisés est suffisant pour calculer les angles.
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))
Un cas particulier est le cas où vos vecteurs ne sont pas placés de manière arbitraire, mais sont situés dans un plan avec un vecteur normal connu n . Ensuite, l’axe de rotation sera également dans la direction n et l’orientation de n fixera une orientation pour cet axe. Dans ce cas, vous pouvez adapter le calcul 2D ci-dessus, y compris n dans le déterminant pour obtenir sa taille 3 × 3.
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)
Une condition pour que cela fonctionne est que le vecteur normal n ait une longueur d’unité. Sinon, vous devrez le normaliser.
Ce déterminant pourrait également être exprimé en tant que produit sortingple , comme @Excrubulent indiqué dans une modification suggérée.
det = n · (v1 × v2)
Cela peut être plus facile à implémenter dans certaines API et donne une perspective différente de ce qui se passe: le produit croisé est proportionnel au sinus de l’angle et sera perpendiculaire au plan, donc multiple de n . Le produit scalaire va donc essentiellement mesurer la longueur de ce vecteur, mais avec le signe correct qui lui est attaché.
Pour calculer l’angle, il vous suffit d’appeler atan2(v1.s_cross(v2), v1.dot(v2))
pour les cas 2D. Où s_cross
est un analogue scalaire de la production croisée (zone signée du parallélogramme). Pour un cas 2D, il s’agirait d’une production de coin. Pour les cas 3D, vous devez définir une rotation dans le sens des aiguilles d’une montre car d’un côté du plan dans le sens des aiguilles d’une montre, une direction, d’un autre côté du plan est une autre direction =)
Edit: c’est l’angle dans le sens inverse des aiguilles d’une montre, l’angle dans le sens des aiguilles d’une montre est juste en face
Cette réponse est la même que celle de MvG, mais l’explique différemment (c’est le résultat de mes efforts pour essayer de comprendre pourquoi la solution de MvG fonctionne). Je l’affiche sur le champ, que les autres trouvent utile.
L’angle anti-horaire theta
de x
à y
, par rapport au sharepoint vue de leur normale donnée n
( ||n|| = 1
), est donné par
atan2 (point (n, croix (x, y)), point (x, y))
(1) = atan2 (|| x || || y || sin (thêta), || x || || y || cos (thêta))
(2) = atan2 (sin (thêta), cos (thêta))
(3) = angle anti-horaire entre l’axe des abscisses et le vecteur (cos (thêta), sin (thêta))
(4) = thêta
où ||x||
dénote la magnitude de x
.
L’étape (1) suit en notant que
croix (x, y) = || x || || y || péché (thêta) n,
et donc
point (n, croix (x, y))
= point (n, || x || || y || sin (thêta) n)
= || x || || y || péché (thêta) point (n, n)
qui est égal à
|| x || || y || le péché (thêta)
si ||n|| = 1
||n|| = 1
.
L’étape (2) découle de la définition de atan2
, notant que atan2(cy, cx) = atan2(y,x)
, où c
est un scalaire. L’étape (3) découle de la définition de atan2
. L’étape (4) découle des définitions géomésortingques de cos
et sin
.
Le produit scalaire (point) de deux vecteurs vous permet d’obtenir le cosinus de l’angle entre eux. Pour obtenir la «direction» de l’angle, vous devez également calculer le produit croisé, il vous permettra de vérifier (via la coordonnée z) que l’angle est dans le sens des aiguilles d’une montre ou non (c.-à-d.
Pour une méthode 2D, vous pouvez utiliser la loi des cosinus et la méthode “direction”.
Pour calculer l’angle du segment P3: P1 passant dans le sens des aiguilles d’une montre pour segmenter P3: P2.
P1 P2 P3
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
opérations comme des suggestions ci-dessus et une opération plus ou moins en virgule flottante.
les méthodes qu’il utilise sont:
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; }
Si, par “voie directe”, vous voulez dire éviter la déclaration if
, alors je ne pense pas qu’il existe une solution vraiment générale.
Cependant, si votre problème spécifique permet de perdre une certaine précision dans la discrétisation des angles et que vous êtes prêt à perdre du temps dans les conversions de types, vous pouvez mapper la plage d’angle phi autorisée de [-pi, pi] sur la plage autorisée d’un type entier signé . Ensuite, vous obtiendrez la complémentarité gratuitement. Cependant, je n’ai pas vraiment utilisé cette astuce dans la pratique. Très probablement, les dépenses liées aux conversions de nombre entier à entier et de nombre entier à flottant l’emporteraient sur les avantages de la franchise. Il est préférable de définir vos priorités sur l’écriture de code autovectorisable ou parallélisable lorsque ce calcul d’angle est effectué.
De plus, si les détails de votre problème sont tels qu’il existe un résultat plus probable pour la direction de l’angle, vous pouvez utiliser les fonctions intégrées du compilateur pour fournir ces informations au compilateur, afin d’optimiser la twig plus efficacement. Par exemple, dans le cas de gcc, c’est la fonction __builtin_expect
. Il est un peu plus pratique à utiliser lorsque vous l’enroulez dans des macros aussi likely
et unlikely
(comme dans le kernel Linux):
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)