Wie kann ich überprüfen, ob sich 2 Segmente schneiden?
Ich habe folgende Daten:
Segment1 [ {x1,y1}, {x2,y2} ]
Segment2 [ {x1,y1}, {x2,y2} ]
Ich muss einen kleinen Algorithmus in Python schreiben, um festzustellen, ob sich die beiden Linien schneiden.
Antworten:
Die Gleichung einer Linie lautet:
Für ein Segment ist es genau das gleiche, außer dass x in einem Intervall I enthalten ist.
Wenn Sie zwei Segmente haben, definieren Sie diese wie folgt:
Die Abcisse Xa des potentiellen Schnittpunkts (Xa, Ya) muss in beiden Intervallen I1 und I2 enthalten sein, die wie folgt definiert sind:
Und wir könnten sagen, dass Xa enthalten ist in:
Nun müssen wir überprüfen, ob dieses Intervall Ia existiert:
if (max(X1,X2) < min(X3,X4)): return False # There is no mutual abcisses
Wir haben also eine zweizeilige Formel und ein gegenseitiges Intervall. Ihre Zeilenformeln sind:
Da wir zwei Punkte pro Segment erhalten haben, können wir A1, A2, b1 und b2 bestimmen:
A1 = (Y1-Y2)/(X1-X2) # Pay attention to not dividing by zero A2 = (Y3-Y4)/(X3-X4) # Pay attention to not dividing by zero b1 = Y1-A1*X1 = Y2-A1*X2 b2 = Y3-A2*X3 = Y4-A2*X4
Wenn die Segmente parallel sind, ist A1 == A2:
if (A1 == A2): return False # Parallel segments
Ein Punkt (Xa, Ya), der auf beiden Linien steht, muss beide Formeln f1 und f2 überprüfen:
Ya = A1 * Xa + b1 Ya = A2 * Xa + b2 A1 * Xa + b1 = A2 * Xa + b2 Xa = (b2 - b1) / (A1 - A2) # Once again, pay attention to not dividing by zero
Als letztes müssen Sie überprüfen, ob Xa in Ia enthalten ist:
if ( (Xa < max( min(X1,X2), min(X3,X4) )) or (Xa > min( max(X1,X2), max(X3,X4) )) ): return False # intersection is out of bound else: return True
Darüber hinaus können Sie beim Start überprüfen, ob zwei der vier angegebenen Punkte nicht gleich sind, um all diese Tests zu vermeiden.
quelle
User @ i_4_got verweist auf diese Seite mit einer sehr effizienten Lösung in Python. Ich reproduziere es hier der Einfachheit halber (da es mich glücklich gemacht hätte, es hier zu haben):
def ccw(A,B,C): return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x) # Return true if line segments AB and CD intersect def intersect(A,B,C,D): return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)
quelle
Sie müssen nicht genau berechnen, wo sich die Segmente schneiden, sondern nur verstehen, ob sie sich überhaupt schneiden. Dies vereinfacht die Lösung.
Die Idee ist, ein Segment als "Anker" zu behandeln und das zweite Segment in 2 Punkte zu trennen.
Jetzt müssen Sie die relative Position jedes Punkts zum "verankerten" Segment (OnLeft, OnRight oder Collinear) ermitteln.
Überprüfen Sie anschließend für beide Punkte, ob einer der Punkte OnLeft und der andere OnRight ist (oder schließen Sie die kollineare Position ein, wenn Sie auch falsche Schnittpunkte einbeziehen möchten ).
Sie müssen den Vorgang dann mit den Rollen Anker und getrennte Segmente wiederholen.
Ein Schnittpunkt liegt nur dann vor, wenn einer der Punkte OnLeft und der andere OnRight ist. Unter diesem Link finden Sie eine detailliertere Erklärung mit Beispielbildern für jeden möglichen Fall.
Das Implementieren einer solchen Methode ist viel einfacher als das tatsächliche Implementieren einer Methode, die den Schnittpunkt findet (angesichts der vielen Eckfälle, die Sie ebenfalls behandeln müssen).
Aktualisieren
Die folgenden Funktionen sollen die Idee veranschaulichen (Quelle: Computational Geometry in C ).
Anmerkung: In diesem Beispiel wird die Verwendung von Ganzzahlen angenommen. Wenn Sie stattdessen eine Gleitkommadarstellung verwenden (was die Sache offensichtlich komplizieren könnte), sollten Sie einen Epsilon-Wert bestimmen, um "Gleichheit" anzuzeigen (hauptsächlich für die
IsCollinear
Auswertung).// points "a" and "b" forms the anchored segment. // point "c" is the evaluated point bool IsOnLeft(Point a, Point b, Point c) { return Area2(a, b, c) > 0; } bool IsOnRight(Point a, Point b, Point c) { return Area2(a, b, c) < 0; } bool IsCollinear(Point a, Point b, Point c) { return Area2(a, b, c) == 0; } // calculates the triangle's size (formed by the "anchor" segment and additional point) int Area2(Point a, Point b, Point c) { return (b.X - a.X) * (c.Y - a.Y) - (c.X - a.X) * (b.Y - a.Y); }
Wenn Sie diese Funktionen verwenden, müssen Sie natürlich daran denken, zu überprüfen, ob jedes Segment "zwischen" dem anderen Segment liegt (da es sich um endliche Segmente und nicht um unendliche Linien handelt).
Mit diesen Funktionen können Sie auch nachvollziehen, ob Sie eine richtige oder eine falsche Kreuzung haben.
quelle
Angenommen, die beiden Segmente haben die Endpunkte A, B und C, D. Die numerisch robuste Methode zur Bestimmung des Schnittpunkts besteht darin, das Vorzeichen der vier Determinanten zu überprüfen:
Für die Schnittmenge muss jede Determinante auf der linken Seite das entgegengesetzte Vorzeichen der auf der rechten Seite haben, es muss jedoch keine Beziehung zwischen den beiden Linien bestehen. Sie überprüfen im Grunde jeden Punkt eines Segments mit dem anderen Segment, um sicherzustellen, dass sie auf gegenüberliegenden Seiten der vom anderen Segment definierten Linie liegen.
Siehe hier: http://www.cs.cmu.edu/~quake/robust.html
quelle
Mit der Shapely- Bibliothek können Sie mithilfe der folgenden
intersects
Methode sehr einfach überprüfen, ob sich Liniensegmente schneiden :from shapely.geometry import LineString line = LineString([(0, 0), (1, 1)]) other = LineString([(0, 1), (1, 0)]) print(line.intersects(other)) # True
line = LineString([(0, 0), (1, 1)]) other = LineString([(0, 1), (1, 2)]) print(line.intersects(other)) # False
quelle
Basierend auf den hervorragenden Antworten von Liran und Grumdrig finden Sie hier einen vollständigen Python-Code, mit dem überprüft werden kann, ob sich geschlossene Segmente überschneiden. Funktioniert für kollineare Segmente, Segmente parallel zur Achse Y, entartete Segmente (der Teufel steckt im Detail). Nimmt ganzzahlige Koordinaten an. Gleitkommakoordinaten erfordern eine Änderung des Punktgleichheitstests.
def side(a,b,c): """ Returns a position of the point c relative to the line going through a and b Points a, b are expected to be different """ d = (c[1]-a[1])*(b[0]-a[0]) - (b[1]-a[1])*(c[0]-a[0]) return 1 if d > 0 else (-1 if d < 0 else 0) def is_point_in_closed_segment(a, b, c): """ Returns True if c is inside closed segment, False otherwise. a, b, c are expected to be collinear """ if a[0] < b[0]: return a[0] <= c[0] and c[0] <= b[0] if b[0] < a[0]: return b[0] <= c[0] and c[0] <= a[0] if a[1] < b[1]: return a[1] <= c[1] and c[1] <= b[1] if b[1] < a[1]: return b[1] <= c[1] and c[1] <= a[1] return a[0] == c[0] and a[1] == c[1] # def closed_segment_intersect(a,b,c,d): """ Verifies if closed segments a, b, c, d do intersect. """ if a == b: return a == c or a == d if c == d: return c == a or c == b s1 = side(a,b,c) s2 = side(a,b,d) # All points are collinear if s1 == 0 and s2 == 0: return \ is_point_in_closed_segment(a, b, c) or is_point_in_closed_segment(a, b, d) or \ is_point_in_closed_segment(c, d, a) or is_point_in_closed_segment(c, d, b) # No touching and on the same side if s1 and s1 == s2: return False s1 = side(c,d,a) s2 = side(c,d,b) # No touching and on the same side if s1 and s1 == s2: return False return True
quelle
Hier ist eine Lösung mit Punktprodukten:
# assumes line segments are stored in the format [(x0,y0),(x1,y1)] def intersects(s0,s1): dx0 = s0[1][0]-s0[0][0] dx1 = s1[1][0]-s1[0][0] dy0 = s0[1][1]-s0[0][1] dy1 = s1[1][1]-s1[0][1] p0 = dy1*(s1[1][0]-s0[0][0]) - dx1*(s1[1][1]-s0[0][1]) p1 = dy1*(s1[1][0]-s0[1][0]) - dx1*(s1[1][1]-s0[1][1]) p2 = dy0*(s0[1][0]-s1[0][0]) - dx0*(s0[1][1]-s1[0][1]) p3 = dy0*(s0[1][0]-s1[1][0]) - dx0*(s0[1][1]-s1[1][1]) return (p0*p1<=0) & (p2*p3<=0)
Hier ist eine Visualisierung in Desmos: Liniensegmentschnitt
quelle
Sie haben zwei Liniensegmente. Definieren Sie ein Segment anhand der Endpunkte A und B und das zweite Segment anhand der Endpunkte C und D. Es gibt einen guten Trick, um zu zeigen, dass sie sich innerhalb der Grenzen der Segmente schneiden müssen. (Beachten Sie, dass sich die Linien selbst möglicherweise über die Grenzen der Segmente hinaus schneiden. Sie müssen also vorsichtig sein. Guter Code sucht auch nach parallelen Linien.)
Der Trick besteht darin, zu testen, dass die Punkte A und B auf gegenüberliegenden Seiten der Linie CD liegen müssen und dass die Punkte C und D auf gegenüberliegenden Seiten der Linie AB liegen müssen.
Da dies Hausaufgaben sind, werde ich Ihnen keine explizite Lösung geben. Ein einfacher Test, um festzustellen, auf welche Seite einer Linie ein Punkt fällt, ist die Verwendung eines Punktprodukts. Berechnen Sie für eine bestimmte Zeilen-CD den Normalenvektor für diese Zeile (ich nenne sie N_C). Testen Sie nun einfach die Vorzeichen dieser beiden Ergebnisse:
und
Wenn diese Ergebnisse entgegengesetzte Vorzeichen haben, sind A und B gegenüberliegende Seiten der Linie CD. Führen Sie nun den gleichen Test für die andere Leitung AB durch. Es hat den normalen Vektor N_A. Vergleichen Sie die Zeichen von
und
Ich überlasse es Ihnen, herauszufinden, wie man einen normalen Vektor berechnet. (In 2D ist das trivial, aber macht sich Ihr Code Sorgen darüber, ob A und B unterschiedliche Punkte sind? Sind auch C und D unterschiedliche Punkte?)
Sie müssen sich immer noch Gedanken über Liniensegmente machen, die entlang derselben unendlichen Linie liegen oder wenn ein Punkt tatsächlich auf das andere Liniensegment selbst fällt. Guter Code wird jedem möglichen Problem gerecht.
quelle
Hier ist C-Code, um zu überprüfen, ob sich zwei Punkte auf den gegenüberliegenden Seiten des Liniensegments befinden. Mit diesem Code können Sie überprüfen, ob sich auch zwei Segmente schneiden.
// true if points p1, p2 lie on the opposite sides of segment s1--s2 bool oppositeSide (Point2f s1, Point2f s2, Point2f p1, Point2f p2) { //calculate normal to the segment Point2f vec = s1-s2; Point2f normal(vec.y, -vec.x); // no need to normalize // vectors to the points Point2f v1 = p1-s1; Point2f v2 = p2-s1; // compare signs of the projections of v1, v2 onto the normal float proj1 = v1.dot(normal); float proj2 = v2.dot(normal); if (proj1==0 || proj2==0) cout<<"collinear points"<<endl; return(SIGN(proj1) != SIGN(proj2));
}}
quelle
Hier ist ein weiterer Python-Code, mit dem überprüft werden kann, ob sich geschlossene Segmente schneiden. Es ist die umgeschriebene Version des C ++ - Codes in http://www.cdn.geeksforgeeks.org/check-if-two-given-line-segments-intersect/ . Diese Implementierung deckt alle Sonderfälle ab (z. B. alle Punkte kolinear).
def on_segment(p, q, r): '''Given three colinear points p, q, r, the function checks if point q lies on line segment "pr" ''' if (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1])): return True return False def orientation(p, q, r): '''Find orientation of ordered triplet (p, q, r). The function returns following values 0 --> p, q and r are colinear 1 --> Clockwise 2 --> Counterclockwise ''' val = ((q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1])) if val == 0: return 0 # colinear elif val > 0: return 1 # clockwise else: return 2 # counter-clockwise def do_intersect(p1, q1, p2, q2): '''Main function to check whether the closed line segments p1 - q1 and p2 - q2 intersect''' o1 = orientation(p1, q1, p2) o2 = orientation(p1, q1, q2) o3 = orientation(p2, q2, p1) o4 = orientation(p2, q2, q1) # General case if (o1 != o2 and o3 != o4): return True # Special Cases # p1, q1 and p2 are colinear and p2 lies on segment p1q1 if (o1 == 0 and on_segment(p1, p2, q1)): return True # p1, q1 and p2 are colinear and q2 lies on segment p1q1 if (o2 == 0 and on_segment(p1, q2, q1)): return True # p2, q2 and p1 are colinear and p1 lies on segment p2q2 if (o3 == 0 and on_segment(p2, p1, q2)): return True # p2, q2 and q1 are colinear and q1 lies on segment p2q2 if (o4 == 0 and on_segment(p2, q1, q2)): return True return False # Doesn't fall in any of the above cases
Unten finden Sie eine Testfunktion, um zu überprüfen, ob sie funktioniert.
import matplotlib.pyplot as plt def test_intersect_func(): p1 = (1, 1) q1 = (10, 1) p2 = (1, 2) q2 = (10, 2) fig, ax = plt.subplots() ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-') ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-') print(do_intersect(p1, q1, p2, q2)) p1 = (10, 0) q1 = (0, 10) p2 = (0, 0) q2 = (10, 10) fig, ax = plt.subplots() ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-') ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-') print(do_intersect(p1, q1, p2, q2)) p1 = (-5, -5) q1 = (0, 0) p2 = (1, 1) q2 = (10, 10) fig, ax = plt.subplots() ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-') ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-') print(do_intersect(p1, q1, p2, q2)) p1 = (0, 0) q1 = (1, 1) p2 = (1, 1) q2 = (10, 10) fig, ax = plt.subplots() ax.plot([p1[0], q1[0]], [p1[1], q1[1]], 'x-') ax.plot([p2[0], q2[0]], [p2[1], q2[1]], 'x-') print(do_intersect(p1, q1, p2, q2))
quelle
closed_segment_intersect()
aus dem Testcode ist nicht definiert.Bestimmen Sie für die Segmente AB und CD die Steigung von CD
Ziehen Sie die CD über A und B und nehmen Sie den Abstand zur CD, der gerade nach oben geht
Überprüfen Sie, ob sie sich auf gegenüberliegenden Seiten befinden
return dist1*dist2<0
quelle
Da Sie nicht erwähnen, dass Sie den Schnittpunkt der Linie finden möchten, ist das Problem einfacher zu lösen. Wenn Sie den Schnittpunkt benötigen, ist die Antwort von OMG_peanuts ein schnellerer Ansatz. Wenn Sie jedoch nur herausfinden möchten, ob sich die Linien schneiden oder nicht, können Sie dies mithilfe der Liniengleichung (ax + by + c = 0) tun. Der Ansatz ist wie folgt:
Beginnen wir mit zwei Liniensegmenten: Segment 1 und Segment 2.
Überprüfen Sie, ob die beiden Liniensegmente Linien ungleich Null und unterschiedliche Segmente sind.
Ab diesem Zeitpunkt gehe ich davon aus, dass die beiden Segmente ungleich Null sind und sich unterscheiden. Berechnen Sie für jedes Liniensegment die Steigung der Linie und erhalten Sie dann die Gleichung einer Linie in Form von ax + by + c = 0. Berechnen Sie nun den Wert von f = ax + by + c für die beiden Punkte der anderes Liniensegment (wiederholen Sie dies auch für das andere Liniensegment).
a2 = (y3-y4)/(x3-x4); b1 = -1; b2 = -1; c1 = y1 - a1*x1; c2 = y3 - a2*x3; // using the sign function from numpy f1_1 = sign(a1*x3 + b1*y3 + c1); f1_2 = sign(a1*x4 + b1*y4 + c1); f2_1 = sign(a2*x1 + b2*y1 + c2); f2_2 = sign(a2*x2 + b2*y2 + c2);
Jetzt bleiben nur noch die verschiedenen Fälle. Wenn für einen Punkt f = 0 ist, berühren sich die beiden Linien an einem Punkt. Wenn f1_1 und f1_2 gleich sind oder f2_1 und f2_2 gleich sind, schneiden sich die Linien nicht. Wenn f1_1 und f1_2 ungleich sind und f2_1 und f2_2 ungleich sind, schneiden sich die Liniensegmente. Je nachdem, ob Sie die Linien, die sich berühren, als "schneidend" betrachten möchten oder nicht, können Sie Ihre Bedingungen anpassen.
quelle
a1
und funktioniert nicht für orthogonale Linien.Wir können dies auch unter Verwendung von Vektoren lösen.
Definieren wir die Segmente als
[start, end]
. Bei zwei solchen Segmenten[A, B]
und einer[C, D]
Länge ungleich Null können wir einen der Endpunkte auswählen, der als Referenzpunkt verwendet werden soll, sodass wir drei Vektoren erhalten:x = 0 y = 1 p = A-C = [C[x]-A[x], C[y]-A[y]] q = B-A = [B[x]-A[x], B[y]-A[y]] r = D-C = [D[x]-C[x], D[y]-C[y]]
Von dort aus können wir nach einem Schnittpunkt suchen, indem wir t und u in berechnen
p + t*r = u*q
. Nachdem wir ein wenig mit der Gleichung herumgespielt haben, erhalten wir:Die Funktion lautet also:
def intersects(a, b): p = [b[0][0]-a[0][0], b[0][1]-a[0][1]] q = [a[1][0]-a[0][0], a[1][1]-a[0][1]] r = [b[1][0]-b[0][0], b[1][1]-b[0][1]] t = (q[1]*p[0] - q[0]*p[1])/(q[0]*r[1] - q[1]*r[0]) \ if (q[0]*r[1] - q[1]*r[0]) != 0 \ else (q[1]*p[0] - q[0]*p[1]) u = (p[0] + t*r[0])/q[0] \ if q[0] != 0 \ else (p[1] + t*r[1])/q[1] return t >= 0 and t <= 1 and u >= 0 and u <= 1
quelle
Auf diese Weise überprüfe ich, ob eine Linie gekreuzt wurde und wo die Kreuzung auftritt. Verwenden wir x1 bis x4 und y1 bis y4
Dann brauchen wir einige Vektoren, um sie darzustellen
Nun betrachten wir die Determinante
Wenn die Determinante 0,0 ist, sind die Liniensegmente parallel. Dies könnte bedeuten, dass sie sich überlappen. Wenn sie sich nur an Endpunkten überlappen, gibt es eine Schnittlösung. Andernfalls gibt es unendlich viele Lösungen. Was sagen Sie bei unendlich vielen Lösungen zu Ihrem Schnittpunkt? Es ist also ein interessanter Sonderfall. Wenn Sie im Voraus wissen, dass sich die Linien nicht überlappen können, können Sie einfach überprüfen, ob
det == 0.0
und wenn ja, einfach sagen, dass sie sich nicht schneiden und fertig sind. Ansonsten fahren wir fortWenn det, det1 und det2 alle Null sind, sind Ihre Linien kolinear und können sich überlappen. Wenn det Null ist, aber entweder det1 oder det2 nicht, dann sind sie nicht kolinear, sondern parallel, sodass es keinen Schnittpunkt gibt. Was jetzt übrig bleibt, wenn det Null ist, ist ein 1D-Problem anstelle von 2D. Wir müssen eine von zwei Möglichkeiten prüfen, je nachdem, ob dx1 Null ist oder nicht (damit wir die Division durch Null vermeiden können). Wenn dx1 Null ist, machen Sie einfach die gleiche Logik mit y-Werten und nicht mit x darunter.
Dies berechnet zwei Skalierer, so dass wir, wenn wir den Vektor (dx1, dy1) mit s skalieren, den Punkt (x3, y3) und mit t (x4, y4) erhalten. Wenn also entweder s oder t zwischen 0,0 und 1,0 liegt, liegt Punkt 3 oder 4 auf unserer ersten Linie. Negativ würde bedeuten, dass der Punkt hinter dem Anfang unseres Vektors liegt, während> 1,0 bedeutet, dass er weiter vor dem Ende unseres Vektors liegt. 0.0 bedeutet, dass es bei (x1, y1) ist und 1.0 bedeutet, dass es bei (x2, y2) ist. Wenn sowohl s als auch t <0,0 oder beide> 1,0 sind, schneiden sie sich nicht. Und das behandelt den Sonderfall der parallelen Linien.
Nun, wenn
det != 0.0
danns = det1 / det t = det2 / det if (s < 0.0 || s > 1.0 || t < 0.0 || t > 1.0) return false // no intersect
Dies ähnelt dem, was wir oben wirklich gemacht haben. Wenn wir nun den obigen Test bestehen, schneiden sich unsere Liniensegmente und wir können den Schnitt ganz einfach so berechnen:
Wenn Sie tiefer in die Mathematik eintauchen möchten, schauen Sie sich Cramers Regel an.
quelle
Die Antwort von Georgy ist bei weitem die sauberste. Musste dies verfolgen, da das Brycboe-Beispiel, obwohl es auch einfach ist, Probleme mit der Kolinearität hatte.
Code zum Testen:
#!/usr/bin/python # # Notes on intersection: # # https://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/ # # /programming/3838329/how-can-i-check-if-two-segments-intersect from shapely.geometry import LineString class Point: def __init__(self,x,y): self.x = x self.y = y def ccw(A,B,C): return (C.y-A.y)*(B.x-A.x) > (B.y-A.y)*(C.x-A.x) def intersect(A,B,C,D): return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D) def ShapelyIntersect(A,B,C,D): return LineString([(A.x,A.y),(B.x,B.y)]).intersects(LineString([(C.x,C.y),(D.x,D.y)])) a = Point(0,0) b = Point(0,1) c = Point(1,1) d = Point(1,0) ''' Test points: b(0,1) c(1,1) a(0,0) d(1,0) ''' # F print(intersect(a,b,c,d)) # T print(intersect(a,c,b,d)) print(intersect(b,d,a,c)) print(intersect(d,b,a,c)) # F print(intersect(a,d,b,c)) # same end point cases: print("same end points") # F - not intersected print(intersect(a,b,a,d)) # T - This shows as intersected print(intersect(b,a,a,d)) # F - this does not print(intersect(b,a,d,a)) # F - this does not print(intersect(a,b,d,a)) print("same end points, using shapely") # T print(ShapelyIntersect(a,b,a,d)) # T print(ShapelyIntersect(b,a,a,d)) # T print(ShapelyIntersect(b,a,d,a)) # T print(ShapelyIntersect(a,b,d,a))
quelle
Wenn Ihre Daten eine Linie definieren, müssen Sie nur beweisen, dass sie nicht parallel sind. Dazu können Sie berechnen
Wenn dieser Koeffizient für Linie1 und Linie2 gleich ist, bedeutet dies, dass die Linie parallel ist. Wenn nicht, bedeutet dies, dass sie sich schneiden.
Wenn sie parallel sind, müssen Sie beweisen, dass sie nicht gleich sind. Dafür rechnen Sie
Wenn Beta für Linie1 und Linie2 gleich ist, bedeutet dies, dass Sie die Linie schneiden, da sie gleich sind
Wenn es sich um Segmente handelt, müssen Sie für jede Zeile noch Alpha und Beta wie oben beschrieben berechnen. Dann müssen Sie überprüfen, ob (beta1 - beta2) / (alpha1 - alpha2) größer als Min (x1_line1, x2_line1) und kleiner als Max (x1_line1, x2_line1) ist.
quelle
Berechnen Sie den Schnittpunkt der Linien, die auf Ihren Segmenten liegen (dies bedeutet im Grunde, ein lineares Gleichungssystem zu lösen), und prüfen Sie dann, ob er zwischen dem Start- und dem Endpunkt Ihrer Segmente liegt.
quelle
Dies ist, was ich für AS3 habe, weiß nicht viel über Python, aber das Konzept ist da
public function getIntersectingPointF($A:Point, $B:Point, $C:Point, $D:Point):Number { var A:Point = $A.clone(); var B:Point = $B.clone(); var C:Point = $C.clone(); var D:Point = $D.clone(); var f_ab:Number = (D.x - C.x) * (A.y - C.y) - (D.y - C.y) * (A.x - C.x); // are lines parallel if (f_ab == 0) { return Infinity }; var f_cd:Number = (B.x - A.x) * (A.y - C.y) - (B.y - A.y) * (A.x - C.x); var f_d:Number = (D.y - C.y) * (B.x - A.x) - (D.x - C.x) * (B.y - A.y); var f1:Number = f_ab/f_d var f2:Number = f_cd / f_d if (f1 == Infinity || f1 <= 0 || f1 >= 1) { return Infinity }; if (f2 == Infinity || f2 <= 0 || f2 >= 1) { return Infinity }; return f1; } public function getIntersectingPoint($A:Point, $B:Point, $C:Point, $D:Point):Point { var f:Number = getIntersectingPointF($A, $B, $C, $D); if (f == Infinity || f <= 0 || f >= 1) { return null }; var retPoint:Point = Point.interpolate($A, $B, 1 - f); return retPoint.clone(); }
quelle
In JAVA implementiert. Es scheint jedoch, dass es nicht für kolineare Linien funktioniert (auch bekannt als Liniensegmente, die ineinander existieren L1 (0,0) (10,10) L2 (1,1) (2,2)
public class TestCode { public class Point { public double x = 0; public double y = 0; public Point(){} } public class Line { public Point p1, p2; public Line( double x1, double y1, double x2, double y2) { p1 = new Point(); p2 = new Point(); p1.x = x1; p1.y = y1; p2.x = x2; p2.y = y2; } } //line segments private static Line s1; private static Line s2; public TestCode() { s1 = new Line(0,0,0,10); s2 = new Line(-1,0,0,10); } public TestCode(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { s1 = new Line(x1,y1, x2,y2); s2 = new Line(x3,y3, x4,y4); } public static void main(String args[]) { TestCode code = null; //////////////////////////// code = new TestCode(0,0,0,10, 0,1,0,5); if( intersect(code) ) { System.out.println( "OK COLINEAR: INTERSECTS" ); } else { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,0,10, 0,1,0,10); if( intersect(code) ) { System.out.println( "OK COLINEAR: INTERSECTS" ); } else { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,10,0, 5,0,15,0); if( intersect(code) ) { System.out.println( "OK COLINEAR: INTERSECTS" ); } else { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,10,0, 0,0,15,0); if( intersect(code) ) { System.out.println( "OK COLINEAR: INTERSECTS" ); } else { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,10,10, 1,1,5,5); if( intersect(code) ) { System.out.println( "OK COLINEAR: INTERSECTS" ); } else { System.out.println( "ERROR COLINEAR: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,0,10, -1,-1,0,10); if( intersect(code) ) { System.out.println( "OK SLOPE END: INTERSECTS" ); } else { System.out.println( "ERROR SLOPE END: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(-10,-10,10,10, -10,10,10,-10); if( intersect(code) ) { System.out.println( "OK SLOPE Intersect(0,0): INTERSECTS" ); } else { System.out.println( "ERROR SLOPE Intersect(0,0): DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(-10,-10,10,10, -3,-2,50,-2); if( intersect(code) ) { System.out.println( "OK SLOPE Line2 VERTIAL: INTERSECTS" ); } else { System.out.println( "ERROR SLOPE Line2 VERTICAL: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(-10,-10,10,10, 50,-2,-3,-2); if( intersect(code) ) { System.out.println( "OK SLOPE Line2 (reversed) VERTIAL: INTERSECTS" ); } else { System.out.println( "ERROR SLOPE Line2 (reversed) VERTICAL: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,0,10, 1,0,1,10); if( intersect(code) ) { System.out.println( "ERROR PARALLEL VERTICAL: INTERSECTS" ); } else { System.out.println( "OK PARALLEL VERTICAL: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,2,10,2, 0,10,10,10); if( intersect(code) ) { System.out.println( "ERROR PARALLEL HORIZONTAL: INTERSECTS" ); } else { System.out.println( "OK PARALLEL HORIZONTAL: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,10,5,13.75, 0,18.75,10,15); if( intersect(code) ) { System.out.println( "ERROR PARALLEL SLOPE=.75: INTERSECTS" ); } else { System.out.println( "OK PARALLEL SLOPE=.75: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,1,1, 2,-1,2,10); if( intersect(code) ) { System.out.println( "ERROR SEPERATE SEGMENTS: INTERSECTS" ); } else { System.out.println( "OK SEPERATE SEGMENTS: DO NOT INTERSECT" ); } //////////////////////////// code = new TestCode(0,0,1,1, -1,-10,-5,10); if( intersect(code) ) { System.out.println( "ERROR SEPERATE SEGMENTS 2: INTERSECTS" ); } else { System.out.println( "OK SEPERATE SEGMENTS 2: DO NOT INTERSECT" ); } } public static boolean intersect( TestCode code ) { return intersect( code.s1, code.s2); } public static boolean intersect( Line line1, Line line2 ) { double i1min = Math.min(line1.p1.x, line1.p2.x); double i1max = Math.max(line1.p1.x, line1.p2.x); double i2min = Math.min(line2.p1.x, line2.p2.x); double i2max = Math.max(line2.p1.x, line2.p2.x); double iamax = Math.max(i1min, i2min); double iamin = Math.min(i1max, i2max); if( Math.max(line1.p1.x, line1.p2.x) < Math.min(line2.p1.x, line2.p2.x) ) return false; double m1 = (line1.p2.y - line1.p1.y) / (line1.p2.x - line1.p1.x ); double m2 = (line2.p2.y - line2.p1.y) / (line2.p2.x - line2.p1.x ); if( m1 == m2 ) return false; //b1 = line1[0][1] - m1 * line1[0][0] //b2 = line2[0][1] - m2 * line2[0][0] double b1 = line1.p1.y - m1 * line1.p1.x; double b2 = line2.p1.y - m2 * line2.p1.x; double x1 = (b2 - b1) / (m1 - m2); if( (x1 < Math.max(i1min, i2min)) || (x1 > Math.min(i1max, i2max)) ) return false; return true; } }
Die bisherige Ausgabe ist
ERROR COLINEAR: DO NOT INTERSECT ERROR COLINEAR: DO NOT INTERSECT ERROR COLINEAR: DO NOT INTERSECT ERROR COLINEAR: DO NOT INTERSECT ERROR COLINEAR: DO NOT INTERSECT OK SLOPE END: INTERSECTS OK SLOPE Intersect(0,0): INTERSECTS OK SLOPE Line2 VERTIAL: INTERSECTS OK SLOPE Line2 (reversed) VERTIAL: INTERSECTS OK PARALLEL VERTICAL: DO NOT INTERSECT OK PARALLEL HORIZONTAL: DO NOT INTERSECT OK PARALLEL SLOPE=.75: DO NOT INTERSECT OK SEPERATE SEGMENTS: DO NOT INTERSECT OK SEPERATE SEGMENTS 2: DO NOT INTERSECT
quelle
Ich dachte, ich würde eine schöne Swift-Lösung beitragen:
struct Pt { var x: Double var y: Double } struct LineSegment { var p1: Pt var p2: Pt } func doLineSegmentsIntersect(ls1: LineSegment, ls2: LineSegment) -> Bool { if (ls1.p2.x-ls1.p1.x == 0) { //handle vertical segment1 if (ls2.p2.x-ls2.p1.x == 0) { //both lines are vertical and parallel return false } let x = ls1.p1.x let slope2 = (ls2.p2.y-ls2.p1.y)/(ls2.p2.x-ls2.p1.x) let c2 = ls2.p1.y-slope2*ls2.p1.x let y = x*slope2+c2 // y intersection point return (y > ls1.p1.y && x < ls1.p2.y) || (y > ls1.p2.y && y < ls1.p1.y) // check if y is between y1,y2 in segment1 } if (ls2.p2.x-ls2.p1.x == 0) { //handle vertical segment2 let x = ls2.p1.x let slope1 = (ls1.p2.y-ls1.p1.y)/(ls1.p2.x-ls1.p1.x) let c1 = ls1.p1.y-slope1*ls1.p1.x let y = x*slope1+c1 // y intersection point return (y > ls2.p1.y && x < ls2.p2.y) || (y > ls2.p2.y && y < ls2.p1.y) // validate that y is between y1,y2 in segment2 } let slope1 = (ls1.p2.y-ls1.p1.y)/(ls1.p2.x-ls1.p1.x) let slope2 = (ls2.p2.y-ls2.p1.y)/(ls2.p2.x-ls2.p1.x) if (slope1 == slope2) { //segments are parallel return false } let c1 = ls1.p1.y-slope1*ls1.p1.x let c2 = ls2.p1.y-slope2*ls2.p1.x let x = (c2-c1)/(slope1-slope2) return (((x > ls1.p1.x && x < ls1.p2.x) || (x > ls1.p2.x && x < ls1.p1.x)) && ((x > ls2.p1.x && x < ls2.p2.x) || (x > ls2.p2.x && x < ls2.p1.x))) //validate that x is between x1,x2 in both segments }
quelle
Eine der oben genannten Lösungen hat so gut funktioniert, dass ich beschlossen habe, ein vollständiges Demonstrationsprogramm mit wxPython zu schreiben. Sie sollten dieses Programm folgendermaßen ausführen können: Python " Ihr Dateiname "
# Click on the window to draw a line. # The program will tell you if this and the other line intersect. import wx class Point: def __init__(self, newX, newY): self.x = newX self.y = newY app = wx.App() frame = wx.Frame(None, wx.ID_ANY, "Main") p1 = Point(90,200) p2 = Point(150,80) mp = Point(0,0) # mouse point highestX = 0 def ccw(A,B,C): return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x) # Return true if line segments AB and CD intersect def intersect(A,B,C,D): return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D) def is_intersection(p1, p2, p3, p4): return intersect(p1, p2, p3, p4) def drawIntersection(pc): mp2 = Point(highestX, mp.y) if is_intersection(p1, p2, mp, mp2): pc.DrawText("intersection", 10, 10) else: pc.DrawText("no intersection", 10, 10) def do_paint(evt): pc = wx.PaintDC(frame) pc.DrawLine(p1.x, p1.y, p2.x, p2.y) pc.DrawLine(mp.x, mp.y, highestX, mp.y) drawIntersection(pc) def do_left_mouse(evt): global mp, highestX point = evt.GetPosition() mp = Point(point[0], point[1]) highestX = frame.Size[0] frame.Refresh() frame.Bind(wx.EVT_PAINT, do_paint) frame.Bind(wx.EVT_LEFT_DOWN, do_left_mouse) frame.Show() app.MainLoop()
quelle
Mit der OMG_Peanuts- Lösung habe ich in SQL übersetzt. (HANA-Skalarfunktion)
Danke OMG_Peanuts, es funktioniert großartig. Ich benutze runde Erde, aber die Entfernungen sind gering, also denke ich, dass es in Ordnung ist.
FUNCTION GA_INTERSECT" ( IN LAT_A1 DOUBLE, IN LONG_A1 DOUBLE, IN LAT_A2 DOUBLE, IN LONG_A2 DOUBLE, IN LAT_B1 DOUBLE, IN LONG_B1 DOUBLE, IN LAT_B2 DOUBLE, IN LONG_B2 DOUBLE) RETURNS RET_DOESINTERSECT DOUBLE LANGUAGE SQLSCRIPT SQL SECURITY INVOKER AS BEGIN DECLARE MA DOUBLE; DECLARE MB DOUBLE; DECLARE BA DOUBLE; DECLARE BB DOUBLE; DECLARE XA DOUBLE; DECLARE MAX_MIN_X DOUBLE; DECLARE MIN_MAX_X DOUBLE; DECLARE DOESINTERSECT INTEGER; SELECT 1 INTO DOESINTERSECT FROM DUMMY; IF LAT_A2-LAT_A1 != 0 AND LAT_B2-LAT_B1 != 0 THEN SELECT (LONG_A2 - LONG_A1)/(LAT_A2 - LAT_A1) INTO MA FROM DUMMY; SELECT (LONG_B2 - LONG_B1)/(LAT_B2 - LAT_B1) INTO MB FROM DUMMY; IF MA = MB THEN SELECT 0 INTO DOESINTERSECT FROM DUMMY; END IF; END IF; SELECT LONG_A1-MA*LAT_A1 INTO BA FROM DUMMY; SELECT LONG_B1-MB*LAT_B1 INTO BB FROM DUMMY; SELECT (BB - BA) / (MA - MB) INTO XA FROM DUMMY; -- Max of Mins IF LAT_A1 < LAT_A2 THEN -- MIN(LAT_A1, LAT_A2) = LAT_A1 IF LAT_B1 < LAT_B2 THEN -- MIN(LAT_B1, LAT_B2) = LAT_B1 IF LAT_A1 > LAT_B1 THEN -- MAX(LAT_A1, LAT_B1) = LAT_A1 SELECT LAT_A1 INTO MAX_MIN_X FROM DUMMY; ELSE -- MAX(LAT_A1, LAT_B1) = LAT_B1 SELECT LAT_B1 INTO MAX_MIN_X FROM DUMMY; END IF; ELSEIF LAT_B2 < LAT_B1 THEN -- MIN(LAT_B1, LAT_B2) = LAT_B2 IF LAT_A1 > LAT_B2 THEN -- MAX(LAT_A1, LAT_B2) = LAT_A1 SELECT LAT_A1 INTO MAX_MIN_X FROM DUMMY; ELSE -- MAX(LAT_A1, LAT_B2) = LAT_B2 SELECT LAT_B2 INTO MAX_MIN_X FROM DUMMY; END IF; END IF; ELSEIF LAT_A2 < LAT_A1 THEN -- MIN(LAT_A1, LAT_A2) = LAT_A2 IF LAT_B1 < LAT_B2 THEN -- MIN(LAT_B1, LAT_B2) = LAT_B1 IF LAT_A2 > LAT_B1 THEN -- MAX(LAT_A2, LAT_B1) = LAT_A2 SELECT LAT_A2 INTO MAX_MIN_X FROM DUMMY; ELSE -- MAX(LAT_A2, LAT_B1) = LAT_B1 SELECT LAT_B1 INTO MAX_MIN_X FROM DUMMY; END IF; ELSEIF LAT_B2 < LAT_B1 THEN -- MIN(LAT_B1, LAT_B2) = LAT_B2 IF LAT_A2 > LAT_B2 THEN -- MAX(LAT_A2, LAT_B2) = LAT_A2 SELECT LAT_A2 INTO MAX_MIN_X FROM DUMMY; ELSE -- MAX(LAT_A2, LAT_B2) = LAT_B2 SELECT LAT_B2 INTO MAX_MIN_X FROM DUMMY; END IF; END IF; END IF; -- Min of Max IF LAT_A1 > LAT_A2 THEN -- MAX(LAT_A1, LAT_A2) = LAT_A1 IF LAT_B1 > LAT_B2 THEN -- MAX(LAT_B1, LAT_B2) = LAT_B1 IF LAT_A1 < LAT_B1 THEN -- MIN(LAT_A1, LAT_B1) = LAT_A1 SELECT LAT_A1 INTO MIN_MAX_X FROM DUMMY; ELSE -- MIN(LAT_A1, LAT_B1) = LAT_B1 SELECT LAT_B1 INTO MIN_MAX_X FROM DUMMY; END IF; ELSEIF LAT_B2 > LAT_B1 THEN -- MAX(LAT_B1, LAT_B2) = LAT_B2 IF LAT_A1 < LAT_B2 THEN -- MIN(LAT_A1, LAT_B2) = LAT_A1 SELECT LAT_A1 INTO MIN_MAX_X FROM DUMMY; ELSE -- MIN(LAT_A1, LAT_B2) = LAT_B2 SELECT LAT_B2 INTO MIN_MAX_X FROM DUMMY; END IF; END IF; ELSEIF LAT_A2 > LAT_A1 THEN -- MAX(LAT_A1, LAT_A2) = LAT_A2 IF LAT_B1 > LAT_B2 THEN -- MAX(LAT_B1, LAT_B2) = LAT_B1 IF LAT_A2 < LAT_B1 THEN -- MIN(LAT_A2, LAT_B1) = LAT_A2 SELECT LAT_A2 INTO MIN_MAX_X FROM DUMMY; ELSE -- MIN(LAT_A2, LAT_B1) = LAT_B1 SELECT LAT_B1 INTO MIN_MAX_X FROM DUMMY; END IF; ELSEIF LAT_B2 > LAT_B1 THEN -- MAX(LAT_B1, LAT_B2) = LAT_B2 IF LAT_A2 < LAT_B2 THEN -- MIN(LAT_A2, LAT_B2) = LAT_A2 SELECT LAT_A2 INTO MIN_MAX_X FROM DUMMY; ELSE -- MIN(LAT_A2, LAT_B2) = LAT_B2 SELECT LAT_B2 INTO MIN_MAX_X FROM DUMMY; END IF; END IF; END IF; IF XA < MAX_MIN_X OR XA > MIN_MAX_X THEN SELECT 0 INTO DOESINTERSECT FROM DUMMY; END IF; RET_DOESINTERSECT := :DOESINTERSECT; END;
quelle
Gelöst aber trotzdem warum nicht mit Python ... :)
def islineintersect(line1, line2): i1 = [min(line1[0][0], line1[1][0]), max(line1[0][0], line1[1][0])] i2 = [min(line2[0][0], line2[1][0]), max(line2[0][0], line2[1][0])] ia = [max(i1[0], i2[0]), min(i1[1], i2[1])] if max(line1[0][0], line1[1][0]) < min(line2[0][0], line2[1][0]): return False m1 = (line1[1][1] - line1[0][1]) * 1. / (line1[1][0] - line1[0][0]) * 1. m2 = (line2[1][1] - line2[0][1]) * 1. / (line2[1][0] - line2[0][0]) * 1. if m1 == m2: return False b1 = line1[0][1] - m1 * line1[0][0] b2 = line2[0][1] - m2 * line2[0][0] x1 = (b2 - b1) / (m1 - m2) if (x1 < max(i1[0], i2[0])) or (x1 > min(i1[1], i2[1])): return False return True
Dies:
print islineintersect([(15, 20), (100, 200)], [(210, 5), (23, 119)])
Ausgabe:
True
Und das:
print islineintersect([(15, 20), (100, 200)], [(-1, -5), (-5, -5)])
Ausgabe:
False
quelle