Comment savoir si un point est à droite ou à gauche d'une ligne
-
21-09-2019 - |
Question
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 tracer une ligne imaginaire entre eux. Maintenant, je veux avoir tous les points qui sont laissés de cette ligne dans un ensemble et ceux qui ont raison de cette ligne dans l'autre ensemble.
Comment puis-je dire à un moment donné z si elle est à gauche ou à droite fixés? J'ai essayé de calculer l'angle entre azb - angles inférieurs à 180 sont sur le côté droit, supérieur à 180 sur le côté gauche - mais à cause de la définition de ArcCos, les angles calculés sont toujours plus petits à 180 °. Y at-il une formule pour calculer des angles supérieurs à 180 ° (ou de toute autre formule de choisir le côté droit ou gauche)?
La solution
Utilisez le signe du déterminant de (AB,AM)
vecteurs, où M(X,Y)
est le point d'interrogation:
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
Il est 0
sur la ligne, et +1
d'un côté, -1
de l'autre côté.
Autres conseils
Essayez ce code qui utilise un :
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;
}
Où a = point de la ligne 1; b = point de la ligne 2; c = point à vérifier contre.
Si la formule est égale à 0, les points sont colinéaires.
Si la ligne est horizontale, alors ce retourne vrai si le point est au-dessus de la ligne.
Vous regardez le signe du déterminant de
| x2-x1 x3-x1 |
| y2-y1 y3-y1 |
Il sera positif pour les points d'un côté et négatif de l'autre (et à zéro pour les points sur la ligne elle-même).
Le vecteur (y1 - y2, x2 - x1)
est perpendiculaire à la ligne, et toujours pointant vers la droite (ou toujours pointant vers la gauche, si vous l'orientation plane est différente de la mienne).
On peut alors calculer le produit scalaire de ce vecteur et (x3 - x1, y3 - y1)
pour déterminer si le point se trouve sur le même côté de la ligne que le vecteur perpendiculaire (produit scalaire> 0
) ou non.
Je mis en place ce en Java et couru un test unitaire (source ci-dessous). Aucun des travaux de solutions ci-dessus. Ce code passe le test unitaire. Si quelqu'un trouve un test unitaire qui ne passe pas, s'il vous plaît laissez-moi savoir.
code: REMARQUE:. nearlyEqual(double,double)
retourne vrai si les deux chiffres 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));
}
En utilisant les de la ligne ab , obtenir le coordonnée x sur la ligne à la même coordonnée y en tant que point à trier.
- Si de point x> x de la ligne de, le point se trouve à droite de la ligne.
- Si le point de
x
- Si le x de points == x de la ligne, le point est sur la ligne.
Vérifiez d'abord 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
Ensuite, calculer la pente: m = (y2-y1)/(x2-x1)
Ensuite, créer une équation de la droite en utilisant la forme de la pente du point: y - y1 = m*(x-x1) + y1
. Par souci de mon explication, simplifier la forme pente à l'origine (pas nécessaire dans votre algorithme): y = mx+b
Maintenant, branchez (x3, y3)
pour x
et y
. Voici quelques détails pseudocode ce qui devrait se produire:
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
En fait, je pense qu'il ya une solution qui est beaucoup plus facile et simple, pour tout polygone donné, permet de dire composé de quatre sommets (p1, p2, p3, p4), trouver les deux sommets extrêmes opposés dans le polygone , en d'autres mots, trouver par exemple le plus haut sommet gauche (disons p1) et le sommet opposé qui est situé au plus bas à droite (laisse dire). Par conséquent, compte tenu de votre point de test C (x, y), maintenant vous devez faire une double vérification entre C et p1 et C et p4:
si cx> p1x ET cy> P1Y ==> signifie que C est plus faible et à droite de p1
suivant
si cx
conclusion, C est à l'intérieur du rectangle.
Merci :)
En supposant que les points sont (Ax, Ay) (Bx, By) et (cx, cy), vous devez calculer:
(Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax)
Ce sera égale à 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é. De quel côté cela est dépend de l'orientation de (x, y) les coordonnées, mais on peut brancher des valeurs de test pour A, B et C dans la formule suivante pour déterminer si les valeurs négatives sont à la gauche ou vers la droite.
@ réponse de AVB en rubis
det = Matrix[
[(x2 - x1), (x3 - x1)],
[(y2 - y1), (y3 - y1)]
].determinant
Si det
est positif de son ci-dessus, si elle est négative son ci-dessous. Si 0, son sur la ligne.
Voici une version, en utilisant à nouveau la logique de produit croisé, écrit 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) est à la gauche de la ligne déterminée par (-3, -1) et (3, 1).
NOTE: Cette implémentation résout un problème qu'aucun des autres (jusqu'à présent) fait! Questions d'ordre en donnant les points qui déterminent la ligne. À savoir, il 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 ce bout de code:
(sort line)
Enfin, comme les autres solutions multiproduits, cette solution retourne un booléen, et ne donne pas un troisième résultat pour colinéarité. Mais il donnera un résultat qui est logique, par exemple:.
(is-left? [[1 1] [3 1]] [10 1])
false
Je voulais fournir une solution inspirée par la physique.
Imaginez une force appliquée le long de la ligne et que vous mesurez le couple de la force autour du point. Si le couple est positif (sens anti-horaire), le point est de la « gauche » de la ligne, mais si le couple est le point négatif est le « droit » de la ligne.
Donc, si le vecteur de force est égale à la durée des deux points définissant la ligne
fx = x_2 - x_1
fy = y_2 - y_1
test pour le côté d'un point (px,py)
basé sur le 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
Une autre façon de se faire une idée des solutions fournies par les fileyeurs est de comprendre un peu les implications de la géométrie.
Soit pqr = [P, Q, R] sont des points qui forme un plan qui est divisé en 2 parties par la ligne [P, R] . Nous voulons savoir si deux points sur pqr avion, A, B, sont du même côté.
Tout point T sur le plan de pqr peut être représenté avec 2 vecteurs: v = PQ et u = RQ, comme suit:
T »= T-Q = i * v + j * u
Maintenant, les implications de la géométrie:
- i + j = 1: T sur la ligne de pr
- i + j <1: T sur Sq
- i + j> 1: T sur SNQ
- i + j = 0: T = Q
- i + j <0: T sur Sq et au-delà Q.
i+j: <0 0 <1 =1 >1
---------Q------[PR]--------- <== this is PQR plane
^
pr line
En général,
- i + j est une mesure de la distance T est la Q ou de la ligne [P, R] et
- le signe i + j-1 implique la sideness T.
Les autres significations de la géométrie de i et j (non lié à cette solution) sont:
- i , j sont les scalaires pour T dans un nouveau système de coordonnées où v, u sont les nouveaux axes et Q est la nouvelle origine;
- i , j peut être considéré comme force de traction P, R , respectivement. Plus i , T est la plus loin R (plus grande traction dans P ).
La valeur de i, j peut être obtenue en résolvant les équations:
i*vx + j*ux = T'x
i*vy + j*uy = T'y
i*vz + j*uz = T'z
Alors nous donne 2 points, A, B sur le plan:
A = a1 * v + a2 * u
B = b1 * v + b2 * u
Si A, B sont du même côté, ce sera vrai:
sign(a1+a2-1) = sign(b1+b2-1)
Notez que cela vaut également pour la question suivante: sont A, B dans le même côté du plan [P, Q, R] , dans laquelle:
T = i * P + j * Q + k * R
et i + j + k = 1 implique que T est sur le plan [P, Q, R] et le signe de i + j + k-1 implique son sideness. De ce que nous avons:
A = a1 * P + a2 * Q + a3 * R
B = b1 * P + b2 * Q + b3 * R
et A, B sont sur le même côté du plan [P, Q, R] si
sign(a1+a2+a3-1) = sign(b1+b2+b3-1)