[algorithm] Wie berechne ich einen Winkel von drei Punkten?


Answers

Es wird sehr einfach, wenn Sie es als zwei Vektoren betrachten, einen von Punkt P1 zu P2 und einen von P1 zu P3

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

Sie können dann die Punktproduktformel invertieren:

um den Winkel zu bekommen:

Erinnere dich daran bedeutet nur: a1 * b1 + a2 * b2 (nur 2 Dimensionen hier ...)

Question

Sagen wir, du hast folgendes:

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

Angenommen, P1 ist der Mittelpunkt eines Kreises. Es ist immer das Gleiche. Ich möchte den Winkel, der aus P2 und P3 , also den Winkel, der neben P1 . Der innere Winkel, um genau zu sein. Es wird immer ein spitzer Winkel sein, also weniger als -90 Grad.

Ich dachte: Mann, das ist einfache Geometrie Mathe. Aber ich habe jetzt nach einer Formel für ungefähr 6 Stunden gesucht, und finde nur Leute, die über komplizierte NASA-Sachen wie Arccos und Vektorskalarprodukte reden. Mein Kopf fühlt sich an wie in einem Kühlschrank.

Einige Mathegurus, die das für ein einfaches Problem halten? Ich glaube nicht, dass die Programmiersprache hier eine Rolle spielt, sondern für diejenigen, die denken, dass dies der Fall ist: Java und Ziel-c. Ich brauche es für beide, aber habe es nicht für diese markiert.




In Objective-C könnten Sie dies tun, indem Sie

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

Oder lesen Sie here mehr




Kürzlich habe ich auch das gleiche Problem ... In Delphi Es ist Objective-C sehr ähnlich.

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;



Der beste Weg, mit der Winkelberechnung umzugehen, besteht darin, atan2(y, x) , das bei einem Punkt x, y den Winkel von diesem Punkt und die X+ -Achse in Bezug auf den Ursprung zurückgibt.

Vorausgesetzt, dass die Berechnung ist

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

-P1 Sie übersetzen die beiden Punkte grundsätzlich mit -P1 (Sie übersetzen also alles so, dass P1 im Ursprung steht) und betrachten dann die Differenz der absoluten Winkel von P3 und P2 .

Die Vorteile von atan2 sind, dass der volle Kreis dargestellt wird (Sie können eine beliebige Zahl zwischen -π und π erhalten), wobei Sie stattdessen mit acos mehrere Fälle behandeln müssen, abhängig von den Vorzeichen, um das korrekte Ergebnis zu berechnen.

Der einzige singuläre Punkt für atan2 ist (0, 0) ... was bedeutet, dass sowohl P2 als auch P3 von P1 verschieden sein müssen, da es in diesem Fall nicht sinnvoll ist, über einen Winkel zu sprechen.




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




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

}




Ich stieß in letzter Zeit auf ein ähnliches Problem, nur musste ich zwischen einem positiven und einem negativen Winkel unterscheiden. Falls dies für irgendjemanden von Nutzen ist, empfehle ich das Code-Snippet, das ich aus dieser Mailingliste entnommen habe, über die Erkennung der Rotation über ein Touch-Event für 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;
 }



Grundsätzlich haben Sie zwei Vektoren, einen Vektor von P1 zu P2 und einen anderen von P1 zu P3. Sie brauchen also nur eine Formel, um den Winkel zwischen zwei Vektoren zu berechnen.

Schaue here nach einer guten Erklärung und der Formel.




Related