Como calcular um ângulo de três pontos?

Vamos dizer que você tem isso:

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

Suponha que P1 seja o ponto central de um círculo. É sempre a mesma coisa. Eu quero o ângulo que é formado por P2 e P3 , ou em outras palavras, o ângulo que é próximo a P1 . O ângulo interno para ser preciso. Sempre será um ângulo agudo, portanto, menor que -90 graus.

Eu pensei: Cara, isso é matemática simples de geometry. Mas eu procurei uma fórmula por cerca de 6 horas agora, e só encontrei pessoas falando sobre coisas complicadas da NASA, como arccos e produtos vetoriais de produtos escalares. Minha cabeça parece estar na geladeira.

Alguns gurus da matemática aqui acham que isso é um problema simples? Eu não acho que a linguagem de programação é importante aqui, mas para aqueles que pensam: java e objective-c. Eu preciso disso para ambos, mas não o marquei para isso.

Se você quer dizer que o ângulo que P1 é o vértice de então usar a Lei dos Cosines deve funcionar:

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

onde P 12 é o comprimento do segmento de P1 a P2, calculado por

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

É muito simples se você pensa que são dois vetores, um do ponto P1 ao P2 e um do P1 ao P3

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

Você pode então inverter a fórmula do produto ponto:
produto pontual
para obter o ângulo:
ângulo entre dois vetores

Lembre-se disso produto pontual significa apenas: a1 * b1 + a2 * b2 (apenas 2 dimensões aqui …)

Se você tem 3 pontos, você tem um triângulo cujos comprimentos de aresta são conhecidos. Então use a regra de cosseno:

http://en.wikipedia.org/wiki/Law_of_cosines

A melhor maneira de lidar com a computação angular é usar atan2(y, x) que, dado um ponto x, y retorna o ângulo daquele ponto e o eixo X+ em relação à origem.

Dado que o cálculo é

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

ou seja, você basicamente traduz os dois pontos por -P1 (em outras palavras, você traduz tudo para que P1 termine na origem) e então considera a diferença dos ângulos absolutos de P3 e de P2 .

As vantagens de atan2 é que o círculo completo é representado (você pode obter qualquer número entre -π e π) onde, em vez disso, você precisa lidar com vários casos, dependendo dos sinais para calcular o resultado correto.

O único ponto singular para atan2 é (0, 0) … o que significa que P2 e P3 devem ser diferentes de P1 pois nesse caso não faz sentido falar sobre um ângulo.

Deixe-me dar um exemplo em JavaScript, eu lutei muito com isso:

 /** * 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)); } 

Bônus: exemplo com canvas HTML5

Basicamente, o que você tem são dois vetores, um vetor de P1 para P2 e outro de P1 para P3. Então tudo que você precisa é uma fórmula para calcular o ângulo entre dois vetores.

Dê uma olhada aqui para uma boa explicação e a fórmula.

texto alternativo

Se você está pensando em P1 como o centro de um círculo, está pensando muito complicado. Você tem um triângulo simples, então o seu problema é solucionável com a lei dos cossenos . Não há necessidade de qualquer transformação de coordenadas polares ou algo assim. Diga que as distâncias são P1-P2 = A, P2-P3 = B e P3-P1 = C:

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

Tudo o que você precisa fazer é calcular o comprimento das distâncias A, B e C. Elas estão facilmente disponíveis nas coordenadas xey dos pontos e no teorema de Pitágoras.

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

Eu me deparei com um problema semelhante recentemente, só que eu precisava diferenciar entre um ângulo positivo e um negativo. Caso isso seja útil para qualquer pessoa, recomendo o snippet de código que peguei desta lista de e-mails sobre como detectar rotação em um evento de toque 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; } 

Em Objective-C você poderia fazer isso

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

Ou leia mais aqui

Solução Geométrica Muito Simples com Explicação

Poucos dias atrás, caiu no mesmo problema e teve que se sentar com o livro de matemática. Eu resolvi o problema combinando e simplificando algumas fórmulas básicas.


Vamos considerar este valor

ângulo

Queremos saber ϴ , então precisamos descobrir α e β primeiro. Agora, para qualquer linha reta

 y = m * x + c 

Let- A = (ax, ay) , B = (bx, por) e O = (ox, oy) . Então, para a linha 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) 

Da mesma forma, para a linha OB

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

Agora precisamos de ϴ = β - α . Na trigonometria, temos uma fórmula

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

Depois de replace o valor de tan α (de eqn-2) e tan b (de eqn-3) em eqn-4, e aplicando simplificação, obtemos

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

Assim,

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

É isso!


Agora, siga os seguintes

ângulo

Este método C # ou Java calcula o â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; } 

Você mencionou um ângulo assinado (-90). Em muitos aplicativos, os ângulos podem ter sinais (positivos e negativos, consulte http://en.wikipedia.org/wiki/Angle ). Se os pontos são (digamos) P2 (1,0), P1 (0,0), P3 (0,1) então o ângulo P3-P1-P2 é convencionalmente positivo (PI / 2) enquanto o ângulo P2-P1- P3 é negativo. Usar os comprimentos dos lados não fará distinção entre + e – portanto, se isso for importante, você precisará usar vetores ou uma function como Math.atan2 (a, b).

Os ângulos também podem se estender além de 2 * PI e, embora isso não seja relevante para a questão atual, foi suficientemente importante que eu escrevesse minha própria class Angle (também para garantir que graus e radianos não se misturassem). As questões sobre se angle1 é menor que angle2 depende criticamente de como os ângulos são definidos. Também pode ser importante decidir se uma linha (-1,0) (0,0) (1,0) é representada como Math.PI ou -Math.PI

meu programa de demonstração de ângulo

Recentemente, eu também tenho o mesmo problema … No Delphi É muito semelhante ao 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; 

Aqui está um método C # para retornar o ângulo (0-360) no sentido anti-horário a partir da horizontal para um ponto em um 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; } 

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

Existe uma resposta simples para isso usando a matemática do ensino médio.

Vamos dizer que você tem 3 pontos

Para obter o ângulo do ponto A ao B

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

Para obter o ângulo do ponto B para o C

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

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

Eu usei este código no projeto recente que fiz, mude o B para P1 .. você pode também remover o "180 +" se você quiser

bem, as outras respostas parecem cobrir tudo o que é necessário, então eu gostaria de adicionar isso se você estiver usando o JMonkeyEngine:

Vector3f.angleBetween(otherVector)

como é isso que eu vim aqui procurando 🙂

  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; } 

}