diff --git a/lib/linesweep.py b/lib/linesweep.py index 83c7fa3..9ae27a0 100644 --- a/lib/linesweep.py +++ b/lib/linesweep.py @@ -2,7 +2,7 @@ from bisect import bisect_left, bisect_right,insort import unittest from collections import namedtuple import math -from shapely.geometry import LineString +from shapely.geometry import LineString, Point from itertools import combinations @@ -31,7 +31,6 @@ def find_intersections(horizontal_segments, vertical_segments, epsilon=0.0): return intersections - def find_all_intersections(segments): # segments: List of ((x1, y1), (x2, y2)) lines = [LineString([p1, p2]) for p1, p2 in segments] @@ -48,7 +47,7 @@ def find_all_intersections(segments): return list(intersections) -def find_touch_cut_close(segments): +def find_touch_cut_close(segments, tol): # segments: Liste aus ((x1, y1), (x2,y2)) mit 1=Anfang, 2=Ende verbindungen = [] for i,(s1_start, s1_end) in enumerate(segments): @@ -58,11 +57,67 @@ def find_touch_cut_close(segments): continue line2 = LineString([s2_start,s2_end]) - if line1.distance(line2) < 0.5: #Toleranz 0,5 Hardcode + if line1.distance(line2) < tol: verbindungen.append((i,j,line1.intersection(line2))) return verbindungen +def find_touch_cut_close_ext(segments,tol): + # segments: Liste aus ((x1, y1), (x2,y2)) mit 1=Anfang, 2=Ende + verbindungen = [] + for i,(s1_start, s1_end) in enumerate(segments): + line1 = LineString([s1_start, s1_end]) + for j, ([s2_start, s2_end]) in enumerate(segments): + if i >= j: + continue + line2 = LineString([s2_start,s2_end]) + + #Prüfe Abstand + if line1.distance(line2) < tol: + schnitt = line1.intersection(line2) #Existiert tatsächlicher SP? + + if schnitt.is_empty: + #Prüfen, welcher Endpunkt von Segment näer an line2 + dist_start = Point(s2_start).distance(line1) #Abstand Statpunkt v. Linie1 zu Linie2 + dist_end = Point(s2_end).distance(line1) #Abstand Endpunkt v. Linie 1 zu Linie2 + + if dist_start > dist_end: + line2_ext = extend_from_point(s2_start, s2_end) + else: + line2_ext =extend_from_point(s2_end, s2_start) + + if dist_start < dist_end: + line1_ext = extend_from_point(s1_start, s1_end) + else: + line1_ext = extend_from_point(s1_end, s1_start) + + schnitt_ext1 = line2_ext.intersection(line1) + schnitt_ext2 = line1_ext.intersection(line2) + + if not schnitt_ext1.is_empty: + verbindungen.append((i,j,schnitt_ext1)) + if not schnitt_ext2.is_empty: + verbindungen.append((i,j,schnitt_ext2)) + else: + verbindungen.append((i,j,schnitt)) + + return verbindungen + +def extend_from_point(p1, p2, max_extension=0.3): + """Verlängert eine Linie von p1 in Richtung p2 um `max_extension`.""" + dx = p2[0] - p1[0] + dy = p2[1] - p1[1] + length = math.hypot(dx, dy) + + if length == 0: + return LineString([p1, p1]) # Kein Verlängern möglich + + # Normierter Richtungsvektor * (ursprüngliche Länge + Verlängerung) + scale = (length + max_extension) / length + new_end = (p1[0] + dx * scale, p1[1] + dy * scale) + + return LineString([p1, new_end]) + @@ -109,61 +164,48 @@ def find_touch_cut_close(segments): # self.assertEqual( [(0,0),(0,0)], b) # class TestScanLineAllgMethods(unittest.TestCase): -# def test_sweep(self): + def test_sweep(self): -# seks = [((1, 2), (5, 2)), ((3, 4), (7, 4)), ((4, 1), (4, 5)), ((6, 3), (6, 6))] + seks = [((1, 2), (5, 2)), ((3, 4), (7, 4)), ((4, 1), (4, 5)), ((6, 3), (6, 6))] -# a = find_all_intersections(seks) -# self.assertEqual( [(4,2),(4,4),(6,4)], a) + a = find_all_intersections(seks) + self.assertEqual( [(4,2),(4,4),(6,4)], a) -# def test_tol_hor(self): -# """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz -# schnittpunkt zwischen gegebener horizontalen und zu kurzen vertikalen -# """ + def test_tol_hor(self): + """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz + schnittpunkt zwischen gegebener horizontalen und zu kurzen vertikalen + """ -# seks = [((0, 0), (10, 0)), ((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))] + seks = [((0, 0), (10, 0)), ((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))] -# b = find_all_intersections(seks, 0.5) -# self.assertEqual( len(b), 2) -# self.assertEqual( [(6,0),(8,0)], b) + b = find_all_intersections(seks, 0.5) + self.assertEqual( len(b), 2) + self.assertEqual( [(6,0),(8,0)], b) -# def test_tol_ver(self): -# """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz -# schnittpunkt zwischen gegebener vertikalen und zu kurzen horizontalen -# """ + def test_tol_ver(self): + """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz + schnittpunkt zwischen gegebener vertikalen und zu kurzen horizontalen + """ -# seks = [((0.2, 0), (9.8, 0)), ((0, 0), (0, 10)), ((10, 0), (10, 10))] + seks = [((0.2, 0), (9.8, 0)), ((0, 0), (0, 10)), ((10, 0), (10, 10))] -# b = find_all_intersections(seks, 0.5) -# self.assertEqual( len(b), 2) -# self.assertEqual( [(6,0),(8,0)], b) + b = find_all_intersections(seks, 0.5) + self.assertEqual( len(b), 2) + self.assertEqual( [(6,0),(8,0)], b) -# def test_tol_diag(self): -# """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz -# Horizontale mit Diagonalen -# """ + def test_tol_diag(self): + """Testet die Anzahl der Schnittpunkte bei gegebener Toleranz + Horizontale mit Diagonalen + """ -# seks = [((0, 0), (5, 0)), ((0, -5), (10, 5))] + seks = [((0, 0), (5, 0)), ((0, -5), (10, 5))] -# b = find_all_intersections(seks, 0.5) -# self.assertEqual( len(b), 1) -# self.assertEqual( [(6,0),(8,0)], b) + b = find_all_intersections(seks, 0.5) + self.assertEqual( len(b), 1) + self.assertEqual( [(6,0),(8,0)], b) -# def test_shapely(self): -# # Beispiel -# segments = [ -# ((0, 0), (4, 4)), # Diagonal / -# ((0, 4), (4, 0)), # Diagonal \ -# ((2, -1), (2, 5)), # Vertikal -# ((-1, 2), (5, 2)) # Horizontal -# ] - -# result = find_all_intersections(segments) -# print(result) - -class TestShapely(unittest.TestCase): - def test_shapely_2_Stern(self): - # Beispiel !Stern mit gemeinsamen Schnittpunkt in (2,2) + def test_shapely(self): + # Beispiel segments = [ ((0, 0), (4, 4)), # Diagonal / ((0, 4), (4, 0)), # Diagonal \ @@ -171,10 +213,12 @@ class TestShapely(unittest.TestCase): ((-1, 2), (5, 2)) # Horizontal ] - result = find_touch_cut_close(segments) - self.assertEqual(len(result),1) + result = find_all_intersections(segments) + print(result) - def test_shapely_2_Rand(self): + +class TestShapely(unittest.TestCase): + def test_shapely(self): #Test mit Strecken horizontal, vertikal, diagonal, Schnittpunkt u. Annaeherung segments = [ ((0, 0), (5, 0)), # Horizontale @@ -183,8 +227,22 @@ class TestShapely(unittest.TestCase): ((0.25, 4), (4, 3)) # Diagonale \ mit 0.25 Abstand zu vertikaler und 1 zu horizontaler ] - result = find_touch_cut_close(segments) + result = find_touch_cut_close(segments, 0.5) self.assertEqual(len(result),3) + + def test_shapely_ext(self): + #Test mit Strecken horizontal, vertikal, diagonal, Schnittpunkt u. Annaeherung + segments = [ + ((0, 0), (5, 0)), # Horizontale + ((0, 0), (0, 5)), # Vertikale mit Schnitt Horizontal in 0,0 + ((0.25, 2), (5, 2)), # Horizontale mit 0.25 Abstand zu vertikaler + ((0.25, 4), (4, 3)), # Diagonale \ mit 0.25 Abstand zu vertikaler und 1 zu horizontaler + ((5.10, -1), (5.1, 3)) # Vertikale mit 0,25 Abstand zu erster u zweiter Horizontalen + ] + + result = find_touch_cut_close_ext(segments, 0.5) + self.assertEqual(len(result),3) + if __name__ == '__main__': diff --git a/lib/linesweep_circle.py b/lib/linesweep_circle.py new file mode 100644 index 0000000..eafeaf2 --- /dev/null +++ b/lib/linesweep_circle.py @@ -0,0 +1,68 @@ +from shapely.geometry import LineString, Point +from shapely.ops import nearest_points + +racks = [ + ((0, 0), (5, 0)), # Horizontal + ((0, 0), (0, 5)), # Vertikal (schneidet exakt) + ((0.25, 2), (5, 2)), # Parallel mit 0.25 Abstand zur Vertikalen + ((0.25, 4), (4, 3)), # Diagonal + ((5.10, -0.25), (5.1, 3)) # Parallel sehr nah an erstem Rack +] + +tol = 0.5 +tol_step = 0.05 + +rack_lines = [LineString([start, end]) for start, end in racks] +rack_endpoints = [(i, pt) for i, rack in enumerate(racks) for pt in rack] + +verbindungen = [] + +# Überprüfe jedes Rack +for i, rack_line in enumerate(rack_lines): + print(f"\n=== Prüfe Rack {i}: {rack_line}") + + for j, pt_coords in rack_endpoints: + if i == j: + continue # Eigenpunkte ignorieren + + pt = Point(pt_coords) + + # Prüfe auf exakten Schnittpunkt + if rack_line.intersects(pt): + intersection = rack_line.intersection(pt) + print(f" ✅ Echter Schnittpunkt mit Endpunkt von Rack {j}: {intersection}") + verbindungen.append((i,j, intersection)) + continue #diesen Punkt überspringen + + #alle Endpunkte prüfen, die NICHT exakt auf der Linie liegen + for j, pt_coords in rack_endpoints: + if i == j: + continue # Eigenpunkte ignorieren + + pt = Point(pt_coords) + + # Prüfe nur, wenn der Punkt **nicht** genau auf der Linie liegt + if rack_line.intersects(pt): + continue # Exakte Schnittpunkte nicht erneut behandeln + + dist = rack_line.distance(pt) + + #überprüfen, ob der Punkt im Toleranzbereich ist + if dist < tol: + print(f" 🔍 Punkt {pt} liegt {dist:.3f} von Linie {i} entfernt") + + radius = tol_step + while radius <= tol: + circle = pt.buffer(radius) # Kreis wächst mit dem Toleranzradius + if circle.intersects(rack_line): # Wenn der Kreis die Linie schneidet + contact = circle.intersection(rack_line) + if contact.geom_type == "Point": + nearest = contact + else: + nearest = nearest_points(pt, contact)[1] # Erster Kontaktpunkt von rack_line + + print(f" 🟡 Berührpunkt bei Radius {radius:.2f}: {nearest}") + verbindungen.append((i,j,nearest)) + break + radius += tol_step +