diff --git a/src/main/java/algolib/geometry/dim2/ConvexHull.java b/src/main/java/algolib/geometry/dim2/ConvexHull.java index 1465d03..8b44a49 100644 --- a/src/main/java/algolib/geometry/dim2/ConvexHull.java +++ b/src/main/java/algolib/geometry/dim2/ConvexHull.java @@ -1,10 +1,11 @@ package algolib.geometry.dim2; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; -/** Algorithm for convex hull in 2D (monotone chain). */ +/** Algorithm for convex hull in 2D (Graham's scan). */ public final class ConvexHull { /** @@ -17,38 +18,29 @@ public static List findConvexHull(List points) if(points.size() < 3) return List.of(); - List sorted = new ArrayList<>(points); + Point2D minPoint = points.stream() + .min(Comparator.comparingDouble((Point2D pt) -> pt.y) + .thenComparingDouble(pt -> pt.x)) + .orElseThrow(); + Vector2D moving = Vector2D.between(Point2D.of(0, 0), minPoint); + List anglePoints = points.stream() + .map(pt -> Geometry2D.translate(pt, moving.negate())) + .collect(Collectors.toCollection(ArrayList::new)); - Geometry2D.sortByX(sorted); + Geometry2D.sortByAngle(anglePoints); - List lowerHull = createHalfHull(sorted); - - Collections.reverse(sorted); - - List upperHull = createHalfHull(sorted); - - lowerHull.remove(lowerHull.size() - 1); - upperHull.remove(upperHull.size() - 1); - lowerHull.addAll(upperHull); - return lowerHull; - } - - // Computes half of convex hull for given points. - private static List createHalfHull(List points) - { List hull = new ArrayList<>(); - for(Point2D pt : points) + for(Point2D pt : anglePoints) { while(hull.size() > 1 - && crossProduct(hull.get(hull.size() - 2), hull.get(hull.size() - 1), pt) - >= 0) + && crossProduct(hull.get(hull.size() - 2), hull.get(hull.size() - 1), pt) >= 0) hull.remove(hull.size() - 1); hull.add(pt); } - return hull; + return hull.stream().map(pt -> Geometry2D.translate(pt, moving)).toList(); } private static double crossProduct(Point2D pt1, Point2D pt2, Point2D pt3) diff --git a/src/test/java/algolib/geometry/dim2/ConvexHullTest.java b/src/test/java/algolib/geometry/dim2/ConvexHullTest.java index bb87349..133d02d 100644 --- a/src/test/java/algolib/geometry/dim2/ConvexHullTest.java +++ b/src/test/java/algolib/geometry/dim2/ConvexHullTest.java @@ -4,7 +4,7 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -// Tests: Algorithm for convex hull in 2D (monotone chain). +// Tests: Algorithm for convex hull in 2D (Graham's scan). public class ConvexHullTest { @Test @@ -55,9 +55,9 @@ public void findConvexHull_ThenPointsInHull() // then Assertions.assertThat(result) - .containsExactly(Point2D.of(-8, -7), Point2D.of(-1, -8), Point2D.of(3, -6), - Point2D.of(6, -4), Point2D.of(10, 2), Point2D.of(5, 9), - Point2D.of(-5, 10), Point2D.of(-7, 7)); + .containsExactly(Point2D.of(-1, -8), Point2D.of(3, -6), Point2D.of(6, -4), + Point2D.of(10, 2), Point2D.of(5, 9), Point2D.of(-5, 10), + Point2D.of(-7, 7), Point2D.of(-8, -7)); } @Test