¿Cómo calcular un ángulo a partir de tres puntos?

Digamos que tienes esto:

P1 = (x=2, y=50) P2 = (x=9, y=40) P3 = (x=5, y=20) 

Supongamos que P1 es el punto central de un círculo. Siempre es el mismo. Quiero el ángulo formado por P2 y P3 , o en otras palabras, el ángulo que está al lado de P1 . El ángulo interno para ser preciso. Siempre será un ángulo agudo, por lo tanto, menos de -90 grados.

Pensé: Hombre, eso es geometría matemática simple. Pero he buscado una fórmula por alrededor de 6 horas ahora, y solo encuentro personas hablando sobre cosas complicadas de la NASA como arcos y productos escalares vectoriales. Tengo la sensación de que está en una nevera.

Algunos gurús de matemáticas aquí piensan que este es un problema simple. No creo que el lenguaje de progtwigción sea importante aquí, sino para aquellos que piensan que sí: java y objective-c. Lo necesito para ambos, pero no lo he etiquetado para estos.

Si te refieres al ángulo con el que P1 es el vértice de, entonces debes usar la Ley de Coseno :

arcos ((P 12 2 + P 13 2 – P 23 2 ) / (2 * P 12 * P 13 ))

donde P 12 es la longitud del segmento de P1 a P2, calculada por

sqrt ((P1 x – P2 x ) 2 + (P1 y – P2 y ) 2 )

Se pone muy simple si lo piensas como dos vectores, uno desde el punto P1 hasta el P2 y uno desde el P1 hasta el P3

asi que:
a = (p1.x – p2.x, p1.y – p2.y)
b = (p1.x – p3.x, p1.y – p3.y)

A continuación, puede invertir la fórmula del producto punto:
producto punto
para obtener el ángulo:
ángulo entre dos vectores

Recuerda eso producto punto solo significa: a1 * b1 + a2 * b2 (solo 2 dimensiones aquí …)

La mejor forma de tratar el cálculo del ángulo es usar atan2(y, x) que, dado un punto x, y devuelve el ángulo desde ese punto y el eje X+ con respecto al origen.

Dado que el cálculo es

 double result = atan2(P3.y - P1.y, P3.x - P1.x) - atan2(P2.y - P1.y, P2.x - P1.x); 

es decir, básicamente -P1 los dos puntos por -P1 (en otras palabras, -P1 todo para que P1 termine en el origen) y luego consideras la diferencia de los angularjs absolutos de P3 y de P2 .

Las ventajas de atan2 es que se representa el círculo completo (puede obtener cualquier número entre -π y π) donde, en cambio, con acos necesita manejar varios casos según los signos para calcular el resultado correcto.

El único punto singular para atan2 es (0, 0) … lo que significa que tanto P2 como P3 deben ser diferentes de P1 ya que en ese caso no tiene sentido hablar de un ángulo.

Déjame dar un ejemplo en JavaScript, he peleado mucho con eso:

 /** * Calculates the angle (in radians) between two vectors pointing outward from one center * * @param p0 first point * @param p1 second point * @param c center point */ function find_angle(p0,p1,c) { var p0c = Math.sqrt(Math.pow(cx-p0.x,2)+ Math.pow(cy-p0.y,2)); // p0->c (b) var p1c = Math.sqrt(Math.pow(cx-p1.x,2)+ Math.pow(cy-p1.y,2)); // p1->c (a) var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+ Math.pow(p1.y-p0.y,2)); // p0->p1 (c) return Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c)); } 

Bono: Ejemplo con HTML5-canvas

Básicamente lo que tienes son dos vectores, un vector de P1 a P2 y otro de P1 a P3. Entonces, todo lo que necesitas es una fórmula para calcular el ángulo entre dos vectores.

Eche un vistazo aquí para una buena explicación y la fórmula.

texto alternativo

Si piensas en P1 como el centro de un círculo, estás pensando que es demasiado complicado. Tienes un triángulo simple, por lo que tu problema es solvente con la ley de los cosenos . No hay necesidad de ninguna transformación de coordenadas polares o somesuch. Digamos que las distancias son P1-P2 = A, P2-P3 = B y P3-P1 = C:

Ángulo = arccos ((B ^ 2-A ^ 2-C ^ 2) / 2AC)

Todo lo que tiene que hacer es calcular la longitud de las distancias A, B y C. Es fácil obtenerlas de las coordenadas xey de sus puntos y del teorema de Pitágoras.

Longitud = sqrt ((X2-X1) ^ 2 + (Y2-Y1) ^ 2)

Me encontré con un problema similar recientemente, solo que necesitaba diferenciar entre angularjs positivos y negativos. En caso de que esto sea de utilidad para cualquier persona, recomiendo el fragmento de código que obtuve de esta lista de correo para detectar la rotación en un evento táctil para Android:

  @Override public boolean onTouchEvent(MotionEvent e) { float x = e.getX(); float y = e.getY(); switch (e.getAction()) { case MotionEvent.ACTION_MOVE: //find an approximate angle between them. float dx = x-cx; float dy = y-cy; double a=Math.atan2(dy,dx); float dpx= mPreviousX-cx; float dpy= mPreviousY-cy; double b=Math.atan2(dpy, dpx); double diff = ab; this.bearing -= Math.toDegrees(diff); this.invalidate(); } mPreviousX = x; mPreviousY = y; return true; } 

En Objective-C puedes hacer esto

 float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI); 

O lee más aquí

Solución geométrica muy simple con explicación

Hace algunos días, uno cayó en el mismo problema y tuvo que sentarse con el libro de matemáticas. Resolví el problema combinando y simplificando algunas fórmulas básicas.


Consideremos esta figura

ángulo

Queremos saber Θ , así que primero tenemos que encontrar α y β . Ahora, para cualquier línea recta-

 y = m * x + c 

Let- A = (ax, ay) , B = (bx, by) y O = (ox, oy) . Entonces para la línea OA

 oy = m1 * ox + c ⇒ c = oy - m1 * ox ...(eqn-1) ay = m1 * ax + c ⇒ ay = m1 * ax + oy - m1 * ox [from eqn-1] ⇒ ay = m1 * ax + oy - m1 * ox ⇒ m1 = (ay - oy) / (ax - ox) ⇒ tan α = (ay - oy) / (ax - ox) [m = slope = tan ϴ] ...(eqn-2) 

De la misma manera, para la línea OB

 tan β = (by - oy) / (bx - ox) ...(eqn-3) 

Ahora, necesitamos ϴ = β - α . En trigonometría tenemos una fórmula

 tan (β-α) = (tan β + tan α) / (1 - tan β * tan α) ...(eqn-4) 

Después de reemplazar el valor de tan α (de eqn-2) y tan b (de eqn-3) en eqn-4, y aplicando la simplificación obtenemos-

 tan (β-α) = ( (ax-ox)*(by-oy)+(ay-oy)*(bx-ox) ) / ( (ax-ox)*(bx-ox)-(ay-oy)*(by-oy) ) 

Asi que,

 ϴ = β-α = tan^(-1) ( ((ax-ox)*(by-oy)+(ay-oy)*(bx-ox)) / ((ax-ox)*(bx-ox)-(ay-oy)*(by-oy)) ) 

¡Eso es!


Ahora, toma la siguiente figura

ángulo

Este método C # o Java calcula el ángulo ( Θ ) –

  private double calculateAngle(double P1X, double P1Y, double P2X, double P2Y, double P3X, double P3Y){ double numerator = P2Y*(P1X-P3X) + P1Y*(P3X-P2X) + P3Y*(P2X-P1X); double denominator = (P2X-P1X)*(P1X-P3X) + (P2Y-P1Y)*(P1Y-P3Y); double ratio = numerator/denominator; double angleRad = Math.Atan(ratio); double angleDeg = (angleRad*180)/Math.PI; if(angleDeg<0){ angleDeg = 180+angleDeg; } return angleDeg; } 

Mencionaste un ángulo firmado (-90). En muchas aplicaciones, los angularjs pueden tener signos (positivos y negativos, ver http://en.wikipedia.org/wiki/Angle ). Si los puntos son (digamos) P2 (1,0), P1 (0,0), P3 (0,1) entonces el ángulo P3-P1-P2 es convencionalmente positivo (PI / 2) mientras que el ángulo P2-P1- P3 es negativo. Usar las longitudes de los lados no distinguirá entre + y – por lo tanto, si esto tiene importancia, necesitará usar vectores o una función como Math.atan2 (a, b).

Los angularjs también pueden extenderse más allá de 2 * PI y aunque esto no es relevante para la pregunta actual, fue lo suficientemente importante que escribí mi propia clase de ángulo (también para asegurarme de que los grados y radianes no se mezclen). Las preguntas sobre si angle1 es menor que angle2 depende críticamente de cómo se definen los angularjs. También puede ser importante decidir si una línea (-1,0) (0,0) (1,0) se representa como Math.PI o -Math.PI

mi programa de demostración de ángulo

Recientemente, yo también tengo el mismo problema … En Delphi Es muy similar a Objective-C.

 procedure TForm1.FormPaint(Sender: TObject); var ARect: TRect; AWidth, AHeight: Integer; ABasePoint: TPoint; AAngle: Extended; begin FCenter := Point(Width div 2, Height div 2); AWidth := Width div 4; AHeight := Height div 4; ABasePoint := Point(FCenter.X+AWidth, FCenter.Y); ARect := Rect(Point(FCenter.X - AWidth, FCenter.Y - AHeight), Point(FCenter.X + AWidth, FCenter.Y + AHeight)); AAngle := ArcTan2(ClickPoint.Y-Center.Y, ClickPoint.X-Center.X) * 180 / pi; AngleLabel.Caption := Format('Angle is %5.2f', [AAngle]); Canvas.Ellipse(ARect); Canvas.MoveTo(FCenter.X, FCenter.Y); Canvas.LineTo(FClickPoint.X, FClickPoint.Y); Canvas.MoveTo(FCenter.X, FCenter.Y); Canvas.LineTo(ABasePoint.X, ABasePoint.Y); end; 

Aquí hay un método de C # para devolver el ángulo (0-360) en sentido antihorario de la horizontal a un punto en un círculo.

  public static double GetAngle(Point centre, Point point1) { // Thanks to Dave Hill // Turn into a vector (from the origin) double x = point1.X - centre.X; double y = point1.Y - centre.Y; // Dot product u dot v = mag u * mag v * cos theta // Therefore theta = cos -1 ((u dot v) / (mag u * mag v)) // Horizontal v = (1, 0) // therefore theta = cos -1 (ux / mag u) // nb, there are 2 possible angles and if uy is positive then angle is in first quadrant, negative then second quadrant double magnitude = Math.Sqrt(x * x + y * y); double angle = 0; if(magnitude > 0) angle = Math.Acos(x / magnitude); angle = angle * 180 / Math.PI; if (y < 0) angle = 360 - angle; return angle; } 

Saludos, Paul

 function p(x, y) {return {x,y}} function normaliseToInteriorAngle(angle) { if (angle < 0) { angle += (2*Math.PI) } if (angle > Math.PI) { angle = 2*Math.PI - angle } return angle } function angle(p1, center, p2) { const transformedP1 = p(p1.x - center.x, p1.y - center.y) const transformedP2 = p(p2.x - center.x, p2.y - center.y) const angleToP1 = Math.atan2(transformedP1.y, transformedP1.x) const angleToP2 = Math.atan2(transformedP2.y, transformedP2.x) return normaliseToInteriorAngle(angleToP2 - angleToP1) } function toDegrees(radians) { return 360 * radians / (2 * Math.PI) } console.log(toDegrees(angle(p(-10, 0), p(0, 0), p(0, -10)))) 

HAY una respuesta simple para esto usando matemáticas de la escuela secundaria.

Digamos que tienes 3 puntos

Para obtener el ángulo del punto A al B

angle = atan2(Ax - Bx, By - Ay)

Para obtener el ángulo del punto B al C

angle2 = atan2(Bx - Cx, Cy - By)

 Answer = 180 + angle2 - angle If (answer < 0){ return answer + 360 }else{ return answer } 

Acabo de usar este código en el proyecto reciente que hice, cambie el B a P1 ... también podría eliminar el "180 +" si lo desea

bueno, las otras respuestas parecen cubrir todo lo requerido, así que me gustaría simplemente agregar esto si estás usando JMonkeyEngine:

Vector3f.angleBetween(otherVector)

como eso es lo que vine a buscar aquí 🙂

  Atan2 output in degrees PI/2 +90 | | | | PI ---.--- 0 +180 ---.--- 0 | | | | -PI/2 +270 public static double CalculateAngleFromHorizontal(double startX, double startY, double endX, double endY) { var atan = Math.Atan2(endY - startY, endX - startX); // Angle in radians var angleDegrees = atan * (180 / Math.PI); // Angle in degrees (can be +/-) if (angleDegrees < 0.0) { angleDegrees = 360.0 + angleDegrees; } return angleDegrees; } // Angle from point2 to point 3 counter clockwise public static double CalculateAngle0To360(double centerX, double centerY, double x2, double y2, double x3, double y3) { var angle2 = CalculateAngleFromHorizontal(centerX, centerY, x2, y2); var angle3 = CalculateAngleFromHorizontal(centerX, centerY, x3, y3); return (360.0 + angle3 - angle2)%360; } // Smaller angle from point2 to point 3 public static double CalculateAngle0To180(double centerX, double centerY, double x2, double y2, double x3, double y3) { var angle = CalculateAngle0To360(centerX, centerY, x2, y2, x3, y3); if (angle > 180.0) { angle = 360 - angle; } return angle; } 

}