16 Stimmen

Wie kann man Dreieck-Dreieck-Kreuzungen am effizientesten erkennen?

Wie kann ich feststellen, ob sich zwei Dreiecke im euklidischen 2D-Raum schneiden? (d.h. klassische 2D-Geometrie) anhand der (X,Y)-Koordinaten jedes Eckpunkts in jedem Dreieck.

0 Stimmen

Was den wirklich effizientesten Algorithmus anbelangt, so wurde an dieser Frage noch nicht viel gearbeitet - niemand hat eindeutig gezeigt, welche Variante die schnellste ist. Ein Problem ist, dass ein Großteil der Diskussion sich auf Tris im 3D-Raum bezieht. Z.B. realtimecollisiondetection.net/blog/?p=29 PS Solche Probleme werden oft in Form von Punkten auf der "richtigen Seite" eines Liniensegments dargestellt. Beispiel: mochima.com/articles/cuj_geometry_article/ Wie Nick in seinem letzten Absatz darlegt, kommt es in der Praxis vor allem darauf an, wie gut man die Auslese durchführt.

18voto

Nick Dandoulakis Punkte 41402

Eine Möglichkeit ist zu prüfen, ob zwei Seiten des Dreiecks A überschneiden. mit einer beliebigen Seite des Dreiecks B, und prüfen Sie dann alle sechs Möglichkeiten eines Punktes von A in B oder eines Punktes von B in A.

Für einen Punkt innerhalb eines Dreiecks siehe zum Beispiel: Punkt im Dreieckstest.

Wenn wir Kollisionen an Polygonen testen, haben wir auch ein umgebendes Rechteck für unsere Polygone. Wir testen also zunächst auf Rechteckkollisionen und wenn es eine treffen. fahren wir mit der Erkennung von Polygonkollisionen fort.

0 Stimmen

Hallo @Joe. Es ist richtig, dass wir alle 3 Seiten von A gegen alle 3 Seiten von B prüfen sollten. Aber da wir prüfen, ob die Ecken von A innerhalb von B liegen (und umgekehrt) - nach der Prüfung der Schnittpunkte der Liniensegmente - funktioniert das ganze Verfahren trotzdem. Denn wenn wir eine Ecke innerhalb des anderen Dreiecks entdecken, haben wir eine Kollision.

1 Stimmen

Bei Dreieckstests werden nur 4 Punkte benötigt. jsfiddle.net/eyal/gxw3632c Dies ist kein schneller Algorithmus für Dreieck-Dreieck-Kreuzungen

4voto

Martin Wang Punkte 957

Python-Implementierung für Linienüberschneidung y Punkt im Dreieck Test mit einer kleinen Änderung.

def line_intersect2(v1,v2,v3,v4):
    '''
    judge if line (v1,v2) intersects with line(v3,v4)
    '''
    d = (v4[1]-v3[1])*(v2[0]-v1[0])-(v4[0]-v3[0])*(v2[1]-v1[1])
    u = (v4[0]-v3[0])*(v1[1]-v3[1])-(v4[1]-v3[1])*(v1[0]-v3[0])
    v = (v2[0]-v1[0])*(v1[1]-v3[1])-(v2[1]-v1[1])*(v1[0]-v3[0])
    if d<0:
        u,v,d= -u,-v,-d
    return (0<=u<=d) and (0<=v<=d)

def point_in_triangle2(A,B,C,P):
    v0 = [C[0]-A[0], C[1]-A[1]]
    v1 = [B[0]-A[0], B[1]-A[1]]
    v2 = [P[0]-A[0], P[1]-A[1]]
    cross = lambda u,v: u[0]*v[1]-u[1]*v[0]
    u = cross(v2,v0)
    v = cross(v1,v2)
    d = cross(v1,v0)
    if d<0:
        u,v,d = -u,-v,-d
    return u>=0 and v>=0 and (u+v) <= d

def tri_intersect2(t1, t2):
    '''
    judge if two triangles in a plane intersect 
    '''
    if line_intersect2(t1[0],t1[1],t2[0],t2[1]): return True
    if line_intersect2(t1[0],t1[1],t2[0],t2[2]): return True
    if line_intersect2(t1[0],t1[1],t2[1],t2[2]): return True
    if line_intersect2(t1[0],t1[2],t2[0],t2[1]): return True
    if line_intersect2(t1[0],t1[2],t2[0],t2[2]): return True
    if line_intersect2(t1[0],t1[2],t2[1],t2[2]): return True
    if line_intersect2(t1[1],t1[2],t2[0],t2[1]): return True
    if line_intersect2(t1[1],t1[2],t2[0],t2[2]): return True
    if line_intersect2(t1[1],t1[2],t2[1],t2[2]): return True
    inTri = True 
    inTri = inTri and point_in_triangle2(t1[0],t1[1],t1[2], t2[0])
    inTri = inTri and point_in_triangle2(t1[0],t1[1],t1[2], t2[1])
    inTri = inTri and point_in_triangle2(t1[0],t1[1],t1[2], t2[2])
    if inTri == True: return True
    inTri = True
    inTri = inTri and point_in_triangle2(t2[0],t2[1],t2[2], t1[0])
    inTri = inTri and point_in_triangle2(t2[0],t2[1],t2[2], t1[1])
    inTri = inTri and point_in_triangle2(t2[0],t2[1],t2[2], t1[2])
    if inTri == True: return True
    return False

0 Stimmen

Dies ist in diesem Fall die falsche Antwort: t1 = [[0,0],[5,0],[0,5]]; t2 = [[-10,0],[-5,0],[-1,6]]; print (tri_intersect2(t1, t2), False)

1 Stimmen

@TimSC Ja, bei zwei parallelen Linien wird der Schnittpunkt nicht erkannt. Sie können erzwingen, dass |d| größer ist als eine kleine positive Zahl in der Funktion line_intersect2 .

2 Stimmen

Sie müssen nicht alle 9 Linienkreuzungen machen, Sie können auch nur 8 machen. Denn wenn sich eines der Dreiecke mit dem anderen schneidet, muss es auch wieder herauskommen. Wenn es also 1 Schnittpunkt gibt, müssen es zwei sein. Außerdem braucht man nicht alle Punkte in den Dreiecktests, denn wenn es keine Schnittpunkte gibt, dann sind entweder alle Punkte innen oder keiner. Du kannst also 8 line_intersect und 2 point in Triangle machen. Oder machen Sie 6 line_intersect und dann 6 Punkt in Triangle. Das hängt davon ab, was für Sie schneller ist.

4voto

Yves Daoust Punkte 53298

Sortieren Sie die Eckpunkte der beiden Dreiecke nach abnehmender Ordinate. Das erfordert höchstens drei Vergleiche pro Dreieck. Führen Sie dann die beiden Sequenzen zusammen. Ich schätze, dass dies höchstens fünf Vergleiche erfordert.

Betrachten Sie nun für jede Ordinate eine horizontale Linie. Sie schneidet die beiden Dreiecke in höchstens einem Liniensegment, und es ist leicht zu prüfen, ob sich die Segmente überschneiden. Wenn ja, oder wenn sie zwischen zwei Linien die Reihenfolge ändern, dann schneiden sich die Dreiecke.

enter image description here


更新しました。

Es gibt eine affine Transformation, die eines der Dreiecke auf (0, 0)-(1, 0)-(0, 1) normalisieren kann. Wendet man sie auf das andere an, vereinfachen sich viele Berechnungen.

enter image description here

0voto

TimSC Punkte 1331

Hier ist mein Versuch, das Dreieck-Dreieck-Kollisionsproblem zu lösen (in Python implementiert):

#2D Triangle-Triangle collisions in python
#Release by Tim Sheerman-Chase 2016 under CC0

import numpy as np

def CheckTriWinding(tri, allowReversed):
    trisq = np.ones((3,3))
    trisq[:,0:2] = np.array(tri)
    detTri = np.linalg.det(trisq)
    if detTri < 0.0:
        if allowReversed:
            a = trisq[2,:].copy()
            trisq[2,:] = trisq[1,:]
            trisq[1,:] = a
        else: raise ValueError("triangle has wrong winding direction")
    return trisq

def TriTri2D(t1, t2, eps = 0.0, allowReversed = False, onBoundary = True):
    #Trangles must be expressed anti-clockwise
    t1s = CheckTriWinding(t1, allowReversed)
    t2s = CheckTriWinding(t2, allowReversed)

    if onBoundary:
        #Points on the boundary are considered as colliding
        chkEdge = lambda x: np.linalg.det(x) < eps
    else:
        #Points on the boundary are not considered as colliding
        chkEdge = lambda x: np.linalg.det(x) <= eps

    #For edge E of trangle 1,
    for i in range(3):
        edge = np.roll(t1s, i, axis=0)[:2,:]

        #Check all points of trangle 2 lay on the external side of the edge E. If
        #they do, the triangles do not collide.
        if (chkEdge(np.vstack((edge, t2s[0]))) and
            chkEdge(np.vstack((edge, t2s[1]))) and  
            chkEdge(np.vstack((edge, t2s[2])))):
            return False

    #For edge E of trangle 2,
    for i in range(3):
        edge = np.roll(t2s, i, axis=0)[:2,:]

        #Check all points of trangle 1 lay on the external side of the edge E. If
        #they do, the triangles do not collide.
        if (chkEdge(np.vstack((edge, t1s[0]))) and
            chkEdge(np.vstack((edge, t1s[1]))) and  
            chkEdge(np.vstack((edge, t1s[2])))):
            return False

    #The triangles collide
    return True

if __name__=="__main__":
    t1 = [[0,0],[5,0],[0,5]]
    t2 = [[0,0],[5,0],[0,6]]
    print (TriTri2D(t1, t2), True)

    t1 = [[0,0],[0,5],[5,0]]
    t2 = [[0,0],[0,6],[5,0]]
    print (TriTri2D(t1, t2, allowReversed = True), True)

    t1 = [[0,0],[5,0],[0,5]]
    t2 = [[-10,0],[-5,0],[-1,6]]
    print (TriTri2D(t1, t2), False)

    t1 = [[0,0],[5,0],[2.5,5]]
    t2 = [[0,4],[2.5,-1],[5,4]]
    print (TriTri2D(t1, t2), True)

    t1 = [[0,0],[1,1],[0,2]]
    t2 = [[2,1],[3,0],[3,2]]
    print (TriTri2D(t1, t2), False)

    t1 = [[0,0],[1,1],[0,2]]
    t2 = [[2,1],[3,-2],[3,4]]
    print (TriTri2D(t1, t2), False)

    #Barely touching
    t1 = [[0,0],[1,0],[0,1]]
    t2 = [[1,0],[2,0],[1,1]]
    print (TriTri2D(t1, t2, onBoundary = True), True)

    #Barely touching
    t1 = [[0,0],[1,0],[0,1]]
    t2 = [[1,0],[2,0],[1,1]]
    print (TriTri2D(t1, t2, onBoundary = False), False)

Es funktioniert auf der Grundlage der Tatsache, dass sich die Dreiecke nicht überschneiden, wenn alle Punkte von Dreieck 1 auf der Außenseite von mindestens einer der Kanten von Dreieck 2 liegen (oder umgekehrt). Natürlich sind Dreiecke niemals konkav.

Ich weiß nicht, ob dieser Ansatz mehr oder weniger effizient ist als die anderen.

Bonus: Ich habe es nach C++ portiert https://gist.github.com/TimSC/5ba18ae21c4459275f90

0voto

Dmitry Kamenetsky Punkte 151

Mir ist klar, dass dies eine sehr alte Frage ist, aber hier ist der Rosetta-Code für diese Aufgabe in vielen Sprachen: https://rosettacode.org/wiki/Determine_if_two_triangles_overlap

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X