Como calcular um ângulo a partir de três pontos? [fechadas]


120

Vamos dizer que você tem isso:

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

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

Eu pensei: Cara, isso é matemática simples em geometria. Mas tenho procurado uma fórmula há cerca de 6 horas e só encontro pessoas falando sobre coisas complicadas da NASA, como arccos e produtos escalares vetoriais. Minha cabeça parece que está na geladeira.

Alguns gurus da matemática aqui que pensam que este é um problema simples? Eu não acho que a linguagem de programação seja importante aqui, mas para quem pensa que é: java e objetivo-c. Preciso disso para ambos, mas não o etiquetei para estes.

Respostas:


87

Se você quer dizer que o ângulo em que P1 é o vértice, a Lei dos Cossenos 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 )



@Rafa Firenze cos ^ -1 é uma notação comum para acos, mas acos é menos ambíguo. en.wikipedia.org/wiki/Inverse_trigonometric_functions
— geon 21/10

Deixarei a edição, pois não faz mal a nada, mas, com os graus de Matemática / CS / EE, cos ^ -1 é certamente a notação mais comum.
— Lance Roberts

1
Apenas um punhado de idiomas usa um sinal de intercalação para 'poder de'; portanto, se você não quiser chamá-lo de arcos, digite apenas cos⁻¹. (Se você estiver usando um sistema operacional comercial que dificulta a digitação de expoentes, espero que existam aplicativos de capas de chaves que você possa comprar ou talvez um plug-in de navegador que possa instalar. Ou pesquise na Web e copie e cole.)
— Michael Scheper

1
@ MichaelScheper, eu estava usando apenas o cursor nos comentários em que o html é limitado. Eu certamente usaria a notação sub / sobrescrita em qualquer resposta real.
— Lance Roberts

47

Fica muito simples se você pensar em dois vetores, um do ponto P1 ao P2 e outro do P1 ao P3

então:
a = (p1.x - p2.x, p1.y - p2.y)
b = (p1.x - p3.x, p1.y - p3.y)

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

Lembre-se de que ponto de produtoapenas significa: a1 * b1 + a2 * b2 (apenas duas dimensões aqui ...)


1
Ah magnitude do vetor
— Daniel Little

Verifique a solução atan2.
— Luc Boissaye 7/0318

25

A melhor maneira de lidar com a computação em ângulo é usar atan2(y, x)que, dado um ponto, x, yretorne o ângulo desse ponto e o X+eixo 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 P1acabe na origem) e depois considera a diferença dos ângulos absolutos de P3e de P2.

A vantagem atan2disso é que o círculo completo é representado (você pode obter qualquer número entre -π e π) onde, em vez disso, acosé necessário lidar com vários casos, dependendo dos sinais para calcular o resultado correto.

O único ponto singular para atan2é (0, 0)... significando que ambos P2e P3devem ser diferentes, P1como nesse caso, não faz sentido falar sobre um ângulo.


Obrigado pela sua resposta. Era exatamente o que eu estava procurando. Solução simples e você pode obter facilmente o ângulo no sentido anti-horário se eu adicionar 2pi quando o valor for negativo.
— Mario

@marcpt: atan2é exatamente o que é necessário para esse problema, mas parece que a maioria das pessoas que chega a essa pergunta simplesmente não consegue ler ou não consegue entender por que as acossoluções baseadas são ruins. Por sorte o suficiente para me deixei a "alguém está errado na internet" ( xkcd.com/386 fase) há muitos anos e eu não estou indo para começar uma luta para defender o óbvio :-)
— 6502

Obrigado por apontar isso, mas você pode lidar com 3D dessa maneira?
— nicoco 28/09

1
@nicoco: em três dimensões, como você define o ângulo? Mais especificamente, o ângulo pode ser negativo ou superior a pi (180 graus)? Dois vetores não paralelos em 3d definem um plano, mas o plano pode ser "visto" de dois lados: visto de um lado A aparecerá "à esquerda" de B e do outro aparecerá "à direita". .
— 6502

@ 6505 Obrigado pela sua resposta, postei antes de pensar no meu problema. Eu já entendi agora.
— nicoco 28/09

19

Deixe-me dar um exemplo em JavaScript, 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(c.x-p0.x,2)+
                        Math.pow(c.y-p0.y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                        Math.pow(c.y-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 tela HTML5


5
Você pode tornar isso mais eficiente, fazendo menos sqrte esquadrinhando. Veja minha resposta aqui (escrita em Ruby) ou nesta demonstração atualizada (JavaScript).
— Phrogz

Você pode usar atan2 para uma solução mais simples.
— Luc Boissaye 7/0318

15

Basicamente, o que você tem são dois vetores, um vetor de P1 a P2 e outro de P1 a P3. Então, tudo o que você precisa é de 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


12

Se você está pensando em P1 como o centro de um círculo, está pensando muito complicado. Você tem um triângulo simples, portanto seu problema é solucionável pela lei dos cossenos . Não há necessidade de qualquer transformação de coordenadas polares ou algo assim. Digamos que as distâncias sejam 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. Essas são facilmente disponíveis nas coordenadas x e y dos seus pontos e no teorema de Pitágoras

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


Estou um pouco confuso como realmente implementar isso como você está tratando P1 etc como valores individuais em vez de (x, y)
— Dominic

@ Tobias Dominic: A Notação P1-P2 = Anão deve ser lida como "Para calcular A, subtraia P2 de P1", mas como "Estou definindo A como a distância de P1 a P2", que pode ser calculada usando a segunda equação. Eu só queria definir uma abreviação para as distâncias, para tornar as equações mais legíveis.
— Treb

8

Encontrei recentemente um problema semelhante, mas precisava diferenciar entre ângulos positivos e negativos. Caso isso seja útil a alguém, recomendo o trecho de código que peguei nesta lista de e-mails sobre a detecção de 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  = a-b;
       this.bearing -= Math.toDegrees(diff);
       this.invalidate();
    }
    mPreviousX = x;
    mPreviousY = y;
    return true;
 }

7

Solução geométrica muito simples com explicação

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


Vamos considerar esta figura-

ângulo

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

y = m * x + c

Seja A = (ax, ay) , B = (bx, by) e O = (boi, 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 ϴ = β - α. Na trigonometria, temos uma fórmula

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

Após substituir o valor de tan α(do eqn-2) e tan b(do eqn-3) no eqn-4, e aplicar a 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, veja a figura a seguir:

ângulo

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

Como esse método pode ser usado para um triângulo equilátero?
— 21717 Vikrant

1
Bem, sua resposta está funcionando bem agora. Foi um problema de lógica no meu código semana antes.
— Vikrant

6

No Objective-C, você poderia fazer isso

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

Ou leia mais aqui


7
Oh não. Existem três pontos, o centro não está em (0,0) e isso dá um ângulo de um triângulo retângulo, não o ângulo do ápice. E que tipo de nome é "xpoint" para um ângulo?
— perfil completo de Jim Balter

4

Você mencionou um ângulo assinado (-90). Em muitas aplicações, 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. O uso dos comprimentos dos lados não fará distinção entre + e - portanto, se isso for importante, você precisará usar vetores ou uma função 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 pergunta atual, era suficientemente importante que eu escrevesse minha própria classe Angle (também para garantir que graus e radianos não se misturassem). As perguntas sobre se o ângulo1 é menor que o ângulo2 dependem 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


4

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;

2

Aqui está um método C # para retornar o ângulo (0-360) no sentido anti-horário 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 (u.x / mag u)
        // nb, there are 2 possible angles and if u.y 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;
    }

Cheers, Paul


2

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


0

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

Digamos que você tenha 3 pontos

Para obter ângulo do ponto A a B

angle = atan2(A.x - B.x, B.y - A.y)

Para obter ângulo do ponto B a C

angle2 = atan2(B.x - C.x, C.y - B.y)

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

Acabei de usar esse código no projeto recente que fiz, altere B para P1. Você também pode remover o "180 +" se quiser


-1

bem, as outras respostas parecem cobrir tudo o 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 :)


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

}

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.