import java.util.*; public class SegmentsIntersectionScanline2 { public static Segment[] findIntersection(Segment[] s) { int n = s.length; Event[] events = new Event[n * 2]; for (int i = 0, cnt = 0; i < n; ++i) { events[cnt++] = new Event(s[i].x1, s[i].y1, 1, s[i]); events[cnt++] = new Event(s[i].x2, s[i].y2, -1, s[i]); } Arrays.sort(events, eventComparator); NavigableSet set = new TreeSet<>(segmentComparator); for (Event event : events) { Segment cur = event.segment; if (event.type == 1) { Segment floor = set.floor(cur); if (floor != null && isCrossOrTouchIntersect(cur, floor)) return new Segment[]{cur, floor}; Segment ceiling = set.ceiling(cur); if (ceiling != null && isCrossOrTouchIntersect(cur, ceiling)) return new Segment[]{cur, ceiling}; // SortedSet head = set.headSet(cur); // if (!head.isEmpty() && isCrossOrTouchIntersect(cur, head.last())) // return new Segment[]{cur, head.last()}; // // SortedSet tail = set.tailSet(cur); // if (!tail.isEmpty() && isCrossOrTouchIntersect(cur, tail.first())) // return new Segment[]{cur, tail.first()}; set.add(cur); } else { Segment lower = set.lower(cur); Segment higher = set.higher(cur); if (lower != null && higher != null && isCrossOrTouchIntersect(lower, higher)) return new Segment[]{lower, higher}; // SortedSet head = set.headSet(cur); // SortedSet tail = set.tailSet(cur, false); // if (!head.isEmpty() && !tail.isEmpty() && isCrossOrTouchIntersect(head.last(), tail.first())) // return new Segment[]{head.last(), tail.first()}; set.remove(cur); } } return null; } public static class Segment { final int x1, y1, x2, y2; public Segment(int x1, int y1, int x2, int y2) { if (x1 < x2 || x1 == x2 && y1 < y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } else { this.x1 = x2; this.y1 = y2; this.x2 = x1; this.y2 = y1; } } } static final Comparator segmentComparator = (a, b) -> { if (a.x1 < b.x1) { long v = cross(a.x1, a.y1, a.x2, a.y2, b.x1, b.y1); if (v != 0) return v > 0 ? -1 : 1; } else if (a.x1 > b.x1) { long v = cross(b.x1, b.y1, b.x2, b.y2, a.x1, a.y1); if (v != 0) return v < 0 ? -1 : 1; } return Integer.compare(a.y1, b.y1); }; static class Event { final int x, y; final int type; final Segment segment; public Event(int x, int y, int type, Segment segment) { this.x = x; this.y = y; this.type = type; this.segment = segment; } } static final Comparator eventComparator = (a, b) -> { if (a.x != b.x) return a.x < b.x ? -1 : 1; if (a.type != b.type) return a.type > b.type ? -1 : 1; return Integer.compare(a.y, b.y); }; static long cross(long ax, long ay, long bx, long by, long cx, long cy) { return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); } static boolean isCrossOrTouchIntersect(Segment s1, Segment s2) { long x1 = s1.x1; long y1 = s1.y1; long x2 = s1.x2; long y2 = s1.y2; long x3 = s2.x1; long y3 = s2.y1; long x4 = s2.x2; long y4 = s2.y2; if (Math.max(x1, x2) < Math.min(x3, x4) || Math.max(x3, x4) < Math.min(x1, x2) || Math.max(y1, y2) < Math.min(y3, y4) || Math.max(y3, y4) < Math.min(y1, y2)) return false; long z1 = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1); long z2 = (x2 - x1) * (y4 - y1) - (y2 - y1) * (x4 - x1); long z3 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); long z4 = (x4 - x3) * (y2 - y3) - (y4 - y3) * (x2 - x3); return (z1 <= 0 || z2 <= 0) && (z1 >= 0 || z2 >= 0) && (z3 <= 0 || z4 <= 0) && (z3 >= 0 || z4 >= 0); } // random test public static void main(String[] args) { Random rnd = new Random(); for (int step = 0; step < 100_000; step++) { int n = rnd.nextInt(100); Segment[] s = new Segment[n]; for (int i = 0; i < n; i++) { int r = 20; int x1 = rnd.nextInt(r) - r / 2; int y1 = rnd.nextInt(r) - r / 2; int x2 = rnd.nextInt(r) - r / 2; int y2 = rnd.nextInt(r) - r / 2; s[i] = new Segment(x1, y1, x2, y2); } Segment[] intersection = findIntersection(s); boolean hasIntersection = hasIntersection(s); if (intersection == null == hasIntersection) throw new RuntimeException(); } } static boolean hasIntersection(Segment[] s) { for (int i = 0; i < s.length; i++) for (int j = i + 1; j < s.length; j++) if (isCrossOrTouchIntersect(s[i], s[j])) return true; return false; } }