vectores punto producto operaciones lenguaje ejemplos cruz con c++ math angle

c++ - punto - Forma directa de calcular el ángulo en el sentido de las agujas del reloj entre 2 vectores



producto cruz de vectores c++ (6)

Quiero averiguar el ángulo en el sentido de las agujas del reloj entre 2 vectores (2D, 3D).

La manera clásica con el producto punto me da el ángulo interno (0-180 grados) y necesito usar algunas declaraciones if para determinar si el resultado es el ángulo que necesito o su complemento.

¿Conoces una forma directa de calcular el ángulo en el sentido de las agujas del reloj?


Caso 2D

Al igual que el producto escalar es proporcional al coseno del ángulo, el determinant es proprocional a su seno. Para que pueda calcular el ángulo de esta manera:

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)

La orientación de este ángulo coincide con la del sistema de coordenadas. En un sistema de coordenadas para zurdos , es decir, x apunta hacia la derecha y hacia abajo, como es habitual en los gráficos por computadora, esto significará que obtendrá un signo positivo para los ángulos en el sentido de las agujas del reloj. Si la orientación del sistema de coordenadas es matemática con y arriba, se obtienen ángulos en sentido antihorario, como es la convención en matemáticas. Cambiar el orden de las entradas cambiará el letrero, por lo tanto, si no está satisfecho con las señales, simplemente cambie las entradas.

Estuche 3D

En 3D, dos vectores colocados arbitrariamente definen su propio eje de rotación, perpendicular a ambos. Ese eje de rotación no viene con una orientación fija, lo que significa que tampoco puede fijar de manera única la dirección del ángulo de rotación. Una convención común es permitir que los ángulos sean siempre positivos, y orientar el eje de tal manera que encaje en un ángulo positivo. En este caso, el producto escalar de los vectores normalizados es suficiente para calcular ángulos.

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))

Plano incrustado en 3D

Un caso especial es el caso donde sus vectores no se colocan arbitrariamente, sino que se encuentran dentro de un plano con un vector normal conocido n . Entonces, el eje de rotación también estará en la dirección n , y la orientación de n fijará una orientación para ese eje. En este caso, puede adaptar el cálculo 2D anterior, incluyendo n en el determinant para hacer su tamaño 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)

Una condición para que esto funcione es que el vector normal n tiene longitud de unidad. Si no, tendrás que normalizarlo.

Como producto triple

Este determinante también podría expresarse como el producto triple , como @Excrubulent señaló en una edición sugerida.

det = n · (v1 × v2)

Esto podría ser más fácil de implementar en algunas API, y da una perspectiva diferente de lo que está sucediendo aquí: el producto cruzado es proporcional al seno del ángulo, y se situará perpendicular al plano, por lo tanto, será un múltiplo de n . El producto de puntos, por lo tanto, básicamente medirá la longitud de ese vector, pero con el signo correcto adjunto.


El producto escalar (punto) de dos vectores te permite obtener el coseno del ángulo entre ellos. Para obtener la ''dirección'' del ángulo, también debe calcular el producto cruzado, le permitirá verificar (a través de la coordenada z) si el ángulo es hacia la derecha o no (es decir, si debe extraerlo de 360 ​​grados o no).


Esta respuesta es la misma que la de MvG, pero la explica de manera diferente (es el resultado de mis esfuerzos para tratar de entender por qué la solución de MvG funciona). Lo estoy publicando en caso de que otros lo encuentren útil.

El ángulo theta en sentido antihorario desde x hasta y , con respecto al punto de vista de su n normal dada ( ||n|| = 1 ), viene dado por

atan2 (punto (n, cruz (x, y)), punto (x, y))

(1) = atan2 (|| x || || y || sin (theta), || x || || y || cos (theta))

(2) = atan2 (sin (theta), cos (theta))

(3) = ángulo antihorario entre el eje xy el vector (cos (theta), sin (theta))

(4) = theta

donde ||x|| denota la magnitud de x .

El paso (1) sigue al señalar que

cruz (x, y) = || x || || y || sin (theta) n,

y entonces

punto (n, cruz (x, y))

= punto (n, || x || || y || sin (theta) n)

= || x || || y || sin (theta) punto (n, n)

que es igual

|| x || || y || pecado (theta)

si ||n|| = 1 ||n|| = 1 .

El paso (2) se sigue de la definición de atan2 , señalando que atan2(cy, cx) = atan2(y,x) , donde c es un escalar. El paso (3) se sigue de la definición de atan2 . El paso (4) se sigue de las definiciones geométricas de cos y sin .


Para calcular el ángulo, solo necesita llamar atan2(v1.s_cross(v2), v1.dot(v2)) para el caso 2D. Donde s_cross es un análogo escalar de la producción cruzada (área firmada de paralelogramo). Para el caso 2D que sería la producción de cuña. Para el caso 3D, debe definir la rotación en el sentido de las agujas del reloj, ya que de un lado del plano en el sentido de las agujas del reloj hay una dirección, del otro lado del plano hay otra dirección =)

Editar: este es el ángulo en sentido contrario a las agujas del reloj, el ángulo en el sentido de las agujas del reloj está justo enfrente


Para un método 2D, podría usar la ley de los cosenos y el método de "dirección".

Para calcular el ángulo del segmento P3: P1 barrido en el sentido de las agujas del reloj para segmentar 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

operaciones como sugerencias anteriores y solo una operación de punto flotante más o menos.

los métodos que usa son:

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 por "vía directa" te refieres a evitar la declaración if , entonces no creo que haya una solución realmente general.

Sin embargo, si su problema específico permitiría perder cierta precisión en la discretización de ángulos y está bien perder un poco de tiempo en las conversiones de tipo, puede mapear el rango permitido de phi [-pi, pi) en el rango permitido de algún tipo de entero con signo . Entonces obtendrías la complementariedad gratis. Sin embargo, realmente no usé este truco en la práctica. Lo más probable es que el gasto de las conversiones de flotante a entero y de entero a flotante supere cualquier beneficio de la franqueza. Es mejor establecer sus prioridades al escribir código autovectorizable o paralelizable cuando este cálculo de ángulo se realiza mucho.

Además, si los detalles de su problema son tales que existe un resultado definitivo más probable para la dirección del ángulo, entonces puede usar las funciones integradas de los compiladores para suministrar esta información al compilador, por lo que puede optimizar la ramificación de manera más eficiente. Por ejemplo, en el caso de gcc, esa es la función __builtin_expect . Es algo más útil de usar cuando lo envuelves en macros likely e unlikely (como en el kernel de Linux):

#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)