From 2b2effd31ea64051512351460d1d6e320d5de97a Mon Sep 17 00:00:00 2001 From: phil Date: Sat, 28 Sep 2024 10:21:19 -0500 Subject: [PATCH 1/3] Proposed clean-ups This is a partial set of clean-ups. There could be others, but they may be controversial (e.g. adopting LINQ in some areas, which may or may not hurt performance). I've also tried to be mindful of target framework versions and also some edge cases (e.g. hashing) where seemingly redundant chunks of code, if removed or changed for another option, may blow things up. See what you think. --- CSharp/Clipper2Lib.Benchmark/Benchmarks.cs | 6 +- CSharp/Clipper2Lib.Benchmark/Program.cs | 2 +- .../Clipper2Lib.Examples/ConsoleDemo/Main.cs | 62 ++-- .../Clipper2Lib.Examples/InflateDemo/Main.cs | 16 +- CSharp/Clipper2Lib/Clipper.Core.cs | 65 ++-- CSharp/Clipper2Lib/Clipper.Engine.cs | 304 +++++++++--------- CSharp/Clipper2Lib/Clipper.Minkowski.cs | 2 +- CSharp/Clipper2Lib/Clipper.Offset.cs | 114 ++++--- CSharp/Clipper2Lib/Clipper.RectClip.cs | 150 ++++----- CSharp/Clipper2Lib/Clipper.cs | 94 +++--- CSharp/Clipper2Lib/HashCode.cs | 2 +- CSharp/Utils/ClipFileIO/Clipper.FileIO.cs | 24 +- CSharp/Utils/SVG/Clipper.SVG.cs | 6 +- 13 files changed, 421 insertions(+), 426 deletions(-) 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)); From 9c18c1bdd3c7ce31028dbafe3af55f8f17bc15db Mon Sep 17 00:00:00 2001 From: phil Date: Sat, 28 Sep 2024 10:53:34 -0500 Subject: [PATCH 2/3] Adding LINQ on top of the other clean-ups Pre-change: // * Summary * BenchmarkDotNet=v0.12.1, OS=endeavouros AMD Ryzen 7 5800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores .NET Core SDK=8.0.108 [Host] : .NET Core 8.0.8 (CoreCLR 8.0.824.36612, CoreFX 8.0.824.36612), X64 RyuJIT Job-GRIUMJ : .NET Core 8.0.8 (CoreCLR 8.0.824.36612, CoreFX 8.0.824.36612), X64 RyuJIT IterationCount=1 LaunchCount=1 WarmupCount=1 | Method | EdgeCount | Mean | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | |--------------- |---------- |-----------:|------:|----------:|----------:|----------:|----------:| | Intersection_N | 1000 | 119.9 ms | NA | 1000.0000 | - | - | 9.04 MB | | Intersection_N | 2000 | 542.1 ms | NA | 1000.0000 | - | - | 16.2 MB | | Intersection_N | 3000 | 1,431.1 ms | NA | 5000.0000 | 4000.0000 | 1000.0000 | 37.28 MB | | Intersection_N | 4000 | 3,008.9 ms | NA | 6000.0000 | 5000.0000 | 1000.0000 | 47.41 MB | | Intersection_N | 5000 | 6,173.7 ms | NA | 9000.0000 | 8000.0000 | 1000.0000 | 74.05 MB | // * Legends * EdgeCount : Value of the 'EdgeCount' parameter Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval Gen 0 : GC Generation 0 collects per 1000 operations Gen 1 : GC Generation 1 collects per 1000 operations Gen 2 : GC Generation 2 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 ms : 1 Millisecond (0.001 sec) // * Diagnostic Output - MemoryDiagnoser * // ***** BenchmarkRunner: End ***** // ** Remained 0 benchmark(s) to run ** Run time: 00:00:47 (47.83 sec), executed benchmarks: 5 Global total time: 00:00:57 (57.28 sec), executed benchmarks: 5 Post-change: // * Summary * BenchmarkDotNet=v0.12.1, OS=endeavouros AMD Ryzen 7 5800H with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores .NET Core SDK=8.0.108 [Host] : .NET Core 8.0.8 (CoreCLR 8.0.824.36612, CoreFX 8.0.824.36612), X64 RyuJIT Job-WWTCPL : .NET Core 8.0.8 (CoreCLR 8.0.824.36612, CoreFX 8.0.824.36612), X64 RyuJIT IterationCount=1 LaunchCount=1 WarmupCount=1 | Method | EdgeCount | Mean | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | |--------------- |---------- |-----------:|------:|----------:|----------:|----------:|----------:| | Intersection_N | 1000 | 127.1 ms | NA | - | - | - | 6.85 MB | | Intersection_N | 2000 | 475.1 ms | NA | 2000.0000 | 1000.0000 | - | 23.04 MB | | Intersection_N | 3000 | 1,408.1 ms | NA | 5000.0000 | 4000.0000 | 1000.0000 | 36.15 MB | | Intersection_N | 4000 | 2,874.6 ms | NA | 5000.0000 | 4000.0000 | 1000.0000 | 41.76 MB | | Intersection_N | 5000 | 5,954.3 ms | NA | 9000.0000 | 8000.0000 | 2000.0000 | 68.62 MB | // * Legends * EdgeCount : Value of the 'EdgeCount' parameter Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval Gen 0 : GC Generation 0 collects per 1000 operations Gen 1 : GC Generation 1 collects per 1000 operations Gen 2 : GC Generation 2 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 ms : 1 Millisecond (0.001 sec) // * Diagnostic Output - MemoryDiagnoser * // ***** BenchmarkRunner: End ***** // ** Remained 0 benchmark(s) to run ** Run time: 00:00:46 (46.34 sec), executed benchmarks: 5 Global total time: 00:00:51 (51.28 sec), executed benchmarks: 5 --- .../Clipper2Lib.Benchmark.csproj | 2 +- CSharp/Clipper2Lib/Clipper.Core.cs | 19 +-- CSharp/Clipper2Lib/Clipper.Engine.cs | 42 ++---- CSharp/Clipper2Lib/Clipper.Minkowski.cs | 7 +- CSharp/Clipper2Lib/Clipper.Offset.cs | 27 ++-- CSharp/Clipper2Lib/Clipper.RectClip.cs | 28 ++-- CSharp/Clipper2Lib/Clipper.cs | 128 ++++++------------ CSharp/Utils/ClipFileIO/Clipper.FileIO.cs | 4 +- CSharp/Utils/SVG/Clipper.SVG.cs | 29 ++-- 9 files changed, 96 insertions(+), 190 deletions(-) diff --git a/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj b/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj index 8ab16403..f58485b9 100644 --- a/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj +++ b/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 Exe diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index af6d2c73..3b8e18b1 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -10,6 +10,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -478,9 +479,7 @@ public Path64(int capacity = 0) : base(capacity) { } public Path64(IEnumerable path) : base(path) { } public override string ToString() { - string s = ""; - foreach (Point64 p in this) - s = s + p.ToString() + ", "; + string s = this.Aggregate("", (current, p) => current + p.ToString() + ", "); if (s != "") s = s.Remove(s.Length - 2); return s; } @@ -493,10 +492,7 @@ public Paths64(int capacity = 0) : base(capacity) { } public Paths64(IEnumerable paths) : base(paths) { } public override string ToString() { - string s = ""; - foreach (Path64 p in this) - s = s + p + "\n"; - return s; + return this.Aggregate("", (current, p) => current + p + "\n"); } } @@ -507,9 +503,7 @@ public PathD(int capacity = 0) : base(capacity) { } public PathD(IEnumerable path) : base(path) { } public string ToString(int precision = 2) { - string s = ""; - foreach (PointD p in this) - s = s + p.ToString(precision) + ", "; + string s = this.Aggregate("", (current, p) => current + p.ToString(precision) + ", "); if (s != "") s = s.Remove(s.Length - 2); return s; } @@ -522,10 +516,7 @@ public PathsD(int capacity = 0) : base(capacity) { } public PathsD(IEnumerable paths) : base(paths) { } public string ToString(int precision = 2) { - string s = ""; - foreach (PathD p in this) - s = s + p.ToString(precision) + "\n"; - return s; + return this.Aggregate("", (current, p) => current + p.ToString(precision) + "\n"); } } diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index ec3bd49d..c1dc41df 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -13,6 +13,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -235,8 +236,7 @@ internal static void EnsureCapacity(this List list, int minCapacity) internal static void AddPathsToVertexList(Paths64 paths, PathType polytype, bool isOpen, List minimaList, List vertexList) { - int totalVertCnt = 0; - foreach (Path64 path in paths) totalVertCnt += path.Count; + int totalVertCnt = paths.Sum(path => path.Count); vertexList.EnsureCapacity(vertexList.Count + totalVertCnt); foreach (Path64 path in paths) @@ -2100,7 +2100,7 @@ private void AddToHorzSegList(OutPt op) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private OutPt GetLastOp(Active hotEdge) + private static OutPt GetLastOp(Active hotEdge) { OutRec outrec = hotEdge.outrec!; return (hotEdge == outrec.frontEdge) ? @@ -2514,7 +2514,7 @@ private static OutPt DuplicateOp(OutPt op, bool insert_after) return result; } - private int HorzSegSort(HorzSegment? hs1, HorzSegment? hs2) + private static int HorzSegSort(HorzSegment? hs1, HorzSegment? hs2) { if (hs1 == null || hs2 == null) return 0; if (hs1.rightOp == null) @@ -2528,9 +2528,7 @@ private int HorzSegSort(HorzSegment? hs1, HorzSegment? hs2) private void ConvertHorzSegsToJoins() { - int k = 0; - foreach (HorzSegment hs in _horzSegList) - if (UpdateHorzSegment(hs)) k++; + int k = _horzSegList.Count(UpdateHorzSegment); if (k < 2) return; _horzSegList.Sort(HorzSegSort); @@ -3034,10 +3032,8 @@ private bool CheckBounds(OutRec outrec) private bool CheckSplitOwner(OutRec outrec, List? splits) { - foreach (int i in splits!) + foreach (OutRec? split in splits!.Select(i => GetRealOutRec(_outrecList[i])).OfType().Where(split => split != outrec && split.recursiveSplit != outrec)) { - OutRec? split = GetRealOutRec(_outrecList[i]); - 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) || @@ -3047,6 +3043,7 @@ private bool CheckSplitOwner(OutRec outrec, List? splits) outrec.owner = split; //found in split return true; } + return false; } private void RecursiveCheckOwners(OutRec outrec, PolyPathBase polypath) @@ -3346,11 +3343,9 @@ public bool Execute(ClipType clipType, FillRule fillRule, if (!success) return false; solutionClosed.EnsureCapacity(solClosed64.Count); - foreach (Path64 path in solClosed64) - solutionClosed.Add(Clipper.ScalePathD(path, _invScale)); + solutionClosed.AddRange(solClosed64.Select(path => Clipper.ScalePathD(path, _invScale))); solutionOpen.EnsureCapacity(solOpen64.Count); - foreach (Path64 path in solOpen64) - solutionOpen.Add(Clipper.ScalePathD(path, _invScale)); + solutionOpen.AddRange(solOpen64.Select(path => Clipper.ScalePathD(path, _invScale))); return true; } @@ -3385,8 +3380,7 @@ public bool Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, Pa if (!success) return false; if (oPaths.Count <= 0) return true; openPaths.EnsureCapacity(oPaths.Count); - foreach (Path64 path in oPaths) - openPaths.Add(Clipper.ScalePathD(path, _invScale)); + openPaths.AddRange(oPaths.Select(path => Clipper.ScalePathD(path, _invScale))); return true; } @@ -3540,13 +3534,7 @@ public PolyPath64 Child(int index) [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Area() { - double result = Polygon == null ? 0 : Clipper.Area(Polygon); - foreach (PolyPathBase polyPathBase in _childs) - { - PolyPath64 child = (PolyPath64) polyPathBase; - result += child.Area(); - } - return result; + return (Polygon == null ? 0 : Clipper.Area(Polygon)) + _childs.Cast().Sum(child => child.Area()); } } public class PolyPathD : PolyPathBase @@ -3589,13 +3577,7 @@ public PolyPathD this[int index] [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Area() { - double result = Polygon == null ? 0 : Clipper.Area(Polygon); - foreach (PolyPathBase polyPathBase in _childs) - { - PolyPathD child = (PolyPathD) polyPathBase; - result += child.Area(); - } - return result; + return (Polygon == null ? 0 : Clipper.Area(Polygon)) + _childs.Cast().Sum(child => child.Area()); } } diff --git a/CSharp/Clipper2Lib/Clipper.Minkowski.cs b/CSharp/Clipper2Lib/Clipper.Minkowski.cs index 26206422..91412b14 100644 --- a/CSharp/Clipper2Lib/Clipper.Minkowski.cs +++ b/CSharp/Clipper2Lib/Clipper.Minkowski.cs @@ -9,6 +9,7 @@ #nullable enable using System; +using System.Linq; namespace Clipper2Lib { @@ -25,13 +26,11 @@ private static Paths64 MinkowskiInternal(Path64 pattern, Path64 path, bool isSum Path64 path2 = new Path64(patLen); if (isSum) { - foreach (Point64 basePt in pattern) - path2.Add(pathPt + basePt); + path2.AddRange(pattern.Select(basePt => pathPt + basePt)); } else { - foreach (Point64 basePt in pattern) - path2.Add(pathPt - basePt); + path2.AddRange(pattern.Select(basePt => pathPt - basePt)); } tmp.Add(path2); } diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 8741322a..11d6dc01 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -140,22 +141,12 @@ public void AddPaths(Paths64 paths, JoinType joinType, EndType endType) private int CalcSolutionCapacity() { - int result = 0; - foreach (Group g in _groupList) - result += (g.endType == EndType.Joined) ? g.inPaths.Count * 2 : g.inPaths.Count; - return result; + return _groupList.Sum(g => (g.endType == EndType.Joined) ? g.inPaths.Count * 2 : g.inPaths.Count); } internal bool CheckPathsReversed() { - bool result = false; - foreach (Group g in _groupList) - if (g.endType == EndType.Polygon) - { - result = g.pathsReversed; - break; - } - return result; + return (from g in _groupList where g.endType == EndType.Polygon select g.pathsReversed).FirstOrDefault(); } private void ExecuteInternal(double delta) @@ -166,9 +157,8 @@ private void ExecuteInternal(double delta) // make sure the offset delta is significant if (Math.Abs(delta) < 0.5) { - foreach (Group group in _groupList) - foreach (Path64 path in group.inPaths) - _solution.Add(path); + foreach (Path64 path in _groupList.SelectMany(group => group.inPaths)) + _solution.Add(path); return; } @@ -240,10 +230,9 @@ internal static int GetLowestPathIdx(Paths64 paths) Point64 botPt = new Point64(long.MaxValue, long.MinValue); for (int i = 0; i < paths.Count; ++i) { - foreach (Point64 pt in paths[i]) - { - if ((pt.Y < botPt.Y) || - ((pt.Y == botPt.Y) && (pt.X >= botPt.X))) continue; + foreach (Point64 pt in paths[i].Where(pt => (pt.Y >= botPt.Y) && + ((pt.Y != botPt.Y) || (pt.X < botPt.X)))) + { result = i; botPt.X = pt.X; botPt.Y = pt.Y; diff --git a/CSharp/Clipper2Lib/Clipper.RectClip.cs b/CSharp/Clipper2Lib/Clipper.RectClip.cs index 27f13e27..ff10c0b1 100644 --- a/CSharp/Clipper2Lib/Clipper.RectClip.cs +++ b/CSharp/Clipper2Lib/Clipper.RectClip.cs @@ -10,6 +10,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -91,10 +92,8 @@ private static bool Path1ContainsPath2(Path64 path1, Path64 path2) // nb: occasionally, due to rounding, path1 may // appear (momentarily) inside or outside path2. int ioCount = 0; - foreach (Point64 pt in path2) + foreach (PointInPolygonResult pip in path2.Select(pt => InternalClipper.PointInPolygon(pt, path1))) { - PointInPolygonResult pip = - InternalClipper.PointInPolygon(pt, path1); switch(pip) { case PointInPolygonResult.IsInside: @@ -482,7 +481,7 @@ protected void GetNextLocation(Path64 path, } // switch } - private bool StartLocsAreClockwise(List startLocs) + private static bool StartLocsAreClockwise(List startLocs) { int result = 0; for (int i = 1; i < startLocs.Count; i++) @@ -637,9 +636,8 @@ private void ExecuteInternal(Path64 path) if (startLocs.Count > 0) { prev = loc; - foreach (Location loc2 in startLocs) + foreach (Location loc2 in startLocs.Where(loc2 => prev != loc2)) { - if (prev == loc2) continue; AddCorner(ref prev, HeadingClockwise(prev, loc2)); prev = loc2; } @@ -654,9 +652,8 @@ public Paths64 Execute(Paths64 paths) { Paths64 result = new Paths64(); if (rect_.IsEmpty()) return result; - foreach (Path64 path in paths) + foreach (Path64 path in paths.Where(path => path.Count >= 3)) { - if (path.Count < 3) continue; pathBounds_ = Clipper.GetBounds(path); if (!rect_.Intersects(pathBounds_)) continue; // the path must be completely outside fRect @@ -671,11 +668,7 @@ public Paths64 Execute(Paths64 paths) for (int i = 0; i < 4; ++i) TidyEdgePair(i, edges_[i * 2], edges_[i * 2 + 1]); - foreach (OutPt2? op in results_) - { - Path64 tmp = GetPath(op); - if (tmp.Count > 0) result.Add(tmp); - } + result.AddRange(results_.Select(GetPath).Where(tmp => tmp.Count > 0)); //clean up after every loop results_.Clear(); @@ -962,9 +955,8 @@ internal RectClipLines64(Rect64 rect) : base(rect) { } { Paths64 result = new Paths64(); if (rect_.IsEmpty()) return result; - foreach (Path64 path in paths) + foreach (Path64 path in paths.Where(path => path.Count >= 2)) { - if (path.Count < 2) continue; pathBounds_ = Clipper.GetBounds(path); if (!rect_.Intersects(pathBounds_)) continue; // the path must be completely outside fRect @@ -973,11 +965,7 @@ internal RectClipLines64(Rect64 rect) : base(rect) { } // fRect, simply by comparing path bounds with fRect. ExecuteInternal(path); - foreach (OutPt2? op in results_) - { - Path64 tmp = GetPath(op); - if (tmp.Count > 0) result.Add(tmp); - } + result.AddRange(results_.Select(GetPath).Where(tmp => tmp.Count > 0)); //clean up after every loop results_.Clear(); diff --git a/CSharp/Clipper2Lib/Clipper.cs b/CSharp/Clipper2Lib/Clipper.cs index 44bbce4a..b72f4bb1 100644 --- a/CSharp/Clipper2Lib/Clipper.cs +++ b/CSharp/Clipper2Lib/Clipper.cs @@ -14,6 +14,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -260,10 +261,7 @@ public static double Area(Path64 path) public static double Area(Paths64 paths) { - double a = 0.0; - foreach (Path64 path in paths) - a += Area(path); - return a; + return paths.Sum(Area); } public static double Area(PathD path) @@ -282,10 +280,7 @@ public static double Area(PathD path) public static double Area(PathsD paths) { - double a = 0.0; - foreach (PathD path in paths) - a += Area(path); - return a; + return paths.Sum(Area); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -302,37 +297,26 @@ public static bool IsPositive(PathD poly) public static string Path64ToString(Path64 path) { - string result = ""; - foreach (Point64 pt in path) - result += pt.ToString(); + string result = path.Aggregate("", (current, pt) => current + pt.ToString()); return result + '\n'; } public static string Paths64ToString(Paths64 paths) { - string result = ""; - foreach (Path64 path in paths) - result += Path64ToString(path); - return result; + return paths.Aggregate("", (current, path) => current + Path64ToString(path)); } public static string PathDToString(PathD path) { - string result = ""; - foreach (PointD pt in path) - result += pt.ToString(); + string result = path.Aggregate("", (current, pt) => current + pt.ToString()); return result + '\n'; } public static string PathsDToString(PathsD paths) { - string result = ""; - foreach (PathD path in paths) - result += PathDToString(path); - return result; + return paths.Aggregate("", (current, path) => current + PathDToString(path)); } public static Path64 OffsetPath(Path64 path, long dx, long dy) { Path64 result = new Path64(path.Count); - foreach (Point64 pt in path) - result.Add(new Point64(pt.X + dx, pt.Y + dy)); + result.AddRange(path.Select(pt => new Point64(pt.X + dx, pt.Y + dy))); return result; } @@ -382,12 +366,11 @@ public static Path64 ScalePath(Path64 path, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return path; Path64 result = new Path64(path.Count); + result.AddRange(path.Select(pt => new Point64(pt.X * scale, pt.Y * scale))); #if USINGZ foreach (Point64 pt in path) result.Add(new Point64(pt.X * scale, pt.Y * scale, pt.Z)); #else - foreach (Point64 pt in path) - result.Add(new Point64(pt.X * scale, pt.Y * scale)); #endif return result; } @@ -396,8 +379,7 @@ public static Paths64 ScalePaths(Paths64 paths, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return paths; Paths64 result = new Paths64(paths.Count); - foreach (Path64 path in paths) - result.Add(ScalePath(path, scale)); + result.AddRange(paths.Select(path => ScalePath(path, scale))); return result; } @@ -405,8 +387,7 @@ public static PathD ScalePath(PathD path, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return path; PathD result = new PathD(path.Count); - foreach (PointD pt in path) - result.Add(new PointD(pt, scale)); + result.AddRange(path.Select(pt => new PointD(pt, scale))); return result; } @@ -414,8 +395,7 @@ public static PathsD ScalePaths(PathsD paths, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return paths; PathsD result = new PathsD(paths.Count); - foreach (PathD path in paths) - result.Add(ScalePath(path, scale)); + result.AddRange(paths.Select(path => ScalePath(path, scale))); return result; } @@ -424,8 +404,7 @@ public static Path64 ScalePath64(PathD path, double scale) { int cnt = path.Count; Path64 res = new Path64(cnt); - foreach (PointD pt in path) - res.Add(new Point64(pt, scale)); + res.AddRange(path.Select(pt => new Point64(pt, scale))); return res; } @@ -433,8 +412,7 @@ public static Paths64 ScalePaths64(PathsD paths, double scale) { int cnt = paths.Count; Paths64 res = new Paths64(cnt); - foreach (PathD path in paths) - res.Add(ScalePath64(path, scale)); + res.AddRange(paths.Select(path => ScalePath64(path, scale))); return res; } @@ -442,8 +420,7 @@ public static PathD ScalePathD(Path64 path, double scale) { int cnt = path.Count; PathD res = new PathD(cnt); - foreach (Point64 pt in path) - res.Add(new PointD(pt, scale)); + res.AddRange(path.Select(pt => new PointD(pt, scale))); return res; } @@ -451,8 +428,7 @@ public static PathsD ScalePathsD(Paths64 paths, double scale) { int cnt = paths.Count; PathsD res = new PathsD(cnt); - foreach (Path64 path in paths) - res.Add(ScalePathD(path, scale)); + res.AddRange(paths.Select(path => ScalePathD(path, scale))); return res; } @@ -460,64 +436,56 @@ public static PathsD ScalePathsD(Paths64 paths, double scale) public static Path64 Path64(PathD path) { Path64 result = new Path64(path.Count); - foreach (PointD pt in path) - result.Add(new Point64(pt)); + result.AddRange(path.Select(pt => new Point64(pt))); return result; } public static Paths64 Paths64(PathsD paths) { Paths64 result = new Paths64(paths.Count); - foreach (PathD path in paths) - result.Add(Path64(path)); + result.AddRange(paths.Select(Path64)); return result; } public static PathsD PathsD(Paths64 paths) { PathsD result = new PathsD(paths.Count); - foreach (Path64 path in paths) - result.Add(PathD(path)); + result.AddRange(paths.Select(PathD)); return result; } public static PathD PathD(Path64 path) { PathD result = new PathD(path.Count); - foreach (Point64 pt in path) - result.Add(new PointD(pt)); + result.AddRange(path.Select(pt => new PointD(pt))); return result; } public static Path64 TranslatePath(Path64 path, long dx, long dy) { Path64 result = new Path64(path.Count); - foreach (Point64 pt in path) - result.Add(new Point64(pt.X + dx, pt.Y + dy)); + result.AddRange(path.Select(pt => new Point64(pt.X + dx, pt.Y + dy))); return result; } public static Paths64 TranslatePaths(Paths64 paths, long dx, long dy) { Paths64 result = new Paths64(paths.Count); - foreach (Path64 path in paths) - result.Add(OffsetPath(path, dx, dy)); + result.AddRange(paths.Select(path => OffsetPath(path, dx, dy))); return result; } public static PathD TranslatePath(PathD path, double dx, double dy) { PathD result = new PathD(path.Count); - foreach (PointD pt in path) - result.Add(new PointD(pt.x + dx, pt.y + dy)); + result.AddRange(path.Select(pt => new PointD(pt.x + dx, pt.y + dy))); return result; } public static PathsD TranslatePaths(PathsD paths, double dx, double dy) { PathsD result = new PathsD(paths.Count); - foreach (PathD path in paths) - result.Add(TranslatePath(path, dx, dy)); + result.AddRange(paths.Select(path => TranslatePath(path, dx, dy))); return result; } @@ -538,8 +506,7 @@ public static PathD ReversePath(PathD path) public static Paths64 ReversePaths(Paths64 paths) { Paths64 result = new Paths64(paths.Count); - foreach (Path64 t in paths) - result.Add(ReversePath(t)); + result.AddRange(paths.Select(ReversePath)); return result; } @@ -547,8 +514,7 @@ public static Paths64 ReversePaths(Paths64 paths) public static PathsD ReversePaths(PathsD paths) { PathsD result = new PathsD(paths.Count); - foreach (PathD path in paths) - result.Add(ReversePath(path)); + result.AddRange(paths.Select(ReversePath)); return result; } @@ -568,14 +534,13 @@ public static Rect64 GetBounds(Path64 path) public static Rect64 GetBounds(Paths64 paths) { Rect64 result = InvalidRect64; - foreach (Path64 path in paths) - foreach (Point64 pt in path) - { - if (pt.X < result.left) result.left = pt.X; - if (pt.X > result.right) result.right = pt.X; - if (pt.Y < result.top) result.top = pt.Y; - if (pt.Y > result.bottom) result.bottom = pt.Y; - } + foreach (Point64 pt in paths.SelectMany(path => path)) + { + if (pt.X < result.left) result.left = pt.X; + if (pt.X > result.right) result.right = pt.X; + if (pt.Y < result.top) result.top = pt.Y; + if (pt.Y > result.bottom) result.bottom = pt.Y; + } return result.left == long.MaxValue ? new Rect64() : result; } @@ -595,14 +560,13 @@ public static RectD GetBounds(PathD path) public static RectD GetBounds(PathsD paths) { RectD result = InvalidRectD; - foreach (PathD path in paths) - foreach (PointD pt in path) - { - if (pt.x < result.left) result.left = pt.x; - if (pt.x > result.right) result.right = pt.x; - if (pt.y < result.top) result.top = pt.y; - if (pt.y > result.bottom) result.bottom = pt.y; - } + foreach (PointD pt in paths.SelectMany(path => path)) + { + if (pt.x < result.left) result.left = pt.x; + if (pt.x > result.right) result.right = pt.x; + if (pt.y < result.top) result.top = pt.y; + if (pt.y > result.bottom) result.bottom = pt.y; + } return Math.Abs(result.left - double.MaxValue) < InternalClipper.floatingPointTolerance ? new RectD() : result; } @@ -855,8 +819,7 @@ public static Path64 RamerDouglasPeucker(Path64 path, double epsilon) public static Paths64 RamerDouglasPeucker(Paths64 paths, double epsilon) { Paths64 result = new Paths64(paths.Count); - foreach (Path64 path in paths) - result.Add(RamerDouglasPeucker(path, epsilon)); + result.AddRange(paths.Select(path => RamerDouglasPeucker(path, epsilon))); return result; } @@ -904,8 +867,7 @@ public static PathD RamerDouglasPeucker(PathD path, double epsilon) public static PathsD RamerDouglasPeucker(PathsD paths, double epsilon) { PathsD result = new PathsD(paths.Count); - foreach (PathD path in paths) - result.Add(RamerDouglasPeucker(path, epsilon)); + result.AddRange(paths.Select(path => RamerDouglasPeucker(path, epsilon))); return result; } @@ -1002,8 +964,7 @@ public static Paths64 SimplifyPaths(Paths64 paths, double epsilon, bool isClosedPaths = true) { Paths64 result = new Paths64(paths.Count); - foreach (Path64 path in paths) - result.Add(SimplifyPath(path, epsilon, isClosedPaths)); + result.AddRange(paths.Select(path => SimplifyPath(path, epsilon, isClosedPaths))); return result; } @@ -1075,8 +1036,7 @@ public static PathsD SimplifyPaths(PathsD paths, double epsilon, bool isClosedPath = true) { PathsD result = new PathsD(paths.Count); - foreach (PathD path in paths) - result.Add(SimplifyPath(path, epsilon, isClosedPath)); + result.AddRange(paths.Select(path => SimplifyPath(path, epsilon, isClosedPath))); return result; } diff --git a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs index f5cdc471..5fef3ad9 100644 --- a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs +++ b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs @@ -9,6 +9,7 @@ using System; using System.IO; using System.Diagnostics; +using System.Linq; namespace Clipper2Lib { @@ -264,8 +265,7 @@ public static Paths64 AffineTranslatePaths(Paths64 paths, long dx, long dy) foreach (Path64 path in paths) { Path64 p = new Path64(path.Count); - foreach (Point64 pt in path) - p.Add(new Point64(pt.X + dx, pt.Y + dy)); + p.AddRange(path.Select(pt => new Point64(pt.X + dx, pt.Y + dy))); result.Add(p); } return result; diff --git a/CSharp/Utils/SVG/Clipper.SVG.cs b/CSharp/Utils/SVG/Clipper.SVG.cs index 4d101ba2..188dc2ed 100644 --- a/CSharp/Utils/SVG/Clipper.SVG.cs +++ b/CSharp/Utils/SVG/Clipper.SVG.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; namespace Clipper2Lib { @@ -198,15 +199,13 @@ public void AddText(string cap, double posX, double posY, int fontSize, uint fon private RectD GetBounds() { RectD bounds = new RectD(RectMax); - foreach (PolyInfo pi in PolyInfoList) - foreach (PathD path in pi.paths) - foreach (PointD pt in path) - { - if (pt.x < bounds.left) bounds.left = pt.x; - if (pt.x > bounds.right) bounds.right = pt.x; - if (pt.y < bounds.top) bounds.top = pt.y; - if (pt.y > bounds.bottom) bounds.bottom = pt.y; - } + foreach (PointD pt in from pi in PolyInfoList from path in pi.paths from pt in path select pt) + { + if (pt.x < bounds.left) bounds.left = pt.x; + if (pt.x > bounds.right) bounds.right = pt.x; + if (pt.y < bounds.top) bounds.top = pt.y; + if (pt.y > bounds.bottom) bounds.bottom = pt.y; + } return !IsValidRect(bounds) ? RectEmpty : bounds; } @@ -254,18 +253,16 @@ public bool SaveToFile(string filename, int maxWidth = 0, int maxHeight = 0, int foreach (PolyInfo pi in PolyInfoList) { writer.Write(" path.Count >= 2).Where(path => pi.IsOpen || path.Count >= 3)) { - if (path.Count < 2) continue; - if (!pi.IsOpen && path.Count < 3) continue; writer.Write(string.Format(NumberFormatInfo.InvariantInfo, " M {0:f2} {1:f2}", - (path[0].x * scale + offsetX), - (path[0].y * scale + offsetY))); + (path[0].x * scale + offsetX), + (path[0].y * scale + offsetY))); for (int j = 1; j < path.Count; j++) { writer.Write(string.Format(NumberFormatInfo.InvariantInfo, " L {0:f2} {1:f2}", - (path[j].x * scale + offsetX), - (path[j].y * scale + offsetY))); + (path[j].x * scale + offsetX), + (path[j].y * scale + offsetY))); } if (!pi.IsOpen) writer.Write(" z"); } From 20d2f9c05fe27910d1cc7b60a88bd4e7427cce19 Mon Sep 17 00:00:00 2001 From: phil Date: Sat, 28 Sep 2024 11:00:30 -0500 Subject: [PATCH 3/3] Oops - roll back the net version for this project to work on GitHub --- CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj b/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj index f58485b9..8ab16403 100644 --- a/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj +++ b/CSharp/Clipper2Lib.Benchmark/Clipper2Lib.Benchmark.csproj @@ -1,6 +1,6 @@ - net8.0 + net6.0 Exe