From 72bcf63921b97b79b8de4d7ab5f8b264eb7d04ef Mon Sep 17 00:00:00 2001 From: Michael Carleton Date: Tue, 3 Mar 2026 23:06:45 +0000 Subject: [PATCH 1/2] 1.5.0 --- src/main/java/clipper2/Clipper.java | 98 ++++++++++--------- src/main/java/clipper2/Minkowski.java | 7 +- src/main/java/clipper2/core/ClipType.java | 4 +- .../java/clipper2/core/InternalClipper.java | 6 +- .../java/clipper2/engine/ClipperBase.java | 49 ++++++---- src/main/java/clipper2/engine/ClipperD.java | 15 +-- .../java/clipper2/offset/ClipperOffset.java | 23 +++-- src/test/java/clipper2/ClipperFileIO.java | 2 +- 8 files changed, 114 insertions(+), 90 deletions(-) diff --git a/src/main/java/clipper2/Clipper.java b/src/main/java/clipper2/Clipper.java index db16b42..efb7f32 100644 --- a/src/main/java/clipper2/Clipper.java +++ b/src/main/java/clipper2/Clipper.java @@ -226,7 +226,7 @@ public static void BooleanOp(ClipType clipType, @Nullable PathsD subject, @Nulla * @see #InflatePaths(PathsD, double, JoinType, EndType, double, double, int) */ public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinType, EndType endType) { - return InflatePaths(paths, delta, joinType, endType, 2.0, 0.25); + return InflatePaths(paths, delta, joinType, endType, 2.0, 0.0); } /** @@ -244,14 +244,14 @@ public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinTyp * @see #InflatePaths(PathsD, double, JoinType, EndType, double, double, int) */ public static PathsD InflatePaths(PathsD paths, double delta, JoinType joinType, EndType endType, double miterLimit) { - return InflatePaths(paths, delta, joinType, endType, miterLimit, 0.25, 8); + return InflatePaths(paths, delta, joinType, endType, miterLimit, 0.0, 8); } /** * @see #InflatePaths(PathsD, double, JoinType, EndType, double, double, int) */ public static PathsD InflatePaths(PathsD paths, double delta, JoinType joinType, EndType endType) { - return InflatePaths(paths, delta, joinType, endType, 2.0, 0.25, 8); + return InflatePaths(paths, delta, joinType, endType, 2.0, 0.0, 8); } /** @@ -992,29 +992,33 @@ public static double PerpendicDistFromLineSqrd(Point64 pt, Point64 line1, Point6 } public static void RDP(Path64 path, int begin, int end, double epsSqrd, List flags) { - int idx = 0; - double maxD = 0; - while (end > begin && path.get(begin).equals(path.get(end))) { - flags.set(end--, false); - } - for (int i = begin + 1; i < end; ++i) { - // PerpendicDistFromLineSqrd - avoids expensive Sqrt() - double d = PerpendicDistFromLineSqrd(path.get(i), path.get(begin), path.get(end)); - if (d <= maxD) { + while (true) { + int idx = 0; + double maxD = 0; + while (end > begin && path.get(begin).equals(path.get(end))) { + flags.set(end--, false); + } + for (int i = begin + 1; i < end; ++i) { + // PerpendicDistFromLineSqrd - avoids expensive Sqrt() + double d = PerpendicDistFromLineSqrd(path.get(i), path.get(begin), path.get(end)); + if (d <= maxD) { + continue; + } + maxD = d; + idx = i; + } + if (maxD <= epsSqrd) { + return; + } + flags.set(idx, true); + if (idx > begin + 1) { + RDP(path, begin, idx, epsSqrd, flags); + } + if (idx < end - 1) { + begin = idx; continue; } - maxD = d; - idx = i; - } - if (maxD <= epsSqrd) { - return; - } - flags.set(idx, true); - if (idx > begin + 1) { - RDP(path, begin, idx, epsSqrd, flags); - } - if (idx < end - 1) { - RDP(path, idx, end, epsSqrd, flags); + break; } } @@ -1081,29 +1085,33 @@ public static Paths64 RamerDouglasPeucker(Paths64 paths, double epsilon) { } public static void RDP(PathD path, int begin, int end, double epsSqrd, List flags) { - int idx = 0; - double maxD = 0; - while (end > begin && path.get(begin).equals(path.get(end))) { - flags.set(end--, false); - } - for (int i = begin + 1; i < end; ++i) { - // PerpendicDistFromLineSqrd - avoids expensive Sqrt() - double d = PerpendicDistFromLineSqrd(path.get(i), path.get(begin), path.get(end)); - if (d <= maxD) { + while (true) { + int idx = 0; + double maxD = 0; + while (end > begin && path.get(begin).equals(path.get(end))) { + flags.set(end--, false); + } + for (int i = begin + 1; i < end; ++i) { + // PerpendicDistFromLineSqrd - avoids expensive Sqrt() + double d = PerpendicDistFromLineSqrd(path.get(i), path.get(begin), path.get(end)); + if (d <= maxD) { + continue; + } + maxD = d; + idx = i; + } + if (maxD <= epsSqrd) { + return; + } + flags.set(idx, true); + if (idx > begin + 1) { + RDP(path, begin, idx, epsSqrd, flags); + } + if (idx < end - 1) { + begin = idx; continue; } - maxD = d; - idx = i; - } - if (maxD <= epsSqrd) { - return; - } - flags.set(idx, true); - if (idx > begin + 1) { - RDP(path, begin, idx, epsSqrd, flags); - } - if (idx < end - 1) { - RDP(path, idx, end, epsSqrd, flags); + break; } } diff --git a/src/main/java/clipper2/Minkowski.java b/src/main/java/clipper2/Minkowski.java index eaed305..3b0e1b3 100644 --- a/src/main/java/clipper2/Minkowski.java +++ b/src/main/java/clipper2/Minkowski.java @@ -7,7 +7,10 @@ import clipper2.core.PathsD; import clipper2.core.Point64; -public class Minkowski { +public final class Minkowski { + + private Minkowski() { + } public static Paths64 Sum(Path64 pattern, Path64 path, boolean isClosed) { return Clipper.Union(MinkowskiInternal(pattern, path, true, isClosed), FillRule.NonZero); @@ -78,4 +81,4 @@ private static Paths64 MinkowskiInternal(Path64 pattern, Path64 path, boolean is return result; } -} \ No newline at end of file +} diff --git a/src/main/java/clipper2/core/ClipType.java b/src/main/java/clipper2/core/ClipType.java index 89bc7f0..72b1b07 100644 --- a/src/main/java/clipper2/core/ClipType.java +++ b/src/main/java/clipper2/core/ClipType.java @@ -30,7 +30,7 @@ */ public enum ClipType { - None, + NoClip, /** Preserves regions covered by both subject and clip polygons */ Intersection, /** Preserves regions covered by subject or clip polygons, or both polygons */ @@ -40,4 +40,4 @@ public enum ClipType { /** Preserves regions covered by subject or clip polygons, but not both */ Xor; -} \ No newline at end of file +} diff --git a/src/main/java/clipper2/core/InternalClipper.java b/src/main/java/clipper2/core/InternalClipper.java index 938d9eb..02584f9 100644 --- a/src/main/java/clipper2/core/InternalClipper.java +++ b/src/main/java/clipper2/core/InternalClipper.java @@ -8,7 +8,6 @@ public final class InternalClipper { public static final double MIN_COORD = -MAX_COORD; private static final long Invalid64 = Long.MAX_VALUE; - public static final double DEFAULT_ARC_TOLERANCE = 0.25; private static final double FLOATING_POINT_TOLERANCE = 1E-12; // private static final double DEFAULT_MIN_EDGE_LENGTH = 0.1; @@ -297,7 +296,10 @@ private static boolean productsAreEqual(long a, long b, long c, long d) { } private static int triSign(long x) { - return x > 0 ? 1 : (x < 0 ? -1 : 0); + if (x < 0) { + return -1; + } + return x > 1 ? 1 : 0; } } diff --git a/src/main/java/clipper2/engine/ClipperBase.java b/src/main/java/clipper2/engine/ClipperBase.java index f46890c..71621bb 100644 --- a/src/main/java/clipper2/engine/ClipperBase.java +++ b/src/main/java/clipper2/engine/ClipperBase.java @@ -25,7 +25,7 @@ */ abstract class ClipperBase { - private ClipType cliptype = ClipType.None; + private ClipType cliptype = ClipType.NoClip; private FillRule fillrule = FillRule.EvenOdd; private Active actives = null; private Active sel = null; @@ -1670,7 +1670,7 @@ private void AdjustCurrXAndCopyToSEL(long topY) { } protected final void ExecuteInternal(ClipType ct, FillRule fillRule) { - if (ct == ClipType.None) { + if (ct == ClipType.NoClip) { return; } fillrule = fillRule; @@ -2723,25 +2723,36 @@ private void DoSplitOp(OutRec outrec, OutPt splitOp) { prevOp.next = newOp2; } - if (absArea2 > 1 && (absArea2 > absArea1 || ((area2 > 0) == (area1 > 0)))) { - OutRec newOutRec = NewOutRec(); - newOutRec.owner = outrec.owner; - splitOp.outrec = newOutRec; - splitOp.next.outrec = newOutRec; + if (absArea2 <= 1 || (!(absArea2 > absArea1) && ((area2 > 0) != (area1 > 0)))) { + return; + } - if (usingPolytree) { - if (outrec.splits == null) { - outrec.splits = new ArrayList<>(); - } - outrec.splits.add(newOutRec.idx); - } + OutRec newOutRec = NewOutRec(); + newOutRec.owner = outrec.owner; + splitOp.outrec = newOutRec; + splitOp.next.outrec = newOutRec; + + OutPt newOp = new OutPt(ip, newOutRec); + newOp.prev = splitOp.next; + newOp.next = splitOp; + newOutRec.pts = newOp; + splitOp.prev = newOp; + splitOp.next.next = newOp; - OutPt newOp = new OutPt(ip, newOutRec); - newOp.prev = splitOp.next; - newOp.next = splitOp; - newOutRec.pts = newOp; - splitOp.prev = newOp; - splitOp.next.next = newOp; + if (!usingPolytree) { + return; + } + + if (Path1InsidePath2(prevOp, newOp)) { + if (newOutRec.splits == null) { + newOutRec.splits = new ArrayList<>(); + } + newOutRec.splits.add(outrec.idx); + } else { + if (outrec.splits == null) { + outrec.splits = new ArrayList<>(); + } + outrec.splits.add(newOutRec.idx); } // else { splitOp = null; splitOp.next = null; } } diff --git a/src/main/java/clipper2/engine/ClipperD.java b/src/main/java/clipper2/engine/ClipperD.java index bbcd733..a721481 100644 --- a/src/main/java/clipper2/engine/ClipperD.java +++ b/src/main/java/clipper2/engine/ClipperD.java @@ -16,6 +16,7 @@ */ public class ClipperD extends ClipperBase { + private static final String PRECISION_RANGE_ERROR = "Error: Precision is out of range."; private double scale; private double invScale; @@ -28,7 +29,7 @@ public ClipperD() { */ public ClipperD(int roundingDecimalPrecision) { if (roundingDecimalPrecision < -8 || roundingDecimalPrecision > 8) { - throw new IllegalArgumentException("Error - RoundingDecimalPrecision exceeds the allowed range."); + throw new IllegalArgumentException(PRECISION_RANGE_ERROR); } scale = Math.pow(10, roundingDecimalPrecision); invScale = 1 / scale; @@ -124,13 +125,13 @@ public boolean Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, if (!success) { return false; } - if (!oPaths.isEmpty()) { - openPaths.ensureCapacity(oPaths.size()); - for (Path64 path : oPaths) { - openPaths.add(Clipper.ScalePathD(path, invScale)); - } + if (oPaths.isEmpty()) { + return true; + } + openPaths.ensureCapacity(oPaths.size()); + for (Path64 path : oPaths) { + openPaths.add(Clipper.ScalePathD(path, invScale)); } - return true; } diff --git a/src/main/java/clipper2/offset/ClipperOffset.java b/src/main/java/clipper2/offset/ClipperOffset.java index 342c4bc..6bee1d4 100644 --- a/src/main/java/clipper2/offset/ClipperOffset.java +++ b/src/main/java/clipper2/offset/ClipperOffset.java @@ -65,7 +65,8 @@ */ public class ClipperOffset { - private static double TOLERANCE = 1.0E-12; + private static final double TOLERANCE = 1.0E-12; + private static final double ARC_CONST = 0.002; private final List groupList = new ArrayList<>(); private Path64 pathOut = new Path64(); @@ -105,7 +106,7 @@ public ClipperOffset(double miterLimit, double arcTolerance) { * @see #ClipperOffset(double, double, boolean, boolean) */ public ClipperOffset(double miterLimit) { - this(miterLimit, 0.25, false, false); + this(miterLimit, 0.0, false, false); } /** @@ -114,7 +115,7 @@ public ClipperOffset(double miterLimit) { * @see #ClipperOffset(double, double, boolean, boolean) */ public ClipperOffset() { - this(2.0, 0.25, false, false); + this(2.0, 0.0, false, false); } /** @@ -151,7 +152,8 @@ public ClipperOffset() { * {{@link #AddPath(Path64, JoinType, EndType) * AddPath()} and * {@link #AddPaths(Paths64, JoinType, EndType) - * AddPaths()}. The default arcTolerance is 0.25. + * AddPaths()}. The default arcTolerance is 0.0, which + * enables automatic scaling (offset radius / 500). * @param preserveCollinear When adjacent edges are collinear in closed path * solutions, the common vertex can safely be removed * to simplify the solution without altering path @@ -460,7 +462,7 @@ private void DoRound(Path64 path, int j, int k, double angle) { // when deltaCallback is assigned, groupDelta won't be constant, // so we'll need to do the following calculations for *every* vertex. double absDelta = Math.abs(groupDelta); - double arcTol = arcTolerance > 0.01 ? arcTolerance : Math.log10(2 + absDelta) * InternalClipper.DEFAULT_ARC_TOLERANCE; + double arcTol = arcTolerance > 0.01 ? arcTolerance : absDelta * ARC_CONST; double stepsPer360 = Math.PI / Math.acos(1 - arcTol / absDelta); stepSin = Math.sin((2 * Math.PI) / stepsPer360); stepCos = Math.cos((2 * Math.PI) / stepsPer360); @@ -530,13 +532,10 @@ private int OffsetPoint(Group group, Path64 path, int j, int k) { if (cosA > -0.999 && (sinA * groupDelta < 0)) { // test for concavity first (#593) // is concave + // Insert 3 points so concave joins create regions that the trailing union + // operation can cleanly remove, including over-shrunk path reversals. pathOut.add(GetPerpendic(path.get(j), normals.get(k))); - // this extra point is the only simple way to ensure that path reversals - // (ie over-shrunk paths) are fully cleaned out with the trailing union op. - // However it's probably safe to skip this whenever an angle is almost flat. - if (cosA < 0.99) { - pathOut.add(path.get(j)); // (#405) - } + pathOut.add(path.get(j)); // (#405, #873, #916) pathOut.add(GetPerpendic(path.get(j), normals.get(j))); } else if (cosA > 0.999 && joinType != JoinType.Round) { // almost straight - less than 2.5 degree (#424, #482, #526 & #724) @@ -682,7 +681,7 @@ private void DoGroupOffset(Group group) { // arcTol - when arcTolerance is undefined (0) then curve imprecision // will be relative to the size of the offset (delta). Obviously very // large offsets will almost always require much less precision. - double arcTol = arcTolerance > 0.01 ? arcTolerance : Math.log10(2 + absDelta) * InternalClipper.DEFAULT_ARC_TOLERANCE; + double arcTol = arcTolerance > 0.01 ? arcTolerance : absDelta * ARC_CONST; double stepsPer360 = Math.PI / Math.acos(1 - arcTol / absDelta); stepSin = Math.sin((2 * Math.PI) / stepsPer360); stepCos = Math.cos((2 * Math.PI) / stepsPer360); diff --git a/src/test/java/clipper2/ClipperFileIO.java b/src/test/java/clipper2/ClipperFileIO.java index 23dff61..5a6912e 100644 --- a/src/test/java/clipper2/ClipperFileIO.java +++ b/src/test/java/clipper2/ClipperFileIO.java @@ -87,7 +87,7 @@ static List loadTestCases(String testFileName) throws IOException { lines.add(""); String caption = ""; - ClipType ct = ClipType.None; + ClipType ct = ClipType.NoClip; FillRule fillRule = FillRule.EvenOdd; long area = 0; int count = 0; From 38294fd20b9414b656e935556ac01004bd655955 Mon Sep 17 00:00:00 2001 From: Michael Carleton Date: Tue, 3 Mar 2026 23:14:59 +0000 Subject: [PATCH 2/2] 1.4.0+1.5.0 tests --- src/main/java/clipper2/core/Rect64.java | 13 ++++- src/test/java/clipper2/TestIsCollinear.java | 38 ++++++++++++++ src/test/java/clipper2/TestOffsets.java | 34 +++++++++++++ src/test/java/clipper2/TestPolygons.java | 19 +++++++ src/test/java/clipper2/TestRect.java | 55 +++++++++++++++++++++ 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 src/test/java/clipper2/TestIsCollinear.java create mode 100644 src/test/java/clipper2/TestRect.java diff --git a/src/main/java/clipper2/core/Rect64.java b/src/main/java/clipper2/core/Rect64.java index ac18840..cfd1d77 100644 --- a/src/main/java/clipper2/core/Rect64.java +++ b/src/main/java/clipper2/core/Rect64.java @@ -91,6 +91,17 @@ public boolean Contains(Rect64 rec) { return rec.left >= left && rec.right <= right && rec.top >= top && rec.bottom <= bottom; } + public static Rect64 opAdd(Rect64 lhs, Rect64 rhs) { + if (!lhs.IsValid()) { + return rhs.clone(); + } + if (!rhs.IsValid()) { + return lhs.clone(); + } + return new Rect64(Math.min(lhs.left, rhs.left), Math.min(lhs.top, rhs.top), Math.max(lhs.right, rhs.right), + Math.max(lhs.bottom, rhs.bottom)); + } + @Override public Rect64 clone() { Rect64 varCopy = new Rect64(); @@ -102,4 +113,4 @@ public Rect64 clone() { return varCopy; } -} \ No newline at end of file +} diff --git a/src/test/java/clipper2/TestIsCollinear.java b/src/test/java/clipper2/TestIsCollinear.java new file mode 100644 index 0000000..056a316 --- /dev/null +++ b/src/test/java/clipper2/TestIsCollinear.java @@ -0,0 +1,38 @@ +package clipper2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import clipper2.core.ClipType; +import clipper2.core.FillRule; +import clipper2.core.InternalClipper; +import clipper2.core.Path64; +import clipper2.core.Paths64; +import clipper2.core.Point64; +import clipper2.engine.Clipper64; + +class TestIsCollinear { + + @Test + void testIsCollinear() { + // A large integer not representable exactly by double. + final long i = 9007199254740993L; + Point64 pt1 = new Point64(0, 0); + Point64 sharedPt = new Point64(i, i * 10); + Point64 pt2 = new Point64(i * 10, i * 100); + assertTrue(InternalClipper.IsCollinear(pt1, sharedPt, pt2)); + } + + @Test + void testIsCollinear2() { // see #831 + final long i = 0x4000000000000L; + Path64 subject = new Path64(new Point64(-i, -i), new Point64(i, -i), new Point64(-i, i), new Point64(i, i)); + Clipper64 clipper = new Clipper64(); + clipper.AddSubject(new Paths64(subject)); + Paths64 solution = new Paths64(); + clipper.Execute(ClipType.Union, FillRule.EvenOdd, solution); + assertEquals(2, solution.size()); + } +} diff --git a/src/test/java/clipper2/TestOffsets.java b/src/test/java/clipper2/TestOffsets.java index c45d6cc..cf28575 100644 --- a/src/test/java/clipper2/TestOffsets.java +++ b/src/test/java/clipper2/TestOffsets.java @@ -9,6 +9,7 @@ import clipper2.core.Path64; import clipper2.core.Paths64; +import clipper2.core.PathsD; import clipper2.core.Point64; import clipper2.core.PointD; import clipper2.offset.ClipperOffset; @@ -237,6 +238,39 @@ void TestOffsets9() { // (#733) assertEquals(0, solution.size()); } + @Test + void TestOffsets10() { // see #715 + Paths64 subjects = new Paths64(List.of(Clipper.MakePath(new long[] { 508685336, -435806096, 509492982, -434729201, 509615525, -434003092, 509615525, + 493372891, 509206033, 494655198, 508129138, 495462844, 507403029, 495585387, -545800889, 495585387, -547083196, 495175895, -547890842, + 494099000, -548013385, 493372891, -548013385, -434003092, -547603893, -435285399, -546526998, -436093045, -545800889, -436215588, 507403029, + -436215588 }), Clipper.MakePath(new long[] { 106954765, -62914568, 106795129, -63717113, 106340524, -64397478, 105660159, -64852084, 104857613, + -65011720, 104055068, -64852084, 103374703, -64397478, 102920097, -63717113, 102760461, -62914568, 102920097, -62112022, 103374703, + -61431657, 104055068, -60977052, 104857613, -60817416, 105660159, -60977052, 106340524, -61431657, 106795129, -62112022 }))); + + ClipperOffset offseter = new ClipperOffset(2, 104857.61318750000); + Paths64 solution = new Paths64(); + offseter.AddPaths(subjects, JoinType.Round, EndType.Polygon); + offseter.Execute(-2212495.6382562499, solution); + assertEquals(2, solution.size()); + } + + @Test + void TestOffsets11() { // see #405 + PathsD subject = new PathsD(); + subject.add(Clipper.MakePath(new double[] { -1.0, -1.0, -1.0, 11.0, 11.0, 11.0, 11.0, -1.0 })); + // offset polygon + PathsD solution = Clipper.InflatePaths(subject, -50, JoinType.Miter, EndType.Polygon); + assertTrue(solution.isEmpty()); + } + + @Test + void TestOffsets12() { // see #873 + Paths64 subject = new Paths64(); + subject.add(Clipper.MakePath(new long[] { 667680768, -36382704, 737202688, -87034880, 742581888, -86055680, 747603968, -84684800 })); + Paths64 solution = Clipper.InflatePaths(subject, -249561088, JoinType.Miter, EndType.Polygon); + assertTrue(solution.isEmpty()); + } + private static Point64 midPoint(Point64 p1, Point64 p2) { Point64 result = new Point64(); result.setX((p1.x + p2.x) / 2); diff --git a/src/test/java/clipper2/TestPolygons.java b/src/test/java/clipper2/TestPolygons.java index c2c1c7f..468cf7e 100644 --- a/src/test/java/clipper2/TestPolygons.java +++ b/src/test/java/clipper2/TestPolygons.java @@ -1,16 +1,20 @@ package clipper2; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.Arrays; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import clipper2.ClipperFileIO.TestCase; +import clipper2.core.ClipType; +import clipper2.core.FillRule; import clipper2.core.Paths64; import clipper2.engine.Clipper64; @@ -75,4 +79,19 @@ final void RunPolygonsTestCase(TestCase test, int testNum, Object o, Object o1) } } + + @Test + void TestCollinearOnMacOs() { // #777 + Paths64 subject = new Paths64(); + subject.add(Clipper.MakePath(new long[] { 0, -453054451, 0, -433253797, -455550000, 0 })); + subject.add(Clipper.MakePath(new long[] { 0, -433253797, 0, 0, -455550000, 0 })); + Clipper64 clipper = new Clipper64(); + clipper.setPreserveCollinear(false); + clipper.AddSubject(subject); + Paths64 solution = new Paths64(); + clipper.Execute(ClipType.Union, FillRule.NonZero, solution); + assertEquals(1, solution.size()); + assertEquals(3, solution.get(0).size()); + assertEquals(Clipper.IsPositive(subject.get(0)), Clipper.IsPositive(solution.get(0))); + } } diff --git a/src/test/java/clipper2/TestRect.java b/src/test/java/clipper2/TestRect.java new file mode 100644 index 0000000..491bbce --- /dev/null +++ b/src/test/java/clipper2/TestRect.java @@ -0,0 +1,55 @@ +package clipper2; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import clipper2.core.Rect64; + +class TestRect { + + private static void assertRectEquals(Rect64 expected, Rect64 actual) { + assertEquals(expected.left, actual.left); + assertEquals(expected.top, actual.top); + assertEquals(expected.right, actual.right); + assertEquals(expected.bottom, actual.bottom); + } + + @Test + void testRectOpAdd() { + { + Rect64 lhs = new Rect64(false); + Rect64 rhs = new Rect64(-1, -1, 10, 10); + Rect64 sum = Rect64.opAdd(lhs, rhs); + assertRectEquals(rhs, sum); + sum = Rect64.opAdd(rhs, lhs); + assertRectEquals(rhs, sum); + } + { + Rect64 lhs = new Rect64(false); + Rect64 rhs = new Rect64(1, 1, 10, 10); + Rect64 sum = Rect64.opAdd(lhs, rhs); + assertRectEquals(rhs, sum); + sum = Rect64.opAdd(rhs, lhs); + assertRectEquals(rhs, sum); + } + { + Rect64 lhs = new Rect64(0, 0, 1, 1); + Rect64 rhs = new Rect64(-1, -1, 0, 0); + Rect64 expected = new Rect64(-1, -1, 1, 1); + Rect64 sum = Rect64.opAdd(lhs, rhs); + assertRectEquals(expected, sum); + sum = Rect64.opAdd(rhs, lhs); + assertRectEquals(expected, sum); + } + { + Rect64 lhs = new Rect64(-10, -10, -1, -1); + Rect64 rhs = new Rect64(1, 1, 10, 10); + Rect64 expected = new Rect64(-10, -10, 10, 10); + Rect64 sum = Rect64.opAdd(lhs, rhs); + assertRectEquals(expected, sum); + sum = Rect64.opAdd(rhs, lhs); + assertRectEquals(expected, sum); + } + } +}