diff --git a/CSharp/Clipper2Lib.Benchmark/Benchmarks.cs b/CSharp/Clipper2Lib.Benchmark/Benchmarks.cs index 778bb654..e122d9a4 100644 --- a/CSharp/Clipper2Lib.Benchmark/Benchmarks.cs +++ b/CSharp/Clipper2Lib.Benchmark/Benchmarks.cs @@ -36,9 +36,9 @@ public void GlobalSetup() { Random rand = new (); - _subj = new (); - _clip = new (); - _solution = new (); + _subj = new Paths64(); + _clip = new Paths64(); + _solution = new Paths64(); _subj.Add(MakeRandomPath(DisplayWidth, DisplayHeight, EdgeCount, rand)); _clip.Add(MakeRandomPath(DisplayWidth, DisplayHeight, EdgeCount, rand)); diff --git a/CSharp/Clipper2Lib.Benchmark/Program.cs b/CSharp/Clipper2Lib.Benchmark/Program.cs index 9b43d714..7855429c 100644 --- a/CSharp/Clipper2Lib.Benchmark/Program.cs +++ b/CSharp/Clipper2Lib.Benchmark/Program.cs @@ -2,7 +2,7 @@ namespace Clipper2Lib.Benchmark { - public class Program + public static class Program { public static void Main() { diff --git a/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs b/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs index 14e7811f..b52330c9 100644 --- a/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs +++ b/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs @@ -13,7 +13,7 @@ namespace ClipperDemo1 { - public class Application + public static class Application { public static void Main() { @@ -32,7 +32,7 @@ public static Paths64 Polytree_Union(Paths64 subjects, FillRule fillrule) { // of course this function is inefficient, // but it's purpose is simply to test polytrees. - PolyTree64 polytree = new PolyTree64(); + PolyTree64 polytree = new(); Clipper.BooleanOp(ClipType.Union, subjects, null, polytree, fillrule); return Clipper.PolyTreeToPaths64(polytree); } @@ -41,7 +41,7 @@ public static void SquaresTest(bool test_polytree = false) { const int size = 10; const int w = 800, h = 600; - FillRule fillrule = FillRule.NonZero; + const FillRule fillrule = FillRule.NonZero; Path64 shape = Clipper.MakePath(new int[] { 0, 0, size, 0, size, size, 0, size }); Paths64 subjects = new(), solution; @@ -61,10 +61,10 @@ public static void SquaresTest(bool test_polytree = false) else solution = Clipper.Union(subjects, fillrule); - SvgWriter svg = new SvgWriter(); + SvgWriter svg = new(); SvgUtils.AddSubject(svg, subjects); SvgUtils.AddSolution(svg, solution, false); - string filename = @"..\..\..\squares.svg"; + const string filename = @"..\..\..\squares.svg"; SvgUtils.SaveToFile(svg, filename, fillrule, w, h, 10); ClipperFileIO.OpenFileWithDefaultApp(filename); } @@ -73,7 +73,7 @@ public static void TrianglesTest(bool test_polytree = false) { const int size = 10; const int w = 800, h = 600; - FillRule fillrule = FillRule.NonZero; + const FillRule fillrule = FillRule.NonZero; Path64 tri1 = Clipper.MakePath(new int[] { 0,0, size * 2,0, size,size * 2 }); Path64 tri2 = Clipper.MakePath(new int[] { size * 2, 0, size, size * 2, size*3, size*2 }); @@ -98,10 +98,10 @@ public static void TrianglesTest(bool test_polytree = false) else solution = Clipper.Union(subjects, fillrule); - SvgWriter svg = new SvgWriter(); + SvgWriter svg = new(); SvgUtils.AddSubject(svg, subjects); SvgUtils.AddSolution(svg, solution, false); - string filename = @"..\..\..\triangles.svg"; + const string filename = @"..\..\..\triangles.svg"; SvgUtils.SaveToFile(svg, filename, fillrule, w, h, 10); ClipperFileIO.OpenFileWithDefaultApp(filename); } @@ -110,7 +110,7 @@ public static void DiamondsTest(bool test_polytree = false) { const int size = 10; const int w = 800, h = 600; - FillRule fillrule = FillRule.NonZero; + const FillRule fillrule = FillRule.NonZero; Path64 shape = Clipper.MakePath(new int[] { size, 0, size * 2, size, size, size * 2, 0, size }); Paths64 subjects = new(), solution; @@ -133,36 +133,34 @@ public static void DiamondsTest(bool test_polytree = false) else solution = Clipper.Union(subjects, fillrule); - SvgWriter svg = new SvgWriter(); + SvgWriter svg = new(); SvgUtils.AddSubject(svg, subjects); SvgUtils.AddSolution(svg, solution, false); - string filename = @"..\..\..\diamonds.svg"; + const string filename = @"..\..\..\diamonds.svg"; SvgUtils.SaveToFile(svg, filename, fillrule, w, h, 10); ClipperFileIO.OpenFileWithDefaultApp(filename); } public static void LoopThruTestPolygons(int start = 0, int end = 0) { - Paths64 subject = new Paths64(); - Paths64 subject_open = new Paths64(); - Paths64 clip = new Paths64(); - Paths64 solution = new Paths64(); - Paths64 solution_open = new Paths64(); - ClipType ct; - FillRule fr; + Paths64 subject = new(); + Paths64 subject_open = new(); + Paths64 clip = new(); + Paths64 solution = new(); + Paths64 solution_open = new(); bool do_all = (start == 0 && end == 0); if (do_all) { start = 1; end = 0xFFFF; } else if (end == 0) end = start; if (do_all) Console.WriteLine("\nCount and area differences (expected vs measured):\n"); - int test_number = start; + int test_number = start; for (; test_number <= end; ++test_number) { if (!ClipperFileIO.LoadTestNum(@"..\..\..\..\..\..\Tests\Polygons.txt", - test_number, subject, subject_open, clip, - out ct, out fr, out long area, out int cnt, out _)) break; - Clipper64 c64 = new Clipper64(); + test_number, subject, subject_open, clip, + out ClipType ct, out FillRule fr, out long area, out int cnt, out _)) break; + Clipper64 c64 = new(); c64.AddSubject(subject); c64.AddOpenSubject(subject_open); c64.AddClip(clip); @@ -179,15 +177,15 @@ public static void LoopThruTestPolygons(int start = 0, int end = 0) double area_diff = area <= 0 ? 0 : Math.Abs(measuredArea / area -1.0); if (count_diff > 0.05) - Console.WriteLine(string.Format("{0}: count {1} vs {2}", test_number, cnt, measuredCnt)); + Console.WriteLine($"{test_number}: count {cnt} vs {measuredCnt}"); if (area_diff > 0.1) - Console.WriteLine(string.Format("{0}: area {1} vs {2}", test_number, area, measuredArea)); + Console.WriteLine($"{test_number}: area {area} vs {measuredArea}"); // don't display when looping through every test continue; } - SvgWriter svg = new SvgWriter(); + SvgWriter svg = new(); SvgUtils.AddSubject(svg, subject); SvgUtils.AddClip(svg, clip); if (fr == FillRule.Negative) @@ -199,11 +197,9 @@ public static void LoopThruTestPolygons(int start = 0, int end = 0) ClipperFileIO.OpenFileWithDefaultApp(filename); } - if (do_all) - { - Console.WriteLine(string.Format("\ntest ended at polygon {0}.\n", test_number)); - Console.ReadKey(); - } + if (!do_all) return; + Console.WriteLine($"\ntest ended at polygon {test_number}.\n"); + Console.ReadKey(); } public static Paths64 LoadPathsFromResource(string resourceName) @@ -231,7 +227,7 @@ public static Paths64 LoadPathsFromResource(string resourceName) public static void ClipTestPolys() { - FillRule fillrule = FillRule.NonZero; + const FillRule fillrule = FillRule.NonZero; Paths64 subject = LoadPathsFromResource("ConsoleDemo.subj.bin"); Paths64 clip = LoadPathsFromResource("ConsoleDemo.clip.bin"); Paths64 solution = Clipper.Intersect(subject, clip, fillrule); @@ -240,8 +236,8 @@ public static void ClipTestPolys() SvgUtils.AddSubject(svg, subject); SvgUtils.AddClip(svg, clip); SvgUtils.AddSolution(svg, solution, false); - SvgUtils.SaveToFile(svg, "..\\..\\..\\clipperD.svg", fillrule, 800, 600, 20); - ClipperFileIO.OpenFileWithDefaultApp("..\\..\\..\\clipperD.svg"); + SvgUtils.SaveToFile(svg, @"..\..\..\clipperD.svg", fillrule, 800, 600, 20); + ClipperFileIO.OpenFileWithDefaultApp(@"..\..\..\clipperD.svg"); } diff --git a/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs b/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs index 33655c3b..2820048c 100644 --- a/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs +++ b/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs @@ -6,7 +6,6 @@ * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ -using System; using System.IO; using System.Reflection; using Clipper2Lib; @@ -14,7 +13,7 @@ namespace ClipperDemo1 { - public class Application + public static class Application { public static void Main() @@ -30,7 +29,7 @@ public static void DoSimpleShapes() ClipperOffset co = new(); //triangle offset - with large miter - Paths64 p0 = new() { Clipper.MakePath(new int[] { 30,150, 60,350, 0,350 }) }; + Paths64 p0 = new() { Clipper.MakePath(new [] { 30,150, 60,350, 0,350 }) }; Paths64 p = new(); for (int i = 0; i < 5; ++i) { @@ -44,7 +43,7 @@ public static void DoSimpleShapes() //rectangle offset - both squared and rounded //nb: using the ClipperOffest class directly here to control //different join types within the same offset operation - p.Add(Clipper.MakePath(new int[] { 100,0, 340,0, 340,200, 100,200, 100, 0 })); + p.Add(Clipper.MakePath(new [] { 100,0, 340,0, 340,200, 100,200, 100, 0 })); SvgUtils.AddOpenSubject(svg, p); co.AddPaths(p, JoinType.Bevel, EndType.Joined); @@ -57,7 +56,7 @@ public static void DoSimpleShapes() co.Execute(10, p); - string filename = "../../../inflate.svg"; + const string filename = "../../../inflate.svg"; SvgUtils.AddSolution(svg, p, false); SvgUtils.AddCaption(svg, "Beveled join", 100, -27); SvgUtils.AddCaption(svg, "Squared join", 160, 23); @@ -81,7 +80,7 @@ public static void DoRabbit() solution.AddRange(pd); } - string filename = "../../../rabbit.svg"; + const string filename = "../../../rabbit.svg"; SvgWriter svg = new (); SvgUtils.AddSolution(svg, solution, false); SvgUtils.SaveToFile(svg, filename, FillRule.EvenOdd, 450, 720, 10); @@ -118,10 +117,9 @@ public static void DoVariableOffset() ClipperOffset co = new(); co.AddPaths(p, JoinType.Square, EndType.Butt); co.Execute( - delegate (Path64 path, PathD path_norms, int currPt, int prevPt) - { return currPt* currPt + 10; } , solution); + (path, path_norms, currPt, prevPt) => currPt * currPt + 10, solution); - string filename = "../../../variable_offset.svg"; + const string filename = "../../../variable_offset.svg"; SvgWriter svg = new(); SvgUtils.AddOpenSubject(svg, p); SvgUtils.AddSolution(svg, solution, true); diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index dc2ee7bd..af6d2c73 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -537,13 +537,13 @@ public enum ClipType Union, Difference, Xor - }; + } public enum PathType { Subject, Clip - }; + } // By far the most widely used filling rules for polygons are EvenOdd // and NonZero, sometimes called Alternate and Winding respectively. @@ -554,7 +554,7 @@ public enum FillRule NonZero, Positive, Negative - }; + } // PointInPolygon internal enum PipResult @@ -562,7 +562,7 @@ internal enum PipResult Inside, Outside, OnEdge - }; + } public static class InternalClipper { @@ -612,8 +612,7 @@ internal static bool IsAlmostZero(double value) internal static int TriSign(long x) // returns 0, 1 or -1 { if (x < 0) return -1; - else if (x > 1) return 1; - else return 0; + return x > 1 ? 1 : 0; } public struct MultiplyUInt64Result @@ -725,24 +724,19 @@ public static bool GetSegmentIntersectPt(Point64 ln1a, internal static bool SegsIntersect(Point64 seg1a, Point64 seg1b, Point64 seg2a, Point64 seg2b, bool inclusive = false) { - if (inclusive) - { - double res1 = CrossProduct(seg1a, seg2a, seg2b); - double res2 = CrossProduct(seg1b, seg2a, seg2b); - if (res1 * res2 > 0) return false; - double res3 = CrossProduct(seg2a, seg1a, seg1b); - double res4 = CrossProduct(seg2b, seg1a, seg1b); - if (res3 * res4 > 0) return false; - // ensure NOT collinear - return (res1 != 0 || res2 != 0 || res3 != 0 || res4 != 0); - } - else - { - return (CrossProduct(seg1a, seg2a, seg2b) * - CrossProduct(seg1b, seg2a, seg2b) < 0) && - (CrossProduct(seg2a, seg1a, seg1b) * - CrossProduct(seg2b, seg1a, seg1b) < 0); - } + if (!inclusive) + return (CrossProduct(seg1a, seg2a, seg2b) * + CrossProduct(seg1b, seg2a, seg2b) < 0) && + (CrossProduct(seg2a, seg1a, seg1b) * + CrossProduct(seg2b, seg1a, seg1b) < 0); + double res1 = CrossProduct(seg1a, seg2a, seg2b); + double res2 = CrossProduct(seg1b, seg2a, seg2b); + if (res1 * res2 > 0) return false; + double res3 = CrossProduct(seg2a, seg1a, seg1b); + double res4 = CrossProduct(seg2b, seg1a, seg1b); + if (res3 * res4 > 0) return false; + // ensure NOT collinear + return (res1 != 0 || res2 != 0 || res3 != 0 || res4 != 0); } public static Point64 GetClosestPtOnSegment(Point64 offPt, Point64 seg1, Point64 seg2) @@ -783,14 +777,14 @@ public static PointInPolygonResult PointInPolygon(Point64 pt, Path64 polygon) if (isAbove) { while (i < end && polygon[i].Y < pt.Y) i++; - if (i == end) continue; } else { while (i < end && polygon[i].Y > pt.Y) i++; - if (i == end) continue; } + if (i == end) continue; + Point64 curr = polygon[i], prev; if (i > 0) prev = polygon[i - 1]; else prev = polygon[len - 1]; @@ -823,20 +817,13 @@ public static PointInPolygonResult PointInPolygon(Point64 pt, Path64 polygon) i++; } - if (isAbove != startingAbove) - { - if (i == len) i = 0; - if (i == 0) - d = CrossProduct(polygon[len - 1], polygon[0], pt); - else - d = CrossProduct(polygon[i - 1], polygon[i], pt); - if (d == 0) return PointInPolygonResult.IsOn; - if ((d < 0) == isAbove) val = 1 - val; - } + if (isAbove == startingAbove) return val == 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside; + if (i == len) i = 0; + d = i == 0 ? CrossProduct(polygon[len - 1], polygon[0], pt) : CrossProduct(polygon[i - 1], polygon[i], pt); + if (d == 0) return PointInPolygonResult.IsOn; + if ((d < 0) == isAbove) val = 1 - val; - if (val == 0) - return PointInPolygonResult.IsOutside; - return PointInPolygonResult.IsInside; + return val == 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside; } } // InternalClipper diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index d227b166..ec3bd49d 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -27,7 +27,7 @@ public enum PointInPolygonResult IsOn = 0, IsInside = 1, IsOutside = 2 - }; + } [Flags] internal enum VertexFlags @@ -37,7 +37,7 @@ internal enum VertexFlags OpenEnd = 2, LocalMax = 4, LocalMin = 8 - }; + } internal class Vertex { @@ -53,7 +53,7 @@ public Vertex(Point64 pt, VertexFlags flags, Vertex? prev) next = null; this.prev = prev; } - }; + } internal readonly struct LocalMinima { @@ -87,7 +87,7 @@ public override int GetHashCode() { return vertex.GetHashCode(); } - }; + } // IntersectNode: a structure representing 2 intersecting edges. // Intersections must be sorted so they are processed from the largest @@ -104,7 +104,7 @@ public IntersectNode(Point64 pt, Active edge1, Active edge2) this.edge1 = edge1; this.edge2 = edge2; } - }; + } internal struct LocMinSorter : IComparer { @@ -131,10 +131,10 @@ public OutPt(Point64 pt, OutRec outrec) prev = this; horz = null; } - }; + } - internal enum JoinWith { None, Left, Right }; - internal enum HorzPosition { Bottom, Middle, Top }; + internal enum JoinWith { None, Left, Right } + internal enum HorzPosition { Bottom, Middle, Top } // OutRec: path data structure for clipping solutions @@ -151,7 +151,7 @@ internal class OutRec public bool isOpen; public List? splits; public OutRec? recursiveSplit; - }; + } internal class HorzSegment { @@ -210,7 +210,7 @@ internal class Active public LocalMinima localMin; // the bottom of an edge 'bound' (also Vatti) internal bool isLeftBound; internal JoinWith joinWith; - }; + } internal static class ClipperEngine { @@ -258,14 +258,14 @@ internal static void AddPathsToVertexList(Paths64 paths, PathType polytype, bool prev_v = curr_v; } } - if (prev_v == null || prev_v.prev == null) continue; + if (prev_v?.prev == null) continue; if (!isOpen && prev_v.pt == v0!.pt) prev_v = prev_v.prev; prev_v.next = v0; v0!.prev = prev_v; if (!isOpen && prev_v.next == prev_v) continue; // OK, we have a valid path - bool going_up, going_up0; + bool going_up; if (isOpen) { curr_v = v0.next; @@ -290,7 +290,7 @@ internal static void AddPathsToVertexList(Paths64 paths, PathType polytype, bool going_up = prev_v.pt.Y > v0.pt.Y; } - going_up0 = going_up; + bool going_up0 = going_up; prev_v = v0; curr_v = v0.next; while (curr_v != v0) @@ -488,9 +488,7 @@ private static double GetDx(Point64 pt1, Point64 pt2) double dy = pt2.Y - pt1.Y; if (dy != 0) return (pt2.X - pt1.X) / dy; - if (pt2.X > pt1.X) - return double.NegativeInfinity; - return double.PositiveInfinity; + return pt2.X > pt1.X ? double.NegativeInfinity : double.PositiveInfinity; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -549,17 +547,13 @@ private static void SetDx(Active ae) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vertex NextVertex(Active ae) { - if (ae.windDx > 0) - return ae.vertexTop!.next!; - return ae.vertexTop!.prev!; + return ae.windDx > 0 ? ae.vertexTop!.next! : ae.vertexTop!.prev!; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vertex PrevPrevVertex(Active ae) { - if (ae.windDx > 0) - return ae.vertexTop!.prev!.prev!; - return ae.vertexTop!.next!.next!; + return ae.windDx > 0 ? ae.vertexTop!.prev!.prev! : ae.vertexTop!.next!.next!; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -577,8 +571,7 @@ private static bool IsMaxima(Active ae) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Active? GetMaximaPair(Active ae) { - Active? ae2; - ae2 = ae.nextInAEL; + Active? ae2 = ae.nextInAEL; while (ae2 != null) { if (ae2.vertexTop == ae.vertexTop) return ae2; // Found! @@ -621,12 +614,9 @@ private struct IntersectListSort : IComparer { public readonly int Compare(IntersectNode a, IntersectNode b) { - if (a.pt.Y == b.pt.Y) - { - if (a.pt.X == b.pt.X) return 0; - return (a.pt.X < b.pt.X) ? -1 : 1; - } - return (a.pt.Y > b.pt.Y) ? -1 : 1; + if (a.pt.Y != b.pt.Y) return (a.pt.Y > b.pt.Y) ? -1 : 1; + if (a.pt.X == b.pt.X) return 0; + return (a.pt.X < b.pt.X) ? -1 : 1; } } @@ -910,7 +900,7 @@ private bool IsContributingClosed(Active ae) { FillRule.Positive => ae.windCount2 > 0, FillRule.Negative => ae.windCount2 < 0, - _ => ae.windCount2 != 0, + _ => ae.windCount2 != 0 }; case ClipType.Union: @@ -918,7 +908,7 @@ private bool IsContributingClosed(Active ae) { FillRule.Positive => ae.windCount2 <= 0, FillRule.Negative => ae.windCount2 >= 0, - _ => ae.windCount2 == 0, + _ => ae.windCount2 == 0 }; case ClipType.Difference: @@ -926,7 +916,7 @@ private bool IsContributingClosed(Active ae) { FillRule.Positive => (ae.windCount2 <= 0), FillRule.Negative => (ae.windCount2 >= 0), - _ => (ae.windCount2 == 0), + _ => (ae.windCount2 == 0) }; return (GetPolyType(ae) == PathType.Subject) ? result : !result; @@ -1122,8 +1112,6 @@ private static bool IsValidAelOrder(Active resident, Active newcomer) [MethodImpl(MethodImplOptions.AggressiveInlining)] private void InsertLeftEdge(Active ae) { - Active ae2; - if (_actives == null) { ae.prevInAEL = null; @@ -1139,7 +1127,7 @@ private void InsertLeftEdge(Active ae) } else { - ae2 = _actives; + Active ae2 = _actives; while (ae2.nextInAEL != null && IsValidAelOrder(ae2.nextInAEL, ae)) ae2 = ae2.nextInAEL; //don't separate joined edges @@ -1162,13 +1150,12 @@ private static void InsertRightEdge(Active ae, Active ae2) private void InsertLocalMinimaIntoAEL(long botY) { - LocalMinima localMinima; - Active? leftBound, rightBound; // Add any local minima (if any) at BotY ... // NB horizontal local minima edges should contain locMin.vertex.prev while (HasLocMinAtY(botY)) { - localMinima = PopLocalMinima(); + LocalMinima localMinima = PopLocalMinima(); + Active? leftBound; if ((localMinima.vertex.flags & VertexFlags.OpenStart) != VertexFlags.None) { leftBound = null; @@ -1188,6 +1175,7 @@ private void InsertLocalMinimaIntoAEL(long botY) SetDx(leftBound); } + Active? rightBound; if ((localMinima.vertex.flags & VertexFlags.OpenEnd) != VertexFlags.None) { rightBound = null; @@ -1461,8 +1449,13 @@ private static OutPt AddOutPt(Active ae, Point64 pt) OutPt opFront = outrec.pts!; OutPt opBack = opFront.next!; - if (toFront && (pt == opFront.pt)) return opFront; - else if (!toFront && (pt == opBack.pt)) return opBack; + switch (toFront) + { + case true when (pt == opFront.pt): + return opFront; + case false when (pt == opBack.pt): + return opBack; + } OutPt newOp = new OutPt(pt, outrec); opBack.prev = newOp; @@ -1829,10 +1822,8 @@ private void AdjustCurrXAndCopyToSEL(long topY) ae.prevInSEL = ae.prevInAEL; ae.nextInSEL = ae.nextInAEL; ae.jump = ae.nextInSEL; - if (ae.joinWith == JoinWith.Left) - ae.curX = ae.prevInAEL!.curX; // this also avoids complications - else - ae.curX = TopX(ae, topY); + ae.curX = ae.joinWith == JoinWith.Left ? ae.prevInAEL!.curX : // this also avoids complications + TopX(ae, topY); // NB don't update ae.curr.Y yet (see AddNewIntersectNode) ae = ae.nextInAEL; } @@ -1868,11 +1859,9 @@ protected void ExecuteInternal(ClipType ct, FillRule fillRule) [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DoIntersections(long topY) { - if (BuildIntersectList(topY)) - { - ProcessIntersectList(); - DisposeIntersectNodes(); - } + if (!BuildIntersectList(topY)) return; + ProcessIntersectList(); + DisposeIntersectNodes(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1892,23 +1881,33 @@ private void AddNewIntersectNode(Active ae1, Active ae2, long topY) { double absDx1 = Math.Abs(ae1.dx); double absDx2 = Math.Abs(ae2.dx); - if (absDx1 > 100 && absDx2 > 100) + switch (absDx1 > 100) { - if (absDx1 > absDx2) + case true when absDx2 > 100: + { + if (absDx1 > absDx2) + ip = InternalClipper.GetClosestPtOnSegment(ip, ae1.bot, ae1.top); + else + ip = InternalClipper.GetClosestPtOnSegment(ip, ae2.bot, ae2.top); + break; + } + case true: ip = InternalClipper.GetClosestPtOnSegment(ip, ae1.bot, ae1.top); - else - ip = InternalClipper.GetClosestPtOnSegment(ip, ae2.bot, ae2.top); - } - else if (absDx1 > 100) - ip = InternalClipper.GetClosestPtOnSegment(ip, ae1.bot, ae1.top); - else if (absDx2 > 100) - ip = InternalClipper.GetClosestPtOnSegment(ip, ae2.bot, ae2.top); - else - { - if (ip.Y < topY) ip.Y = topY; - else ip.Y = _currentBotY; - if (absDx1 < absDx2) ip.X = TopX(ae1, ip.Y); - else ip.X = TopX(ae2, ip.Y); + break; + default: + { + if (absDx2 > 100) + ip = InternalClipper.GetClosestPtOnSegment(ip, ae2.bot, ae2.top); + else + { + if (ip.Y < topY) ip.Y = topY; + else ip.Y = _currentBotY; + if (absDx1 < absDx2) ip.X = TopX(ae1, ip.Y); + else ip.X = TopX(ae2, ip.Y); + } + + break; + } } } IntersectNode node = new IntersectNode(ip, ae1, ae2); @@ -1937,7 +1936,7 @@ private static void Insert1Before2InSEL(Active ae1, Active ae2) private bool BuildIntersectList(long topY) { - if (_actives == null || _actives.nextInAEL == null) return false; + if (_actives?.nextInAEL == null) return false; // Calculate edge positions at the top of the current scanbeam, and from this // we will determine the intersections required to reach these new positions. @@ -1948,23 +1947,23 @@ private bool BuildIntersectList(long topY) // stored in FIntersectList ready to be processed in ProcessIntersectList. // Re merge sorts see https://stackoverflow.com/a/46319131/359538 - Active? left = _sel, right, lEnd, rEnd, currBase, prevBase, tmp; + Active? left = _sel; while (left!.jump != null) { - prevBase = null; - while (left != null && left.jump != null) + Active? prevBase = null; + while (left?.jump != null) { - currBase = left; - right = left.jump; - lEnd = right; - rEnd = right.jump; + Active? currBase = left; + Active? right = left.jump; + Active? lEnd = right; + Active? rEnd = right.jump; left.jump = rEnd; while (left != lEnd && right != rEnd) { if (right!.curX < left!.curX) { - tmp = right.prevInSEL!; + Active? tmp = right.prevInSEL!; for (; ; ) { AddNewIntersectNode(tmp, right, topY); @@ -1976,13 +1975,11 @@ private bool BuildIntersectList(long topY) right = ExtractFromSEL(tmp); lEnd = right; Insert1Before2InSEL(tmp, left); - if (left == currBase) - { - currBase = tmp; - currBase.jump = rEnd; - if (prevBase == null) _sel = currBase; - else prevBase.jump = currBase; - } + if (left != currBase) continue; + currBase = tmp; + currBase.jump = rEnd; + if (prevBase == null) _sel = currBase; + else prevBase.jump = currBase; } else left = left.nextInSEL; } @@ -2126,7 +2123,6 @@ private void DoHorizontal(Active horz) * / | / | / * *******************************************************************************/ { - Point64 pt; bool horzIsOpen = IsOpen(horz); long Y = horz.bot.Y; @@ -2178,6 +2174,7 @@ private void DoHorizontal(Active horz) // if horzEdge is a maxima, keep going until we reach // its maxima pair, otherwise check for break conditions + Point64 pt; if (vertex_max != horz.vertexTop || IsOpenEnd(horz)) { // otherwise stop when 'ae' is beyond the end of the horizontal line @@ -2243,7 +2240,7 @@ private void DoHorizontal(Active horz) DeleteFromAEL(horz); return; } - else if (NextVertex(horz).pt.Y != horz.top.Y) + if (NextVertex(horz).pt.Y != horz.top.Y) break; //still more horizontals in bound to process ... @@ -2300,30 +2297,26 @@ private void DoTopOfScanbeam(long y) [MethodImpl(MethodImplOptions.AggressiveInlining)] private Active? DoMaxima(Active ae) { - Active? prevE; - Active? nextE, maxPair; - prevE = ae.prevInAEL; - nextE = ae.nextInAEL; + Active? prevE = ae.prevInAEL; + Active? nextE = ae.nextInAEL; if (IsOpenEnd(ae)) { if (IsHotEdge(ae)) AddOutPt(ae, ae.top); - if (!IsHorizontal(ae)) + if (IsHorizontal(ae)) return nextE; + if (IsHotEdge(ae)) { - if (IsHotEdge(ae)) - { - if (IsFront(ae)) - ae.outrec!.frontEdge = null; - else - ae.outrec!.backEdge = null; - ae.outrec = null; - } - DeleteFromAEL(ae); + if (IsFront(ae)) + ae.outrec!.frontEdge = null; + else + ae.outrec!.backEdge = null; + ae.outrec = null; } + DeleteFromAEL(ae); return nextE; } - maxPair = GetMaximaPair(ae); + Active? maxPair = GetMaximaPair(ae); if (maxPair == null) return nextE; // eMaxPair is horizontal if (IsJoined(ae)) Split(ae, ae.top); @@ -2528,10 +2521,9 @@ private int HorzSegSort(HorzSegment? hs1, HorzSegment? hs2) { return hs2.rightOp == null ? 0 : 1; } - else if (hs2.rightOp == null) + if (hs2.rightOp == null) return -1; - else - return hs1.leftOp!.pt.X.CompareTo(hs2.leftOp!.pt.X); + return hs1.leftOp!.pt.X.CompareTo(hs2.leftOp!.pt.X); } private void ConvertHorzSegsToJoins() @@ -2664,29 +2656,34 @@ private static PointInPolygonResult PointInOpPolygon(Point64 pt, OutPt op) op2 = op2.next!; } - if (isAbove != startingAbove) + if (isAbove == startingAbove) return val == 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside; { double d = InternalClipper.CrossProduct(op2.prev.pt, op2.pt, pt); if (d == 0) return PointInPolygonResult.IsOn; if ((d < 0) == isAbove) val = 1 - val; } - if (val == 0) return PointInPolygonResult.IsOutside; - else return PointInPolygonResult.IsInside; + return val == 0 ? PointInPolygonResult.IsOutside : PointInPolygonResult.IsInside; } private static bool Path1InsidePath2(OutPt op1, OutPt op2) { // we need to make some accommodation for rounding errors // so we won't jump if the first vertex is found outside - PointInPolygonResult result; int outside_cnt = 0; OutPt op = op1; do { - result = PointInOpPolygon(op.pt, op2); - if (result == PointInPolygonResult.IsOutside) ++outside_cnt; - else if (result == PointInPolygonResult.IsInside) --outside_cnt; + PointInPolygonResult result = PointInOpPolygon(op.pt, op2); + switch (result) + { + case PointInPolygonResult.IsOutside: + ++outside_cnt; + break; + case PointInPolygonResult.IsInside: + --outside_cnt; + break; + } op = op.next!; } while (op != op1 && Math.Abs(outside_cnt) < 2); if (Math.Abs(outside_cnt) > 1) return (outside_cnt < 0); @@ -2696,7 +2693,7 @@ private static bool Path1InsidePath2(OutPt op1, OutPt op2) return InternalClipper.PointInPolygon(mp, path2) != PointInPolygonResult.IsOutside; } - private void MoveSplits(OutRec fromOr, OutRec toOr) + private static void MoveSplits(OutRec fromOr, OutRec toOr) { if (fromOr.splits == null) return; toOr.splits ??= new List(); @@ -2889,33 +2886,29 @@ private void DoSplitOp(OutRec outrec, OutPt splitOp) // So the only way for these areas to have the same sign is if // the split triangle is larger than the path containing prevOp or // if there's more than one self=intersection. - 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; + OutRec newOutRec = NewOutRec(); + newOutRec.owner = outrec.owner; + splitOp.outrec = newOutRec; + splitOp.next.outrec = newOutRec; - OutPt newOp = new OutPt(ip, newOutRec) { prev = splitOp.next, next = splitOp }; - newOutRec.pts = newOp; - splitOp.prev = newOp; - splitOp.next.next = newOp; + OutPt newOp = new OutPt(ip, newOutRec) { prev = splitOp.next, next = splitOp }; + newOutRec.pts = newOp; + splitOp.prev = newOp; + splitOp.next.next = newOp; - if (_using_polytree) - { - if (Path1InsidePath2(prevOp, newOp)) - { - newOutRec.splits ??= new List(); - newOutRec.splits.Add(outrec.idx); - } - else - { - outrec.splits ??= new List(); - outrec.splits.Add(newOutRec.idx); - } - } + if (!_using_polytree) return; + if (Path1InsidePath2(prevOp, newOp)) + { + newOutRec.splits ??= new List(); + newOutRec.splits.Add(outrec.idx); + } + else + { + outrec.splits ??= new List(); + outrec.splits.Add(newOutRec.idx); } //else { splitOp = null; splitOp.next = null; } } @@ -2936,8 +2929,8 @@ private void FixSelfIntersects(OutRec outrec) op2 = outrec.pts; continue; } - else - op2 = op2.next; + + op2 = op2.next; if (op2 == outrec.pts) break; } } @@ -2975,8 +2968,7 @@ internal static bool BuildPath(OutPt? op, bool reverse, bool isOpen, Path64 path op2 = op2.next!; } - if (path.Count == 3 && !isOpen && IsVerySmallTriangle(op2)) return false; - else return true; + return path.Count != 3 || isOpen || !IsVerySmallTriangle(op2); } protected bool BuildPaths(Paths64 solutionClosed, Paths64 solutionOpen) @@ -3048,14 +3040,12 @@ private bool CheckSplitOwner(OutRec outrec, List? splits) if (split == null || split == outrec || split.recursiveSplit == outrec) continue; split.recursiveSplit = outrec; //#599 if (split.splits != null && CheckSplitOwner(outrec, split.splits)) return true; - if (IsValidOwner(outrec, split) && - CheckBounds(split) && - split.bounds.Contains(outrec.bounds) && - Path1InsidePath2(outrec.pts!, split.pts!)) - { - outrec.owner = split; //found in split - return true; - } + if (!IsValidOwner(outrec, split) || + !CheckBounds(split) || + !split.bounds.Contains(outrec.bounds) || + !Path1InsidePath2(outrec.pts!, split.pts!)) continue; + outrec.owner = split; //found in split + return true; } return false; } @@ -3070,7 +3060,7 @@ private void RecursiveCheckOwners(OutRec outrec, PolyPathBase polypath) { if (outrec.owner.splits != null && CheckSplitOwner(outrec, outrec.owner.splits)) break; - else if (outrec.owner.pts != null && CheckBounds(outrec.owner) && + if (outrec.owner.pts != null && CheckBounds(outrec.owner) && Path1InsidePath2(outrec.pts!, outrec.owner.pts!)) break; outrec.owner = outrec.owner.owner; } @@ -3235,7 +3225,7 @@ public ZCallback64? ZCallback { public class ClipperD : ClipperBase { - private readonly string precision_range_error = "Error: Precision is out of range."; + private const string precision_range_error = "Error: Precision is out of range."; private readonly double _scale; private readonly double _invScale; @@ -3393,12 +3383,10 @@ public bool Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, Pa } ClearSolutionOnly(); if (!success) return false; - if (oPaths.Count > 0) - { - openPaths.EnsureCapacity(oPaths.Count); - foreach (Path64 path in oPaths) - openPaths.Add(Clipper.ScalePathD(path, _invScale)); - } + if (oPaths.Count <= 0) return true; + openPaths.EnsureCapacity(oPaths.Count); + foreach (Path64 path in oPaths) + openPaths.Add(Clipper.ScalePathD(path, _invScale)); return true; } @@ -3453,7 +3441,7 @@ public object Current } } - }; + } public bool IsHole => GetIsHole(); @@ -3492,9 +3480,9 @@ internal string ToStringInternal(int idx, int level) if (_childs.Count == 1) plural = ""; padding = padding.PadLeft(level * 2); if ((level & 1) == 0) - result += string.Format("{0}+- hole ({1}) contains {2} nested polygon{3}.\n", padding, idx, _childs.Count, plural); + result += $"{padding}+- hole ({idx}) contains {_childs.Count} nested polygon{plural}.\n"; else - result += string.Format("{0}+- polygon ({1}) contains {2} hole{3}.\n", padding, idx, _childs.Count, plural); + result += $"{padding}+- polygon ({idx}) contains {_childs.Count} hole{plural}.\n"; for (int i = 0; i < Count; i++) if (_childs[i].Count > 0) @@ -3507,7 +3495,7 @@ public override string ToString() if (Level > 0) return ""; //only accept tree root string plural = "s"; if (_childs.Count == 1) plural = ""; - string result = string.Format("Polytree with {0} polygon{1}.\n", _childs.Count, plural); + string result = $"Polytree with {_childs.Count} polygon{plural}.\n"; for (int i = 0; i < Count; i++) if (_childs[i].Count > 0) result += _childs[i].ToStringInternal(i, 1); diff --git a/CSharp/Clipper2Lib/Clipper.Minkowski.cs b/CSharp/Clipper2Lib/Clipper.Minkowski.cs index 5b38c739..26206422 100644 --- a/CSharp/Clipper2Lib/Clipper.Minkowski.cs +++ b/CSharp/Clipper2Lib/Clipper.Minkowski.cs @@ -12,7 +12,7 @@ namespace Clipper2Lib { - public class Minkowski + public static class Minkowski { private static Paths64 MinkowskiInternal(Path64 pattern, Path64 path, bool isSum, bool isClosed) { diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index b4b51fe9..8741322a 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -19,7 +19,7 @@ public enum JoinType Square, Bevel, Round - }; + } public enum EndType { @@ -28,7 +28,7 @@ public enum EndType Butt, Square, Round - }; + } public class ClipperOffset { @@ -67,7 +67,7 @@ public Group(Paths64 paths, JoinType joinType, EndType endType = EndType.Polygon } } - private static readonly double Tolerance = 1.0E-12; + private const double Tolerance = 1.0E-12; private readonly List _groupList = new List(); private Path64 pathOut = new Path64(); @@ -91,7 +91,7 @@ public Group(Paths64 paths, JoinType joinType, EndType endType = EndType.Polygon public delegate double DeltaCallback64(Path64 path, PathD path_norms, int currPt, int prevPt); - public ClipperOffset.DeltaCallback64? DeltaCallback { get; set; } + public DeltaCallback64? DeltaCallback { get; set; } #if USINGZ internal void ZCB(Point64 bot1, Point64 top1, @@ -185,10 +185,8 @@ private void ExecuteInternal(double delta) FillRule fillRule = pathsReversed ? FillRule.Negative : FillRule.Positive; // clean up self-intersections ... - Clipper64 c = new Clipper64(); - c.PreserveCollinear = PreserveCollinear; - // the solution should retain the orientation of the input - c.ReverseSolution = ReverseSolution != pathsReversed; + Clipper64 c = new Clipper64 { PreserveCollinear = PreserveCollinear, // the solution should retain the orientation of the input + ReverseSolution = ReverseSolution != pathsReversed }; #if USINGZ c.ZCallback = ZCB; #endif @@ -564,18 +562,25 @@ private void OffsetPoint(Group group, Path64 path, int j, ref int k) // almost straight - less than 2.5 degree (#424, #482, #526 & #724) DoMiter(path, j, k, cosA); } - else if (_joinType == JoinType.Miter) + else switch (_joinType) { // miter unless the angle is sufficiently acute to exceed ML - if (cosA > _mitLimSqr - 1) DoMiter(path, j, k, cosA); - else DoSquare(path, j, k); + case JoinType.Miter when cosA > _mitLimSqr - 1: + DoMiter(path, j, k, cosA); + break; + case JoinType.Miter: + DoSquare(path, j, k); + break; + case JoinType.Round: + DoRound(path, j, k, Math.Atan2(sinA, cosA)); + break; + case JoinType.Bevel: + DoBevel(path, j, k); + break; + default: + DoSquare(path, j, k); + break; } - else if (_joinType == JoinType.Round) - DoRound(path, j, k, Math.Atan2(sinA, cosA)); - else if (_joinType == JoinType.Bevel) - DoBevel(path, j, k); - else - DoSquare(path, j, k); k = j; } @@ -701,50 +706,61 @@ private void DoGroupOffset(Group group) pathOut = new Path64(); int cnt = p.Count; - if (cnt == 1) + switch (cnt) { - Point64 pt = p[0]; - - if (DeltaCallback != null) - { - _groupDelta = DeltaCallback(p, _normals, 0, 0); - if (group.pathsReversed) _groupDelta = -_groupDelta; - absDelta = Math.Abs(_groupDelta); - } - - // single vertex so build a circle or square ... - if (group.endType == EndType.Round) + case 1: { - double r = absDelta; - int steps = (int) Math.Ceiling(_stepsPerRad * 2 * Math.PI); - pathOut = Clipper.Ellipse(pt, r, r, steps); + Point64 pt = p[0]; + + if (DeltaCallback != null) + { + _groupDelta = DeltaCallback(p, _normals, 0, 0); + if (group.pathsReversed) _groupDelta = -_groupDelta; + absDelta = Math.Abs(_groupDelta); + } + + // single vertex so build a circle or square ... + if (group.endType == EndType.Round) + { + int steps = (int) Math.Ceiling(_stepsPerRad * 2 * Math.PI); + pathOut = Clipper.Ellipse(pt, absDelta, absDelta, steps); #if USINGZ pathOut = InternalClipper.SetZ(pathOut, pt.Z); #endif - } - else - { - int d = (int) Math.Ceiling(_groupDelta); - Rect64 r = new Rect64(pt.X - d, pt.Y - d, pt.X + d, pt.Y + d); - pathOut = r.AsPath(); + } + else + { + int d = (int) Math.Ceiling(_groupDelta); + Rect64 r = new Rect64(pt.X - d, pt.Y - d, pt.X + d, pt.Y + d); + pathOut = r.AsPath(); #if USINGZ pathOut = InternalClipper.SetZ(pathOut, pt.Z); #endif + } + _solution.Add(pathOut); + continue; // end of offsetting a single point } - _solution.Add(pathOut); - continue; - } // end of offsetting a single point - + case 2 when group.endType == EndType.Joined: + _endType = (group.joinType == JoinType.Round) ? + EndType.Round : + EndType.Square; + break; + } - if (cnt == 2 && group.endType == EndType.Joined) - _endType = (group.joinType == JoinType.Round) ? - EndType.Round : - EndType.Square; BuildNormals(p); - if (_endType == EndType.Polygon) OffsetPolygon(group, p); - else if (_endType == EndType.Joined) OffsetOpenJoined(group, p); - else OffsetOpenPath(group, p); + switch (_endType) + { + case EndType.Polygon: + OffsetPolygon(group, p); + break; + case EndType.Joined: + OffsetOpenJoined(group, p); + break; + default: + OffsetOpenPath(group, p); + break; + } } } } diff --git a/CSharp/Clipper2Lib/Clipper.RectClip.cs b/CSharp/Clipper2Lib/Clipper.RectClip.cs index face68f9..27f13e27 100644 --- a/CSharp/Clipper2Lib/Clipper.RectClip.cs +++ b/CSharp/Clipper2Lib/Clipper.RectClip.cs @@ -33,7 +33,7 @@ public class RectClip64 protected enum Location { left, top, right, bottom, inside - }; + } readonly protected Rect64 rect_; readonly protected Point64 mp_; @@ -113,8 +113,7 @@ private static bool IsClockwise(Location prev, Location curr, { if (AreOpposites(prev, curr)) return InternalClipper.CrossProduct(prevPt, rectMidPoint, currPt) < 0; - else - return HeadingClockwise(prev, curr); + return HeadingClockwise(prev, curr); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -173,7 +172,7 @@ private static bool IsHeadingClockwise(Point64 pt1, Point64 pt2, int edgeIdx) 0 => pt2.Y < pt1.Y, 1 => pt2.X > pt1.X, 2 => pt2.Y > pt1.Y, - _ => pt2.X < pt1.X, + _ => pt2.X < pt1.X }; } @@ -206,11 +205,9 @@ private static void UncoupleEdge(OutPt2 op) for (int i = 0; i < op.edge.Count; i++) { OutPt2? op2 = op.edge[i]; - if (op2 == op) - { - op.edge[i] = null; - break; - } + if (op2 != op) continue; + op.edge[i] = null; + break; } op.edge = null; } @@ -229,10 +226,7 @@ private static void SetNewOwner(OutPt2 op, int newIdx) private void AddCorner(Location prev, Location curr) { - if (HeadingClockwise(prev, curr)) - Add(rectPath_[(int) prev]); - else - Add(rectPath_[(int) curr]); + Add(HeadingClockwise(prev, curr) ? rectPath_[(int) prev] : rectPath_[(int) curr]); } private void AddCorner(ref Location loc, bool isClockwise) @@ -290,17 +284,17 @@ private static bool GetSegmentIntersection(Point64 p1, { ip = p1; if (res2 == 0) return false; // segments are collinear - else if (p1 == p3 || p1 == p4) return true; + if (p1 == p3 || p1 == p4) return true; //else if (p2 == p3 || p2 == p4) { ip = p2; return true; } - else if (IsHorizontal(p3, p4)) return ((p1.X > p3.X) == (p1.X < p4.X)); - else return ((p1.Y > p3.Y) == (p1.Y < p4.Y)); + if (IsHorizontal(p3, p4)) return ((p1.X > p3.X) == (p1.X < p4.X)); + return ((p1.Y > p3.Y) == (p1.Y < p4.Y)); } - else if (res2 == 0) + if (res2 == 0) { ip = p2; if (p2 == p3 || p2 == p4) return true; - else if (IsHorizontal(p3, p4)) return ((p2.X > p3.X) == (p2.X < p4.X)); - else return ((p2.Y > p3.Y) == (p2.Y < p4.Y)); + if (IsHorizontal(p3, p4)) return ((p2.X > p3.X) == (p2.X < p4.X)); + return ((p2.Y > p3.Y) == (p2.Y < p4.Y)); } if ((res1 > 0) == (res2 > 0)) @@ -315,15 +309,15 @@ private static bool GetSegmentIntersection(Point64 p1, { ip = p3; if (p3 == p1 || p3 == p2) return true; - else if (IsHorizontal(p1, p2)) return ((p3.X > p1.X) == (p3.X < p2.X)); - else return ((p3.Y > p1.Y) == (p3.Y < p2.Y)); + if (IsHorizontal(p1, p2)) return ((p3.X > p1.X) == (p3.X < p2.X)); + return ((p3.Y > p1.Y) == (p3.Y < p2.Y)); } - else if (res4 == 0) + if (res4 == 0) { ip = p4; if (p4 == p1 || p4 == p2) return true; - else if (IsHorizontal(p1, p2)) return ((p4.X > p1.X) == (p4.X < p2.X)); - else return ((p4.Y > p1.Y) == (p4.Y < p2.Y)); + if (IsHorizontal(p1, p2)) return ((p4.X > p1.X) == (p4.X < p2.X)); + return ((p4.Y > p1.Y) == (p4.Y < p2.Y)); } if ((res3 > 0) == (res4 > 0)) { @@ -346,62 +340,54 @@ static protected bool GetIntersection(Path64 rectPath, Point64 p, Point64 p2, re case Location.left: if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], out ip)) return true; - else if (p.Y < rectPath[0].Y && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) + if (p.Y < rectPath[0].Y && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) { loc = Location.top; return true; } - else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) - { - loc = Location.bottom; - return true; - } - else return false; + + if (!GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) return false; + loc = Location.bottom; + return true; case Location.right: if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) return true; - else if (p.Y < rectPath[0].Y && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) + if (p.Y < rectPath[0].Y && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) { loc = Location.top; return true; } - else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) - { - loc = Location.bottom; - return true; - } - else return false; + + if (!GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) return false; + loc = Location.bottom; + return true; case Location.top: if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) return true; - else if (p.X < rectPath[0].X && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], out ip)) + if (p.X < rectPath[0].X && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], out ip)) { loc = Location.left; return true; } - else if (p.X > rectPath[1].X && GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) - { - loc = Location.right; - return true; - } - else return false; + + if (p.X <= rectPath[1].X || !GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) return false; + loc = Location.right; + return true; case Location.bottom: if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) return true; - else if (p.X < rectPath[3].X && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], out ip)) + if (p.X < rectPath[3].X && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], out ip)) { loc = Location.left; return true; } - else if (p.X > rectPath[2].X && GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) - { - loc = Location.right; - return true; - } - else return false; + + if (p.X <= rectPath[2].X || !GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) return false; + loc = Location.right; + return true; default: if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], out ip)) @@ -409,22 +395,20 @@ static protected bool GetIntersection(Path64 rectPath, Point64 p, Point64 p2, re loc = Location.left; return true; } - else if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) + if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], out ip)) { loc = Location.top; return true; } - else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) + if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], out ip)) { loc = Location.right; return true; } - else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) - { - loc = Location.bottom; - return true; - } - else return false; + + if (!GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], out ip)) return false; + loc = Location.bottom; + return true; } } @@ -636,19 +620,15 @@ private void ExecuteInternal(Path64 path) if (firstCross == Location.inside) { // path never intersects - if (startingLoc != Location.inside) + if (startingLoc == Location.inside) return; + if (!pathBounds_.Contains(rect_) || + !Path1ContainsPath2(path, rectPath_)) return; + bool startLocsClockwise = StartLocsAreClockwise(startLocs); + for (int j = 0; j < 4; j++) { - if (pathBounds_.Contains(rect_) && - Path1ContainsPath2(path, rectPath_)) - { - bool startLocsClockwise = StartLocsAreClockwise(startLocs); - for (int j = 0; j < 4; j++) - { - int k = startLocsClockwise ? j : 3 - j; // ie reverse result path - Add(rectPath_[k]); - AddToEdge(edges_[k * 2], results_[0]!); - } - } + int k = startLocsClockwise ? j : 3 - j; // ie reverse result path + Add(rectPath_[k]); + AddToEdge(edges_[k * 2], results_[0]!); } } else if (loc != Location.inside && @@ -680,7 +660,7 @@ public Paths64 Execute(Paths64 paths) pathBounds_ = Clipper.GetBounds(path); if (!rect_.Intersects(pathBounds_)) continue; // the path must be completely outside fRect - else if (rect_.Contains(pathBounds_)) + if (rect_.Contains(pathBounds_)) { // the path must be completely inside rect_ result.Add(path); @@ -749,13 +729,11 @@ private void CheckEdges() uint combinedSet = (edgeSet1 & edgeSet2); for (int j = 0; j < 4; ++j) { - if ((combinedSet & (1 << j)) != 0) - { - if (IsHeadingClockwise(op2.prev!.pt, op2.pt, j)) - AddToEdge(edges_[j * 2], op2); - else - AddToEdge(edges_[j * 2 + 1], op2); - } + if ((combinedSet & (1 << j)) == 0) continue; + if (IsHeadingClockwise(op2.prev!.pt, op2.pt, j)) + AddToEdge(edges_[j * 2], op2); + else + AddToEdge(edges_[j * 2 + 1], op2); } } edgeSet1 = edgeSet2; @@ -770,11 +748,10 @@ private void TidyEdgePair(int idx, List cw, List ccw) bool isHorz = ((idx == 1) || (idx == 3)); bool cwIsTowardLarger = ((idx == 1) || (idx == 2)); int i = 0, j = 0; - OutPt2? p1, p2, p1a, p2a, op, op2; while (i < cw.Count) { - p1 = cw[i]; + OutPt2? p1 = cw[i]; if (p1 == null || p1.next == p1.prev) { cw[i++] = null; @@ -793,6 +770,9 @@ private void TidyEdgePair(int idx, List cw, List ccw) continue; } + OutPt2? p2; + OutPt2? p1a; + OutPt2? p2a; if (cwIsTowardLarger) { // p1 >>>> p1a; @@ -855,6 +835,8 @@ private void TidyEdgePair(int idx, List cw, List ccw) SetNewOwner(p1a, new_idx); } + OutPt2? op; + OutPt2? op2; if (cwIsTowardLarger) { op = p2; @@ -942,7 +924,7 @@ private void TidyEdgePair(int idx, List cw, List ccw) } } - private Path64 GetPath(OutPt2? op) + private static Path64 GetPath(OutPt2? op) { Path64 result = new Path64(); if (op == null || op.prev == op.next) return result; @@ -1005,7 +987,7 @@ internal RectClipLines64(Rect64 rect) : base(rect) { } return result; } - private Path64 GetPath(OutPt2? op) + private static Path64 GetPath(OutPt2? op) { Path64 result = new Path64(); if (op == null || op == op.next) return result; diff --git a/CSharp/Clipper2Lib/Clipper.cs b/CSharp/Clipper2Lib/Clipper.cs index bb15959b..44bbce4a 100644 --- a/CSharp/Clipper2Lib/Clipper.cs +++ b/CSharp/Clipper2Lib/Clipper.cs @@ -813,21 +813,31 @@ public static double PerpendicDistFromLineSqrd(Point64 pt, Point64 line1, Point6 internal static void RDP(Path64 path, int begin, int end, double epsSqrd, List flags) { - int idx = 0; - double max_d = 0; - while (end > begin && path[begin] == path[end]) flags[end--] = false; - for (int i = begin + 1; i < end; ++i) + while (true) { - // PerpendicDistFromLineSqrd - avoids expensive Sqrt() - double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]); - if (d <= max_d) continue; - max_d = d; - idx = i; + int idx = 0; + double max_d = 0; + while (end > begin && path[begin] == path[end]) flags[end--] = false; + for (int i = begin + 1; i < end; ++i) + { + // PerpendicDistFromLineSqrd - avoids expensive Sqrt() + double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]); + if (d <= max_d) continue; + max_d = d; + idx = i; + } + + if (max_d <= epsSqrd) return; + flags[idx] = true; + if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags); + if (idx < end - 1) + { + begin = idx; + continue; + } + + break; } - if (max_d <= epsSqrd) return; - flags[idx] = true; - if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags); - if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags); } public static Path64 RamerDouglasPeucker(Path64 path, double epsilon) @@ -852,21 +862,31 @@ public static Paths64 RamerDouglasPeucker(Paths64 paths, double epsilon) internal static void RDP(PathD path, int begin, int end, double epsSqrd, List flags) { - int idx = 0; - double max_d = 0; - while (end > begin && path[begin] == path[end]) flags[end--] = false; - for (int i = begin + 1; i < end; ++i) + while (true) { - // PerpendicDistFromLineSqrd - avoids expensive Sqrt() - double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]); - if (d <= max_d) continue; - max_d = d; - idx = i; + int idx = 0; + double max_d = 0; + while (end > begin && path[begin] == path[end]) flags[end--] = false; + for (int i = begin + 1; i < end; ++i) + { + // PerpendicDistFromLineSqrd - avoids expensive Sqrt() + double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]); + if (d <= max_d) continue; + max_d = d; + idx = i; + } + + if (max_d <= epsSqrd) return; + flags[idx] = true; + if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags); + if (idx < end - 1) + { + begin = idx; + continue; + } + + break; } - if (max_d <= epsSqrd) return; - flags[idx] = true; - if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags); - if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags); } public static PathD RamerDouglasPeucker(PathD path, double epsilon) @@ -921,7 +941,7 @@ public static Path64 SimplifyPath(Path64 path, bool[] flags = new bool[len]; double[] dsq = new double[len]; - int curr = 0, prev, start, next, prior2; + int curr = 0; if (isClosedPath) { @@ -941,7 +961,7 @@ public static Path64 SimplifyPath(Path64 path, { if (dsq[curr] > epsSqr) { - start = curr; + int start = curr; do { curr = GetNext(curr, high, ref flags); @@ -949,10 +969,11 @@ public static Path64 SimplifyPath(Path64 path, if (curr == start) break; } - prev = GetPrior(curr, high, ref flags); - next = GetNext(curr, high, ref flags); + int prev = GetPrior(curr, high, ref flags); + int next = GetNext(curr, high, ref flags); if (next == prev) break; + int prior2; if (dsq[next] < dsq[curr]) { prior2 = prev; @@ -995,7 +1016,7 @@ public static PathD SimplifyPath(PathD path, bool[] flags = new bool[len]; double[] dsq = new double[len]; - int curr = 0, prev, start, next, prior2; + int curr = 0; if (isClosedPath) { dsq[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]); @@ -1013,7 +1034,7 @@ public static PathD SimplifyPath(PathD path, { if (dsq[curr] > epsSqr) { - start = curr; + int start = curr; do { curr = GetNext(curr, high, ref flags); @@ -1021,10 +1042,11 @@ public static PathD SimplifyPath(PathD path, if (curr == start) break; } - prev = GetPrior(curr, high, ref flags); - next = GetNext(curr, high, ref flags); + int prev = GetPrior(curr, high, ref flags); + int next = GetNext(curr, high, ref flags); if (next == prev) break; + int prior2; if (dsq[next] < dsq[curr]) { prior2 = prev; @@ -1181,7 +1203,7 @@ private static void ShowPolyPathStructure(PolyPath64 pp, int level) } else { - Console.WriteLine(spaces + caption + string.Format("({0})", pp.Count)); + Console.WriteLine(spaces + caption + $"({pp.Count})"); foreach (PolyPath64 child in pp) { ShowPolyPathStructure(child, level + 1); } } } @@ -1202,7 +1224,7 @@ private static void ShowPolyPathStructure(PolyPathD pp, int level) } else { - Console.WriteLine(spaces + caption + string.Format("({0})", pp.Count)); + Console.WriteLine(spaces + caption + $"({pp.Count})"); foreach (PolyPathD child in pp) { ShowPolyPathStructure(child, level + 1); } } } diff --git a/CSharp/Clipper2Lib/HashCode.cs b/CSharp/Clipper2Lib/HashCode.cs index dd209738..62889210 100644 --- a/CSharp/Clipper2Lib/HashCode.cs +++ b/CSharp/Clipper2Lib/HashCode.cs @@ -50,7 +50,7 @@ public struct HashCode { private static readonly uint s_seed = GenerateGlobalSeed(); - private const uint Prime1 = 2654435761U; + // private const uint Prime1 = 2654435761U; private const uint Prime2 = 2246822519U; private const uint Prime3 = 3266489917U; private const uint Prime4 = 668265263U; diff --git a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs index 6c49c7a8..f5cdc471 100644 --- a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs +++ b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs @@ -20,17 +20,16 @@ public static Paths64 PathFromStr(string? s) if (s == null) return new Paths64(); Path64 p = new Path64(); Paths64 pp = new Paths64(); - int len = s.Length, i = 0, j; + int len = s.Length, i = 0; while (i < len) { - bool isNeg; while (s[i] < 33 && i < len) i++; if (i >= len) break; //get X ... - isNeg = s[i] == 45; + bool isNeg = s[i] == 45; if (isNeg) i++; if (i >= len || s[i] < 48 || s[i] > 57) break; - j = i + 1; + int j = i + 1; while (j < len && s[j] > 47 && s[j] < 58) j++; if (!long.TryParse(s.Substring(i, j - i), out long x)) break; if (isNeg) x = -x; @@ -80,7 +79,6 @@ public static bool LoadTestNum(string filename, int num, ct = ClipType.Intersection; fillRule = FillRule.EvenOdd; bool result = false; - int GetIdx; if (num < 1) num = 1; caption = ""; area = 0; @@ -141,6 +139,7 @@ public static bool LoadTestNum(string filename, int num, continue; } + int GetIdx; if (s.IndexOf("SUBJECTS_OPEN", StringComparison.Ordinal) == 0) GetIdx = 2; else if (s.IndexOf("SUBJECTS", StringComparison.Ordinal) == 0) GetIdx = 1; else if (s.IndexOf("CLIPS", StringComparison.Ordinal) == 0) GetIdx = 3; @@ -159,9 +158,18 @@ public static bool LoadTestNum(string filename, int num, else return result; continue; } - if (GetIdx == 1) subj.Add(paths[0]); - else if (GetIdx == 2) subj_open.Add(paths[0]); - else clip.Add(paths[0]); + switch (GetIdx) + { + case 1: + subj.Add(paths[0]); + break; + case 2: + subj_open.Add(paths[0]); + break; + default: + clip.Add(paths[0]); + break; + } } } return result; diff --git a/CSharp/Utils/SVG/Clipper.SVG.cs b/CSharp/Utils/SVG/Clipper.SVG.cs index e3481da0..4d101ba2 100644 --- a/CSharp/Utils/SVG/Clipper.SVG.cs +++ b/CSharp/Utils/SVG/Clipper.SVG.cs @@ -207,9 +207,7 @@ private RectD GetBounds() if (pt.y < bounds.top) bounds.top = pt.y; if (pt.y > bounds.bottom) bounds.bottom = pt.y; } - if (!IsValidRect(bounds)) - return RectEmpty; - return bounds; + return !IsValidRect(bounds) ? RectEmpty : bounds; } private static string ColorToHtml(uint clr) @@ -281,7 +279,7 @@ public bool SaveToFile(string filename, int maxWidth = 0, int maxHeight = 0, int writer.Write(string.Format(NumberFormatInfo.InvariantInfo, svg_path_format2, ColorToHtml(pi.PenClr), GetAlpha(pi.PenClr), pi.PenWidth)); - if (pi.ShowCoords) + if (!pi.ShowCoords) continue; { writer.Write("\n", coordStyle.FontName, coordStyle.FontSize, ColorToHtml(coordStyle.FontColor));