web-dev-qa-db-de.com

So bestimmen Sie, ob sich ein Punkt rechts oder links von einer Linie befindet

Ich habe eine Reihe von Punkten. Ich möchte sie in zwei verschiedene Sätze aufteilen. Dazu wähle ich zwei Punkte aus ( a und b ) und zeichne eine imaginäre Linie zwischen ihnen. Jetzt möchte ich alle Punkte haben, die von dieser Linie in einem Satz übrig sind, und diejenigen, die von dieser Linie in dem anderen Satz rechts liegen.

Wie kann ich für einen bestimmten Punkt z feststellen, ob er links oder rechts ist? Ich habe versucht, den Winkel zwischen azb - Winkeln kleiner als 180 auf der rechten Seite zu berechnen, größer als 180 auf der linken Seite. Aufgrund der Definition von ArcCos sind die berechneten Winkel jedoch immer kleiner als 180 ° . Gibt es eine Formel zur Berechnung von Winkeln, die größer als 180 ° ist (oder eine andere Formel zur Wahl der rechten oder linken Seite)?

108
Aaginor

Verwenden Sie das Vorzeichen der Vektordeterminante (AB,AM), wobei M(X,Y) der Abfragepunkt ist:

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

Es ist 0 in der Zeile und +1 auf der einen Seite, -1 auf der anderen Seite.

170
Eric Bainville

Versuchen Sie diesen Code, der ein cross product verwendet:

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

Wobei a = Linienpunkt 1; b = Linienpunkt 2; c = zu überprüfender Punkt.

Wenn die Formel gleich 0 ist, sind die Punkte kolinear.

Wenn die Linie horizontal ist, wird true zurückgegeben, wenn der Punkt über der Linie liegt.

195
jethro

Du siehst das Zeichen der Determinante von 

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

Sie ist auf der einen Seite positiv und auf der anderen Seite negativ (und auf der Linie selbst auf Null).

40
AVB

Der Vektor (y1 - y2, x2 - x1) steht senkrecht zu der Linie und zeigt immer nach rechts (oder immer nach links, wenn die Ebenenorientierung von meiner abweicht).

Sie können dann das Punktprodukt dieses Vektors und (x3 - x1, y3 - y1) berechnen, um zu bestimmen, ob der Punkt auf derselben Seite der Linie wie der senkrechte Vektor liegt (Punktprodukt> 0) oder nicht.

9
Victor Nicollet

Ich habe dies in Java implementiert und einen Komponententest durchgeführt (Quelle unten). Keine der oben genannten Lösungen funktioniert. Dieser Code besteht den Komponententest. Wenn jemand einen Unit-Test findet, der nicht bestanden wird, lass es mich wissen.

Code: HINWEIS: nearlyEqual(double,double) gibt true zurück, wenn die beiden Zahlen sehr nahe beieinander liegen.

/*
 * @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;
}

Hier ist der Unit-Test:

@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

Ermitteln Sie mit der Gleichung der Linieab die x-Koordinate der Linie an derselben y-Koordinate wie der zu sortierende Punkt.

  • Wenn Punkt x> Linie x ist, befindet sich der Punkt rechts von der Linie.
  • Wenn Punkt X <Zeile x ist, befindet sich der Punkt links von der Linie.
  • Wenn Punkt x == Linie x ist, befindet sich der Punkt auf der Linie.
4
mbeckish

Prüfen Sie zunächst, ob Sie eine vertikale Linie haben:

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

Berechnen Sie dann die Steigung: m = (y2-y1)/(x2-x1)

Erstellen Sie dann eine Gleichung der Linie unter Verwendung der Punktneigungsform: y - y1 = m*(x-x1) + y1. Vereinfachen Sie es zu meiner Erklärung auf die Form eines Steigungsabschnitts (in Ihrem Algorithmus nicht erforderlich): y = mx+b.

Stecken Sie jetzt (x3, y3) für x und y ein. Hier ist ein Pseudocode, der genau beschreibt, was passieren soll:

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

im Grunde denke ich, dass es eine Lösung gibt, die für jedes gegebene Polygon viel einfacher und geradliniger ist, beispielsweise aus vier Scheitelpunkten (p1, p2, p3, p4) bestehen, die zwei extrem entgegengesetzten Scheitelpunkte im Polygon finden, in einem anderen Wörter, finden Sie zum Beispiel den äußersten linken Scheitelpunkt (sagen wir p1) und den gegenüberliegenden Scheitelpunkt, der sich am weitesten rechts unten befindet (sagen wir). Wenn Sie nun Ihren Testpunkt C (x, y) haben, müssen Sie nun noch einmal zwischen C und p1 und C und p4 prüfen:

wenn cx> p1x AND cy> p1y ==> bedeutet, dass C unterhalb und rechts von p1 liegt. next wenn cx <p2x AND cy <p2y ==> bedeutet, dass C oben und links von p4 steht

fazit: C ist innerhalb des Rechtecks.

Vielen Dank :) 

1
Mohamed

@ AVBs Antwort in Ruby

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

Wenn det positiv ist, ist es oben; Wenn 0, ist es in der Zeile.

1
boulder_ruby

Hier ist eine Version, die wiederum die produktübergreifende Logik verwendet und in Clojure geschrieben ist. 

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

Verwendungsbeispiel:

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

Was bedeutet, dass der Punkt (0, 10) links von der durch (-3, -1) und (3, 1) bestimmten Linie liegt.

HINWEIS: Diese Implementierung löst ein Problem, das keine der anderen (bisher) hat! Reihenfolge zählt bei der Angabe der Punkte, die die Linie bestimmen. Das heißt, es ist in gewissem Sinne eine "gerichtete Linie". Mit dem obigen Code erzeugt dieser Aufruf auch das Ergebnis von true:

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

Das liegt an diesem Codeausschnitt:

(sort line)

Wie auch bei den anderen Cross-Product-basierten Lösungen gibt diese Lösung einen Boolean-Wert zurück und liefert kein drittes Ergebnis für die Kollinearität. Aber es wird ein Ergebnis geben, das sinnvoll ist, z.

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

Angenommen, die Punkte sind (Axt, Ay) (Bx, By) und (Cx, Cy), müssen Sie Folgendes berechnen:

(Bx - Axt) * (Cy - Ay) - (By - Ay) * (Cx - Axt)

Dies ist gleich Null, wenn sich der Punkt C auf der Linie befindet, die durch die Punkte A und B gebildet wird, und je nach Seite ein anderes Vorzeichen hat. Welche Seite dies ist, hängt von der Orientierung Ihrer (x, y) -Koordinaten ab. Sie können jedoch Testwerte für A, B und C in diese Formel einfügen, um zu bestimmen, ob negative Werte links oder rechts liegen.

1
user2154342

Ich wollte eine von der Physik inspirierte Lösung anbieten. 

Stellen Sie sich eine Kraft entlang der Linie vor, und Sie messen das Drehmoment der Kraft um den Punkt. Wenn das Drehmoment positiv ist (gegen den Uhrzeigersinn), befindet sich der Punkt auf der linken Seite der Linie, wenn das Drehmoment negativ ist, ist der Punkt auf der rechten Seite der Linie.

Wenn also der Kraftvektor der Spanne der zwei Punkte entspricht, die die Linie definieren 

fx = x_2 - x_1
fy = y_2 - y_1

sie prüfen die Seite eines Punktes (px,py) basierend auf dem Vorzeichen des folgenden Tests

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

Eine alternative Möglichkeit, sich ein Bild von den Lösungen zu machen, die Netters bieten, besteht darin, einige geometrische Implikationen zu verstehen.

Sei pqr= [P, Q, R] Punkte, die eine Ebene bilden, die durch eine Linie in zwei Seiten unterteilt ist [P, R]. Wir müssen herausfinden, ob zwei Punkte auf pqr Ebene A, B auf derselben Seite liegen.

Jeder Punkt T auf der pqr-Ebene kann mit 2 Vektoren dargestellt werden: v = PQ und = RQ, as:

T '= T-Q = i * v + j * u

Nun die Auswirkungen auf die Geometrie:

  1. i + j = 1: T in der Pr-Zeile
  2. i + j <1: T auf Sq
  3. i + j> 1: T auf Snq
  4. i + j = 0: T = Q
  5. i + j <0: T auf Sq und darüber hinaus Q.

i+j: <0 0 <1 =1 >1 ---------Q------[PR]--------- <== this is PQR plane ^ pr line

Im Allgemeinen,

  • i + j ist ein Maß dafür, wie weit T von Q oder der Linie [P, R] entfernt ist und
  • das Vorzeichen von i + j-1 impliziert die Nebenwirkung von T.

Die anderen Geometrie-Bedeutungen von i und j (nicht mit dieser Lösung verwandt) sind:

  • i, j sind die Skalare für T in einem neuen Koordinatensystem, wobei v, die neuen Achsen sind und Q ist der neue Ursprung;
  • i, j kann als Zugkraft für P, R gesehen werden. Je größer i, desto weiter ist T von R entfernt (größerer Zug von P ).

Der Wert von i, j kann durch Lösen der folgenden Gleichungen erhalten werden:

i*vx + j*ux = T'x
i*vy + j*uy = T'y
i*vz + j*uz = T'z

Wir bekommen also 2 Punkte, A, B im Flugzeug:

A = a1 * v + a2 * u B = b1 * v + b2 * u

Wenn sich A, B auf derselben Seite befinden, gilt Folgendes:

sign(a1+a2-1) = sign(b1+b2-1)

Beachten Sie, dass dies auch für die Frage gilt: Befinden sich A, B auf derselben Seite der Ebene [P, Q, R], in der:

T = i * P + j * Q + k * R

und i + j + k = 1 impliziert, dass T auf der Ebene [P, Q, R] liegt, und das Vorzeichen von i + j + k-1 impliziert seine Breite . Daraus haben wir:

A = a1 * P + a2 * Q + a3 * R B = b1 * P + b2 * Q + b3 * R

und A, B sind auf der gleichen Seite der Ebene [P, Q, R] wenn

sign(a1+a2+a3-1) = sign(b1+b2+b3-1)

0
runsun