diff --git a/README.md b/README.md index 55be8e0..c40858b 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ A Java port of _[Clipper2](https://github.com/AngusJohnson/Clipper2)_. ### Port Info * _tangiblesoftwaresolutions_' C# to Java Converter did the heavy lifting (but then a lot of manual work was required). -* Wrapper objects are used to replicate C# `ref` (pass-by-reference) behaviour. This isn't very "Javaific" but avoids an unmanageable refactoring effort. -* Code passes all polygon and line tests. +* Wrapper objects are used to replicate C# `ref` (pass-by-reference) behaviour. This isn't very Java-esque but avoids an unmanageable refactoring effort. +* Code passes all tests: polygon, line and polytree. * Uses lower-case (x,y) for point coordinates. * Benchmarks can be run by appending `jmh:benchmark` to the chosen maven goal. * `scanlineList` from `ClipperBase` uses Java `TreeSet` (variable renamed to `scanlineSet`). \ No newline at end of file diff --git a/src/main/java/clipper2/Clipper.java b/src/main/java/clipper2/Clipper.java index bff65ec..ae85fbc 100644 --- a/src/main/java/clipper2/Clipper.java +++ b/src/main/java/clipper2/Clipper.java @@ -650,16 +650,12 @@ private static void AddPolyNodeToPaths(PolyPath64 polyPath, Paths64 paths) { if (!polyPath.getPolygon().isEmpty()) { paths.add(polyPath.getPolygon()); } - for (int i = 0; i < polyPath.getCount(); i++) { - AddPolyNodeToPaths((PolyPath64) polyPath.children.get(i), paths); - } + polyPath.iterator().forEachRemaining(p -> AddPolyNodeToPaths((PolyPath64) p, paths)); } public static Paths64 PolyTreeToPaths64(PolyTree64 polyTree) { Paths64 result = new Paths64(); - for (int i = 0; i < polyTree.getCount(); i++) { - AddPolyNodeToPaths((PolyPath64) polyTree.children.get(i), result); - } + polyTree.iterator().forEachRemaining(p -> AddPolyNodeToPaths((PolyPath64) p, result)); return result; } @@ -667,9 +663,7 @@ public static void AddPolyNodeToPathsD(PolyPathD polyPath, PathsD paths) { if (!polyPath.getPolygon().isEmpty()) { paths.add(polyPath.getPolygon()); } - for (int i = 0; i < polyPath.getCount(); i++) { - AddPolyNodeToPathsD((PolyPathD) polyPath.children.get(i), paths); - } + polyPath.iterator().forEachRemaining(p -> AddPolyNodeToPathsD((PolyPathD) p, paths)); } public static PathsD PolyTreeToPathsD(PolyTreeD polyTree) { diff --git a/src/main/java/clipper2/engine/Clipper64.java b/src/main/java/clipper2/engine/Clipper64.java index c625f28..7d7c5de 100644 --- a/src/main/java/clipper2/engine/Clipper64.java +++ b/src/main/java/clipper2/engine/Clipper64.java @@ -91,7 +91,7 @@ public final boolean Execute(ClipType clipType, FillRule fillRule, PolyTree64 po try { ExecuteInternal(clipType, fillRule); BuildTree(polytree, openPaths); - } catch (java.lang.Exception e) { + } catch (Exception e) { succeeded = false; } diff --git a/src/main/java/clipper2/engine/ClipperBase.java b/src/main/java/clipper2/engine/ClipperBase.java index 6f70ee5..dc8d738 100644 --- a/src/main/java/clipper2/engine/ClipperBase.java +++ b/src/main/java/clipper2/engine/ClipperBase.java @@ -29,7 +29,7 @@ abstract class ClipperBase { private ClipType cliptype; - private FillRule fillrule; + private FillRule fillrule = FillRule.EvenOdd; private Active actives = null; private Active sel = null; private Joiner horzJoiners = null; @@ -43,8 +43,8 @@ abstract class ClipperBase { private long currentBotY; private boolean isSortedMinimaList; private boolean hasOpenPaths; - public boolean usingPolytree; - public boolean succeeded; + boolean usingPolytree; + boolean succeeded; private boolean preserveCollinear; private boolean reverseSolution; @@ -1230,7 +1230,7 @@ private OutPt AddLocalMaxPoly(Active ae1, Active ae2, Point64 pt) { result = outrec.pts; outrec.owner = GetRealOutRec(outrec.owner); - if (usingPolytree && outrec.owner.frontEdge == null) { + if (usingPolytree && outrec.owner != null && outrec.owner.frontEdge == null) { outrec.owner = GetRealOutRec(outrec.owner.owner); } } diff --git a/src/main/java/clipper2/engine/PolyPathBase.java b/src/main/java/clipper2/engine/PolyPathBase.java index c4d5e91..b7fb01c 100644 --- a/src/main/java/clipper2/engine/PolyPathBase.java +++ b/src/main/java/clipper2/engine/PolyPathBase.java @@ -7,8 +7,8 @@ public abstract class PolyPathBase implements Iterable { - public PolyPathBase parent; - public List children = new ArrayList<>(); + final PolyPathBase parent; + List children = new ArrayList<>(); PolyPathBase(PolyPathBase parent) { this.parent = parent; @@ -28,10 +28,6 @@ public final PolyPathIterator iterator() { * of a polygon. */ public final boolean getIsHole() { - return GetIsHole(); - } - - private boolean GetIsHole() { boolean result = true; PolyPathBase pp = parent; while (pp != null) { diff --git a/src/main/java/clipper2/engine/PolyPathIterator.java b/src/main/java/clipper2/engine/PolyPathIterator.java index afa389c..cc5f1a2 100644 --- a/src/main/java/clipper2/engine/PolyPathIterator.java +++ b/src/main/java/clipper2/engine/PolyPathIterator.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.NoSuchElementException; -class PolyPathIterator implements Iterator { +public class PolyPathIterator implements Iterator { List ppbList; int position = 0; diff --git a/src/test/java/clipper2/TestPolytree.java b/src/test/java/clipper2/TestPolytree.java index c4faf8a..f5caf59 100644 --- a/src/test/java/clipper2/TestPolytree.java +++ b/src/test/java/clipper2/TestPolytree.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.stream.Stream; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -29,7 +28,6 @@ private static final Stream testCases() throws IOException { @MethodSource("testCases") @ParameterizedTest(name = "{1} {2} {3}") - @Disabled final void RunPolytreeTestCase(TestCase test, String caption, Object o, Object o1) { PolyTree64 solutionTree = new PolyTree64(); Paths64 solution_open = new Paths64(); @@ -44,7 +42,7 @@ final void RunPolytreeTestCase(TestCase test, String caption, Object o, Object o for (Point64 pt : pointsOfInterestOutside) { for (var path : subject) { - assertSame(PointInPolygonResult.IsOutside, Clipper.PointInPolygon(pt, path), + assertEquals(PointInPolygonResult.IsOutside, Clipper.PointInPolygon(pt, path), "outside point of interest found inside subject"); } } @@ -59,7 +57,7 @@ final void RunPolytreeTestCase(TestCase test, String caption, Object o, Object o poi_inside_counter++; } } - assertSame(1, poi_inside_counter, String.format("poi_inside_counter - expected 1 but got %1$s", poi_inside_counter)); + assertEquals(1, poi_inside_counter, String.format("poi_inside_counter - expected 1 but got %1$s", poi_inside_counter)); } clipper.AddSubject(subject); @@ -71,10 +69,10 @@ final void RunPolytreeTestCase(TestCase test, String caption, Object o, Object o double a1 = Clipper.Area(solutionPaths), a2 = solutionTree.Area(); assertTrue(a1 > 330000, String.format("solution has wrong area - value expected: 331,052; value returned; %1$s ", a1)); -// + assertTrue(Math.abs(a1 - a2) < 0.0001, String.format("solution tree has wrong area - value expected: %1$s; value returned; %2$s ", a1, a2)); -// + assertTrue(CheckPolytreeFullyContainsChildren(solutionTree), "The polytree doesn't properly contain its children"); for (Point64 pt : pointsOfInterestOutside) { @@ -87,7 +85,7 @@ final void RunPolytreeTestCase(TestCase test, String caption, Object o, Object o } } - private boolean CheckPolytreeFullyContainsChildren(PolyTree64 polytree) { + private static boolean CheckPolytreeFullyContainsChildren(PolyTree64 polytree) { for (var p : polytree) { PolyPath64 child = (PolyPath64) p; if (child.getCount() > 0 && !PolyPathFullyContainsChildren(child)) { @@ -97,7 +95,7 @@ private boolean CheckPolytreeFullyContainsChildren(PolyTree64 polytree) { return true; } - private boolean PolyPathFullyContainsChildren(PolyPath64 pp) { + private static boolean PolyPathFullyContainsChildren(PolyPath64 pp) { for (var c : pp) { var child = (PolyPath64) c; for (Point64 pt : child.getPolygon()) { @@ -112,7 +110,7 @@ private boolean PolyPathFullyContainsChildren(PolyPath64 pp) { return true; } - private boolean PolytreeContainsPoint(PolyTree64 pp, Point64 pt) { + private static boolean PolytreeContainsPoint(PolyTree64 pp, Point64 pt) { int counter = 0; for (int i = 0; i < pp.getCount(); i++) { PolyPath64 child = (PolyPath64) pp.get(i); @@ -124,12 +122,12 @@ private boolean PolytreeContainsPoint(PolyTree64 pp, Point64 pt) { return counter != 0; } - private void PolyPathContainsPoint(PolyPath64 pp, Point64 pt, RefObject counter) { + private static void PolyPathContainsPoint(PolyPath64 pp, Point64 pt, RefObject counter) { if (Clipper.PointInPolygon(pt, pp.getPolygon()) != PointInPolygonResult.IsOutside) { if (pp.getIsHole()) { - --counter.argValue; + counter.argValue--; } else { - ++counter.argValue; + counter.argValue++; } } for (int i = 0; i < pp.getCount(); i++) {