1+ package clipper2 ;
2+
3+ import static org .junit .jupiter .api .Assertions .*;
4+
5+ import clipper2 .core .FillRule ;
6+ import clipper2 .core .Path64 ;
7+ import clipper2 .core .Paths64 ;
8+ import clipper2 .core .Rect64 ;
9+
10+ import org .junit .jupiter .api .Test ;
11+
12+ /**
13+ * RectClip tests. Ported from C++ version.
14+ */
15+ class TestRectClip {
16+
17+ @ Test
18+ void testRectClip () {
19+ Paths64 sub = new Paths64 ();
20+ Paths64 clp = new Paths64 ();
21+ Paths64 sol ; // Solution will be assigned by RectClip
22+
23+ Rect64 rect = new Rect64 (100 , 100 , 700 , 500 );
24+ clp .add (rect .AsPath ());
25+
26+ // Test case 1: Subject is identical to clip rect
27+ sub .add (Clipper .MakePath (new long [] { 100 , 100 , 700 , 100 , 700 , 500 , 100 , 500 }));
28+ sol = Clipper .RectClip (rect , sub );
29+ // Use Math.abs because area can be negative depending on orientation
30+ assertEquals (Math .abs (Clipper .Area (sub )), Math .abs (Clipper .Area (sol )), "Test 1 failed" );
31+
32+ // Test case 2: Subject partially outside but covers same area within clip rect
33+ sub .clear ();
34+ sub .add (Clipper .MakePath (new long [] { 110 , 110 , 700 , 100 , 700 , 500 , 100 , 500 }));
35+ sol = Clipper .RectClip (rect , sub );
36+ // Area might differ slightly due to clipping precise shape, but conceptually
37+ // check against original subject
38+ // A better check might involve comparing vertices if area isn't exact?
39+ // Or check against the expected clipped shape area if known.
40+ // For now, let's keep the original logic's intent, assuming Area reflects the
41+ // clipped portion.
42+ assertEquals (Math .abs (Clipper .Area (sub )), Math .abs (Clipper .Area (sol )), "Test 2 failed" ); // Might be brittle
43+
44+ // Test case 3: Subject partially outside, clipped area should equal clip rect
45+ // area
46+ sub .clear ();
47+ sub .add (Clipper .MakePath (new long [] { 90 , 90 , 700 , 100 , 700 , 500 , 100 , 500 }));
48+ sol = Clipper .RectClip (rect , sub );
49+ assertEquals (Math .abs (Clipper .Area (clp )), Math .abs (Clipper .Area (sol )), "Test 3 failed" );
50+
51+ // Test case 4: Subject fully inside clip rect
52+ sub .clear ();
53+ sub .add (Clipper .MakePath (new long [] { 110 , 110 , 690 , 110 , 690 , 490 , 110 , 490 }));
54+ sol = Clipper .RectClip (rect , sub );
55+ assertEquals (Math .abs (Clipper .Area (sub )), Math .abs (Clipper .Area (sol )), "Test 4 failed" );
56+
57+ // Test case 5: Subject touches edge, should result in empty solution
58+ sub .clear ();
59+ clp .clear (); // Clear previous clip path
60+ rect = new Rect64 (390 , 290 , 410 , 310 );
61+ // No need to add rect.AsPath() to clp for RectClip, rect object is passed
62+ // directly
63+ sub .add (Clipper .MakePath (new long [] { 410 , 290 , 500 , 290 , 500 , 310 , 410 , 310 }));
64+ sol = Clipper .RectClip (rect , sub );
65+ assertTrue (sol .isEmpty (), "Test 5 failed - should be empty" );
66+
67+ // Test case 6: Triangle outside rect
68+ sub .clear ();
69+ sub .add (Clipper .MakePath (new long [] { 430 , 290 , 470 , 330 , 390 , 330 }));
70+ sol = Clipper .RectClip (rect , sub );
71+ assertTrue (sol .isEmpty (), "Test 6 failed - should be empty" );
72+
73+ // Test case 7: Triangle outside rect
74+ sub .clear ();
75+ sub .add (Clipper .MakePath (new long [] { 450 , 290 , 480 , 330 , 450 , 330 }));
76+ sol = Clipper .RectClip (rect , sub );
77+ assertTrue (sol .isEmpty (), "Test 7 failed - should be empty" );
78+
79+ // Test case 8: Complex polygon clipped, check bounds of result
80+ sub .clear ();
81+ sub .add (Clipper .MakePath (new long [] { 208 , 66 , 366 , 112 , 402 , 303 , 234 , 332 , 233 , 262 , 243 , 140 , 215 , 126 , 40 , 172 }));
82+ rect = new Rect64 (237 , 164 , 322 , 248 );
83+ sol = Clipper .RectClip (rect , sub );
84+ assertFalse (sol .isEmpty (), "Test 8 failed - should not be empty" ); // Basic check
85+ Rect64 solBounds = Clipper .GetBounds (sol );
86+ // Check if the resulting bounds match the clipping rectangle bounds
87+ // Note: The clipped polygon might not *fill* the entire rect, but its bounds
88+ // should ideally be constrained *within* or *equal to* the clip rect if it
89+ // intersects fully.
90+ // The C++ test checks if the width/height *match* the clip rect width/height.
91+ // This implies the clipped result must touch all four sides of the clip rect.
92+ assertEquals (rect .getWidth (), solBounds .getWidth (), "Test 8 failed - Width mismatch" );
93+ assertEquals (rect .getHeight (), solBounds .getHeight (), "Test 8 failed - Height mismatch" );
94+ }
95+
96+ @ Test
97+ void testRectClip2 () {
98+ Rect64 rect = new Rect64 (54690 , 0 , 65628 , 6000 );
99+ Paths64 subject = new Paths64 ();
100+ subject .add (Clipper .MakePath (new long [] { 700000 , 6000 , 0 , 6000 , 0 , 5925 , 700000 , 5925 }));
101+
102+ Paths64 solution = Clipper .RectClip (rect , subject );
103+
104+ assertNotNull (solution , "TestRectClip2 Solution should not be null" );
105+ assertEquals (1 , solution .size (), "TestRectClip2 Should have 1 path" );
106+ assertEquals (4 , solution .get (0 ).size (), "TestRectClip2 Path should have 4 points" );
107+ }
108+
109+ @ Test
110+ void testRectClip3 () {
111+ Rect64 r = new Rect64 (-1800000000L , -137573171L , -1741475021L , 3355443L );
112+ Paths64 subject = new Paths64 ();
113+ Paths64 solution ;
114+
115+ subject .add (Clipper .MakePath (new long [] { -1800000000L , 10005000L , -1800000000L , -5000L , -1789994999L , -5000L , -1789994999L , 10005000L }));
116+
117+ solution = Clipper .RectClip (r , subject );
118+
119+ assertNotNull (solution , "TestRectClip3 Solution should not be null" );
120+ assertEquals (1 , solution .size (), "TestRectClip3 Should have 1 path" );
121+ assertFalse (solution .get (0 ).isEmpty (), "TestRectClip3 Path should not be empty" );
122+ Path64 expectedPath = Clipper .MakePath (new long [] { -1789994999L , 3355443L , -1800000000L , 3355443L , -1800000000L , -5000L , -1789994999L , -5000L });
123+ assertEquals (Math .abs (Clipper .Area (expectedPath )), Math .abs (Clipper .Area (solution .get (0 ))), "TestRectClip3 Area check" );
124+
125+ }
126+
127+ private static Path64 clipper (Rect64 r , Paths64 subject ) {
128+ return Clipper .Intersect (subject , new Paths64 (r .AsPath ()), FillRule .EvenOdd ).get (0 );
129+ }
130+ }
0 commit comments