programming-examples/java/Data_Structures/ConvexHull.java

105 lines
3.4 KiB
Java
Raw Normal View History

2019-11-15 12:59:38 +01:00
import java.util.*;
public class ConvexHull {
public static Point[] convexHull(Point[] points) {
Arrays.sort(points, (a, b) -> Integer.compare(a.x, b.x) != 0 ? Integer.compare(a.x, b.x) : Integer.compare(a.y, b.y));
int n = points.length;
Point[] hull = new Point[n + 1];
int cnt = 0;
for (int i = 0; i < 2 * n; i++) {
int j = i < n ? i : 2 * n - 1 - i;
while (cnt >= 2 && isNotRightTurn(hull[cnt - 2], hull[cnt - 1], points[j]))
--cnt;
hull[cnt++] = points[j];
}
return Arrays.copyOf(hull, cnt - 1);
}
static boolean isNotRightTurn(Point a, Point b, Point c) {
long cross = (long) (a.x - b.x) * (c.y - b.y) - (long) (a.y - b.y) * (c.x - b.x);
long dot = (long) (a.x - b.x) * (c.x - b.x) + (long) (a.y - b.y) * (c.y - b.y);
return cross < 0 || cross == 0 && dot <= 0;
}
public static class Point {
public final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static Point[] convexHull2(Point[] p) {
int n = p.length;
if (n <= 1)
return p;
Arrays.sort(p, (a, b) -> Integer.compare(a.x, b.x) != 0 ? Integer.compare(a.x, b.x) : Integer.compare(a.y, b.y));
Point[] h = new Point[n * 2];
int cnt = 0;
for (int i = 0; i < n; h[cnt++] = p[i++])
while (cnt > 1 && cross(h[cnt - 2], h[cnt - 1], p[i]) >= 0)
--cnt;
for (int i = n - 2, t = cnt; i >= 0; h[cnt++] = p[i--])
while (cnt > t && cross(h[cnt - 2], h[cnt - 1], p[i]) >= 0)
--cnt;
return Arrays.copyOf(h, cnt - 1 - (h[0].x == h[1].x && h[0].y == h[1].y ? 1 : 0));
}
static long cross(Point a, Point b, Point c) {
return (long) (b.x - a.x) * (c.y - a.y) - (long) (b.y - a.y) * (c.x - a.x);
}
// random test
public static void main(String[] args) {
Random rnd = new Random(1);
for (int step = 0; step < 100_000; step++) {
int n = rnd.nextInt(10) + 1;
Point[] points = new Point[n];
for (int i = 0; i < n; i++) {
int range = 10;
points[i] = new Point(rnd.nextInt(range) - range / 2, rnd.nextInt(range) - range / 2);
}
Point[] convexHull = convexHull(points);
Point[] convexHull2 = convexHull2(points);
for (int i = 0; i < Math.max(convexHull.length, convexHull2.length); i++)
if (convexHull[i].x != convexHull2[i].x || convexHull[i].y != convexHull2[i].y)
throw new RuntimeException();
for (int i = 0; i <= convexHull.length; i++) {
final Point[] hull;
if (i == 0) {
hull = convexHull;
} else {
List<Point> list = new ArrayList<>();
Collections.addAll(list, convexHull);
list.remove(i - 1);
hull = list.toArray(new Point[list.size()]);
}
boolean exterior = false;
for (Point point : points)
exterior |= pointInPolygon(point.x, point.y, hull) == -1;
if (exterior != (i > 0))
throw new RuntimeException();
}
}
}
static int pointInPolygon(int qx, int qy, Point[] points) {
int n = points.length;
int cnt = 0;
for (int i = 0, j = n - 1; i < n; j = i++) {
if (points[i].y == qy && (points[i].x == qx || points[j].y == qy && (points[i].x <= qx || points[j].x <= qx) && (points[i].x >= qx || points[j].x >= qx)))
return 0; // boundary
if ((points[i].y > qy) != (points[j].y > qy)) {
long det = (long) (points[i].x - qx) * (points[j].y - qy) - (long) (points[j].x - qx) * (points[i].y - qy);
if (det == 0)
return 0; // boundary
if ((det > 0) != (points[j].y - points[i].y > 0))
++cnt;
}
}
return cnt % 2 == 0 ? -1 /* exterior */ : 1 /* interior */;
}
}