from bisect import bisect_left, bisect_right,insort import unittest from collections import namedtuple import math def find_intersections(horizontal_segments, vertical_segments, epsilon=0.0): horizontal_segments.sort(key=lambda h: h[0][1]) # sortiere nach y horizontal_ys = [] horizontal_x_ranges = [] for (x1, y), (x2, _) in horizontal_segments: if x1 > x2: x1, x2 = x2, x1 horizontal_ys.append(y) horizontal_x_ranges.append((x1, x2)) intersections = [] for (x, y1), (_, y2) in vertical_segments: if y1 > y2: y1, y2 = y2, y1 for idx, y in enumerate(horizontal_ys): if y1 - epsilon <= y <= y2 + epsilon: x_start, x_end = horizontal_x_ranges[idx] if x_start - epsilon <= x <= x_end + epsilon: intersections.append((x, y)) return intersections Point = namedtuple('Point', ['x', 'y']) Segment = namedtuple('Segment', ['p1', 'p2']) def ccw(a, b, c): return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x) def intersect(s1, s2, tol=1e-8): """Erweiterte Schnittprüfung mit Toleranz.""" # Klassische Schnitttest a, b = s1.p1, s1.p2 c, d = s2.p1, s2.p2 if ccw(a, c, d) != ccw(b, c, d) and ccw(a, b, c) != ccw(a, b, d): return True # Zusätzliche Toleranzprüfung: Mindestabstand der Segmente return segment_distance(s1, s2) < tol def point_distance(p1, p2): return math.hypot(p1.x - p2.x, p1.y - p2.y) def point_segment_distance(p, seg): """Abstand eines Punkts zu einem Segment.""" x, y = p.x, p.y x1, y1 = seg.p1.x, seg.p1.y x2, y2 = seg.p2.x, seg.p2.y dx, dy = x2 - x1, y2 - y1 if dx == dy == 0: return point_distance(p, seg.p1) t = max(0, min(1, ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy))) nearest = Point(x1 + t * dx, y1 + t * dy) return point_distance(p, nearest) def segment_distance(s1, s2): """Kleinster Abstand zweier Segmente.""" return min( point_segment_distance(s1.p1, s2), point_segment_distance(s1.p2, s2), point_segment_distance(s2.p1, s1), point_segment_distance(s2.p2, s1), ) def segment_cmp_x(segment, x): p1, p2 = segment.p1, segment.p2 if p1.x == p2.x: return min(p1.y, p2.y) slope = (p2.y - p1.y) / (p2.x - p1.x) y = p1.y + slope * (x - p1.x) return y def schnittpunkttest(segments, tolerance=1e-8): events = [] for seg in segments: left = seg.p1 if seg.p1.x < seg.p2.x else seg.p2 right = seg.p2 if seg.p1.x < seg.p2.x else seg.p1 events.append((left.x, True, seg)) events.append((right.x, False, seg)) events.sort() L = [] for x, is_left, seg in events: key = lambda s: segment_cmp_x(s, x) idx = bisect_left(L, seg, key=key) if is_left: insort(L, seg, key=key) idx = bisect_left(L, seg, key=key) pred = L[idx - 1] if idx > 0 else None succ = L[idx + 1] if idx < len(L) - 1 else None if (pred and intersect(pred, seg, tolerance)) or (succ and intersect(succ, seg, tolerance)): #print("ja") return else: idx = bisect_left(L, seg, key=key) if idx < len(L) and L[idx] == seg: pred = L[idx - 1] if idx > 0 else None succ = L[idx + 1] if idx < len(L) - 1 else None L.pop(idx) if pred and succ and intersect(pred, succ, tolerance): #print("ja") return #print("nein") def sektionen(segmente, tolerance): laenge = len(segmente) lseg = list() for r1 in range(laenge): s1 = segmente[r1] p1 = Point(s1[0][0],s1[0][1]) p2 = Point(s1[1][0],s1[1][1]) seg1 = Segment( p1, p2 ) lseg.append(seg1) schnittpunkttest(lseg, tolerance) # class TestSweepMethods(unittest.TestCase): # def test_sweep(self): # horizontal = [((1, 2), (5, 2)), ((3, 4), (7, 4))] # vertical = [((4, 1), (4, 5)), ((6, 3), (6, 6))] # a = find_intersections(horizontal, vertical) # 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 # """ # horizontal = [((0, 0), (10, 0))] # vertical = [((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))] # b = find_intersections(horizontal, vertical, 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 # """ # horizontal = [((0.2, 0), (9.8, 0))] # vertical = [((0, 0), (0, 10)), ((10, 0), (10, 10))] # b = find_intersections(horizontal, vertical, 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 # """ # horizontal = [((0, 0), (5, 0))] # diagonal = [((0, -5), (10, 5))] # b = find_intersections(horizontal, diagonal, 0.5) # self.assertEqual( len(b), 1) # self.assertEqual( [(0,0),(0,0)], b) class TestScanLineAllgMethods(unittest.TestCase): # def test_sweep(self): # seks = [((1, 2), (5, 2)), ((3, 4), (7, 4)), ((4, 1), (4, 5)), ((6, 3), (6, 6))] # a = sektionen(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 # """ # seks = [((0, 0), (10, 0)), ((4, 0.6), (4, 5)), ((6, 0.2), (6, 6)),((8,-0.2),(8, 4))] # b = sektionen(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 # """ # seks = [((0.2, 0), (9.8, 0)), ((0, 0), (0, 10)), ((10, 0), (10, 10))] # b = sektionen(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 # """ # seks = [((0, 0), (5, 0)), ((0, -5), (10, 5))] # b = sektionen(seks, 0.5) # self.assertEqual( len(b), 1) # self.assertEqual( [(6,0),(8,0)], b) def test_chatgpt(self): s1 = Segment(Point(0, 0), Point(4, 4)) # Diagonale s2 = Segment(Point(0, 4), Point(4, 0)) # Kreuzt s1 → Schnitt s3 = Segment(Point(5, 5), Point(6, 6)) # Weit entfernt, kein Schnitt s4 = Segment(Point(1, 1), Point(1, 3)) # Schneidet s1 → weiterer Schnitt schnittpunkttest([s1, s2, s3, s4], tolerance=1e-8) # Ausgabe: ja if __name__ == '__main__': unittest.main()