Wie bestimme ich, ob sich zwei Linien schneiden oder nicht, und wenn ja, an welchem x, y-Punkt?
geometry
line-intersection
KingNestor
quelle
quelle
Antworten:
Es gibt einen guten Ansatz für dieses Problem, bei dem Vektorkreuzprodukte verwendet werden. Definieren Sie das zweidimensionale Vektorkreuzprodukt v × w als v x w y - v y w x .
Angenommen, die beiden Liniensegmente verlaufen von p nach p + r und von q nach q + s . Dann ist jeder Punkt auf der ersten Linie als p + t r (für einen Skalarparameter t ) und jeder Punkt auf der zweiten Linie als q + u s (für einen Skalarparameter u ) darstellbar.
Die beiden Linien schneiden sich, wenn wir t und u so finden können, dass:
Kreuzen Sie beide Seiten mit s und bekommen Sie
Und da s × s = 0 ist, bedeutet dies
Und deshalb nach t lösen :
Auf die gleiche Weise können wir für u lösen :
Um die Anzahl der Berechnungsschritte zu verringern, ist es zweckmäßig, dies wie folgt umzuschreiben (wobei zu beachten ist, dass s × r = - r × s ):
Jetzt gibt es vier Fälle:
Wenn r × s = 0 und ( q - p ) × r = 0 ist, sind die beiden Linien kollinear.
Drücken Sie in diesem Fall die Endpunkte des zweiten Segments ( q und q + s ) als Gleichung des ersten Liniensegments ( p + t r ) aus:
Wenn das Intervall zwischen t 0 und t 1 das Intervall [0, 1] schneidet, sind die Liniensegmente kollinear und überlappen sich; ansonsten sind sie kollinear und disjunkt.
Es ist zu beachten, dass wenn s und r in entgegengesetzte Richtungen zeigen, s · r <0 ist und das zu prüfende Intervall daher [ t 1 , t 0 ] und nicht [ t 0 , t 1 ] ist.
Wenn r × s = 0 und ( q - p ) × r ≠ 0 ist, sind die beiden Linien parallel und schneiden sich nicht.
Wenn r × s ≤ 0 und 0 ≤ t ≤ 1 und 0 ≤ u ≤ 1 ist, treffen sich die beiden Liniensegmente am Punkt p + t r = q + u s .
Andernfalls sind die beiden Liniensegmente nicht parallel, schneiden sich jedoch nicht.
Kredit: Diese Methode ist die zweidimensionale Spezialisierung des 3D-Linienschnittalgorithmus aus dem Artikel "Schnittmenge zweier Linien im Dreiraum" von Ronald Goldman, veröffentlicht in Graphics Gems , Seite 304. In drei Dimensionen ist dies der übliche Fall Die Linien sind schief (weder parallel noch schneidend). In diesem Fall gibt die Methode die Punkte an, an denen sich die beiden Linien am nächsten nähern.
quelle
/ (r × s)
, aber(r × s)
ist ein Vektor, richtig? Ein Vektor(0, 0, rx * sy - ry * sx)
. Und die linke Seite ist ähnlich ein Vektor parallel zur z-Achse. Also ... teile ich einfach die z-Komponente durch die andere z-Komponente? Ist die Formel für t tatsächlich|(q − p) × s| / |(r × s)|
?FWIW erkennt die folgende Funktion (in C) sowohl Linienschnittpunkte als auch den Schnittpunkt. Es basiert auf einem Algorithmus in Andre LeMothes " Tricks of the Windows Game Programming Gurus ". Es ist nicht unähnlich zu einigen Algorithmen in anderen Antworten (z. B. Gareths). LeMothe verwendet dann Cramers Regel (frag mich nicht), um die Gleichungen selbst zu lösen.
Ich kann bestätigen, dass es in meinem schwachen Asteroiden-Klon funktioniert und mit den Randfällen, die in anderen Antworten von Elemental, Dan und Wodzu beschrieben wurden, korrekt umzugehen scheint. Es ist wahrscheinlich auch schneller als der von KingNestor veröffentlichte Code, da alles Multiplikation und Division ist, keine Quadratwurzeln!
Ich denke, es gibt ein gewisses Potenzial für eine Division durch Null, obwohl es in meinem Fall kein Problem war. Einfach zu modifizieren, um den Absturz trotzdem zu vermeiden.
Übrigens muss ich sagen, dass in LeMothes Buch, obwohl er anscheinend den Algorithmus richtig macht, das konkrete Beispiel, dass er Stecker in den falschen Zahlen zeigt und Berechnungen falsch macht. Zum Beispiel:
Das hat mich stundenlang verwirrt . :(
quelle
s
undt
direkt zu testen, sollte als nächstes die Beziehung zwischen den beiden Zählern und dem Nenner getestet werden. Nur wenn bestätigt wird, dass sich die Linien schneiden, müssen Sie den Wert vont
(aber nichts
) berechnen .Das Problem reduziert sich auf diese Frage: Überschneiden sich zwei Linien von A nach B und von C nach D? Dann können Sie es viermal fragen (zwischen der Linie und jeder der vier Seiten des Rechtecks).
Hier ist die Vektormathematik dafür. Ich gehe davon aus, dass die Linie von A nach B die fragliche Linie ist und die Linie von C nach D eine der Rechtecklinien ist. Meine Notation ist, dass dies
Ax
die "x-Koordinate von A" undCy
die "y-Koordinate von C" ist. Und "*
" bedeutet Punktprodukt, also zA*B = Ax*Bx + Ay*By
.Diese
h
Nummer ist der Schlüssel. Wennh
zwischen0
und1
, schneiden sich die Linien, sonst nicht. WennF*P
Null ist, können Sie die Berechnung natürlich nicht durchführen, aber in diesem Fall sind die Linien parallel und schneiden sich daher nur in den offensichtlichen Fällen.Der genaue Schnittpunkt ist
C + F*h
.Mehr Spaß:
Wenn
h
ist genau0
oder1
die Linien berühren an einem Endpunkt. Sie können dies als "Schnittpunkt" betrachten oder nicht, wie Sie es für richtig halten.Insbesondere müssen
h
Sie die Länge der Linie multiplizieren, um die andere Linie genau zu berühren.Daher
h<0
bedeutet If , dass die Rechtecklinie "hinter" der angegebenen Linie liegt (wobei "Richtung" "von A nach B" ist) und wennh>1
die Rechtecklinie "vor" der angegebenen Linie liegt.Ableitung:
A und C sind Vektoren, die auf den Beginn der Linie zeigen; E und F sind die Vektoren von den Enden von A und C, die die Linie bilden.
Für irgendwelche zwei nicht parallele Linien in der Ebene, muss es genau ein Paar von Skalar-
g
undh
derart , daß diese Gleichung gilt:Warum? Da sich zwei nicht parallele Linien schneiden müssen, können Sie beide Linien jeweils um einen bestimmten Betrag skalieren und berühren.
( Auf den ersten Blick sieht dies wie eine einzelne Gleichung mit zwei Unbekannten aus! Dies ist jedoch nicht der Fall, wenn man bedenkt, dass es sich um eine 2D-Vektorgleichung handelt, was bedeutet, dass dies wirklich ein Paar von Gleichungen in
x
und isty
.)Wir müssen eine dieser Variablen eliminieren. Ein einfacher Weg ist es, den
E
Term Null zu machen . Nehmen Sie dazu das Punktprodukt beider Seiten der Gleichung mit einem Vektor, der mit E auf Null punktiert. Dieser Vektor, den ichP
oben aufgerufen habe , und ich haben die offensichtliche Transformation von E durchgeführt.Sie haben jetzt:
quelle
A + E*g = C + F*h
Die beiden Linien schneiden sich genau dann, wenn die Lösung dieser Gleichung (vorausgesetzt, sie sind nicht parallel) beideg
undh
zwischen 0 und 1 liegt (in- oder exklusiv, je nachdem, ob Sie an einem Endpunkt berühren).Ich habe versucht, den von Jason oben so elegant beschriebenen Algorithmus zu implementieren. Leider habe ich während der Arbeit mit der Mathematik beim Debuggen viele Fälle gefunden, für die es nicht funktioniert.
Betrachten Sie zum Beispiel die Punkte A (10,10) B (20,20) C (10,1) D (1,10) ergibt h = 0,5, und dennoch wird durch Untersuchung klar, dass diese Segmente nicht nahe beieinander liegen andere.
Die grafische Darstellung macht deutlich, dass 0 <h <1-Kriterien nur anzeigen, dass der Schnittpunkt auf CD liegen würde, wenn er vorhanden wäre, aber nichts darüber aussagen, ob dieser Punkt auf AB liegt. Um sicherzustellen, dass es einen Kreuzungspunkt gibt, müssen Sie die symmetrische Berechnung für die Variable g durchführen. Die Anforderung für das Abfangen lautet: 0 <g <1 UND 0 <h <1
quelle
Hier ist eine Verbesserung von Gavins Antwort. Die Lösung von marcp ist ebenfalls ähnlich, verschiebt jedoch weder die Teilung.
Dies stellt sich tatsächlich auch als praktische Anwendung der Antwort von Gareth Rees heraus, da das Kreuzproduktäquivalent in 2D das Perp-Punkt-Produkt ist, von dem dieser Code drei verwendet. Wenn Sie zu 3D wechseln und das Kreuzprodukt verwenden und am Ende sowohl s als auch t interpolieren, erhalten Sie die beiden nächstgelegenen Punkte zwischen den Linien in 3D. Wie auch immer, die 2D-Lösung:
Grundsätzlich wird die Teilung auf den letzten Moment verschoben und die meisten Tests werden verschoben, bis bestimmte Berechnungen durchgeführt wurden, wodurch Early-Outs hinzugefügt werden. Schließlich wird auch die Division durch Null vermieden, die auftritt, wenn die Linien parallel sind.
Möglicherweise möchten Sie auch einen Epsilon-Test anstelle eines Vergleichs mit Null verwenden. Linien, die extrem nahe an der Parallele liegen, können zu leicht abweichenden Ergebnissen führen. Dies ist kein Fehler, sondern eine Einschränkung bei der Gleitkomma-Mathematik.
quelle
s32_y
anstelle von etwas verwenden, das beschreibt, wie es istpoint2YDifference
?Frage C: Wie erkennen Sie, ob sich zwei Liniensegmente schneiden oder nicht?
Ich habe nach dem gleichen Thema gesucht und war mit den Antworten nicht zufrieden. Deshalb habe ich einen Artikel geschrieben, in dem sehr detailliert erklärt wird, wie überprüft wird, ob sich zwei Liniensegmente mit vielen Bildern schneiden . Es gibt vollständigen (und getesteten) Java-Code.
Hier ist der Artikel, der auf die wichtigsten Teile zugeschnitten ist:
Der Algorithmus, der prüft, ob das Liniensegment a das Liniensegment b schneidet, sieht folgendermaßen aus:
Was sind Begrenzungsrahmen? Hier sind zwei Begrenzungsrahmen aus zwei Liniensegmenten:
Wenn beide Begrenzungsrahmen einen Schnittpunkt haben, verschieben Sie das Liniensegment a so, dass ein Punkt bei (0 | 0) liegt. Jetzt haben Sie eine Linie durch den durch a definierten Ursprung. Verschieben Sie nun das Liniensegment b auf die gleiche Weise und prüfen Sie, ob sich die neuen Punkte des Liniensegments b auf verschiedenen Seiten der Linie a befinden. Wenn dies der Fall ist, überprüfen Sie es umgekehrt. Ist dies auch der Fall, schneiden sich die Liniensegmente. Wenn nicht, kreuzen sie sich nicht.
Frage A: Wo schneiden sich zwei Liniensegmente?
Sie wissen, dass sich zwei Liniensegmente a und b schneiden. Wenn Sie das nicht wissen, überprüfen Sie es mit den Tools, die ich Ihnen in "Frage C" gegeben habe.
Jetzt können Sie einige Fälle durchgehen und die Lösung mit Mathematik der 7. Klasse erhalten (siehe Code und interaktives Beispiel ).
Frage B: Wie erkennen Sie, ob sich zwei Linien schneiden oder nicht?
Angenommen, Ihr Punkt
A = (x1, y1)
, PunktB = (x2, y2)
,C = (x_3, y_3)
,D = (x_4, y_4)
. Ihre erste Zeile wird durch AB (mit A! = B) und Ihre zweite durch CD (mit C! = D) definiert.Frage D: Wo schneiden sich zwei Linien?
Überprüfen Sie mit Frage B, ob sie sich überhaupt schneiden.
Die Linien a und b werden durch zwei Punkte für jede Linie definiert. Sie können grundsätzlich dieselbe Logik anwenden, die in Frage A verwendet wurde.
quelle
Die Antwort, die hier einmal akzeptiert wurde, ist falsch (sie wurde seitdem nicht akzeptiert, also Hurra!). Es werden nicht alle Nicht-Schnittpunkte korrekt entfernt. Trivial scheint es zu funktionieren, aber es kann fehlschlagen, insbesondere in dem Fall, dass 0 und 1 für h als gültig angesehen werden.
Betrachten Sie den folgenden Fall:
Linien bei (4,1) - (5,1) und (0,0) - (0,2)
Dies sind senkrechte Linien, die sich eindeutig nicht überlappen.
A = (4,1)
B = (5,1)
C = (0,0)
D = (0,2)
E = (5,1) - (4,1) = (- 1,0)
F = (0,2) - (0,0) = (0, -2)
P = (0,1)
h = ((4,1) - (0,0)) Punkt (0,1) / ((0) , -2) Punkt (0,1)) = 0
Gemäß der obigen Antwort treffen sich diese beiden Liniensegmente an einem Endpunkt (Werte von 0 und 1). Dieser Endpunkt wäre:
(0,0) + (0, -2) * 0 = (0,0)
Anscheinend treffen sich die beiden Liniensegmente bei (0,0), was sich auf Linie CD befindet, aber nicht auf Linie AB. Was läuft also falsch? Die Antwort ist, dass die Werte 0 und 1 nicht gültig sind und nur manchmal PASSIEREN, um den Endpunktschnitt korrekt vorherzusagen. Wenn die Verlängerung einer Linie (aber nicht der anderen) das Liniensegment treffen würde, sagt der Algorithmus einen Schnittpunkt von Liniensegmenten voraus, dies ist jedoch nicht korrekt. Ich stelle mir vor, dass durch Testen, beginnend mit AB gegen CD und dann auch mit CD gegen AB, dieses Problem beseitigt würde. Nur wenn beide einschließlich zwischen 0 und 1 liegen, kann gesagt werden, dass sie sich schneiden.
Ich empfehle die Verwendung der Vektorkreuzproduktmethode, wenn Sie Endpunkte vorhersagen müssen.
-Dan
quelle
Python-Version der Antwort von iMalc:
quelle
denom = float(...)
Das Finden des richtigen Schnittpunkts zweier Liniensegmente ist eine nicht triviale Aufgabe mit vielen Randfällen. Hier ist eine gut dokumentierte, funktionierende und getestete Lösung in Java.
Im Wesentlichen können drei Dinge passieren, wenn der Schnittpunkt zweier Liniensegmente gefunden wird:
Die Segmente schneiden sich nicht
Es gibt einen eindeutigen Schnittpunkt
Die Kreuzung ist ein weiteres Segment
HINWEIS : Im Code gehe ich davon aus, dass ein Liniensegment (x1, y1), (x2, y2) mit x1 = x2 und y1 = y2 ein gültiges Liniensegment ist. Mathematisch gesehen besteht ein Liniensegment aus verschiedenen Punkten, aber ich erlaube der Vollständigkeit halber, dass Segmente Punkte in dieser Implementierung sind.
Der Code stammt aus meinem Github-Repo
Hier ist ein einfaches Anwendungsbeispiel:
quelle
Ich wollte nur erwähnen, dass eine gute Erklärung und explizite Lösung in der Reihe Numeric Recipes zu finden ist. Ich habe die 3. Ausgabe und die Antwort finden Sie auf Seite 1117, Abschnitt 21.4. Eine andere Lösung mit einer anderen Nomenklatur findet sich in einem Artikel von Marina Gavrilova Reliable Line Section Intersection Testing . Ihre Lösung ist meiner Meinung nach etwas einfacher.
Meine Implementierung ist unten:
quelle
Oben sind viele Lösungen verfügbar, aber ich denke, die unten stehende Lösung ist ziemlich einfach und leicht zu verstehen.
Zwei Segmente Vector AB und Vector CD schneiden sich genau dann, wenn
Insbesondere befinden sich a und b genau dann auf der gegenüberliegenden Seite des Segments CD, wenn genau eines der beiden Tripel a, c, d und b, c, d gegen den Uhrzeigersinn ist.
Hier stellt CCW gegen den Uhrzeigersinn dar, was basierend auf der Ausrichtung der Punkte wahr / falsch zurückgibt.
Quelle: http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf Seite 2
quelle
CCW
Test definiert? Mit dem Zeichen des äußeren Produkts?C und Ziel-C
Basierend auf der Antwort von Gareth Rees
Viele der Funktionen und Strukturen sind privat, aber Sie sollten ziemlich einfach wissen können, was los ist. Dies ist in diesem Repo öffentlich https://github.com/hfossli/AGGeometryKit/
quelle
Das funktioniert gut für mich. Von hier genommen .
quelle
Ich habe einige dieser Antworten ausprobiert, aber sie haben bei mir nicht funktioniert (sorry Leute); Nach einigem Suchen im Internet fand ich dies .
Mit einer kleinen Änderung an seinem Code habe ich jetzt diese Funktion, die den Schnittpunkt zurückgibt, oder wenn keine Kreuzung gefunden wird, gibt sie -1, -1 zurück.
quelle
Es scheint ein gewisses Interesse an Gavins Antwort zu bestehen, für die Cortijon eine Javascript-Version in der Kommentaren und iMalc eine Version mit etwas weniger Berechnungen bereitstellte . Einige haben auf Mängel bei verschiedenen Codevorschlägen hingewiesen, andere haben die Effizienz einiger Codevorschläge kommentiert.
Der von iMalc über Gavins Antwort bereitgestellte Algorithmus ist der, den ich derzeit in einem Javascript-Projekt verwende, und ich wollte hier nur eine bereinigte Version bereitstellen, wenn er jemandem helfen kann.
quelle
t = dx2 * dy3 - dx3 * dy2;
...t/d
kommt es inscrossProduct = (line1XDifference * line2YDifference) - (line2XDifference * line1YDifference)
undscaledResult = crossProduct / dotProduct
?p1x, p1y
usw. Punkte durch ihre x- und y-Werte beschreiben, alsop1x
eine Abkürzung fürpoint1x
, ebenso wied1x
in meinen Augen eine Abkürzung für den griechischen Buchstaben,deltaX
oder man könnte sagendifferenceInX
. (mehr)Ich denke, es gibt eine viel einfachere Lösung für dieses Problem. Ich habe mir heute eine andere Idee ausgedacht und sie scheint gut zu funktionieren (zumindest vorerst in 2D). Sie müssen lediglich den Schnittpunkt zwischen zwei Linien berechnen und dann prüfen, ob der berechnete Schnittpunkt innerhalb der Begrenzungsrahmen beider Liniensegmente liegt. Wenn dies der Fall ist, schneiden sich die Liniensegmente. Das ist es.
BEARBEITEN:
So berechne ich den Schnittpunkt (ich weiß nicht mehr, wo ich diesen Codeausschnitt gefunden habe)
kommt von
und dies ist meine (zum Zweck der Antwort vereinfachte) BoundingBox-Klasse:
quelle
Diese Lösung kann helfen
quelle
Ich habe Kris 'obige Antwort auf JavaScript portiert. Nachdem er zahlreiche verschiedene Antworten ausprobiert hatte, lieferte er die richtigen Punkte. Ich dachte, ich würde verrückt, dass ich nicht die Punkte bekam, die ich brauchte.
quelle
Ich habe viele Möglichkeiten ausprobiert und dann beschlossen, meine eigenen zu schreiben. Hier ist es also:
Basierend auf diesen beiden Formeln: (Ich habe sie aus der Gleichung von Linien und anderen Formeln vereinfacht)
quelle
Dies basiert auf der Antwort von Gareth Ree. In diesem Fall wird auch die Überlappung der Liniensegmente zurückgegeben. In C ++ codiert, ist V eine einfache Vektorklasse. Wobei das Kreuzprodukt zweier Vektoren in 2D einen einzelnen Skalar zurückgibt. Es wurde von meinem automatischen Testsystem meiner Schule getestet und bestanden.
quelle
Hier ist eine grundlegende Implementierung eines Liniensegments in C # mit dem entsprechenden Schnittpunkterkennungscode. Es ist eine 2D-Vektor- / Punktstruktur erforderlich, die aufgerufen werden
Vector2f
kann. Sie können diese jedoch durch einen anderen Typ mit X / Y-Eigenschaften ersetzen. Sie können auch ersetzenfloat
mitdouble
, wenn das passt besser auf Ihre Bedürfnisse.Dieser Code wird in meiner .NET-Physikbibliothek Boing verwendet .
quelle
Ein C ++ - Programm, um zu überprüfen, ob sich zwei bestimmte Liniensegmente schneiden
quelle
Basierend auf der Antwort von @Gareth Rees, Version für Python:
quelle
Wenn jede Seite des Rechtecks ein Liniensegment ist und der vom Benutzer gezeichnete Teil ein Liniensegment ist, müssen Sie nur das vom Benutzer gezeichnete Segment auf Schnittpunkte mit den vier Seitenliniensegmenten überprüfen. Dies sollte angesichts der Start- und Endpunkte jedes Segments eine ziemlich einfache Übung sein.
quelle
Basierend auf der Antwort von t3chb0t:
quelle
Ich habe diesen Algorithmus aus dem Buch "Multiple View Geometry" gelesen.
folgenden Text mit
'als Transponierungszeichen
* als Punktprodukt
x als Kreuzprodukt bei Verwendung als Operator
1. Liniendefinition
Ein Punkt x_vec = (x, y) 'liegt auf der Linie ax + by + c = 0
wir bezeichnen L = (a, b, c) ', den Punkt als (x, y, 1)' als homogene Koordinaten
Die Liniengleichung kann wie folgt geschrieben werden
(x, y, 1) (a, b, c) '= 0 oder x' * L = 0
2. Schnittpunkt von Linien
wir haben zwei Linien L1 = (a1, b1, c1) ', L2 = (a2, b2, c2)'
Angenommen, x ist ein Punkt, ein Vektor und x = L1 x L2 (L1 Kreuzprodukt L2).
Seien Sie vorsichtig, x ist immer ein 2D-Punkt. Bitte lesen Sie homogene Koordinaten, wenn Sie sich nicht sicher sind, ob (L1xL2) ein Vektor mit drei Elementen und x ein 2D-Koordinaten ist.
Laut Triple Product wissen wir das
L1 * (L1 x L2) = 0 und L2 * (L1 x L2) = 0 aufgrund der Co-Ebene von L1, L2
wir ersetzen (L1xL2) durch den Vektor x, dann haben wir L1 * x = 0, L2 * x = 0, was bedeutet, dass x sowohl auf L1 als auch auf L2 liegt, x ist der Schnittpunkt.
Seien Sie vorsichtig, hier ist x homogene Koordinaten. Wenn das letzte Element von x Null ist, bedeutet dies, dass L1 und L2 parallel sind.
quelle
Viele Antworten haben alle Berechnungen in einer einzigen Funktion zusammengefasst. Wenn Sie die Liniensteigungen, y-Abschnitte oder x-Abschnitte für die Verwendung an anderer Stelle in Ihrem Code berechnen müssen, führen Sie diese Berechnungen redundant durch. Ich habe die jeweiligen Funktionen getrennt, offensichtliche Variablennamen verwendet und meinen Code kommentiert, um das Verfolgen zu erleichtern. Ich musste wissen, ob sich Linien unendlich über ihre Endpunkte hinaus schneiden, also in JavaScript:
http://jsfiddle.net/skibulk/evmqq00u/
quelle