web-dev-qa-db-fra.com

Comment savoir si un point est à droite ou à gauche d'une ligne

J'ai un ensemble de points. Je veux les séparer en 2 ensembles distincts. Pour ce faire, je choisis deux points ( a et b ) et trace une ligne imaginaire entre eux. Maintenant, je veux avoir tous les points qui restent de cette ligne dans un ensemble et ceux qui sont juste de cette ligne dans l'autre ensemble.

Comment puis-je savoir pour un point donné z s'il est placé à gauche ou à droite? J'ai essayé de calculer l'angle entre azb -, les angles inférieurs à 180 sont situés à droite, ceux supérieurs à 180 à gauche - mais, à cause de la définition d'ArcCos, les angles calculés sont toujours inférieurs à 180 °. . Existe-t-il une formule permettant de calculer des angles supérieurs à 180 ° (ou toute autre formule permettant de choisir le côté droit ou gauche)?

108
Aaginor

Utilisez le signe du déterminant de vecteurs (AB,AM), où M(X,Y) est le point de requête:

position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))

Il s'agit de 0 sur la ligne et +1 d'un côté, -1 de l'autre.

170
Eric Bainville

Essayez ce code qui utilise un produit croisé :

public bool isLeft(Point a, Point b, Point c){
     return ((b.X - a.X)*(c.Y - a.Y) - (b.Y - a.Y)*(c.X - a.X)) > 0;
}

a = point 1 de la ligne; b = point 2 de la ligne; c = point contre lequel vérifier.

Si la formule est égale à 0, les points sont colinéaires.

Si la ligne est horizontale, cela retourne vrai si le point est au dessus de la ligne.

195
jethro

Vous regardez le signe du déterminant de 

| x2-x1  x3-x1 |
| y2-y1  y3-y1 |

Ce sera positif pour les points d'un côté et négatif de l'autre (et nul pour les points de la ligne elle-même).

40
AVB

Le vecteur (y1 - y2, x2 - x1) est perpendiculaire à la ligne et pointe toujours vers la droite (ou toujours vers la gauche si l'orientation du plan est différente de la mienne).

Vous pouvez ensuite calculer le produit scalaire de ce vecteur et de (x3 - x1, y3 - y1) pour déterminer si le point se trouve du même côté de la ligne que le vecteur perpendiculaire (produit scalaire> 0) ou non.

9
Victor Nicollet

Je l'ai implémenté en Java et ai exécuté un test unitaire (source ci-dessous). Aucune des solutions ci-dessus ne fonctionne. Ce code passe le test unitaire. Si quelqu'un trouve un test unitaire qui ne passe pas, s'il vous plaît faites le moi savoir.

Code: REMARQUE: nearlyEqual(double,double) renvoie vrai si les deux nombres sont très proches.

/*
 * @return integer code for which side of the line ab c is on.  1 means
 * left turn, -1 means right turn.  Returns
 * 0 if all three are on a line
 */
public static int findSide(
        double ax, double ay, 
        double bx, double by,
        double cx, double cy) {
    if (nearlyEqual(bx-ax,0)) { // vertical line
        if (cx < bx) {
            return by > ay ? 1 : -1;
        }
        if (cx > bx) {
            return by > ay ? -1 : 1;
        } 
        return 0;
    }
    if (nearlyEqual(by-ay,0)) { // horizontal line
        if (cy < by) {
            return bx > ax ? -1 : 1;
        }
        if (cy > by) {
            return bx > ax ? 1 : -1;
        } 
        return 0;
    }
    double slope = (by - ay) / (bx - ax);
    double yIntercept = ay - ax * slope;
    double cSolution = (slope*cx) + yIntercept;
    if (slope != 0) {
        if (cy > cSolution) {
            return bx > ax ? 1 : -1;
        }
        if (cy < cSolution) {
            return bx > ax ? -1 : 1;
        }
        return 0;
    }
    return 0;
}

Voici le test unitaire:

@Test public void testFindSide() {
    assertTrue("1", 1 == Utility.findSide(1, 0, 0, 0, -1, -1));
    assertTrue("1.1", 1 == Utility.findSide(25, 0, 0, 0, -1, -14));
    assertTrue("1.2", 1 == Utility.findSide(25, 20, 0, 20, -1, 6));
    assertTrue("1.3", 1 == Utility.findSide(24, 20, -1, 20, -2, 6));

    assertTrue("-1", -1 == Utility.findSide(1, 0, 0, 0, 1, 1));
    assertTrue("-1.1", -1 == Utility.findSide(12, 0, 0, 0, 2, 1));
    assertTrue("-1.2", -1 == Utility.findSide(-25, 0, 0, 0, -1, -14));
    assertTrue("-1.3", -1 == Utility.findSide(1, 0.5, 0, 0, 1, 1));

    assertTrue("2.1", -1 == Utility.findSide(0,5, 1,10, 10,20));
    assertTrue("2.2", 1 == Utility.findSide(0,9.1, 1,10, 10,20));
    assertTrue("2.3", -1 == Utility.findSide(0,5, 1,10, 20,10));
    assertTrue("2.4", -1 == Utility.findSide(0,9.1, 1,10, 20,10));

    assertTrue("vertical 1", 1 == Utility.findSide(1,1, 1,10, 0,0));
    assertTrue("vertical 2", -1 == Utility.findSide(1,10, 1,1, 0,0));
    assertTrue("vertical 3", -1 == Utility.findSide(1,1, 1,10, 5,0));
    assertTrue("vertical 3", 1 == Utility.findSide(1,10, 1,1, 5,0));

    assertTrue("horizontal 1", 1 == Utility.findSide(1,-1, 10,-1, 0,0));
    assertTrue("horizontal 2", -1 == Utility.findSide(10,-1, 1,-1, 0,0));
    assertTrue("horizontal 3", -1 == Utility.findSide(1,-1, 10,-1, 0,-9));
    assertTrue("horizontal 4", 1 == Utility.findSide(10,-1, 1,-1, 0,-9));

    assertTrue("positive slope 1", 1 == Utility.findSide(0,0, 10,10, 1,2));
    assertTrue("positive slope 2", -1 == Utility.findSide(10,10, 0,0, 1,2));
    assertTrue("positive slope 3", -1 == Utility.findSide(0,0, 10,10, 1,0));
    assertTrue("positive slope 4", 1 == Utility.findSide(10,10, 0,0, 1,0));

    assertTrue("negative slope 1", -1 == Utility.findSide(0,0, -10,10, 1,2));
    assertTrue("negative slope 2", -1 == Utility.findSide(0,0, -10,10, 1,2));
    assertTrue("negative slope 3", 1 == Utility.findSide(0,0, -10,10, -1,-2));
    assertTrue("negative slope 4", -1 == Utility.findSide(-10,10, 0,0, -1,-2));

    assertTrue("0", 0 == Utility.findSide(1, 0, 0, 0, -1, 0));
    assertTrue("1", 0 == Utility.findSide(0,0, 0, 0, 0, 0));
    assertTrue("2", 0 == Utility.findSide(0,0, 0,1, 0,2));
    assertTrue("3", 0 == Utility.findSide(0,0, 2,0, 1,0));
    assertTrue("4", 0 == Utility.findSide(1, -2, 0, 0, -1, 2));
}
5
Al Globus

En utilisant l'équation de la ligneab, obtenez la coordonnée x de la ligne à la même coordonnée y que le point à trier.

  • Si x du point> x de la ligne, le point est à droite de la ligne.
  • Si le point est X <la ligne x, le point est à gauche de la ligne.
  • Si le point de x == ligne x, le point est sur la ligne.
4
mbeckish

Commencez par vérifier si vous avez une ligne verticale:

if (x2-x1) == 0
  if x3 < x2
     it's on the left
  if x3 > x2
     it's on the right
  else
     it's on the line

Puis, calcule la pente: m = (y2-y1)/(x2-x1)

Créez ensuite une équation de la ligne en utilisant la forme de la pente du point: y - y1 = m*(x-x1) + y1. Par souci d’explication, simplifiez-le pour l’intercepter sous forme de pente (non nécessaire dans votre algorithme): y = mx+b.

Maintenant, connectez (x3, y3) pour x et y. Voici un pseudocode détaillant ce qui devrait arriver:

if m > 0
  if y3 > m*x3 + b
    it's on the left
  else if y3 < m*x3 + b
    it's on the right
  else
    it's on the line
else if m < 0
  if y3 < m*x3 + b
    it's on the left
  if y3 > m*x3+b
    it's on the right
  else
    it's on the line
else
  horizontal line; up to you what you do
3
maksim

en gros, je pense qu’il existe une solution beaucoup plus simple et simple, pour tout polygone donné, disons composée de quatre sommets (p1, p2, p3, p4), trouver les deux sommets extrêmes opposés dans le polygone, dans un autre mots, recherchez par exemple le sommet le plus en haut à gauche (disons p1) et le sommet opposé qui se situe au plus en bas à droite (disons). Par conséquent, étant donné votre point de test C (x, y), vous devez maintenant effectuer une double vérification entre C et p1 et C et p4:

si cx> p1x ET cy> p1y ==> signifie que C est inférieur et à droite de p1 next si cx <p2x ET cy <p2y ==> signifie que C est supérieur et à gauche de p4

conclusion, C est à l'intérieur du rectangle.

Merci :) 

1
Mohamed

La réponse de AVB en Ruby

det = Matrix[
  [(x2 - x1), (x3 - x1)],
  [(y2 - y1), (y3 - y1)]
].determinant

Si det est positif, son supérieur, si négatif, inférieur. Si 0, c'est sur la ligne.

1
boulder_ruby

Voici une version, utilisant à nouveau la logique de produits croisés, écrite en Clojure. 

(defn is-left? [line point]
  (let [[[x1 y1] [x2 y2]] (sort line)
        [x-pt y-pt] point]
    (> (* (- x2 x1) (- y-pt y1)) (* (- y2 y1) (- x-pt x1)))))

Exemple d'utilisation:

(is-left? [[-3 -1] [3 1]] [0 10])
true

Ce qui revient à dire que le point (0, 10) se situe à gauche de la droite déterminée par (-3, -1) et (3, 1).

REMARQUE: cette implémentation résout un problème qu'aucun autre (jusqu'à présent) ne fait! L'ordre compte lorsque vous attribuez les points qui déterminent la ligne. C'est-à-dire que c'est une "ligne dirigée", dans un certain sens. Donc, avec le code ci-dessus, cette invocation produit également le résultat de true:

(is-left? [[3 1] [-3 -1]] [0 10])
true

C'est à cause de cet extrait de code:

(sort line)

Enfin, comme pour les autres solutions basées sur plusieurs produits, cette solution renvoie un booléen et ne donne pas un troisième résultat en termes de colinéarité. Mais cela donnera un résultat logique, par exemple:

(is-left? [[1 1] [3 1]] [10 1])
false
1
Purplejacket

En supposant que les points sont (Ax, Ay) (Bx, By) et (Cx, Cy), vous devez calculer:

(Bx - Axe) * (Cy - Ay) - (Par - Ay) * (Cx - Axe)

Cela sera égal à zéro si le point C est sur la ligne formée par les points A et B et aura un signe différent en fonction du côté. Son côté dépend de l'orientation de vos coordonnées (x, y), mais vous pouvez insérer des valeurs de test pour A, B et C dans cette formule afin de déterminer si les valeurs négatives se trouvent à gauche ou à droite.

1
user2154342

Je voulais proposer une solution inspirée de la physique. 

Imaginez une force appliquée le long de la ligne et vous mesurez le couple de la force autour du point. Si le couple est positif (dans le sens inverse des aiguilles d'une montre), le point est situé à gauche de la ligne, mais si le couple est négatif, le point correspond à la droite de la ligne.

Donc, si le vecteur de force est égal à la portée des deux points définissant la ligne 

fx = x_2 - x_1
fy = y_2 - y_1

vous testez le côté d'un point (px,py) en fonction du signe du test suivant

var torque = fx*(py-y_1)-fy*(px-x_1)
if  torque>0  then
     "point on left side"
else if torque <0 then
     "point on right side"  
else
     "point on line"
end if
0
ja72