Skip to content

Commit a45d874

Browse files
committed
add rect clip tests
1 parent bdf1063 commit a45d874

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

src/test/java/clipper2/TestOffsets.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import clipper2.offset.EndType;
1616
import clipper2.offset.JoinType;
1717

18-
public class TestOffsets {
18+
class TestOffsets {
1919

2020
@Test
2121
void TestOffsets2() { // see #448 & #456

src/test/java/clipper2/TestPolygons.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ private static final Stream<Arguments> testCases() throws IOException {
2323
@MethodSource("testCases")
2424
@ParameterizedTest(name = "{1}: {2} {3}")
2525
final void RunPolygonsTestCase(TestCase test, int testNum, Object o, Object o1) {
26-
if (testNum == 168) {
27-
return; // NOTE this singular test fails in the Java port -- skipping for now...
28-
}
2926
Clipper64 c64 = new Clipper64();
3027
Paths64 solution = new Paths64();
3128
Paths64 solution_open = new Paths64();
@@ -45,8 +42,8 @@ final void RunPolygonsTestCase(TestCase test, int testNum, Object o, Object o1)
4542

4643
// check polygon counts
4744
if (storedCount > 0) {
48-
if (Arrays.asList(140, 150, 165, 166, 172, 173, 176, 177, 179).contains(testNum)) {
49-
assertTrue(countDiff <= 9);
45+
if (Arrays.asList(140, 150, 165, 166, 168, 172, 173, 176, 177, 179).contains(testNum)) {
46+
assertTrue(countDiff <= 7, "Diff=" + countDiff);
5047
} else if (testNum >= 120) {
5148
assertTrue(countDiff <= 6);
5249
} else if (Arrays.asList(27, 121, 126).contains(testNum)) {
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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

Comments
 (0)