web-dev-qa-db-fra.com

Comment calculer un angle à partir de trois points?

Disons que vous avez ceci:

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

Supposons que P1 est le centre d'un cercle. C'est toujours la même chose ... Je veux l'angle composé par P2 et P3, autrement dit l'angle qui est à côté de P1. L'angle intérieur pour être précis. Ce sera toujours un angle aigu, donc moins de -90 degrés.

J'ai pensé: Mec, c'est de la mathématique géométrique simple. Mais je cherche une formule depuis environ 6 heures maintenant et je ne trouve que des gens qui parlent de choses compliquées de la NASA telles que des arccos et des produits vectoriels scalaires. J'ai la tête dans un frigo.

Certains gourous des mathématiques pensent que c'est un problème simple? Je ne pense pas que le langage de programmation importe ici, mais pour ceux qui le pensent: Java et objective-c. J'en ai besoin pour les deux, mais je ne l'ai pas tagué pour ceux-ci.

120
HelloMoon

Si vous voulez dire que l'angle dont P1 est le sommet, utiliser la loi des cosinus devrait alors fonctionner:

arccos((P122 + P132 - P232)/(2 * P12 * P13))

où P12 est la longueur du segment de P1 à P2, calculée par

sqrt ((P1x - P2x)2 + (P1y - P2y)2)

87
Lance Roberts

Cela devient très simple si vous le considérez comme deux vecteurs, l'un du point P1 à P2 et l'autre de P1 à P3. 

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

Vous pouvez ensuite inverser la formule du produit scalaire:
dot product
pour obtenir l'angle:
angle between two vectors

Rappelez-vous que dot product signifie simplement: a1 * b1 + a2 * b2 (seulement 2 dimensions ici ...)

47
Andrea Ambu

La meilleure façon de traiter le calcul d'angle est d'utiliser atan2(y, x) qui donne un point x, y qui renvoie l'angle à partir de ce point et de l'axe X+ par rapport à l'origine.

Étant donné que le calcul est

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

c’est-à-dire que vous traduisez essentiellement les deux points par -P1 (autrement dit, vous traduisez tout pour que P1 aboutisse à l’origine), puis vous considérez la différence entre les angles absolus de P3 et de P2.

Les avantages de atan2 sont que le cercle complet est représenté (vous pouvez obtenir n’importe quel nombre compris entre -π et π), où avec acos vous devez gérer plusieurs cas en fonction des signes pour obtenir le résultat correct.

Le seul point singulier pour atan2 est (0, 0)..., ce qui signifie que P2 et P3 doivent être différents de P1 car dans ce cas, cela n'a pas de sens de parler d'un angle.

25
6502

Laissez-moi vous donner un exemple en JavaScript, je me suis beaucoup battu avec ça:

/**
 * 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: Exemple avec HTML5-canvas

19
shaman.sir

En gros, vous avez deux vecteurs, un vecteur de P1 à P2 et un autre de P1 à P3. Il suffit donc d’une formule pour calculer l’angle entre deux vecteurs.

Regardez ici pour une bonne explication et la formule.

alt text

15
Andre Miller

Si vous pensez que P1 est le centre d'un cercle, vous pensez que vous êtes trop compliqué… .. Vous avez un simple triangle, votre problème peut donc être résolu avec la loi des cosinus . Pas besoin de transformations de coordonnées polaires ni de telles choses. Disons que les distances sont P1-P2 = A, P2-P3 = B et P3-P1 = C:

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

Tout ce que vous avez à faire est de calculer la longueur des distances A, B et C . Celles-ci sont facilement disponibles à partir des coordonnées x et y de vos points et de théorème de Pythagore

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

12
Treb

J'ai rencontré un problème similaire récemment, il me suffisait de distinguer les angles positif et négatif. Si cela peut être utile à tout le monde, je recommande l'extrait de code que j'ai extrait de cette liste de diffusion concernant la détection de la rotation sur un événement tactile pour 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;
 }
8
Marc

Solution géométrique très simple avec explication

Il y a quelques jours, un est tombé dans le même problème et a dû s'asseoir avec le livre de mathématiques. J'ai résolu le problème en combinant et en simplifiant certaines formules de base.


Permet de considérer ce chiffre

 angle

Nous voulons savoir ϴ, nous devons donc commencer par découvrir α et β. Maintenant, pour toute ligne droite 

y = m * x + c

Soit- A = (ax, ay), B = (bx, par), et O = (ox, oy). Donc pour la ligne 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 même, pour la ligne OB -

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

Maintenant, nous avons besoin de ϴ = β - α. En trigonométrie, nous avons une formule

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

Après avoir remplacé la valeur de tan α (de eqn-2) et de tan b (de eqn-3) dans eqn-4, et en appliquant une simplification,

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

Alors,

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

C'est ça! 


Maintenant, prenez la figure suivante

 angle

Cette méthode C # ou Java calcule l’angle (ϴ) -

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

En Objective-C, vous pouvez le faire en

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

Ou en lire plus ici

6

Vous avez mentionné un angle signé (-90). Dans de nombreuses applications, les angles peuvent avoir des signes (positifs et négatifs, voir http://en.wikipedia.org/wiki/Angle ). Si les points sont (disons) P2 (1,0), P1 (0,0), P3 (0,1), l'angle P3-P1-P2 est généralement positif (PI/2), tandis que l'angle P2-P1- P3 est négatif. Utiliser les longueurs des côtés ne fera pas la distinction entre + et - donc si cela est important, vous devrez utiliser des vecteurs ou une fonction telle que Math.atan2 (a, b).

Les angles peuvent également s'étendre au-delà de 2 * PI et bien que cela ne soit pas pertinent pour la question actuelle, il était suffisamment important que j'ai écrit ma propre classe Angle (également pour m'assurer que les degrés et les radians ne sont pas mélangés). La question de savoir si angle1 est inférieur à angle2 dépend de manière critique de la définition des angles. Il peut également être important de décider si une ligne (-1,0) (0,0) (1,0) est représentée par Math.PI ou -Math.PI.

4
peter.murray.rust

my angle demo program

Récemment, j'ai moi aussi le même problème ... À Delphi .__, il ressemble beaucoup à 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;
4
antonio

Voici une méthode C # pour renvoyer l'angle (0-360) dans le sens inverse des aiguilles d'une montre par rapport à l'horizontale pour un point sur un cercle.

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

A bientôt, Paul

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

2
Dominic