11package com .thealgorithms .geometry ;
22
33import static org .junit .jupiter .api .Assertions .assertEquals ;
4+ import static org .junit .jupiter .api .Assertions .assertTrue ;
45
56import java .util .Arrays ;
67import java .util .List ;
@@ -10,31 +11,117 @@ public class ConvexHullTest {
1011
1112 @ Test
1213 void testConvexHullBruteForce () {
14+ // Test 1: Triangle with intermediate point
1315 List <Point > points = Arrays .asList (new Point (0 , 0 ), new Point (1 , 0 ), new Point (10 , 1 ));
1416 List <Point > expected = Arrays .asList (new Point (0 , 0 ), new Point (1 , 0 ), new Point (10 , 1 ));
1517 assertEquals (expected , ConvexHull .convexHullBruteForce (points ));
1618
19+ // Test 2: Collinear points
1720 points = Arrays .asList (new Point (0 , 0 ), new Point (1 , 0 ), new Point (10 , 0 ));
1821 expected = Arrays .asList (new Point (0 , 0 ), new Point (10 , 0 ));
1922 assertEquals (expected , ConvexHull .convexHullBruteForce (points ));
2023
24+ // Test 3: Complex polygon
2125 points = Arrays .asList (new Point (0 , 3 ), new Point (2 , 2 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (3 , 0 ), new Point (0 , 0 ), new Point (3 , 3 ), new Point (2 , -1 ), new Point (2 , -4 ), new Point (1 , -3 ));
2226 expected = Arrays .asList (new Point (2 , -4 ), new Point (1 , -3 ), new Point (0 , 0 ), new Point (3 , 0 ), new Point (0 , 3 ), new Point (3 , 3 ));
2327 assertEquals (expected , ConvexHull .convexHullBruteForce (points ));
2428 }
2529
2630 @ Test
2731 void testConvexHullRecursive () {
32+ // Test 1: Triangle - CCW order starting from bottom-left
33+ // The algorithm includes (1,0) as it's detected as an extreme point
2834 List <Point > points = Arrays .asList (new Point (0 , 0 ), new Point (1 , 0 ), new Point (10 , 1 ));
35+ List <Point > result = ConvexHull .convexHullRecursive (points );
2936 List <Point > expected = Arrays .asList (new Point (0 , 0 ), new Point (1 , 0 ), new Point (10 , 1 ));
30- assertEquals (expected , ConvexHull .convexHullRecursive (points ));
37+ assertEquals (expected , result );
38+ assertTrue (isCounterClockwise (result ), "Points should be in counter-clockwise order" );
3139
40+ // Test 2: Collinear points
3241 points = Arrays .asList (new Point (0 , 0 ), new Point (1 , 0 ), new Point (10 , 0 ));
42+ result = ConvexHull .convexHullRecursive (points );
3343 expected = Arrays .asList (new Point (0 , 0 ), new Point (10 , 0 ));
34- assertEquals (expected , ConvexHull . convexHullRecursive ( points ) );
44+ assertEquals (expected , result );
3545
36- points = Arrays .asList (new Point (0 , 3 ), new Point (2 , 2 ), new Point (1 , 1 ), new Point (2 , 1 ), new Point (3 , 0 ), new Point (0 , 0 ), new Point (3 , 3 ), new Point (2 , -1 ), new Point (2 , -4 ), new Point (1 , -3 ));
37- expected = Arrays .asList (new Point (2 , -4 ), new Point (1 , -3 ), new Point (0 , 0 ), new Point (3 , 0 ), new Point (0 , 3 ), new Point (3 , 3 ));
38- assertEquals (expected , ConvexHull .convexHullRecursive (points ));
46+ // Test 3: Complex polygon
47+ // Convex hull vertices in CCW order from bottom-most point (2,-4):
48+ // (2,-4) -> (3,0) -> (3,3) -> (0,3) -> (0,0) -> (1,-3) -> back to (2,-4)
49+ points = Arrays .asList (
50+ new Point (0 , 3 ), new Point (2 , 2 ), new Point (1 , 1 ),
51+ new Point (2 , 1 ), new Point (3 , 0 ), new Point (0 , 0 ),
52+ new Point (3 , 3 ), new Point (2 , -1 ), new Point (2 , -4 ),
53+ new Point (1 , -3 )
54+ );
55+ result = ConvexHull .convexHullRecursive (points );
56+ expected = Arrays .asList (
57+ new Point (2 , -4 ), // Bottom-most, left-most (starting point)
58+ new Point (3 , 0 ), // Right side going up
59+ new Point (3 , 3 ), // Top right corner
60+ new Point (0 , 3 ), // Top left corner
61+ new Point (0 , 0 ), // Left side coming down
62+ new Point (1 , -3 ) // Bottom section, back towards start
63+ );
64+ assertEquals (expected , result );
65+ assertTrue (isCounterClockwise (result ), "Points should be in counter-clockwise order" );
66+ }
67+
68+ @ Test
69+ void testConvexHullRecursiveAdditionalCases () {
70+ // Test 4: Square (all corners on hull)
71+ List <Point > points = Arrays .asList (
72+ new Point (0 , 0 ), new Point (2 , 0 ),
73+ new Point (2 , 2 ), new Point (0 , 2 )
74+ );
75+ List <Point > result = ConvexHull .convexHullRecursive (points );
76+ List <Point > expected = Arrays .asList (
77+ new Point (0 , 0 ), new Point (2 , 0 ),
78+ new Point (2 , 2 ), new Point (0 , 2 )
79+ );
80+ assertEquals (expected , result );
81+ assertTrue (isCounterClockwise (result ), "Square points should be in CCW order" );
82+
83+ // Test 5: Pentagon with interior point
84+ points = Arrays .asList (
85+ new Point (0 , 0 ), new Point (4 , 0 ), new Point (5 , 3 ),
86+ new Point (2 , 5 ), new Point (-1 , 3 ), new Point (2 , 2 ) // (2,2) is interior
87+ );
88+ result = ConvexHull .convexHullRecursive (points );
89+ // CCW from (0,0): (0,0) -> (4,0) -> (5,3) -> (2,5) -> (-1,3)
90+ expected = Arrays .asList (
91+ new Point (0 , 0 ), new Point (4 , 0 ), new Point (5 , 3 ),
92+ new Point (2 , 5 ), new Point (-1 , 3 )
93+ );
94+ assertEquals (expected , result );
95+ assertTrue (isCounterClockwise (result ), "Pentagon points should be in CCW order" );
96+
97+ // Test 6: Simple triangle (clearly convex)
98+ points = Arrays .asList (
99+ new Point (0 , 0 ), new Point (4 , 0 ), new Point (2 , 3 )
100+ );
101+ result = ConvexHull .convexHullRecursive (points );
102+ expected = Arrays .asList (
103+ new Point (0 , 0 ), new Point (4 , 0 ), new Point (2 , 3 )
104+ );
105+ assertEquals (expected , result );
106+ assertTrue (isCounterClockwise (result ), "Triangle points should be in CCW order" );
107+ }
108+
109+ /**
110+ * Helper method to verify if points are in counter-clockwise order.
111+ * Uses the signed area method: positive area means CCW.
112+ */
113+ private boolean isCounterClockwise (List <Point > points ) {
114+ if (points .size () < 3 ) {
115+ return true ; // Less than 3 points, trivially true
116+ }
117+
118+ long signedArea = 0 ;
119+ for (int i = 0 ; i < points .size (); i ++) {
120+ Point p1 = points .get (i );
121+ Point p2 = points .get ((i + 1 ) % points .size ());
122+ signedArea += (long ) p1 .x () * p2 .y () - (long ) p2 .x () * p1 .y ();
123+ }
124+
125+ return signedArea > 0 ; // Positive signed area means counter-clockwise
39126 }
40127}
0 commit comments