programming-examples/java/Data_Structures/SegmentsIntersectionScanline.java
2019-11-15 12:59:38 +01:00

140 lines
3.8 KiB
Java

import java.util.*;
public class SegmentsIntersectionScanline {
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<Segment> 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};
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};
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<Segment> 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<Event> 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;
}
}