Come calcolare un angolo da tre punti? [chiuso]


120

Diciamo che hai questo:

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

Supponiamo che P1sia il punto centrale di un cerchio. È sempre lo stesso. Voglio l'angolo che è composto da P2e P3, o in altre parole l'angolo che è vicino a P1. L'angolo interno per essere precisi. Sarà sempre un angolo acuto, quindi inferiore a -90 gradi.

Ho pensato: amico, questa è semplice matematica geometrica. Ma ho cercato una formula per circa 6 ore e trovo solo persone che parlano di cose complicate della NASA come arccos e roba di prodotti scalari vettoriali. Mi sembra di avere la testa in un frigorifero.

Alcuni guru della matematica qui che pensano che questo sia un semplice problema? Non penso che il linguaggio di programmazione sia importante qui, ma per coloro che pensano che lo sia: Java e Objective-C. Ne ho bisogno per entrambi, ma non l'ho etichettato per questi.

Risposte:


87

Se intendi l'angolo di cui P1 è il vertice, allora l'uso della Legge dei coseni dovrebbe funzionare:

ARccOS((P 12 2 + P 13 2 - P 23 2 ) / (2 * P 12 * P 13 ))

dove P 12 è la lunghezza del segmento da P1 a P2, calcolata da

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



@Rafa Firenze cos ^ -1 è una notazione comune per acos, ma acos è meno ambigua. en.wikipedia.org/wiki/Inverse_trigonometric_functions
geon

Lascio la modifica poiché non fa male a nulla, ma avendo i gradi Math / CS / EE, cos ^ -1 è sicuramente la notazione più comune.
Lance Roberts

1
Solo una manciata di lingue usa un accento circonflesso per "potere di", quindi se non vuoi chiamarlo arcos, digita cos⁻¹. (Se stai utilizzando un sistema operativo commerciale che rende difficile la digitazione degli esponenti, mi aspetto che ci sarebbero applicazioni per i keycap che potresti acquistare, o forse un plug-in del browser che potresti installare. Oppure puoi cercare sul web e copiare e incollare.)
Michael Scheper

1
@MichaelScheper, stavo usando solo il cursore nei commenti in cui html è limitato. Certamente userei solo la notazione sub / apice in qualsiasi risposta effettiva.
Lance Roberts

47

Diventa molto semplice se lo pensi come due vettori, uno dal punto P1 a P2 e uno da P1 a P3

quindi:
a = (p1.x - p2.x, p1.y - p2.y)
b = (p1.x - p3.x, p1.y - p3.y)

È quindi possibile invertire la formula del prodotto puntuale:
prodotto puntino
per ottenere l'angolo:
angolo tra due vettori

Ricorda che prodotto puntinosignifica solo: a1 * b1 + a2 * b2 (solo 2 dimensioni qui ...)


1
Ah magnitudo del vettore
Daniel Little

Controlla la soluzione atan2.
Luc Boissaye

25

Il modo migliore per gestire il calcolo dell'angolo è usare atan2(y, x)che dato un punto x, yrestituisca l'angolo da quel punto e l' X+asse rispetto all'origine.

Dato che il calcolo è

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

cioè fondamentalmente traduci i due punti per -P1(in altre parole traduci tutto in modo che P1finisca nell'origine) e poi consideri la differenza degli angoli assoluti di P3e di P2.

Il vantaggio di atan2è che viene rappresentato il cerchio completo (puoi ottenere qualsiasi numero compreso tra -π e π) dove invece acosdevi gestire diversi casi a seconda dei segni per calcolare il risultato corretto.

L'unico punto singolare per atan2è (0, 0)... il che significa che entrambi P2e P3devono essere diversi da P1come in quel caso non ha senso parlare di un angolo.


Grazie per la tua risposta. Era esattamente quello che stavo cercando. Soluzione semplice e puoi ottenere facilmente l'angolo antiorario se aggiungo solo 2pi quando il valore è negativo.
Mario

@marcpt: atan2è esattamente ciò che è necessario per questo problema, ma sembra che la maggior parte delle persone che arrivano a questa domanda non riesca a leggere o non riesca a capire perché le acossoluzioni basate su base sono cattive. Fortunatamente per me ho lasciato la fase "qualcuno ha torto su Internet" ( xkcd.com/386 ) molti anni fa e non ho intenzione di iniziare una lotta per difendere l'ovvio :-)
6502

Grazie per averlo sottolineato, ma puoi gestire il 3D in questo modo?
nicoco

1
@nicoco: in tre dimensioni come definisci l'angolo? Più specificamente, l'angolo può essere negativo o superiore a pi greco (180 gradi)? Due vettori non paralleli in 3d definiscono un piano, ma il piano può essere "visto" da due lati: guardato da un lato A apparirà "a sinistra" di B e dall'altro apparirà "a destra" .. .
6502

@ 6505 Grazie per la tua risposta, ho postato prima di riflettere sul mio problema. Ho capito ora, però.
nicoco

19

Faccio un esempio in JavaScript, ho combattuto molto con questo:

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

Bonus: esempio con HTML5-canvas


5
Puoi renderlo più efficiente facendo meno sqrte squadrando. Vedi la mia risposta qui (scritta in Ruby), o in questa demo aggiornata (JavaScript).
Phrogz

Potresti usare atan2 per una soluzione più semplice.
Luc Boissaye

15

Fondamentalmente quello che hai sono due vettori, un vettore da P1 a P2 e un altro da P1 a P3. Quindi tutto ciò di cui hai bisogno è una formula per calcolare l'angolo tra due vettori.

Dai un'occhiata qui per una buona spiegazione e la formula.

testo alternativo


12

Se stai pensando a P1 come al centro di un cerchio, stai pensando troppo complicato. Hai un triangolo semplice, quindi il tuo problema è risolvibile con la legge del coseno . Non c'è bisogno di alcuna trasformazione delle coordinate polari o qualcosa del genere. Supponiamo che le distanze siano P1-P2 = A, P2-P3 = B e P3-P1 = C:

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

Tutto quello che devi fare è calcolare la lunghezza delle distanze A, B e C.Queste sono facilmente disponibili dalle coordinate xey dei tuoi punti e dal teorema di Pitagora

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


Sono un po 'confuso su come implementarlo effettivamente poiché stai trattando P1 ecc.Come valori individuali piuttosto che (x, y)
Dominic

@Dominic Tobias: La notazione P1-P2 = Anon dovrebbe essere letta come "Per calcolare A, sottrai P2 da P1", ma come "Sto definendo A come la distanza da P1 a P2", che può quindi essere calcolata usando la seconda equazione. Volevo solo definire una scorciatoia per le distanze, per rendere le equazioni più leggibili.
Treb

8

Di recente mi sono imbattuto in un problema simile, solo che dovevo distinguere tra angoli positivi e negativi. Nel caso in cui questo sia utile a chiunque, consiglio lo snippet di codice che ho preso da questa mailing list sul rilevamento della rotazione su un evento touch per 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

Soluzione geometrica molto semplice con spiegazione

Pochi giorni fa, un cadde nello stesso problema e dovette sedersi con il libro di matematica. Ho risolto il problema combinando e semplificando alcune formule di base.


Consideriamo questa figura-

angolo

Vogliamo conoscere ϴ , quindi dobbiamo prima scoprire α e β . Ora, per qualsiasi linea retta-

y = m * x + c

Siano- A = (ax, ay) , B = (bx, by) e O = (ox, oy) . Quindi per la linea 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)

Allo stesso modo, per la riga OB -

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

Ora, abbiamo bisogno ϴ = β - α. Nella trigonometria abbiamo una formula-

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

Dopo aver sostituito il valore di tan α(da eqn-2) e tan b(da eqn-3) in eqn-4, e applicando la semplificazione otteniamo-

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

Così,

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

Questo è tutto!


Ora, prendi la seguente figura:

angolo

Questo metodo C # o Java calcola l'angolo ( ϴ ) -

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

Come può essere utilizzato questo metodo per un triangolo equilatero?
Vikrant

1
Bene, la tua risposta ora funziona bene. Era un problema logico nella mia settimana di codice prima.
Vikrant

6

In Objective-C potresti farlo da

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

Oppure leggi di più qui


7
Ehm, no. Ci sono tre punti, il centro non è a (0,0), e questo dà un angolo di un triangolo rettangolo, non l'angolo dell'apice. E che tipo di nome è "xpoint" per un angolo?
Jim Balter

4

Hai menzionato un angolo firmato (-90). In molte applicazioni gli angoli possono avere segni (positivi e negativi, vedere http://en.wikipedia.org/wiki/Angle ). Se i punti sono (diciamo) P2 (1,0), P1 (0,0), P3 (0,1) allora l'angolo P3-P1-P2 è convenzionalmente positivo (PI / 2) mentre l'angolo P2-P1- P3 è negativo. L'uso delle lunghezze dei lati non farà distinzione tra + e - quindi se questo è importante dovrai usare vettori o una funzione come Math.atan2 (a, b).

Gli angoli possono anche estendersi oltre 2 * PI e sebbene questo non sia rilevante per la domanda attuale, era sufficientemente importante che io scrivessi la mia classe Angolo (anche per assicurarmi che gradi e radianti non si confondessero). Le domande sul fatto che l'angolo1 sia minore dell'angolo2 dipende in modo critico da come vengono definiti gli angoli. Potrebbe anche essere importante decidere se una linea (-1,0) (0,0) (1,0) è rappresentata come Math.PI o -Math.PI


4

il mio programma demo di angolo

Recentemente, anch'io ho lo stesso problema ... In Delphi è molto simile 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;

2

Ecco un metodo C # per restituire l'angolo (0-360) in senso antiorario dall'orizzontale per un punto su un cerchio.

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

Salute, 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

c'è una risposta semplice per questo usando la matematica delle scuole superiori.

Supponiamo che tu abbia 3 punti

Per ottenere l'angolo dal punto A al punto B

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

Per ottenere l'angolo dal punto 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
}

Ho appena usato questo codice nel recente progetto che ho fatto, cambia la B in P1 .. potresti anche rimuovere il "180 +" se vuoi


-1

beh, le altre risposte sembrano coprire tutto ciò che è richiesto, quindi vorrei aggiungerlo solo se stai usando JMonkeyEngine:

Vector3f.angleBetween(otherVector)

poiché è quello che sono venuto qui a cercare :)


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

}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.