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.
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)
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:
pour obtenir l'angle:
Rappelez-vous que signifie simplement: a1 * b1 + a2 * b2 (seulement 2 dimensions ici ...)
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.
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
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.
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)
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;
}
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
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
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;
}
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
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.
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;
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
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))))