2011-09-28 36 views
236

En un lenguaje de programación (Python, C#, etc.) necesito determinar cómo calcular el ángulo entre una línea y el eje horizontal?¿Cómo calcular el ángulo entre una línea y el eje horizontal?

creo una imagen que mejor describe lo que quiero:

no words can describe this

Dada (P1 x, P1 y) y (P2 x, P2 y) ¿Cuál es el La mejor forma de calcular este ángulo? El origen está en el topleft y solo se usa el cuadrante positivo.

Respuesta

376

Primero encuentre la diferencia entre el punto de inicio y el punto final (aquí, esto es más un segmento de línea dirigida, no una "línea", ya que las líneas se extienden infinitamente y no comienzan en un punto particular).

deltaY = P2_y - P1_y 
deltaX = P2_x - P1_x 

a continuación, calcular el ángulo (que se extiende desde el eje X positivo en P1 al eje Y positivos en P1).

angleInDegrees = arctan(deltaY/deltaX) * 180/PI 

Pero arctan puede no ser ideal, ya que divide las diferencias de esta manera, se borrará la distinción necesaria para distinguir qué cuadrante se encuentra en el ángulo (ver más abajo). Utilice la siguiente vez si su idioma incluye una función de atan2:

angleInDegrees = atan2(deltaY, deltaX) * 180/PI 

EDITAR (22 de febrero, 2017): En general, sin embargo, llamar atan2(deltaY,deltaX) sólo para conseguir el ángulo apropiado para cos y sin puede ser poco elegante. En esos casos, a menudo puede hacer lo siguiente en su lugar:

  1. Tratar (deltaX, deltaY) como vector.
  2. Normalice ese vector en un vector unitario. Para ello, dividir deltaX y deltaY por la longitud del vector (sqrt(deltaX*deltaX+deltaY*deltaY)), a menos que la longitud es 0.
  3. Después de eso, deltaX ahora ser el coseno del ángulo entre el vector y el eje horizontal (en la dirección de la X positiva al eje Y positivo en P1).
  4. Y deltaY ahora será el seno de ese ángulo.
  5. Si la longitud del vector es 0, no tendrá un ángulo entre ella y el eje horizontal (por lo que no tendrá un seno y coseno significativos).

EDITAR (28 de febrero, 2017): Incluso sin normalizar (deltaX, deltaY):

  • El signo de deltaX le indicará si el coseno se describe en el paso 3 es positivo o negativo.
  • El signo de deltaY le dirá si el seno descrito en el paso 4 es positivo o negativo.
  • Los signos de deltaX y deltaY le dirá qué cuadrante el ángulo está en, en relación con el eje X positivo en P1:
    • +deltaX, +deltaY: de 0 a 90 grados.
    • -deltaX, +deltaY: 90 a 180 grados.
    • -deltaX, -deltaY: 180 a 270 grados (-180 a -90 grados).
    • +deltaX, -deltaY: 270 a 360 grados (-90 a 0 grados).

Una implementación en Python usando radianes (proporcionados el 19 de julio, 2015 Eric Leschinski, que editó mi respuesta):

from math import * 
def angle_trunc(a): 
    while a < 0.0: 
     a += pi * 2 
    return a 

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark): 
    deltaY = y_landmark - y_orig 
    deltaX = x_landmark - x_orig 
    return angle_trunc(atan2(deltaY, deltaX)) 

angle = getAngleBetweenPoints(5, 2, 1,4) 
assert angle >= 0, "angle must be >= 0" 
angle = getAngleBetweenPoints(1, 1, 2, 1) 
assert angle == 0, "expecting angle to be 0" 
angle = getAngleBetweenPoints(2, 1, 1, 1) 
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle) 
angle = getAngleBetweenPoints(2, 1, 2, 3) 
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle) 
angle = getAngleBetweenPoints(2, 1, 2, 0) 
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle) 
angle = getAngleBetweenPoints(1, 1, 2, 2) 
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle) 
angle = getAngleBetweenPoints(-1, -1, -2, -2) 
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle) 
angle = getAngleBetweenPoints(-1, -1, -1, 2) 
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle) 

pasar todas las pruebas. Ver https://en.wikipedia.org/wiki/Unit_circle

+31

Si has encontrado este y que está utilizando JavaScript es muy importante Tenga en cuenta que Math.sin y Math.¡toma radianes para que no necesites convertir el resultado en grados! Por lo tanto, ignore el bit * 180/PI. Tardé 4 horas en descubrirlo. :) – sidonaldson

+0

¿Qué debería uno usar para calcular el ángulo a lo largo del eje vertical? – ZeMoon

+3

@akashg: '90 - angleInDegrees'? – jbaums

47

Lo siento, pero estoy bastante seguro de que la respuesta de Peter es incorrecta. Tenga en cuenta que el eje y baja por la página (común en los gráficos). Como tal, el cálculo deltaY debe invertirse, o se obtiene la respuesta incorrecta.

considerar:

System.out.println (Math.toDegrees(Math.atan2(1,1))); 
System.out.println (Math.toDegrees(Math.atan2(-1,1))); 
System.out.println (Math.toDegrees(Math.atan2(1,-1))); 
System.out.println (Math.toDegrees(Math.atan2(-1,-1))); 

da

45.0 
-45.0 
135.0 
-135.0 

lo tanto, si en el ejemplo anterior, P1 es (1,1) y P2 es (2,2) [porque Y aumenta final de la página ], el código de arriba dará 45.0 grados para el ejemplo mostrado, que es incorrecto. Cambia el orden del cálculo deltaY y funciona correctamente.

+3

Lo invertí como sugirió y mi rotación fue hacia atrás. –

+8

Sería bueno si pudiera mostrar el camino correcto. –

+1

En mi código estoy corrige esto con: 'double arc = Math.atan2 (mouse.y - obj.getPy(), mouse.x - obj.getPx()); \t \t degrees = Math.toDegrees (arc); \t \t if (grados <0) \t \t \t degrees + = 360; \t \t else if (degrees> 360) \t \t \t degrees - = 360; ' –

1

¡He encontrado una solución en Python que está funcionando bien!

from math import atan2,degrees 

def GetAngleOfLineBetweenTwoPoints(p1, p2): 
    return degrees(atan2(p2 - p1, 1)) 

print GetAngleOfLineBetweenTwoPoints(1,3) 
0

Sobre la base de referencia "Peter O" .. Aquí está la versión de Java

private static final float angleBetweenPoints(PointF a, PointF b) { 
float deltaY = b.y - a.y; 
float deltaX = b.x - a.x; 
return (float) (Math.atan2(deltaY, deltaX)); } 
1

Teniendo en cuenta la pregunta exacta, que nos sitúa en un "especial" sistema de coordenadas donde los medios positiva del eje Movimiento hacia abajo (como una pantalla o una vista de la interfaz), es necesario adaptar esta función como esta, y negativa las coordenadas y:

Ejemplo en Swift 2,0

func angle_between_two_points(pa:CGPoint,pb:CGPoint)->Double{ 
    let deltaY:Double = (Double(-pb.y) - Double(-pa.y)) 
    let deltaX:Double = (Double(pb.x) - Double(pa.x)) 
    var a = atan2(deltaY,deltaX) 
    while a < 0.0 { 
     a = a + M_PI*2 
    } 
    return a 
} 

Esta función da una respuesta correcta a la pregunta. Respuesta está en radianes, por lo que el uso, para ver los ángulos en grados, es:

let p1 = CGPoint(x: 1.5, y: 2) //estimated coords of p1 in question 
let p2 = CGPoint(x: 2, y : 3) //estimated coords of p2 in question 

print(angle_between_two_points(p1, pb: p2)/(M_PI/180)) 
//returns 296.56 
0
deltaY = Math.Abs(P2.y - P1.y); 
deltaX = Math.Abs(P2.x - P1.x); 

angleInDegrees = Math.atan2(deltaY, deltaX) * 180/PI 

if(p2.y > p1.y) // Second point is lower than first, angle goes down (180-360) 
{ 
    if(p2.x < p1.x)//Second point is to the left of first (180-270) 
    angleInDegrees += 180; 
    else (270-360) 
    angleInDegrees += 270; 
} 
else if (p2.x < p1.x) //Second point is top left of first (90-180) 
    angleInDegrees += 90; 
0

función de Matlab:

function [lineAngle] = getLineAngle(x1, y1, x2, y2) 
    deltaY = y2 - y1; 
    deltaX = x2 - x1; 

    lineAngle = rad2deg(atan2(deltaY, deltaX)); 

    if deltaY < 0 
     lineAngle = lineAngle + 360; 
    end 
end 
Cuestiones relacionadas