From 623f0a298dbd8d3442327240adc7548dc44b4d56 Mon Sep 17 00:00:00 2001 From: masbug Date: Sun, 23 Jun 2024 14:33:23 +0200 Subject: [PATCH 01/31] Compile warnings cleanup, fixes for sign-conversion issues (#858) * CPP: BencMark compile fix * CPP: Fixed sing-conversion compile warnings, unsigned path indices --- CPP/BenchMark/GetIntersectPtBenchmark.cpp | 2 +- CPP/BenchMark/PointInPolygonBenchmark.cpp | 2 +- CPP/CMakeLists.txt | 2 +- .../include/clipper2/clipper.core.h | 1 + CPP/Clipper2Lib/include/clipper2/clipper.h | 8 ++--- .../include/clipper2/clipper.offset.h | 2 +- .../include/clipper2/clipper.rectclip.h | 4 +-- CPP/Clipper2Lib/src/clipper.engine.cpp | 4 +-- CPP/Clipper2Lib/src/clipper.offset.cpp | 14 ++++---- CPP/Clipper2Lib/src/clipper.rectclip.cpp | 35 +++++++++++-------- 10 files changed, 40 insertions(+), 34 deletions(-) diff --git a/CPP/BenchMark/GetIntersectPtBenchmark.cpp b/CPP/BenchMark/GetIntersectPtBenchmark.cpp index 229bcff1..ac9cef30 100644 --- a/CPP/BenchMark/GetIntersectPtBenchmark.cpp +++ b/CPP/BenchMark/GetIntersectPtBenchmark.cpp @@ -29,7 +29,7 @@ struct SetConsoleTextColor public: SetConsoleTextColor(TextColor color) : _color(color) {}; - static friend std::ostream& operator<< (std::ostream& out, SetConsoleTextColor const& scc) + friend std::ostream& operator<< (std::ostream& out, SetConsoleTextColor const& scc) { return out << "\x1B[" << scc._color << "m"; } diff --git a/CPP/BenchMark/PointInPolygonBenchmark.cpp b/CPP/BenchMark/PointInPolygonBenchmark.cpp index 353ddbd7..33e7a924 100644 --- a/CPP/BenchMark/PointInPolygonBenchmark.cpp +++ b/CPP/BenchMark/PointInPolygonBenchmark.cpp @@ -28,7 +28,7 @@ struct SetConsoleTextColor public: SetConsoleTextColor(ConsoleTextColor color) : _color(color) {}; - static friend std::ostream& operator<< (std::ostream& out, SetConsoleTextColor const& scc) + friend std::ostream& operator<< (std::ostream& out, SetConsoleTextColor const& scc) { return out << "\x1B[" << scc._color << "m"; } diff --git a/CPP/CMakeLists.txt b/CPP/CMakeLists.txt index 1e8b0271..99b656ac 100644 --- a/CPP/CMakeLists.txt +++ b/CPP/CMakeLists.txt @@ -98,7 +98,7 @@ if (NOT (CLIPPER2_USINGZ STREQUAL "OFF")) if (MSVC) target_compile_options(Clipper2Z PRIVATE /W4 /WX) else() - target_compile_options(Clipper2Z PRIVATE -Wall -Wextra -Wpedantic -Werror -Wno-c++20-compat) + target_compile_options(Clipper2Z PRIVATE -Wall -Wextra -Wpedantic -Werror -Wno-c++20-compat -Wsign-conversion) target_link_libraries(Clipper2Z PUBLIC -lm) endif() endif() diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index 8dc40755..925c0468 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "clipper2/clipper.version.h" namespace Clipper2Lib diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.h b/CPP/Clipper2Lib/include/clipper2/clipper.h index 4f55df87..a2fe5c3c 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.h @@ -582,7 +582,7 @@ namespace Clipper2Lib { } template - inline Path Ellipse(const Rect& rect, int steps = 0) + inline Path Ellipse(const Rect& rect, size_t steps = 0) { return Ellipse(rect.MidPoint(), static_cast(rect.Width()) *0.5, @@ -591,12 +591,12 @@ namespace Clipper2Lib { template inline Path Ellipse(const Point& center, - double radiusX, double radiusY = 0, int steps = 0) + double radiusX, double radiusY = 0, size_t steps = 0) { if (radiusX <= 0) return Path(); if (radiusY <= 0) radiusY = radiusX; if (steps <= 2) - steps = static_cast(PI * sqrt((radiusX + radiusY) / 2)); + steps = static_cast(PI * sqrt((radiusX + radiusY) / 2)); double si = std::sin(2 * PI / steps); double co = std::cos(2 * PI / steps); @@ -604,7 +604,7 @@ namespace Clipper2Lib { Path result; result.reserve(steps); result.push_back(Point(center.x + radiusX, static_cast(center.y))); - for (int i = 1; i < steps; ++i) + for (size_t i = 1; i < steps; ++i) { result.push_back(Point(center.x + radiusX * dx, center.y + radiusY * dy)); double x = dx * co - dy * si; diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h index 19ee9676..bb075a6d 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h @@ -34,7 +34,7 @@ class ClipperOffset { class Group { public: Paths64 paths_in; - int lowest_path_idx = -1; + std::optional lowest_path_idx{}; bool is_reversed = false; JoinType join_type; EndType end_type; diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h index ff043f25..a5ea0497 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h @@ -50,9 +50,9 @@ namespace Clipper2Lib OutPt2List edges_[8]; // clockwise and counter-clockwise std::vector start_locs_; void CheckEdges(); - void TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw); + void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw); void GetNextLocation(const Path64& path, - Location& loc, int& i, int highI); + Location& loc, size_t& i, size_t highI); OutPt2* Add(Point64 pt, bool start_new = false); void AddCorner(Location prev, Location curr); void AddCorner(Location& loc, bool isClockwise); diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index de288d7f..8f120267 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -618,9 +618,9 @@ namespace Clipper2Lib { std::vector& vertexLists, LocalMinimaList& locMinList) { const auto total_vertex_count = - std::accumulate(paths.begin(), paths.end(), 0, + std::accumulate(paths.begin(), paths.end(), size_t(0), [](const auto& a, const Path64& path) - {return a + static_cast(path.size()); }); + {return a + path.size(); }); if (total_vertex_count == 0) return; Vertex* vertices = new Vertex[total_vertex_count], * v = vertices; diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index e0959188..508a7f08 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -20,9 +20,9 @@ const double floating_point_tolerance = 1e-12; // Miscellaneous methods //------------------------------------------------------------------------------ -int GetLowestClosedPathIdx(const Paths64& paths) +std::optional GetLowestClosedPathIdx(const Paths64& paths) { - int result = -1; + std::optional result; Point64 botPt = Point64(INT64_MAX, INT64_MIN); for (size_t i = 0; i < paths.size(); ++i) { @@ -30,7 +30,7 @@ int GetLowestClosedPathIdx(const Paths64& paths) { if ((pt.y < botPt.y) || ((pt.y == botPt.y) && (pt.x >= botPt.x))) continue; - result = static_cast(i); + result = i; botPt.x = pt.x; botPt.y = pt.y; } @@ -129,11 +129,11 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType // the lowermost path must be an outer path, so if its orientation is negative, // then flag the whole group is 'reversed' (will negate delta etc.) // as this is much more efficient than reversing every path. - is_reversed = (lowest_path_idx >= 0) && Area(paths_in[lowest_path_idx]) < 0; + is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; } else { - lowest_path_idx = -1; + lowest_path_idx = std::nullopt; is_reversed = false; } } @@ -441,7 +441,7 @@ void ClipperOffset::DoGroupOffset(Group& group) { // a straight path (2 points) can now also be 'polygon' offset // where the ends will be treated as (180 deg.) joins - if (group.lowest_path_idx < 0) delta_ = std::abs(delta_); + if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_); group_delta_ = (group.is_reversed) ? -delta_ : delta_; } else @@ -491,7 +491,7 @@ void ClipperOffset::DoGroupOffset(Group& group) if (group.join_type == JoinType::Round) { double radius = abs_delta; - int steps = static_cast(std::ceil(steps_per_rad_ * 2 * PI)); //#617 + size_t steps = steps_per_rad_ > 0 ? static_cast(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617 path_out = Ellipse(pt, radius, radius, steps); #ifdef USINGZ for (auto& p : path_out) p.z = pt.z; diff --git a/CPP/Clipper2Lib/src/clipper.rectclip.cpp b/CPP/Clipper2Lib/src/clipper.rectclip.cpp index 3fc6fac2..a34971ce 100644 --- a/CPP/Clipper2Lib/src/clipper.rectclip.cpp +++ b/CPP/Clipper2Lib/src/clipper.rectclip.cpp @@ -320,9 +320,9 @@ namespace Clipper2Lib { // this method is only called by InternalExecute. // Later splitting & rejoining won't create additional op's, // though they will change the (non-storage) results_ count. - int curr_idx = static_cast(results_.size()) - 1; + size_t curr_idx = results_.size(); OutPt2* result; - if (curr_idx < 0 || start_new) + if (curr_idx == 0 || start_new) { result = &op_container_.emplace_back(OutPt2()); result->pt = pt; @@ -332,6 +332,7 @@ namespace Clipper2Lib { } else { + --curr_idx; OutPt2* prevOp = results_[curr_idx]; if (prevOp->pt == pt) return prevOp; result = &op_container_.emplace_back(OutPt2()); @@ -349,27 +350,27 @@ namespace Clipper2Lib { void RectClip64::AddCorner(Location prev, Location curr) { if (HeadingClockwise(prev, curr)) - Add(rect_as_path_[static_cast(prev)]); + Add(rect_as_path_[static_cast(prev)]); else - Add(rect_as_path_[static_cast(curr)]); + Add(rect_as_path_[static_cast(curr)]); } void RectClip64::AddCorner(Location& loc, bool isClockwise) { if (isClockwise) { - Add(rect_as_path_[static_cast(loc)]); + Add(rect_as_path_[static_cast(loc)]); loc = GetAdjacentLocation(loc, true); } else { loc = GetAdjacentLocation(loc, false); - Add(rect_as_path_[static_cast(loc)]); + Add(rect_as_path_[static_cast(loc)]); } } void RectClip64::GetNextLocation(const Path64& path, - Location& loc, int& i, int highI) + Location& loc, size_t& i, size_t highI) { switch (loc) { @@ -425,26 +426,30 @@ namespace Clipper2Lib { void RectClip64::ExecuteInternal(const Path64& path) { - int i = 0, highI = static_cast(path.size()) - 1; + if (path.size() < 1) + return; + + size_t highI = path.size() - 1; Location prev = Location::Inside, loc; Location crossing_loc = Location::Inside; Location first_cross_ = Location::Inside; if (!GetLocation(rect_, path[highI], loc)) { - i = highI - 1; - while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i; - if (i < 0) + size_t i = highI; + while (i > 0 && !GetLocation(rect_, path[i - 1], prev)) + --i; + if (i == 0) { // all of path must be inside fRect for (const auto& pt : path) Add(pt); return; } if (prev == Location::Inside) loc = Location::Inside; - i = 0; } Location startingLoc = loc; /////////////////////////////////////////////////// + size_t i = 0; while (i <= highI) { prev = loc; @@ -639,7 +644,7 @@ namespace Clipper2Lib { } } - void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw) + void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw) { if (ccw.empty()) return; bool isHorz = ((idx == 1) || (idx == 3)); @@ -867,7 +872,7 @@ namespace Clipper2Lib { ExecuteInternal(path); CheckEdges(); - for (int i = 0; i < 4; ++i) + for (size_t i = 0; i < 4; ++i) TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]); for (OutPt2*& op : results_) @@ -924,7 +929,7 @@ namespace Clipper2Lib { op_container_ = std::deque(); start_locs_.clear(); - int i = 1, highI = static_cast(path.size()) - 1; + size_t i = 1, highI = path.size() - 1; Location prev = Location::Inside, loc; Location crossing_loc; From 4240912f997bb3f11ca0aa1f02a8cba4a9dd507e Mon Sep 17 00:00:00 2001 From: angusj Date: Fri, 5 Jul 2024 21:24:57 +1000 Subject: [PATCH 02/31] Fixed a minor bug in Clipper.RectClip (#864) --- .../include/clipper2/clipper.engine.h | 32 +++++++++++-------- .../include/clipper2/clipper.rectclip.h | 13 ++++---- CPP/Clipper2Lib/src/clipper.rectclip.cpp | 29 ++++++++++++++--- CPP/Tests/TestRectClip.cpp | 10 ++++++ CSharp/Clipper2Lib/Clipper.RectClip.cs | 25 +++++++++++++-- Delphi/Clipper2Lib/Clipper.RectClip.pas | 30 ++++++++++++++--- 6 files changed, 107 insertions(+), 32 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index a4c8fd82..f6108832 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 April 2024 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -343,6 +343,7 @@ namespace Clipper2Lib { Path64 polygon_; public: explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {} + explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; } ~PolyPath64() { childs_.resize(0); @@ -363,10 +364,7 @@ namespace Clipper2Lib { PolyPath64* AddChild(const Path64& path) override { - auto p = std::make_unique(this); - auto* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = path; - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } void Clear() override @@ -401,6 +399,19 @@ namespace Clipper2Lib { scale_ = parent ? parent->scale_ : 1.0; } + explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + int error_code = 0; + polygon_ = ScalePath(path, scale_, error_code); + } + + explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + polygon_ = path; + } + ~PolyPathD() { childs_.resize(0); } @@ -423,19 +434,12 @@ namespace Clipper2Lib { PolyPathD* AddChild(const Path64& path) override { - int error_code = 0; - auto p = std::make_unique(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = ScalePath(path, scale_, error_code); - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } PolyPathD* AddChild(const PathD& path) { - auto p = std::make_unique(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = path; - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } void Clear() override diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h index a5ea0497..bfcfacf2 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 1 November 2023 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -18,6 +18,7 @@ namespace Clipper2Lib { + // Location: the order is important here, see StartLocsIsClockwise() enum class Location { Left, Top, Right, Bottom, Inside }; class OutPt2; @@ -26,10 +27,10 @@ namespace Clipper2Lib class OutPt2 { public: Point64 pt; - size_t owner_idx; - OutPt2List* edge; - OutPt2* next; - OutPt2* prev; + size_t owner_idx = 0; + OutPt2List* edge = nullptr; + OutPt2* next = nullptr; + OutPt2* prev = nullptr; }; //------------------------------------------------------------------------------ diff --git a/CPP/Clipper2Lib/src/clipper.rectclip.cpp b/CPP/Clipper2Lib/src/clipper.rectclip.cpp index a34971ce..23809b5e 100644 --- a/CPP/Clipper2Lib/src/clipper.rectclip.cpp +++ b/CPP/Clipper2Lib/src/clipper.rectclip.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 27 April 2024 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * @@ -424,6 +424,23 @@ namespace Clipper2Lib { } //switch } + bool StartLocsAreClockwise(const std::vector& startlocs) + { + int result = 0; + for (size_t i = 1; i < startlocs.size(); ++i) + { + int d = static_cast(startlocs[i]) - static_cast(startlocs[i - 1]); + switch (d) + { + case -1: result -= 1; break; + case 1: result += 1; break; + case -3: result += 1; break; + case 3: result -= 1; break; + } + } + return result > 0; + } + void RectClip64::ExecuteInternal(const Path64& path) { if (path.size() < 1) @@ -446,7 +463,7 @@ namespace Clipper2Lib { } if (prev == Location::Inside) loc = Location::Inside; } - Location startingLoc = loc; + Location starting_loc = loc; /////////////////////////////////////////////////// size_t i = 0; @@ -548,7 +565,7 @@ namespace Clipper2Lib { if (first_cross_ == Location::Inside) { // path never intersects - if (startingLoc != Location::Inside) + if (starting_loc != Location::Inside) { // path is outside rect // but being outside, it still may not contain rect @@ -557,11 +574,13 @@ namespace Clipper2Lib { { // yep, the path does fully contain rect // so add rect to the solution + bool is_clockwise_path = StartLocsAreClockwise(start_locs_); for (size_t j = 0; j < 4; ++j) { - Add(rect_as_path_[j]); + size_t k = is_clockwise_path ? j : 3 - j; // reverses result path + Add(rect_as_path_[k]); // we may well need to do some splitting later, so - AddToEdge(edges_[j * 2], results_[0]); + AddToEdge(edges_[k * 2], results_[0]); } } } diff --git a/CPP/Tests/TestRectClip.cpp b/CPP/Tests/TestRectClip.cpp index c320a9bc..4b8972a0 100644 --- a/CPP/Tests/TestRectClip.cpp +++ b/CPP/Tests/TestRectClip.cpp @@ -63,4 +63,14 @@ TEST(Clipper2Tests, TestRectClip3) //#637 solution = RectClip(r, subject); //std::cout << solution << std::endl; EXPECT_TRUE(solution.size() == 1); +} + +TEST(Clipper2Tests, TestRectClipOrientation) //#864 +{ + const Rect64 rect(1222, 1323, 3247, 3348); + const Path64 subject = MakePath({ 375,1680, 1915,4716, 5943,586, 3987,152 }); + RectClip64 clip(rect); + const auto solution = clip.Execute({ subject }); + ASSERT_EQ(solution.size(), 1); + EXPECT_EQ(IsPositive(subject), IsPositive(solution.front())); } \ No newline at end of file diff --git a/CSharp/Clipper2Lib/Clipper.RectClip.cs b/CSharp/Clipper2Lib/Clipper.RectClip.cs index 43fcf90e..face68f9 100644 --- a/CSharp/Clipper2Lib/Clipper.RectClip.cs +++ b/CSharp/Clipper2Lib/Clipper.RectClip.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 7 May 2024 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * @@ -498,6 +498,23 @@ protected void GetNextLocation(Path64 path, } // switch } + private bool StartLocsAreClockwise(List startLocs) + { + int result = 0; + for (int i = 1; i < startLocs.Count; i++) + { + int d = (int)startLocs[i] - (int)startLocs[i - 1]; + switch (d) + { + case -1: result -= 1; break; + case 1: result += 1; break; + case -3: result += 1; break; + case 3: result -= 1; break; + } + } + return result > 0; + } + private void ExecuteInternal(Path64 path) { if (path.Count < 3 || rect_.IsEmpty()) return; @@ -624,10 +641,12 @@ private void ExecuteInternal(Path64 path) if (pathBounds_.Contains(rect_) && Path1ContainsPath2(path, rectPath_)) { + bool startLocsClockwise = StartLocsAreClockwise(startLocs); for (int j = 0; j < 4; j++) { - Add(rectPath_[j]); - AddToEdge(edges_[j * 2], results_[0]!); + int k = startLocsClockwise ? j : 3 - j; // ie reverse result path + Add(rectPath_[k]); + AddToEdge(edges_[k * 2], results_[0]!); } } } diff --git a/Delphi/Clipper2Lib/Clipper.RectClip.pas b/Delphi/Clipper2Lib/Clipper.RectClip.pas index 8a0cf05c..91fbba66 100644 --- a/Delphi/Clipper2Lib/Clipper.RectClip.pas +++ b/Delphi/Clipper2Lib/Clipper.RectClip.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 27 April 2024 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * @@ -650,9 +650,28 @@ function TRectClip64.Execute(const paths: TPaths64): TPaths64; end; //------------------------------------------------------------------------------ +function StartLocsAreClockwise(const startLocs: TList): Boolean; +var + i,j, res: integer; +begin + res := 0; + for i := 1 to startLocs.Count -1 do + begin + j := Ord(TLocation(startLocs[i])) - Ord(TLocation(startLocs[i - 1])); + case j of + -1: dec(res); + 1: inc(res); + -3: inc(res); + 3: dec(res); + end; + end; + result := res > 0; +end; +//------------------------------------------------------------------------------ + procedure TRectClip64.ExecuteInternal(const path: TPath64); var - i,highI : integer; + i,j, highI : integer; prevPt,ip,ip2 : TPoint64; loc, prevLoc : TLocation; loc2 : TLocation; @@ -661,6 +680,7 @@ procedure TRectClip64.ExecuteInternal(const path: TPath64); crossingLoc : TLocation; prevCrossLoc : TLocation; isCw : Boolean; + startLocsCW : Boolean; begin if (Length(path) < 3) then Exit; fStartLocs.Clear; @@ -797,10 +817,12 @@ procedure TRectClip64.ExecuteInternal(const path: TPath64); begin // yep, the path does fully contain rect // so add rect to the solution + startLocsCW := StartLocsAreClockwise(fStartLocs); for i := 0 to 3 do begin - Add(fRectPath[i]); - AddToEdge(fEdges[i*2], fResults[0]); + if startLocsCW then j := i else j := 3 - i; + Add(fRectPath[j]); + AddToEdge(fEdges[j*2], fResults[0]); end; end; end; From 68b3a2c99ee7370d38931a3f29cc4e58315eae5e Mon Sep 17 00:00:00 2001 From: angusj Date: Sat, 13 Jul 2024 13:01:34 +1000 Subject: [PATCH 03/31] Fixed an integer overflow error in ClipperOffset (#867) --- CSharp/Clipper2Lib/Clipper.Offset.cs | 4 ++-- Delphi/Clipper2Lib/Clipper.Offset.pas | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 1e03d37c..31ebcaea 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 April 2024 * +* Date : 13 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -513,8 +513,8 @@ private void BuildNormals(Path64 path) { int cnt = path.Count; _normals.Clear(); + if (cnt == 0) return; _normals.EnsureCapacity(cnt); - for (int i = 0; i < cnt - 1; i++) _normals.Add(GetUnitNormal(path[i], path[i + 1])); _normals.Add(GetUnitNormal(path[cnt - 1], path[0])); diff --git a/Delphi/Clipper2Lib/Clipper.Offset.pas b/Delphi/Clipper2Lib/Clipper.Offset.pas index 7e7d99f0..f85be544 100644 --- a/Delphi/Clipper2Lib/Clipper.Offset.pas +++ b/Delphi/Clipper2Lib/Clipper.Offset.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 17 April 2024 * +* Date : 6 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -236,9 +236,7 @@ function UnsafeGet(List: TList; Index: Integer): Pointer; constructor TGroup.Create(const pathsIn: TPaths64; jt: TJoinType; et: TEndType); var i, len: integer; - a: double; isJoined: boolean; - pb: PBoolean; begin Self.joinType := jt; Self.endType := et; @@ -345,7 +343,6 @@ procedure TClipperOffset.DoGroupOffset(group: TGroup); i,j, len, steps: Integer; r, stepsPer360, arcTol: Double; absDelta: double; - isShrinking: Boolean; rec: TRect64; pt0: TPoint64; begin @@ -450,6 +447,7 @@ procedure TClipperOffset.BuildNormals; begin len := Length(fInPath); SetLength(fNorms, len); + if len = 0 then Exit; for i := 0 to len-2 do fNorms[i] := GetUnitNormal(fInPath[i], fInPath[i+1]); fNorms[len -1] := GetUnitNormal(fInPath[len -1], fInPath[0]); From 736ddb0b53d97fd5f65dd3d9bbf8a0993eaf387c Mon Sep 17 00:00:00 2001 From: angusj Date: Sat, 13 Jul 2024 13:08:09 +1000 Subject: [PATCH 04/31] Release version to 1.4.0 --- CPP/CMakeLists.txt | 2 +- CPP/Clipper2Lib/include/clipper2/clipper.version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CPP/CMakeLists.txt b/CPP/CMakeLists.txt index 99b656ac..0e68fa9f 100644 --- a/CPP/CMakeLists.txt +++ b/CPP/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(Clipper2 VERSION 1.3.0 LANGUAGES C CXX) +project(Clipper2 VERSION 1.4.0 LANGUAGES C CXX) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 17) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.version.h b/CPP/Clipper2Lib/include/clipper2/clipper.version.h index d7644067..61464095 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.version.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.version.h @@ -1,6 +1,6 @@ #ifndef CLIPPER_VERSION_H #define CLIPPER_VERSION_H -constexpr auto CLIPPER2_VERSION = "1.3.0"; +constexpr auto CLIPPER2_VERSION = "1.4.0"; #endif // CLIPPER_VERSION_H From 7f41c18ba8011307c87ec862998053775695f123 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Tue, 16 Jul 2024 17:38:19 -0400 Subject: [PATCH 05/31] throw() -> noexcept (#869) `throw()` was deprecated in C++11 and removed in C++17. `noexcept` is the appropriate fix. --- CPP/Clipper2Lib/include/clipper2/clipper.core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index 925c0468..a157d6e7 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -31,7 +31,7 @@ namespace Clipper2Lib public: explicit Clipper2Exception(const char* description) : m_descr(description) {} - virtual const char* what() const throw() override { return m_descr.c_str(); } + virtual const char* what() const noexcept override { return m_descr.c_str(); } private: std::string m_descr; }; From 6a346b4f887aaf09765dd5741c610c110f1b6079 Mon Sep 17 00:00:00 2001 From: LarsSkiba <105490553+LarsSkiba@users.noreply.github.com> Date: Mon, 22 Jul 2024 04:03:34 +0200 Subject: [PATCH 06/31] Add operator+ and operator+= to Rect (#875) Co-authored-by: Lars Skiba --- .../include/clipper2/clipper.core.h | 16 +++++ CPP/Tests/TestRect.cpp | 62 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 CPP/Tests/TestRect.cpp diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index a157d6e7..d3ad715b 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -362,6 +362,22 @@ namespace Clipper2Lib top == other.top && bottom == other.bottom; } + Rect& operator+=(const Rect& other) + { + left = (std::min)(left, other.left); + top = (std::min)(top, other.top); + right = (std::max)(right, other.right); + bottom = (std::max)(bottom, other.bottom); + return *this; + } + + Rect operator+(const Rect& other) const + { + Rect result = *this; + result += other; + return result; + } + friend std::ostream& operator<<(std::ostream& os, const Rect& rect) { os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") "; return os; diff --git a/CPP/Tests/TestRect.cpp b/CPP/Tests/TestRect.cpp new file mode 100644 index 00000000..4eefc5fd --- /dev/null +++ b/CPP/Tests/TestRect.cpp @@ -0,0 +1,62 @@ +#include +#include "clipper2/clipper.core.h" + +using namespace Clipper2Lib; + +TEST(Clipper2Tests, TestRectOpPlus) +{ + { + Rect64 lhs = Rect64::InvalidRect(); + Rect64 rhs(-1, -1, 10, 10); + { + Rect64 sum = lhs + rhs; + EXPECT_EQ(rhs, sum); + } + { + std::swap(lhs, rhs); + Rect64 sum = lhs + rhs; + EXPECT_EQ(lhs, sum); + } + } + { + Rect64 lhs = Rect64::InvalidRect(); + Rect64 rhs(1, 1, 10, 10); + { + Rect64 sum = lhs + rhs; + EXPECT_EQ(rhs, sum); + } + { + std::swap(lhs, rhs); + Rect64 sum = lhs + rhs; + EXPECT_EQ(lhs, sum); + } + } + { + Rect64 lhs(0, 0, 1, 1); + Rect64 rhs(-1, -1, 0, 0); + Rect64 expected(-1, -1, 1, 1); + { + Rect64 sum = lhs + rhs; + EXPECT_EQ(expected, sum); + } + { + std::swap(lhs, rhs); + Rect64 sum = lhs + rhs; + EXPECT_EQ(expected, sum); + } + } + { + Rect64 lhs(-10, -10, -1, -1); + Rect64 rhs(1, 1, 10, 10); + Rect64 expected(-10, -10, 10, 10); + { + Rect64 sum = lhs + rhs; + EXPECT_EQ(expected, sum); + } + { + std::swap(lhs, rhs); + Rect64 sum = lhs + rhs; + EXPECT_EQ(expected, sum); + } + } +} \ No newline at end of file From 2a99f839552ee1f0b9d594a572e380843c5bed6c Mon Sep 17 00:00:00 2001 From: angusj Date: Wed, 24 Jul 2024 22:41:22 +1000 Subject: [PATCH 07/31] Clipper.Offset - fixed a bug occasionally encountered when over shrinking paths (#873) --- CPP/Clipper2Lib/src/clipper.offset.cpp | 52 ++++++++++++++------------ CPP/Tests/TestOffsets.cpp | 17 +++++++++ CSharp/Clipper2Lib/Clipper.Offset.cs | 14 ++++--- Delphi/Clipper2Lib/Clipper.Offset.pas | 15 +++++--- 4 files changed, 64 insertions(+), 34 deletions(-) diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 508a7f08..3b226249 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 April 2024 * +* Date : 24 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -38,30 +38,34 @@ std::optional GetLowestClosedPathIdx(const Paths64& paths) return result; } -PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) +static inline double Hypot(double x, double y) +{ + // given that this is an internal function, and given the x and y parameters + // will always be coordinate values (or the difference between coordinate values), + // x and y should always be within INT64_MIN to INT64_MAX. Consequently, + // there should be no risk that the following computation will overflow + // see https://stackoverflow.com/a/32436148/359538 + return std::sqrt(x * x + y * y); +} + +static PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) { double dx, dy, inverse_hypot; if (pt1 == pt2) return PointD(0.0, 0.0); dx = static_cast(pt2.x - pt1.x); dy = static_cast(pt2.y - pt1.y); - inverse_hypot = 1.0 / hypot(dx, dy); + inverse_hypot = 1.0 / Hypot(dx, dy); dx *= inverse_hypot; dy *= inverse_hypot; return PointD(dy, -dx); } -inline bool AlmostZero(double value, double epsilon = 0.001) +static inline bool AlmostZero(double value, double epsilon = 0.001) { return std::fabs(value) < epsilon; } -inline double Hypot(double x, double y) -{ - //see https://stackoverflow.com/a/32436148/359538 - return std::sqrt(x * x + y * y); -} - -inline PointD NormalizeVector(const PointD& vec) +static inline PointD NormalizeVector(const PointD& vec) { double h = Hypot(vec.x, vec.y); if (AlmostZero(h)) return PointD(0,0); @@ -69,17 +73,17 @@ inline PointD NormalizeVector(const PointD& vec) return PointD(vec.x * inverseHypot, vec.y * inverseHypot); } -inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2) +static inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2) { return NormalizeVector(PointD(vec1.x + vec2.x, vec1.y + vec2.y)); } -inline bool IsClosedPath(EndType et) +static inline bool IsClosedPath(EndType et) { return et == EndType::Polygon || et == EndType::Joined; } -inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) +static inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) { #ifdef USINGZ return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z); @@ -88,7 +92,7 @@ inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) #endif } -inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) +static inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) { #ifdef USINGZ return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z); @@ -97,7 +101,7 @@ inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) #endif } -inline void NegatePath(PathD& path) +static inline void NegatePath(PathD& path) { for (PointD& pt : path) { @@ -129,11 +133,11 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType // the lowermost path must be an outer path, so if its orientation is negative, // then flag the whole group is 'reversed' (will negate delta etc.) // as this is much more efficient than reversing every path. - is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; + is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; } else { - lowest_path_idx = std::nullopt; + lowest_path_idx = std::nullopt; is_reversed = false; } } @@ -315,17 +319,19 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593) { - // is concave (so insert 3 points that will create a negative region) + // is concave + // by far the simplest way to construct concave joins, especially those joining very + // short segments, is to insert 3 points that produce negative regions. These regions + // will be removed later by the finishing union operation. This is also the best way + // to ensure that path reversals (ie over-shrunk paths) are removed. #ifdef USINGZ path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); #else path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); #endif - // this extra point is the only simple way to ensure that path reversals - // (ie over-shrunk paths) are fully cleaned out with the trailing union op. - // However it's probably safe to skip this whenever an angle is almost flat. - if (cos_a < 0.99) path_out.push_back(path[j]); // (#405) + // when the angle is almost flat (cos_a ~= 1), it's safe to skip this middle point + if (cos_a < 0.999) path_out.push_back(path[j]); // (#405, #873) #ifdef USINGZ path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); diff --git a/CPP/Tests/TestOffsets.cpp b/CPP/Tests/TestOffsets.cpp index 9fdc15e8..006e4b6a 100644 --- a/CPP/Tests/TestOffsets.cpp +++ b/CPP/Tests/TestOffsets.cpp @@ -658,3 +658,20 @@ TEST(Clipper2Tests, TestOffsets10) // see #715 EXPECT_EQ(solution.size(), 2); } +TEST(Clipper2Tests, TestOffsets11) // see #405 +{ + PathsD subject, solution; + subject.push_back(MakePathD({ -1.0, -1.0, -1.0, 11.0, 11.0, 11.0, 11.0, -1.0 })); + // offset polyline + solution = InflatePaths(subject, -50, JoinType::Miter, EndType::Polygon); + EXPECT_TRUE(solution.empty()); +} + +TEST(Clipper2Tests, TestOffsets12) // see #873 +{ + Paths64 subject = { + MakePath({667680768, -36382704, 737202688, -87034880, 742581888, -86055680, 747603968, -84684800}) + }; + Paths64 solution = InflatePaths(subject, -249561088, JoinType::Miter, EndType::Polygon); + EXPECT_TRUE(solution.empty()); +} \ No newline at end of file diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 31ebcaea..b4b51fe9 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 13 July 2024 * +* Date : 24 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -548,11 +548,15 @@ private void OffsetPoint(Group group, Path64 path, int j, ref int k) if (cosA > -0.999 && (sinA * _groupDelta < 0)) // test for concavity first (#593) { // is concave + // by far the simplest way to construct concave joins, especially those joining very + // short segments, is to insert 3 points that produce negative regions. These regions + // will be removed later by the finishing union operation. This is also the best way + // to ensure that path reversals (ie over-shrunk paths) are removed. pathOut.Add(GetPerpendic(path[j], _normals[k])); - // this extra point is the only simple way to ensure that path reversals - // (ie over-shrunk paths) are fully cleaned out with the trailing union op. - // However it's probably safe to skip this whenever an angle is almost flat. - if (cosA < 0.99) pathOut.Add(path[j]); // (#405) + + // when the angle is almost flat (cos_a ~= 1), it's safe to skip this middle point + if (cosA < 0.999) pathOut.Add(path[j]); // (#405, #873) + pathOut.Add(GetPerpendic(path[j], _normals[j])); } else if ((cosA > 0.999) && (_joinType != JoinType.Round)) diff --git a/Delphi/Clipper2Lib/Clipper.Offset.pas b/Delphi/Clipper2Lib/Clipper.Offset.pas index f85be544..e0cab16b 100644 --- a/Delphi/Clipper2Lib/Clipper.Offset.pas +++ b/Delphi/Clipper2Lib/Clipper.Offset.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 6 July 2024 * +* Date : 24 July 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -989,16 +989,19 @@ procedure TClipperOffset.DoRound(j, k: Integer; angle: double); if (cosA > -0.999) and (sinA * fGroupDelta < 0) then begin // is concave + // by far the simplest way to construct concave joins, especially those + // joining very short segments, is to insert 3 points that produce negative + // regions. These regions will be removed later by the finishing union + // operation. This is also the best way to ensure that path reversals + // (ie over-shrunk paths) are removed. {$IFDEF USINGZ} AddPoint(GetPerpendic(fInPath[j], fNorms[k], fGroupDelta), fInPath[j].Z); {$ELSE} AddPoint(GetPerpendic(fInPath[j], fNorms[k], fGroupDelta)); {$ENDIF} - // this extra point is the only simple way to ensure that path reversals - // (ie over-shrunk paths) are fully cleaned out with the trailing union op. - // However it's probably safe to skip this whenever an angle is almost flat. - if (cosA < 0.99) then - AddPoint(fInPath[j]); // (#405) + // when the angle is almost flat (cos_a ~= 1), + // it's safe to skip inserting this middle point + if (cosA < 0.999) then AddPoint(fInPath[j]); // (#405, #873) {$IFDEF USINGZ} AddPoint(GetPerpendic(fInPath[j], fNorms[j], fGroupDelta), fInPath[j].Z); {$ELSE} From c19da7257e7d3efdd02aede46d011fcf2f1bf4cc Mon Sep 17 00:00:00 2001 From: angusj Date: Wed, 24 Jul 2024 22:51:42 +1000 Subject: [PATCH 08/31] Fixed compile bugs in previous commit. --- CPP/Clipper2Lib/src/clipper.offset.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 3b226249..834bfd6c 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -38,7 +38,7 @@ std::optional GetLowestClosedPathIdx(const Paths64& paths) return result; } -static inline double Hypot(double x, double y) +inline double Hypot(double x, double y) { // given that this is an internal function, and given the x and y parameters // will always be coordinate values (or the difference between coordinate values), @@ -60,12 +60,12 @@ static PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) return PointD(dy, -dx); } -static inline bool AlmostZero(double value, double epsilon = 0.001) +inline bool AlmostZero(double value, double epsilon = 0.001) { return std::fabs(value) < epsilon; } -static inline PointD NormalizeVector(const PointD& vec) +inline PointD NormalizeVector(const PointD& vec) { double h = Hypot(vec.x, vec.y); if (AlmostZero(h)) return PointD(0,0); @@ -73,12 +73,12 @@ static inline PointD NormalizeVector(const PointD& vec) return PointD(vec.x * inverseHypot, vec.y * inverseHypot); } -static inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2) +inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2) { return NormalizeVector(PointD(vec1.x + vec2.x, vec1.y + vec2.y)); } -static inline bool IsClosedPath(EndType et) +inline bool IsClosedPath(EndType et) { return et == EndType::Polygon || et == EndType::Joined; } @@ -92,7 +92,7 @@ static inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double #endif } -static inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) +inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) { #ifdef USINGZ return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z); @@ -101,7 +101,7 @@ static inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double #endif } -static inline void NegatePath(PathD& path) +inline void NegatePath(PathD& path) { for (PointD& pt : path) { From 5c96b89c83910662c93759a533d80a8b521cadf8 Mon Sep 17 00:00:00 2001 From: Angus Johnson Date: Fri, 16 Aug 2024 16:20:40 +1000 Subject: [PATCH 09/31] Preparing to merge export_usingz branch (#878) * Branch from main - to test USINGZ in clipper.export.h * clipper.header.h - Replaced problematic bit_cast function * clipper.export.h - tweaked new Reinterpret function * Tweaked a C++ CI test routine * clipper.export.h - minor tweak to Reinterpret function * tweaks to CI config. * another tweak to C++ CI configuration --- .github/workflows/actions_cpp.yml | 26 +- .github/workflows/actions_csharp.yml | 2 +- .../include/clipper2/clipper.core.h | 14 +- .../include/clipper2/clipper.export.h | 467 ++++++++--- CPP/Tests/TestExportHeaders.cpp | 63 +- CPP/Tests/TestPolygons.cpp | 2 +- DLL/CPP_DLL/Clipper2ZDll.vcxproj | 186 +++++ DLL/CPP_DLL/Clipper2_DLL.sln | 2 +- ...r2_Export.vcxproj => Clipper2_Dll.vcxproj} | 13 +- DLL/CPP_DLL/Clipper2_Z_DLL.sln | 31 + DLL/CSharp_TestApp/CSharp_TestApp.csproj | 11 - DLL/CSharp_TestApp/Program.cs | 291 ------- DLL/CSharp_TestApp2/CSharp_TestApp2.csproj | 16 - DLL/CSharp_TestApp2/CSharp_TestApp2.sln | 63 -- DLL/CSharp_TestApp2/Program.cs | 184 ----- .../COMPILE AND ADD Clipper2_64.dll HERE | 0 DLL/Delphi_TestApp/Test_DLL.dpr | 736 ----------------- DLL/Delphi_TestApp/Test_DLL.dproj | 138 ---- .../CSharp_TestApps/COPY Clipper2 DLLs HERE} | 0 .../CSharp_TestApp/CSharp_TestApp.csproj | 19 + .../CSharp_TestApp/CSharp_TestApp.sln | 0 .../CSharp_TestApp/Clipper2DllCore.cs | 392 +++++++++ .../CSharp_TestApp/Clipper2DllSvg.cs | 469 +++++++++++ .../CSharp_TestApps/CSharp_TestApp/Program.cs | 253 ++++++ .../CSharp_TestApp/polytree_sample.png | Bin .../CSharp_TestApp2/CSharp_TestApp2.csproj | 23 + .../CSharp_TestApp2/CSharp_TestApp2.sln | 49 ++ .../CSharp_TestApp2/Clipper2DllCore.cs | 392 +++++++++ .../CSharp_TestApp2/Clipper2DllSvg.cs | 469 +++++++++++ .../CSharp_TestApp2/Program.cs | 123 +++ .../Output/COPY Clipper2 DLLs HERE} | 0 .../TestApp1/Clipper2DllCore.pas | 329 ++++++++ .../TestApp1/Clipper2DllSVG.pas | 774 ++++++++++++++++++ .../Delphi_TestApps/TestApp1/Test1_DLL.dpr | 135 +++ .../Delphi_TestApps/TestApp1/Test1_DLL.dproj | 189 +++++ 35 files changed, 4256 insertions(+), 1605 deletions(-) create mode 100644 DLL/CPP_DLL/Clipper2ZDll.vcxproj rename DLL/CPP_DLL/{Clipper2_Export.vcxproj => Clipper2_Dll.vcxproj} (96%) create mode 100644 DLL/CPP_DLL/Clipper2_Z_DLL.sln delete mode 100644 DLL/CSharp_TestApp/CSharp_TestApp.csproj delete mode 100644 DLL/CSharp_TestApp/Program.cs delete mode 100644 DLL/CSharp_TestApp2/CSharp_TestApp2.csproj delete mode 100644 DLL/CSharp_TestApp2/CSharp_TestApp2.sln delete mode 100644 DLL/CSharp_TestApp2/Program.cs delete mode 100644 DLL/Delphi_TestApp/COMPILE AND ADD Clipper2_64.dll HERE delete mode 100644 DLL/Delphi_TestApp/Test_DLL.dpr delete mode 100644 DLL/Delphi_TestApp/Test_DLL.dproj rename DLL/{CSharp_TestApp/COMPILE AND ADD Clipper2_64.dll HERE => TEST_APPS/CSharp_TestApps/COPY Clipper2 DLLs HERE} (100%) create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/CSharp_TestApp.csproj rename DLL/{ => TEST_APPS/CSharp_TestApps}/CSharp_TestApp/CSharp_TestApp.sln (100%) create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllCore.cs create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllSvg.cs create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs rename DLL/{ => TEST_APPS/CSharp_TestApps}/CSharp_TestApp/polytree_sample.png (100%) create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.csproj create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.sln create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllCore.cs create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllSvg.cs create mode 100644 DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs rename DLL/{CSharp_TestApp2/COMPILE AND ADD Clipper2_64.dll HERE => TEST_APPS/Delphi_TestApps/Output/COPY Clipper2 DLLs HERE} (100%) create mode 100644 DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas create mode 100644 DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas create mode 100644 DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dpr create mode 100644 DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dproj diff --git a/.github/workflows/actions_cpp.yml b/.github/workflows/actions_cpp.yml index 4e93a4a4..c8f0e486 100644 --- a/.github/workflows/actions_cpp.yml +++ b/.github/workflows/actions_cpp.yml @@ -6,12 +6,12 @@ jobs: windows-latest: runs-on: 'windows-latest' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v2 - name: Build run: | mkdir CPP/build @@ -25,8 +25,8 @@ jobs: ubuntu-latest-gcc-default: runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Build @@ -42,8 +42,8 @@ jobs: ubuntu-latest-gcc-11: runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Install gcc 11 @@ -64,8 +64,8 @@ jobs: ubuntu-latest-clang-default: runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Build @@ -83,8 +83,8 @@ jobs: ubuntu-latest-clang-13: runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Install clang 13 @@ -107,8 +107,8 @@ jobs: macos-latest: runs-on: 'macos-latest' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - name: Build diff --git a/.github/workflows/actions_csharp.yml b/.github/workflows/actions_csharp.yml index 96704e79..2dae9afd 100644 --- a/.github/workflows/actions_csharp.yml +++ b/.github/workflows/actions_csharp.yml @@ -9,7 +9,7 @@ jobs: run: working-directory: ./CSharp steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Copy Clipper2Lib to USINGZ directory run: cp Clipper2Lib/*.cs USINGZ/ - name: Setup .NET Core diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index d3ad715b..40124804 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -107,6 +107,10 @@ namespace Clipper2Lib //https://en.wikipedia.org/wiki/Nonzero-rule enum class FillRule { EvenOdd, NonZero, Positive, Negative }; +#ifdef USINGZ + using z_type = int64_t; +#endif + // Point ------------------------------------------------------------------------ template @@ -114,10 +118,10 @@ namespace Clipper2Lib T x; T y; #ifdef USINGZ - int64_t z; + z_type z; template - inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0) + inline void Init(const T2 x_ = 0, const T2 y_ = 0, const z_type z_ = 0) { if constexpr (std::is_integral_v && is_round_invocable::value && !std::is_integral_v) @@ -137,7 +141,7 @@ namespace Clipper2Lib explicit Point() : x(0), y(0), z(0) {}; template - Point(const T2 x_, const T2 y_, const int64_t z_ = 0) + Point(const T2 x_, const T2 y_, const z_type z_ = 0) { Init(x_, y_); z = z_; @@ -150,7 +154,7 @@ namespace Clipper2Lib } template - explicit Point(const Point& p, int64_t z_) + explicit Point(const Point& p, z_type z_) { Init(p.x, p.y, z_); } @@ -160,7 +164,7 @@ namespace Clipper2Lib return Point(x * scale, y * scale, z); } - void SetZ(const int64_t z_value) { z = z_value; } + void SetZ(const z_type z_value) { z = z_value; } friend std::ostream& operator<<(std::ostream& os, const Point& point) { diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index 53a44536..3dfd2618 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 14 May 2024 * +* Date : 16 August 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * @@ -19,69 +19,95 @@ The path structures used extensively in other parts of this library are all based on std::vector classes. Since C++ classes can't be accessed by other -languages, these paths are converted into very simple array data structures -(of either int64_t for CPath64 or double for CPathD) that can be parsed by -just about any programming language. +languages, these paths are converted here into very simple array data +structures that can be parsed by just about any programming language. + +These 2D paths are defined by series of x and y coordinates together with an +optional user-defined 'z' value (see Z-values below). Hence, a vertex refers +to a single x and y coordinate (+/- a user-defined value). These values will +be either type int64_t or type double. Data structures have names that +indicate the array type by a suffixed '64' or a 'D'. For example, the data +structure CPath64 contains an array of int64_t values, whereas the data +structure CPathD contains an array of double. Where documentation omits +the type suffix (eg CPath), it is simply agnostic to the array's data type. + +For conciseness, the following letters are used in the diagrams below: +N: Number of vertices in a given path +C: Count of structure's paths +A: Array size (as distinct from the size in memory) + CPath64 and CPathD: -These are arrays of consecutive x and y path coordinates preceeded by -a pair of values containing the path's length (N) and a 0 value. -__________________________________ -|counter|coord1|coord2|...|coordN| -|N, 0 |x1, y1|x2, y2|...|xN, yN| -__________________________________ +These are arrays of consecutive vertices preceeded by a pair of values +containing the number of vertices (N) in the path, and a 0 value. +_______________________________________________________________ +| counters | vertex1 | vertex2 | ... | vertexN | +| N, 0 | x1, y1, (z1) | x2, y2, (z2) | ... | xN, yN, (zN) | +--------------------------------------------------------------- + CPaths64 and CPathsD: -These are also arrays containing any number of consecutive CPath64 or -CPathD structures. But preceeding these consecutive paths, there is pair of -values that contain the total length of the array structure (A) and the -number of CPath64 or CPathD it contains (C). The space these structures will -occupy in memory = A * sizeof(int64_t) or A * sizeof(double) respectively. -_______________________________ -|counter|path1|path2|...|pathC| -|A , C | | -_______________________________ +These are also arrays containing any number of consecutive CPath +structures. Preceeding these consecutive paths, there is a pair of +values that contain the length of the array structure (A) and +the count of following CPath structures (C). + Memory allocation for CPaths64 = A * sizeof(int64_t) + Memory allocation for CPathsD = A * sizeof(double) +__________________________________________ +| counters | path1 | path2 | ... | pathC | +| A, C | | | ... | | +------------------------------------------ + CPolytree64 and CPolytreeD: -These are also arrays consisting of CPolyPath structures that represent -individual paths in a tree structure. However, the very first (ie top) -CPolyPath is just the tree container that doesn't have a path. And because -of that, its structure will be very slightly different from the remaining -CPolyPath. This difference will be discussed below. +These structures consist of two values followed by a series of CPolyPath +structures. The first value indicates the total length of the array (A). +The second value indicates the number of following CPolyPath structures +that are the top level CPolyPath in the CPolytree (C). These CPolyPath +may, in turn, contain their own nested CPolyPath children that +collectively make a tree structure. +_________________________________________________________ +| counters | CPolyPath1 | CPolyPath2 | ... | CPolyPathC | +| A, C | | | ... | | +--------------------------------------------------------- -CPolyPath64 and CPolyPathD: -These are simple arrays consisting of a series of path coordinates followed -by any number of child (ie nested) CPolyPath. Preceeding these are two values -indicating the length of the path (N) and the number of child CPolyPath (C). -____________________________________________________________ -|counter|coord1|coord2|...|coordN| child1|child2|...|childC| -|N , C |x1, y1|x2, y2|...|xN, yN| | -____________________________________________________________ - -As mentioned above, the very first CPolyPath structure is just a container -that owns (both directly and indirectly) every other CPolyPath in the tree. -Since this first CPolyPath has no path, instead of a path length, its very -first value will contain the total length of the CPolytree array (not its -total bytes length). - -Again, all theses exported structures (CPaths64, CPathsD, CPolyTree64 & -CPolyTreeD) are arrays of either type int64_t or double, and the first -value in these arrays will always be the length of that array. - -These array structures are allocated in heap memory which will eventually -need to be released. However, since applications dynamically linking to -these functions may use different memory managers, the only safe way to -free up this memory is to use the exported DisposeArray64 and -DisposeArrayD functions (see below). -*/ +CPolyPath64 and CPolyPathD: +These array structures consist of a pair of counter values followed by a +series of polygon vertices and a series of nested CPolyPath children. +The first counter values indicates the number of vertices in the +polygon (N), and the second counter indicates the CPolyPath child count (C). +_____________________________________________________________________________ +|cntrs |vertex1 |vertex2 |...|vertexN |child1|child2|...|childC| +|N, C |x1, y1, (z1)| x2, y2, (z2)|...|xN, yN, (zN)| | |...| | +----------------------------------------------------------------------------- + + +DisposeArray64 & DisposeArrayD: +All array structures are allocated in heap memory which will eventually +need to be released. However, since applications linking to these DLL +functions may use different memory managers, the only safe way to release +this memory is to use the exported DisposeArray functions. + + +(Optional) Z-Values: +Structures will only contain user-defined z-values when the USINGZ +pre-processor identifier is used. The library does not assign z-values +because this field is intended for users to assign custom values to vertices. +Z-values in input paths (subject and clip) will be copied to solution paths. +New vertices at path intersections will generate a callback event that allows +users to assign z-values at these new vertices. The user's callback function +must conform with the DLLZCallback definition and be registered with the +DLL via SetZCallback. To assist the user in assigning z-values, the library +passes in the callback function the new intersection point together with +the four vertices that define the two segments that are intersecting. +*/ #ifndef CLIPPER2_EXPORT_H #define CLIPPER2_EXPORT_H #include #include - #include "clipper2/clipper.core.h" #include "clipper2/clipper.engine.h" #include "clipper2/clipper.offset.h" @@ -127,6 +153,12 @@ inline Rect CRectToRect(const CRect& rect) return result; } +template +inline T1 Reinterpret(T2 value) { + return *reinterpret_cast(&value); +} + + #ifdef _WIN32 #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) #else @@ -178,11 +210,22 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, double delta, uint8_t jointype, uint8_t endtype, double miter_limit = 2.0, double arc_tolerance = 0.0, bool reverse_solution = false); + EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, double delta, uint8_t jointype, uint8_t endtype, int precision = 2, double miter_limit = 2.0, double arc_tolerance = 0.0, bool reverse_solution = false); +EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path, + double delta, uint8_t jointype, uint8_t endtype, + double miter_limit = 2.0, double arc_tolerance = 0.0, + bool reverse_solution = false); + +EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path, + double delta, uint8_t jointype, uint8_t endtype, + int precision = 2, double miter_limit = 2.0, + double arc_tolerance = 0.0, bool reverse_solution = false); + // RectClip & RectClipLines: EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths); @@ -197,6 +240,15 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, // INTERNAL FUNCTIONS ////////////////////////////////////////////////////// +#ifdef USINGZ +ZCallback64 dllCallback64 = nullptr; +ZCallbackD dllCallbackD = nullptr; + +constexpr int EXPORT_VERTEX_DIMENSIONALITY = 3; +#else +constexpr int EXPORT_VERTEX_DIMENSIONALITY = 2; +#endif + template static void GetPathCountAndCPathsArrayLen(const Paths& paths, size_t& cnt, size_t& array_len) @@ -206,30 +258,47 @@ static void GetPathCountAndCPathsArrayLen(const Paths& paths, for (const Path& path : paths) if (path.size()) { - array_len += path.size() * 2 + 2; + array_len += path.size() * EXPORT_VERTEX_DIMENSIONALITY + 2; ++cnt; } } -static size_t GetPolyPath64ArrayLen(const PolyPath64& pp) +static size_t GetPolyPathArrayLen64(const PolyPath64& pp) +{ + size_t result = 2; // poly_length + child_count + result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY; + //plus nested children :) + for (size_t i = 0; i < pp.Count(); ++i) + result += GetPolyPathArrayLen64(*pp[i]); + return result; +} + +static size_t GetPolyPathArrayLenD(const PolyPathD& pp) { size_t result = 2; // poly_length + child_count - result += pp.Polygon().size() * 2; + result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY; //plus nested children :) for (size_t i = 0; i < pp.Count(); ++i) - result += GetPolyPath64ArrayLen(*pp[i]); + result += GetPolyPathArrayLenD(*pp[i]); return result; } -static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree, +static void GetPolytreeCountAndCStorageSize64(const PolyTree64& tree, size_t& cnt, size_t& array_len) { cnt = tree.Count(); // nb: top level count only - array_len = GetPolyPath64ArrayLen(tree); + array_len = GetPolyPathArrayLen64(tree); +} + +static void GetPolytreeCountAndCStorageSizeD(const PolyTreeD& tree, + size_t& cnt, size_t& array_len) +{ + cnt = tree.Count(); // nb: top level count only + array_len = GetPolyPathArrayLenD(tree); } template -static T* CreateCPaths(const Paths& paths) +static T* CreateCPathsFromPathsT(const Paths& paths) { size_t cnt = 0, array_len = 0; GetPathCountAndCPathsArrayLen(paths, cnt, array_len); @@ -245,11 +314,38 @@ static T* CreateCPaths(const Paths& paths) { *v++ = pt.x; *v++ = pt.y; +#ifdef USINGZ + *v++ = Reinterpret(pt.z); +#endif } } return result; } +CPathsD CreateCPathsDFromPathsD(const PathsD& paths) +{ + if (!paths.size()) return nullptr; + size_t cnt, array_len; + GetPathCountAndCPathsArrayLen(paths, cnt, array_len); + CPathsD result = new double[array_len], v = result; + *v++ = (double)array_len; + *v++ = (double)cnt; + for (const PathD& path : paths) + { + if (!path.size()) continue; + *v = (double)path.size(); + ++v; *v++ = 0; + for (const PointD& pt : path) + { + *v++ = pt.x; + *v++ = pt.y; +#ifdef USINGZ + * v++ = Reinterpret(pt.z); +#endif + } + } + return result; +} CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale) { @@ -268,13 +364,16 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale) { *v++ = pt.x * scale; *v++ = pt.y * scale; +#ifdef USINGZ + *v++ = Reinterpret(pt.z); +#endif } } return result; } template -static Path ConvertCPath(T* path) +static Path ConvertCPathToPathT(T* path) { Path result; if (!path) return result; @@ -284,14 +383,19 @@ static Path ConvertCPath(T* path) result.reserve(cnt); for (size_t j = 0; j < cnt; ++j) { - T x = *v++, y = *v++; - result.push_back(Point(x, y)); + T x = *v++, y = *v++; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + result.push_back(Point(x, y, z)); +#else + result.push_back(Point(x, y)); +#endif } return result; } template -static Paths ConvertCPaths(T* paths) +static Paths ConvertCPathsToPathsT(T* paths) { Paths result; if (!paths) return result; @@ -301,19 +405,45 @@ static Paths ConvertCPaths(T* paths) for (size_t i = 0; i < cnt; ++i) { size_t cnt2 = static_cast(*v); - v += 2; + v += 2; Path path; path.reserve(cnt2); for (size_t j = 0; j < cnt2; ++j) { T x = *v++, y = *v++; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + path.push_back(Point(x, y, z)); +#else path.push_back(Point(x, y)); +#endif } result.push_back(path); } return result; } +static Path64 ConvertCPathDToPath64WithScale(const CPathD path, double scale) +{ + Path64 result; + if (!path) return result; + double* v = path; + size_t cnt = static_cast(*v); + v += 2; // skip 0 value + result.reserve(cnt); + for (size_t j = 0; j < cnt; ++j) + { + double x = *v++ * scale; + double y = *v++ * scale; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + result.push_back(Point64(x, y, z)); +#else + result.push_back(Point64(x, y)); +#endif + } + return result; +} static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) { @@ -333,42 +463,78 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) { double x = *v++ * scale; double y = *v++ * scale; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + path.push_back(Point64(x, y, z)); +#else path.push_back(Point64(x, y)); +#endif } result.push_back(path); } return result; } -template -static void CreateCPolyPath(const PolyPath64* pp, T*& v, T scale) +static void CreateCPolyPath64(const PolyPath64* pp, int64_t*& v) { - *v++ = static_cast(pp->Polygon().size()); - *v++ = static_cast(pp->Count()); + *v++ = static_cast(pp->Polygon().size()); + *v++ = static_cast(pp->Count()); for (const Point64& pt : pp->Polygon()) { - *v++ = static_cast(pt.x * scale); - *v++ = static_cast(pt.y * scale); + *v++ = pt.x; + *v++ = pt.y; +#ifdef USINGZ + * v++ = Reinterpret(pt.z); // raw memory copy +#endif } for (size_t i = 0; i < pp->Count(); ++i) - CreateCPolyPath(pp->Child(i), v, scale); + CreateCPolyPath64(pp->Child(i), v); } -template -static T* CreateCPolyTree(const PolyTree64& tree, T scale) +static void CreateCPolyPathD(const PolyPathD* pp, double*& v) +{ + *v++ = static_cast(pp->Polygon().size()); + *v++ = static_cast(pp->Count()); + for (const PointD& pt : pp->Polygon()) + { + *v++ = pt.x; + *v++ = pt.y; +#ifdef USINGZ + * v++ = Reinterpret(pt.z); // raw memory copy +#endif + } + for (size_t i = 0; i < pp->Count(); ++i) + CreateCPolyPathD(pp->Child(i), v); +} + +static int64_t* CreateCPolyTree64(const PolyTree64& tree) { - if (scale == 0) scale = 1; size_t cnt, array_len; - GetPolytreeCountAndCStorageSize(tree, cnt, array_len); + GetPolytreeCountAndCStorageSize64(tree, cnt, array_len); if (!cnt) return nullptr; // allocate storage - T* result = new T[array_len]; - T* v = result; + int64_t* result = new int64_t[array_len]; + int64_t* v = result; + *v++ = static_cast(array_len); + *v++ = static_cast(tree.Count()); + for (size_t i = 0; i < tree.Count(); ++i) + CreateCPolyPath64(tree.Child(i), v); + return result; +} - *v++ = static_cast(array_len); - *v++ = static_cast(tree.Count()); +static double* CreateCPolyTreeD(const PolyTreeD& tree) +{ + double scale = std::log10(tree.Scale()); + size_t cnt, array_len; + GetPolytreeCountAndCStorageSizeD(tree, cnt, array_len); + if (!cnt) return nullptr; + // allocate storage + double* result = new double[array_len]; + double* v = result; + *v++ = static_cast(array_len); + *v++ = static_cast(tree.Count()); for (size_t i = 0; i < tree.Count(); ++i) - CreateCPolyPath(tree.Child(i), v, scale); + CreateCPolyPathD(tree.Child(i), v); return result; } @@ -391,20 +557,24 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, if (fillrule > static_cast(FillRule::Negative)) return -3; Paths64 sub, sub_open, clp, sol, sol_open; - sub = ConvertCPaths(subjects); - sub_open = ConvertCPaths(subjects_open); - clp = ConvertCPaths(clips); + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); Clipper64 clipper; clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallback64) + clipper.SetZCallback(dllCallback64); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) return -1; // clipping bug - should never happen :) - solution = CreateCPaths(sol); - solution_open = CreateCPaths(sol_open); + solution = CreateCPathsFromPathsT(sol); + solution_open = CreateCPathsFromPathsT(sol_open); return 0; //success !! } @@ -417,22 +587,26 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype, if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; Paths64 sub, sub_open, clp, sol_open; - sub = ConvertCPaths(subjects); - sub_open = ConvertCPaths(subjects_open); - clp = ConvertCPaths(clips); + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); PolyTree64 tree; Clipper64 clipper; clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallback64) + clipper.SetZCallback(dllCallback64); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open)) return -1; // clipping bug - should never happen :) - sol_tree = CreateCPolyTree(tree, (int64_t)1); - solution_open = CreateCPaths(sol_open); + sol_tree = CreateCPolyTree64(tree); + solution_open = CreateCPathsFromPathsT(sol_open); return 0; //success !! } @@ -445,23 +619,27 @@ EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype, if (precision < -8 || precision > 8) return -5; if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; - const double scale = std::pow(10, precision); + //const double scale = std::pow(10, precision); - Paths64 sub, sub_open, clp, sol, sol_open; - sub = ConvertCPathsDToPaths64(subjects, scale); - sub_open = ConvertCPathsDToPaths64(subjects_open, scale); - clp = ConvertCPathsDToPaths64(clips, scale); + PathsD sub, sub_open, clp, sol, sol_open; + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); - Clipper64 clipper; + ClipperD clipper(precision); clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallbackD) + clipper.SetZCallback(dllCallbackD); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) return -1; - solution = CreateCPathsDFromPaths64(sol, 1 / scale); - solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale); + solution = CreateCPathsDFromPathsD(sol); + solution_open = CreateCPathsDFromPathsD(sol_open); return 0; } @@ -474,27 +652,30 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype, if (precision < -8 || precision > 8) return -5; if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; - - double scale = std::pow(10, precision); + //double scale = std::pow(10, precision); int err = 0; - Paths64 sub, sub_open, clp, sol_open; - sub = ConvertCPathsDToPaths64(subjects, scale); - sub_open = ConvertCPathsDToPaths64(subjects_open, scale); - clp = ConvertCPathsDToPaths64(clips, scale); + PathsD sub, sub_open, clp, sol_open; + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); - PolyTree64 tree; - Clipper64 clipper; + PolyTreeD tree; + ClipperD clipper(precision); clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallbackD) + clipper.SetZCallback(dllCallbackD); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open)) return -1; // clipping bug - should never happen :) - solution = CreateCPolyTree(tree, 1/scale); - solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale); + solution = CreateCPolyTreeD(tree); + solution_open = CreateCPathsDFromPathsD(sol_open); return 0; //success !! } @@ -503,13 +684,13 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, double arc_tolerance, bool reverse_solution) { Paths64 pp; - pp = ConvertCPaths(paths); + pp = ConvertCPathsToPathsT(paths); ClipperOffset clip_offset( miter_limit, arc_tolerance, reverse_solution); clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); Paths64 result; clip_offset.Execute(delta, result); - return CreateCPaths(result); + return CreateCPathsFromPathsT(result); } EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, @@ -525,18 +706,49 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); Paths64 result; clip_offset.Execute(delta * scale, result); - return CreateCPathsDFromPaths64(result, 1 / scale); } + +EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path, + double delta, uint8_t jointype, uint8_t endtype, double miter_limit, + double arc_tolerance, bool reverse_solution) +{ + Path64 pp; + pp = ConvertCPathToPathT(path); + ClipperOffset clip_offset(miter_limit, + arc_tolerance, reverse_solution); + clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype)); + Paths64 result; + clip_offset.Execute(delta, result); + return CreateCPathsFromPathsT(result); +} + +EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path, + double delta, uint8_t jointype, uint8_t endtype, + int precision, double miter_limit, + double arc_tolerance, bool reverse_solution) +{ + if (precision < -8 || precision > 8 || !path) return nullptr; + + const double scale = std::pow(10, precision); + ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution); + Path64 pp = ConvertCPathDToPath64WithScale(path, scale); + clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype)); + Paths64 result; + clip_offset.Execute(delta * scale, result); + + return CreateCPathsDFromPaths64(result, 1 / scale); +} + EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths) { if (CRectIsEmpty(rect) || !paths) return nullptr; Rect64 r64 = CRectToRect(rect); class RectClip64 rc(r64); - Paths64 pp = ConvertCPaths(paths); + Paths64 pp = ConvertCPathsToPathsT(paths); Paths64 result = rc.Execute(pp); - return CreateCPaths(result); + return CreateCPathsFromPathsT(result); } EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision) @@ -560,9 +772,9 @@ EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, if (CRectIsEmpty(rect) || !paths) return nullptr; Rect64 r = CRectToRect(rect); class RectClipLines64 rcl (r); - Paths64 pp = ConvertCPaths(paths); + Paths64 pp = ConvertCPathsToPathsT(paths); Paths64 result = rcl.Execute(pp); - return CreateCPaths(result); + return CreateCPathsFromPathsT(result); } EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, @@ -581,20 +793,35 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) { - Path64 path = ConvertCPath(cpath); - Path64 pattern = ConvertCPath(cpattern); + Path64 path = ConvertCPathToPathT(cpath); + Path64 pattern = ConvertCPathToPathT(cpattern); Paths64 solution = MinkowskiSum(pattern, path, is_closed); - return CreateCPaths(solution); + return CreateCPathsFromPathsT(solution); } EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) { - Path64 path = ConvertCPath(cpath); - Path64 pattern = ConvertCPath(cpattern); + Path64 path = ConvertCPathToPathT(cpath); + Path64 pattern = ConvertCPathToPathT(cpattern); Paths64 solution = MinkowskiDiff(pattern, path, is_closed); - return CreateCPaths(solution); + return CreateCPathsFromPathsT(solution); } -} // end Clipper2Lib namespace +#ifdef USINGZ +typedef void (*DLLZCallback64)(const Point64& e1bot, const Point64& e1top, const Point64& e2bot, const Point64& e2top, Point64& pt); +typedef void (*DLLZCallbackD)(const PointD& e1bot, const PointD& e1top, const PointD& e2bot, const PointD& e2top, PointD& pt); + +EXTERN_DLL_EXPORT void SetZCallback64(DLLZCallback64 callback) +{ + dllCallback64 = callback; +} +EXTERN_DLL_EXPORT void SetZCallbackD(DLLZCallbackD callback) +{ + dllCallbackD = callback; +} + +#endif + +} #endif // CLIPPER2_EXPORT_H diff --git a/CPP/Tests/TestExportHeaders.cpp b/CPP/Tests/TestExportHeaders.cpp index f559474b..423d46ee 100644 --- a/CPP/Tests/TestExportHeaders.cpp +++ b/CPP/Tests/TestExportHeaders.cpp @@ -2,7 +2,9 @@ #include "clipper2/clipper.h" #include "clipper2/clipper.core.h" #include "clipper2/clipper.export.h" + using namespace Clipper2Lib; + static bool CreatePolyPath64FromCPolyPath(CPolyPath64& v, PolyPath64& owner) { int64_t poly_len = *v++, child_count = *v++; @@ -12,13 +14,19 @@ static bool CreatePolyPath64FromCPolyPath(CPolyPath64& v, PolyPath64& owner) for (size_t i = 0; i < poly_len; ++i) { int64_t x = *v++, y = *v++; - path.push_back(Point64(x,y)); +#ifdef USINGZ + auto z = Reinterpret(*v++); + path.push_back(Point64(x, y, z)); +#else + path.push_back(Point64(x, y)); +#endif } PolyPath64* new_owner = owner.AddChild(path); for (size_t i = 0; i < child_count; ++i) CreatePolyPath64FromCPolyPath(v, *new_owner); return true; } + static bool BuildPolyTree64FromCPolyTree(CPolyTree64 tree, PolyTree64& result) { result.Clear(); @@ -28,31 +36,41 @@ static bool BuildPolyTree64FromCPolyTree(CPolyTree64 tree, PolyTree64& result) if (!CreatePolyPath64FromCPolyPath(v, result)) return false; return true; } + static bool CreatePolyPathDFromCPolyPath(CPolyPathD& v, PolyPathD& owner) { - int64_t poly_len = *v++, child_count = *v++; + size_t poly_len = static_cast(*v++); + size_t child_count = static_cast(*v++); if (!poly_len) return false; PathD path; path.reserve(poly_len); for (size_t i = 0; i < poly_len; ++i) { - int64_t x = *v++, y = *v++; + double x = *v++, y = *v++; +#ifdef USINGZ + auto z = Reinterpret(*v++); + path.push_back(PointD(x, y, z)); +#else path.push_back(PointD(x, y)); +#endif } PolyPathD* new_owner = owner.AddChild(path); for (size_t i = 0; i < child_count; ++i) CreatePolyPathDFromCPolyPath(v, *new_owner); return true; } + static bool BuildPolyTreeDFromCPolyTree(CPolyTreeD tree, PolyTreeD& result) { result.Clear(); double* v = tree; - int64_t array_len = *v++, child_count = *v++; + int64_t array_len = static_cast(*v++); + int64_t child_count = static_cast(*v++); for (size_t i = 0; i < child_count; ++i) if (!CreatePolyPathDFromCPolyPath(v, result)) return false; return true; } + TEST(Clipper2Tests, ExportHeader64) { uint8_t None = 0, Intersection = 1, Union = 2, Difference = 3, Xor = 4; @@ -69,10 +87,10 @@ TEST(Clipper2Tests, ExportHeader64) // Normally clipper.export.h will be compiled into a DLL/so so it can be called // by non C++ applications. If CreateCPaths64 was an exported function and it // was called by a non C++ application, it would crash that application. - CPaths64 c_subj = CreateCPaths(subj); - CPaths64 c_clip = CreateCPaths(clip); + CPaths64 c_subj = CreateCPathsFromPathsT(subj); + CPaths64 c_clip = CreateCPathsFromPathsT(clip); BooleanOp64(Intersection, EvenOdd, c_subj, c_subj_open, c_clip, c_sol, c_sol_open); - solution = ConvertCPaths(c_sol); + solution = ConvertCPathsToPathsT(c_sol); //clean up !!! delete[] c_subj; delete[] c_clip; @@ -80,6 +98,7 @@ TEST(Clipper2Tests, ExportHeader64) DisposeArray64(c_sol_open); EXPECT_EQ(solution.size(), 5); } + TEST(Clipper2Tests, ExportHeaderD) { uint8_t None = 0, Intersection = 1, Union = 2, Difference = 3, Xor = 4; @@ -96,10 +115,10 @@ TEST(Clipper2Tests, ExportHeaderD) // Normally clipper.export.h will be compiled into a DLL/so so it can be called // by non C++ applications. If CreateCPathsD was an exported function and it // was called by a non C++ application, it would crash that application. - CPathsD c_subj = CreateCPaths(subj); - CPathsD c_clip = CreateCPaths(clip); + CPathsD c_subj = CreateCPathsFromPathsT(subj); + CPathsD c_clip = CreateCPathsFromPathsT(clip); BooleanOpD(Intersection, EvenOdd, c_subj, c_subj_open, c_clip, c_sol, c_sol_open); - solution = ConvertCPaths(c_sol); + solution = ConvertCPathsToPathsT(c_sol); //clean up !!! delete[] c_subj; delete[] c_clip; @@ -107,6 +126,7 @@ TEST(Clipper2Tests, ExportHeaderD) DisposeArrayD(c_sol_open); EXPECT_EQ(solution.size(), 5); } + TEST(Clipper2Tests, ExportHeaderTree64) { uint8_t None = 0, Intersection = 1, Union = 2, Difference = 3, Xor = 4; @@ -121,8 +141,8 @@ TEST(Clipper2Tests, ExportHeaderTree64) // More likely, clipper.export.h will be compiled into a DLL/so so it can be // called by non C++ applications. If CreateCPaths64 was an exported function // and it was called by a non C++ application, it would crash that application. - CPaths64 c_subj = CreateCPaths(subj); - CPaths64 c_clip = CreateCPaths(clip); + CPaths64 c_subj = CreateCPathsFromPathsT(subj); + CPaths64 c_clip = CreateCPathsFromPathsT(clip); int64_t* c_sol_tree = nullptr; BooleanOp_PolyTree64(Intersection, EvenOdd, c_subj, c_subj_open, c_clip, c_sol_tree, c_sol_open); PolyTree64 sol_tree; @@ -135,12 +155,15 @@ TEST(Clipper2Tests, ExportHeaderTree64) delete[] c_clip; DisposeArray64(c_sol_tree); DisposeArray64(c_sol_open); + PolyPath64* pp = &sol_tree; - for (int i = 0; i < 4; ++i) + for (int i = 0; i < 5; ++i) { - EXPECT_TRUE(pp->Count() == 1); pp = pp->Child(0); + EXPECT_TRUE(pp->Count() == 1); + pp = pp->Child(0); } } + TEST(Clipper2Tests, ExportHeaderTreeD) { uint8_t None = 0, Intersection = 1, Union = 2, Difference = 3, Xor = 4; @@ -155,11 +178,12 @@ TEST(Clipper2Tests, ExportHeaderTreeD) // More likely, clipper.export.h will be compiled into a DLL/so so it can be // called by non C++ applications. If CreateCPathsD was an exported function // and it was called by a non C++ application, it would crash that application. - CPathsD c_subj = CreateCPaths(subj); - CPathsD c_clip = CreateCPaths(clip); + CPathsD c_subj = CreateCPathsFromPathsT(subj); + CPathsD c_clip = CreateCPathsFromPathsT(clip); static const int precision = 4; CPolyPathD c_sol_tree = nullptr; - BooleanOp_PolyTreeD(Intersection, EvenOdd, c_subj, c_subj_open, c_clip, c_sol_tree, c_sol_open, precision); + BooleanOp_PolyTreeD(Intersection, EvenOdd, c_subj, c_subj_open, c_clip, + c_sol_tree, c_sol_open, precision); PolyTreeD sol_tree; // convert CPolyTreeD to PolyTreeD BuildPolyTreeDFromCPolyTree(c_sol_tree, sol_tree); @@ -171,8 +195,9 @@ TEST(Clipper2Tests, ExportHeaderTreeD) DisposeArrayD(c_sol_tree); DisposeArrayD(c_sol_open); PolyPathD* pp = &sol_tree; - for (int i = 0; i < 4; ++i) + for (int i = 0; i < 5; ++i) { - EXPECT_TRUE(pp->Count() == 1); pp = pp->Child(0); + EXPECT_TRUE(pp->Count() == 1); + pp = pp->Child(0); } } \ No newline at end of file diff --git a/CPP/Tests/TestPolygons.cpp b/CPP/Tests/TestPolygons.cpp index e2a18c4b..0d9514bf 100644 --- a/CPP/Tests/TestPolygons.cpp +++ b/CPP/Tests/TestPolygons.cpp @@ -58,7 +58,7 @@ TEST(Clipper2Tests, TestMultiplePolygons) if (stored_count <= 0) ; // skip count else if (IsInList(test_number, { 120, 121, 130, 138, - 140, 148, 163, 165, 166, 167, 168, 172, 175, 178, 180 })) + 140, 148, 163, 165, 166, 167, 168, 172, 173, 175, 178, 180 })) EXPECT_NEAR(measured_count, stored_count, 5) << " in test " << test_number; else if (IsInList(test_number, { 27, 181 })) EXPECT_NEAR(measured_count, stored_count, 2) << " in test " << test_number; diff --git a/DLL/CPP_DLL/Clipper2ZDll.vcxproj b/DLL/CPP_DLL/Clipper2ZDll.vcxproj new file mode 100644 index 00000000..cf7f662c --- /dev/null +++ b/DLL/CPP_DLL/Clipper2ZDll.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {fb0a93ce-8e28-4e67-8d05-54b38b218f08} + Clipper2 + 10.0 + Clipper2ZDll + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + Clipper2_Z_64 + ..\..\CPP\Clipper2Lib\include;$(IncludePath) + + + Clipper2_Z_64 + ..\..\CPP\Clipper2Lib\include;$(IncludePath) + + + Clipper2_Z_64 + ..\..\CPP\Clipper2Lib\include;$(IncludePath) + + + Clipper2_Z_64 + ..\..\CPP\Clipper2Lib\include;$(IncludePath) + + + + Level3 + true + true + NotUsing + pch.h + stdcpp17 + stdc17 + USINGZ;%(PreprocessorDefinitions) + + + Windows + true + false + + + + + Level3 + true + true + true + true + NotUsing + pch.h + stdcpp17 + stdc17 + USINGZ;%(PreprocessorDefinitions) + + + Windows + true + true + true + false + + + + + Level3 + true + true + NotUsing + pch.h + stdcpp17 + stdc17 + USINGZ;%(PreprocessorDefinitions) + + + Windows + true + false + + + + + Level3 + true + true + true + true + NotUsing + pch.h + stdcpp17 + stdc17 + USINGZ;%(PreprocessorDefinitions) + + + Windows + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DLL/CPP_DLL/Clipper2_DLL.sln b/DLL/CPP_DLL/Clipper2_DLL.sln index 93a82e75..c518302e 100644 --- a/DLL/CPP_DLL/Clipper2_DLL.sln +++ b/DLL/CPP_DLL/Clipper2_DLL.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32901.215 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Clipper2_Export", "Clipper2_Export.vcxproj", "{FB0A93CE-8E28-4E67-8D05-54B38B218F08}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Clipper2_Dll", "Clipper2_Dll.vcxproj", "{FB0A93CE-8E28-4E67-8D05-54B38B218F08}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/DLL/CPP_DLL/Clipper2_Export.vcxproj b/DLL/CPP_DLL/Clipper2_Dll.vcxproj similarity index 96% rename from DLL/CPP_DLL/Clipper2_Export.vcxproj rename to DLL/CPP_DLL/Clipper2_Dll.vcxproj index 4ff8c26f..25be6e08 100644 --- a/DLL/CPP_DLL/Clipper2_Export.vcxproj +++ b/DLL/CPP_DLL/Clipper2_Dll.vcxproj @@ -24,31 +24,32 @@ {fb0a93ce-8e28-4e67-8d05-54b38b218f08} Clipper2 10.0 + Clipper2Dll DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode @@ -75,11 +76,11 @@ ..\..\CPP\Clipper2Lib\include;$(IncludePath) - Clipper2_32 + Clipper2_64 ..\..\CPP\Clipper2Lib\include;$(IncludePath) - Clipper2_32 + Clipper2_64 ..\..\CPP\Clipper2Lib\include;$(IncludePath) diff --git a/DLL/CPP_DLL/Clipper2_Z_DLL.sln b/DLL/CPP_DLL/Clipper2_Z_DLL.sln new file mode 100644 index 00000000..429835b6 --- /dev/null +++ b/DLL/CPP_DLL/Clipper2_Z_DLL.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32901.215 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Clipper2ZDll", "Clipper2ZDll.vcxproj", "{FB0A93CE-8E28-4E67-8D05-54B38B218F08}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Debug|x64.ActiveCfg = Debug|x64 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Debug|x64.Build.0 = Debug|x64 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Debug|x86.ActiveCfg = Debug|Win32 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Debug|x86.Build.0 = Debug|Win32 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Release|x64.ActiveCfg = Release|x64 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Release|x64.Build.0 = Release|x64 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Release|x86.ActiveCfg = Release|Win32 + {FB0A93CE-8E28-4E67-8D05-54B38B218F08}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD166825-2BC9-4724-BCF1-F70F90CB74F2} + EndGlobalSection +EndGlobal diff --git a/DLL/CSharp_TestApp/CSharp_TestApp.csproj b/DLL/CSharp_TestApp/CSharp_TestApp.csproj deleted file mode 100644 index fe6a125f..00000000 --- a/DLL/CSharp_TestApp/CSharp_TestApp.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - Exe - net6.0 - enable - enable - x64 - - - diff --git a/DLL/CSharp_TestApp/Program.cs b/DLL/CSharp_TestApp/Program.cs deleted file mode 100644 index b0c491f1..00000000 --- a/DLL/CSharp_TestApp/Program.cs +++ /dev/null @@ -1,291 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 29 October 2023 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -using System; -using System.Runtime.InteropServices; - -namespace ClipperDllDemo -{ - - public class Application - { - - // CreateCPaths: The CPaths structure is defined in - // clipper.export.h and is a simple array of long[] or - // double[] that represents a number of path contours. - - static T[]? CreateCPath(T[] coords) - { - int pathLen = coords.Length / 2; - if (pathLen == 0) return null; - int arrayLen = pathLen * 2 + 2; - T[] result = new T[arrayLen]; - result[0] = (T)Convert.ChangeType(pathLen, typeof(T)); - result[1] = (T)Convert.ChangeType(0, typeof(T)); - coords.CopyTo(result, 2); - return result; - } - - static T[] CreateCPaths(List listOfCPath) - { - int pathCount = listOfCPath.Count(); - int arrayLen = 2; - foreach (T[] path in listOfCPath) - arrayLen += path.Length; - T[] result = new T[arrayLen]; - - result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); - result[1] = (T)Convert.ChangeType(pathCount, typeof(T)); - - int idx = 2; - foreach (T[] cpath in listOfCPath) - { - cpath.CopyTo(result, idx); - idx += cpath.Length; - } - return result; - } - - // or create a cpaths array that contains just 1 path - static T[] CreateCPaths(T[] coords) - { - int pathLen = coords.Length / 2; - int arrayLen = pathLen *2 + 2 + 2; - T[] result = new T[arrayLen]; - - result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); - result[1] = (T)Convert.ChangeType(1, typeof(T)); // 1 path - - result[2] = (T)Convert.ChangeType(pathLen, typeof(T)); - result[3] = (T)Convert.ChangeType(0, typeof(T)); - - coords.CopyTo(result, 4); - return result; - } - - public static string XyCoordsAsString(T X, T Y, int precision = 0) - { - if (typeof(T) == typeof(long)) // ignore precision - return $"{X},{Y} "; - else - return string.Format($"{{0:F{precision}}},{{1:F{precision}}} ", X, Y); - } - - public static void DisplayCPath(T[] cpaths, ref int idx, string spaceIndent) - { - int vertexCnt = Convert.ToInt32(cpaths[idx]); - idx += 2; - for (int i = 0; i < vertexCnt; i++) - Console.Write(spaceIndent + - XyCoordsAsString(cpaths[idx++], cpaths[idx++], 2)); - Console.Write("\n"); - } - - public static void DisplayCPaths(T[]? cpaths, string spaceIndent) - { - if (cpaths == null) return; - int pathCnt = Convert.ToInt32(cpaths[1]); - int idx = 2; - for (int i = 0; i < pathCnt; i++) - DisplayCPath(cpaths, ref idx, spaceIndent); - } - - // Note: The CPolyTree structure defined in clipper.export.h is - // a simple array of T that contains any number of nested path contours. - - public static void DisplayPolyPath(T[] polypath, - ref int idx, bool isHole, string spaceIndent, int precision) - { - int polyCnt = Convert.ToInt32(polypath[idx++]); - int childCnt = Convert.ToInt32(polypath[idx++]); - string preamble = isHole ? "Hole: " : (spaceIndent == "") ? - "Polygon: " : "Nested Polygon: "; - Console.Write(spaceIndent + preamble); - spaceIndent += " "; - for (int i = 0; i < polyCnt; i++) - Console.Write(XyCoordsAsString(polypath[idx++], polypath[idx++], precision)); - Console.Write("\n"); - for (int i = 0; i < childCnt; i++) - DisplayPolyPath(polypath, ref idx, !isHole, spaceIndent, precision); - } - - public static void DisplayPolytree(T[] polytree, int precision) - { - int cnt = Convert.ToInt32(polytree[1]); - int idx = 2; - for (int i = 0; i < cnt; i++) - DisplayPolyPath(polytree, ref idx, false, " ", precision); - } - - public static T[]? GetArrayFromIntPtr(IntPtr paths) - { - if (paths == IntPtr.Zero) return null; - if (typeof(T) == typeof(long)) - { - long[] len = new long[1]; - Marshal.Copy(paths, len, 0, 1); - long[] res = new long[(int)len[0]]; - Marshal.Copy(paths, res, 0, (int)len[0]); - return res as T[]; - } - else if (typeof(T) == typeof(double)) - { - double[] len = new double[1]; - Marshal.Copy(paths, len, 0, 1); - double[] res = new double[(int)len[0]]; - Marshal.Copy(paths, res, 0, (int)len[0]); - return res as T[]; - } - else return null; - } - - // DLL exported function definitions ///////////////////// - - const string clipperDll = @"..\..\..\..\Clipper2_64.dll"; - - [DllImport(clipperDll, EntryPoint = - "Version", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr Version(); - - [DllImport(clipperDll, EntryPoint = - "BooleanOp64", CallingConvention = CallingConvention.Cdecl)] - static extern Int32 BooleanOp64(byte clipType, byte fillRule, - long[] subjects, long[]? openSubs, long[]? clips, - out IntPtr solution, out IntPtr openSol, bool preserveCollinear, bool reverseSolution); - - [DllImport(clipperDll, EntryPoint = - "BooleanOpD", CallingConvention = CallingConvention.Cdecl)] - static extern Int32 BooleanOpD(byte clipType, byte fillRule, - double[] subjects, double[]? openSubs, double[]? clips, - out IntPtr solution, out IntPtr openSol, Int32 precision, bool preserveCollinear, bool reverseSolution); - - [DllImport(clipperDll, EntryPoint = - "DisposeArray64", CallingConvention = CallingConvention.Cdecl)] - static extern void DisposeArray64(ref IntPtr intptr); - - // DisposeExported(): since all these functions behave identically ... - [DllImport(clipperDll, EntryPoint = - "DisposeArrayD", CallingConvention = CallingConvention.Cdecl)] - static extern void DisposeArrayD(ref IntPtr intptr); - - [DllImport(clipperDll, EntryPoint = - "BooleanOp_PolyTree64", CallingConvention = CallingConvention.Cdecl)] - static extern Int32 BooleanOp_PolyTree64(byte cliptype, - byte fillrule, long[] subjects, long[]? openSubs, long[]? clips, - out IntPtr solTree, out IntPtr openSol, - bool preserve_collinear, bool reverse_solution); - - [DllImport(clipperDll, EntryPoint = - "BooleanOp_PolyTreeD", CallingConvention = CallingConvention.Cdecl)] - static extern Int32 BooleanOp_PolyTreeD(byte cliptype, - byte fillrule, double[] subjects, double[]? openSubs, double[]? clips, - out IntPtr solTree, out IntPtr openSol, Int32 precision, - bool preserve_collinear, bool reverse_solution); - - - public static readonly byte None = 0, Intersection = 1, Union = 2, Difference = 3, Xor = 4; - public static readonly byte EvenOdd = 0, NonZero = 1, Positive = 2, Negative = 3; - - - /// Main Entry //////////////////////////////////////////////////////////// - public static void Main() - { - - //string? ver = Marshal.PtrToStringAnsi(Version()); - //Console.WriteLine(ver +"\n"); - - // test BooleanOp64() /////////////////////////////////////////////////// - Console.WriteLine("BooleanOp64:"); - long[] cSubject = CreateCPaths(new long[] { 0, 0, 100, 0, 100, 100, 0, 100 }); - long[] cClip = CreateCPaths(new long[] { 20, 20, 120, 20, 120, 120, 20, 120 }); - - if (BooleanOp64(Intersection, NonZero, cSubject, - null, cClip, out IntPtr cSol, out IntPtr cSolOpen, false, false) != 0) return; - - long[]? cSolution = GetArrayFromIntPtr(cSol); - // clean up unmanaged memory - DisposeArray64(ref cSol); - DisposeArray64(ref cSolOpen); - - DisplayCPaths(cSolution, " "); - ///////////////////////////////////////////////////////////////////////// - - // test BooleanOpD() //////////////////////////////////////////////////// - Console.WriteLine("BooleanOpD:"); - double[] cSubjectD = CreateCPaths(new double[] { 0, 0, 100, 0, 100, 100, 0, 100 }); - double[] cClipD = CreateCPaths(new double[] { 20, 20, 120, 20, 120, 120, 20, 120 }); - int resultD = BooleanOpD(Intersection, NonZero, cSubjectD, - null, cClipD, out IntPtr cSolD, out IntPtr cSolOpenD, 2, false, false); - if (resultD != 0) return; - double[]? cSolutionD = GetArrayFromIntPtr(cSolD); - // clean up unmanaged memory - DisposeArrayD(ref cSolD); - DisposeArrayD(ref cSolOpenD); - - DisplayCPaths(cSolutionD, " "); - ///////////////////////////////////////////////////////////////////////// - - - - // test BooleanOp_PolyTree64() ////////////////////////////////////////// - Console.WriteLine("BooleanOp_PolyTree64:"); - - List subList = new(5); - for (int i = 1; i < 6; ++i) - subList.Add(CreateCPath(new long[] { - -i * 20, -i * 20, i * 20, -i * 20, i * 20, i * 20, -i * 20, i * 20 })!); - - long[] cSubject3 = CreateCPaths(subList); - long[] cClip3 = CreateCPaths(new long[] { -90, -120, 90, -120, 90, 120, -90, 120 }); - - int result3 = BooleanOp_PolyTree64(Intersection, EvenOdd, cSubject3, null, cClip3, - out IntPtr cSol_pt64, out IntPtr cSolOpen_pt64, false, false); - if (result3 != 0) return; - - long[]? cPolyTree64 = GetArrayFromIntPtr(cSol_pt64); - // clean up unmanaged memory - DisposeArray64(ref cSol_pt64); - DisposeArray64(ref cSolOpen_pt64); - - if (cPolyTree64 == null) return; - DisplayPolytree(cPolyTree64, 2); - ///////////////////////////////////////////////////////////////////////// - - - // test BooleanOp_PolyTreeD() /////////////////////////////////////////// - Console.WriteLine("BooleanOp_PolyTreeD:"); - - List subList2 = new(5); - for (int i = 1; i < 6; ++i) - subList2.Add(CreateCPath(new double[] { - -i * 20, -i * 20, i * 20, -i * 20, i * 20, i * 20, -i * 20, i * 20 })!); - - double[] cSubject4 = CreateCPaths(subList2); - double[] cClip4 = CreateCPaths(new double[] { -90, -120, 90, -120, 90, 120, -90, 120 }); - - int result4 = BooleanOp_PolyTreeD(Intersection, EvenOdd, cSubject4, null, cClip4, - out IntPtr cSol_ptD, out IntPtr cSolOpen_ptD, 2, false, false); - if (result4 != 0) return; - - double[]? cPolyTreeD = GetArrayFromIntPtr(cSol_ptD); - - // clean up unmanaged memory - DisposeArrayD(ref cSol_ptD); - DisposeArrayD(ref cSolOpen_ptD); - - if (cPolyTreeD == null) return; - DisplayPolytree(cPolyTreeD, 2); - ///////////////////////////////////////////////////////////////////////// - - - Console.WriteLine("\nPress any key to exit ... "); - Console.ReadKey(); - } - - } //end Application -} //namespace diff --git a/DLL/CSharp_TestApp2/CSharp_TestApp2.csproj b/DLL/CSharp_TestApp2/CSharp_TestApp2.csproj deleted file mode 100644 index 85e03793..00000000 --- a/DLL/CSharp_TestApp2/CSharp_TestApp2.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net6.0 - enable - enable - x64 - - - - - - - - diff --git a/DLL/CSharp_TestApp2/CSharp_TestApp2.sln b/DLL/CSharp_TestApp2/CSharp_TestApp2.sln deleted file mode 100644 index 3b598060..00000000 --- a/DLL/CSharp_TestApp2/CSharp_TestApp2.sln +++ /dev/null @@ -1,63 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33801.468 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp_TestApp2", "CSharp_TestApp2.csproj", "{3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Clipper2Lib", "..\..\CSharp\Clipper2Lib\Clipper2Lib.csproj", "{401DBA71-BB90-495E-9F91-CC495FEE264D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Clipper2.SVG", "..\..\CSharp\Utils\SVG\Clipper2.SVG.csproj", "{77C119ED-E0A4-4A76-B858-E7B4AF4DE892}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|Any CPU.ActiveCfg = Debug|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x64.ActiveCfg = Debug|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x64.Build.0 = Debug|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x86.ActiveCfg = Debug|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x86.Build.0 = Debug|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|Any CPU.ActiveCfg = Release|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x64.ActiveCfg = Release|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x64.Build.0 = Release|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x86.ActiveCfg = Release|x64 - {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x86.Build.0 = Release|x64 - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Debug|x64.ActiveCfg = Debug|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Debug|x64.Build.0 = Debug|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Debug|x86.ActiveCfg = Debug|x86 - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Debug|x86.Build.0 = Debug|x86 - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Release|Any CPU.Build.0 = Release|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Release|x64.ActiveCfg = Release|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Release|x64.Build.0 = Release|Any CPU - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Release|x86.ActiveCfg = Release|x86 - {401DBA71-BB90-495E-9F91-CC495FEE264D}.Release|x86.Build.0 = Release|x86 - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Debug|x64.ActiveCfg = Debug|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Debug|x64.Build.0 = Debug|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Debug|x86.ActiveCfg = Debug|x86 - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Debug|x86.Build.0 = Debug|x86 - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Release|Any CPU.Build.0 = Release|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Release|x64.ActiveCfg = Release|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Release|x64.Build.0 = Release|Any CPU - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Release|x86.ActiveCfg = Release|x86 - {77C119ED-E0A4-4A76-B858-E7B4AF4DE892}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {DAE344E0-A107-49C8-B269-1E1896665E6B} - EndGlobalSection -EndGlobal diff --git a/DLL/CSharp_TestApp2/Program.cs b/DLL/CSharp_TestApp2/Program.cs deleted file mode 100644 index 845ac998..00000000 --- a/DLL/CSharp_TestApp2/Program.cs +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 26 October 2023 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -using System.Diagnostics; -using System.Runtime.InteropServices; -using Clipper2Lib; - -namespace ClipperDllDemo -{ - public class Application - { - - // Define miscellaneous functions //////////////////////////// - public static void OpenFileWithDefaultApp(string filename) - { - string path = Path.GetFullPath(filename); - if (!File.Exists(path)) return; - Process p = new() { StartInfo = new ProcessStartInfo(path) { UseShellExecute = true } }; - p.Start(); - } - - public static Path64 MakePath(int[] arr) - { - int len = arr.Length / 2; - Path64 p = new(len); - for (int i = 0; i < len; i++) - p.Add(new Point64(arr[i * 2], arr[i * 2 + 1])); - return p; - } - - private static Point64 MakeRandomPt(int maxWidth, int maxHeight, Random rand) - { - long x = rand.Next(maxWidth); - long y = rand.Next(maxHeight); - return new Point64(x, y); - } - - public static Path64 MakeRandomPath(int width, int height, int count, Random rand) - { - Path64 result = new(count); - for (int i = 0; i < count; ++i) - result.Add(MakeRandomPt(width, height, rand)); - return result; - } - - static Path64 GetPath64FromCPath(long[] cpaths, ref int idx) - { - int cnt = (int)cpaths[idx]; idx += 2; - Path64 result = new(cnt); - for (int i = 0; i < cnt; i++) - { - long x = cpaths[idx++]; - long y = cpaths[idx++]; - result.Add(new Point64(x, y)); - } - return result; - } - - static Paths64 GetPaths64FromCPaths(long[] cpaths) - { - int cnt = (int)cpaths[1], idx = 2; - Paths64 result = new(cnt); - for (int i = 0; i < cnt; i++) - result.Add(GetPath64FromCPath(cpaths, ref idx)); - return result; - } - - static long[] CreateCPaths64(Paths64 pp) - { - int len = pp.Count, len2 = 2; - for (int i = 0; i < len; i++) - if (pp[i].Count > 0) - len2 += pp[i].Count * 2 + 2; - long[] result = new long[len2]; - result[0] = 0; - result[1] = len; - int rPos = 2; - for (int i = 0; i < len; i++) - { - len2 = pp[i].Count; - if (len2 == 0) continue; - result[rPos++] = len2; - result[rPos++] = 0; - for (int j = 0; j < len2; j++) - { - result[rPos++] = pp[i][j].X; - result[rPos++] = pp[i][j].Y; - } - } - return result; - } - - public static long[]? GetPathsFromIntPtr(IntPtr paths) - { - if (paths == IntPtr.Zero) return null; - long[] len = new long[1]; - Marshal.Copy(paths, len, 0, 1); - long[] result = new long[len[0]]; - Marshal.Copy(paths, result, 0, (int)len[0]); - return result; - } - - // Define DLL exported functions ///////////////////// - - public const string clipperDll = @"..\..\..\..\Clipper2_64.dll"; - - [DllImport(clipperDll, EntryPoint = "Version", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr Version(); - - [DllImport(clipperDll, EntryPoint = "BooleanOp64", CallingConvention = CallingConvention.Cdecl)] - static extern int BooleanOp64(byte clipType, byte fillRule, - long[] subject, long[]? subOpen, long[]? clip, - out IntPtr solution, out IntPtr solOpen, bool preserveCollinear, bool reverseSolution); - - [DllImport(clipperDll, EntryPoint = "DisposeArray64", CallingConvention = CallingConvention.Cdecl)] - static extern void DisposeArray64(ref IntPtr paths); - - static readonly byte None = 0, Intersection = 1, Union = 2, Difference = 3, Xor = 4; - static readonly byte EvenOdd = 0, NonZero = 1, Positive = 2, Negative = 3; - - public static void Main() - { - - //string? ver = Marshal.PtrToStringAnsi(Version()); - //Console.WriteLine(ver + "\n"); - - long timeMsec; - Random rand = new(); - - //////////////////////////////////////////////////////////////////////// - int edgeCount = 2500; - //////////////////////////////////////////////////////////////////////// - - Paths64 subject = new() { MakeRandomPath(600,400, edgeCount, rand)}; - Paths64 clip = new() { MakeRandomPath(600, 400, edgeCount, rand) }; - - ////////////////////////////////////////////////////////////////////// - // Use Dynamically Linked C++ compiled library (ie use the DLL) - // NB: time will include ALL the overhead of swapping path structures - Stopwatch sw1 = Stopwatch.StartNew(); - long[] cSubject = CreateCPaths64(subject); - long[] cClip = CreateCPaths64(clip); - if (BooleanOp64(Intersection, NonZero, cSubject, - null, cClip, out IntPtr cSol, out IntPtr cSolOpen, false, false) != 0) - return; - - long[]? cSolution = GetPathsFromIntPtr(cSol); - if (cSolution == null) return; - DisposeArray64(ref cSol); - DisposeArray64(ref cSolOpen); - Paths64 solution = GetPaths64FromCPaths(cSolution); - sw1.Stop(); - timeMsec = sw1.ElapsedMilliseconds; - Console.WriteLine($"Time using DLL (C++ code): {timeMsec} ms"); - ////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////// - // Use Clipper2's statically linked C# compiled library - Stopwatch sw2 = Stopwatch.StartNew(); - Clipper.Intersect(subject, clip, FillRule.NonZero); - sw2.Stop(); - timeMsec = sw2.ElapsedMilliseconds; - Console.WriteLine($"Time using C# code : {timeMsec} ms"); - ////////////////////////////////////////////////////////////////////// - - string fileName = "../../../clipper2_dll.svg"; - SvgWriter svg = new(FillRule.NonZero); - SvgUtils.AddSubject(svg, subject); - SvgUtils.AddClip(svg, clip); - SvgUtils.AddSolution(svg, solution, false); - svg.SaveToFile(fileName, 800, 600, 20); - OpenFileWithDefaultApp(fileName); - - Console.WriteLine("Press any key to exit ... "); - Console.ReadKey(); - } - - } //end Application -} //namespace diff --git a/DLL/Delphi_TestApp/COMPILE AND ADD Clipper2_64.dll HERE b/DLL/Delphi_TestApp/COMPILE AND ADD Clipper2_64.dll HERE deleted file mode 100644 index e69de29b..00000000 diff --git a/DLL/Delphi_TestApp/Test_DLL.dpr b/DLL/Delphi_TestApp/Test_DLL.dpr deleted file mode 100644 index 7869bd03..00000000 --- a/DLL/Delphi_TestApp/Test_DLL.dpr +++ /dev/null @@ -1,736 +0,0 @@ -program Test_DLL; - -// Make sure that the Clipper2 DLLS are in either -// the OS Path or in the application's folder. - -{$APPTYPE CONSOLE} -{$R *.res} - -uses - Windows, - Math, - ShellApi, - SysUtils, - Clipper in '..\..\Delphi\Clipper2Lib\Clipper.pas', - Clipper.Core in '..\..\Delphi\Clipper2Lib\Clipper.Core.pas', - Clipper.Engine in '..\..\Delphi\Clipper2Lib\Clipper.Engine.pas', - Clipper.SVG in '..\..\Delphi\Utils\Clipper.SVG.pas', - Colors in '..\..\Delphi\Utils\Colors.pas', - Timer in '..\..\Delphi\Utils\Timer.pas'; - -type - CInt64arr = array[0..$FFFF] of Int64; - PCInt64arr = ^CInt64arr; - CPath64 = PCInt64arr; - CPaths64 = PCInt64arr; - CPolyPath64 = PCInt64arr; - CPolytree64 = PCInt64arr; - - CDblarr = array[0..$FFFF] of Double; - PCDblarr = ^CDblarr; - CPathD = PCDblarr; - CPathsD = PCDblarr; - CPolyPathD = PCDblarr; - CPolytreeD = PCDblarr; - -const -{$IFDEF WIN64} - CLIPPER2_DLL = 'Clipper2_64.dll'; -{$ELSE} - CLIPPER2_DLL = 'Clipper2_32.dll'; -{$ENDIF} - - -//////////////////////////////////////////////////////// -// Clipper2 DLL functions -//////////////////////////////////////////////////////// - -function Version(): PAnsiChar; cdecl; - external CLIPPER2_DLL name 'Version'; - -procedure DisposeExportedArray64(var cps: PCInt64arr); cdecl; - external CLIPPER2_DLL name 'DisposeArray64'; -procedure DisposeExportedArrayD(var cp: PCDblarr); cdecl; - external CLIPPER2_DLL name 'DisposeArrayD'; - -function BooleanOp64(cliptype: UInt8; fillrule: UInt8; - const subjects: CPaths64; const subjects_open: CPaths64; - const clips: CPaths64; out solution: CPaths64; - out solution_open: CPaths64; - preserve_collinear: boolean = true; - reverse_solution: boolean = false): integer; cdecl; - external CLIPPER2_DLL name 'BooleanOp64'; -function BooleanOp_PolyTree64(cliptype: UInt8; fillrule: UInt8; - const subjects: CPaths64; const subjects_open: CPaths64; - const clips: CPaths64; out solution: CPolyTree64; - out solution_open: CPaths64; - preserve_collinear: boolean = true; - reverse_solution: boolean = false): integer; cdecl; - external CLIPPER2_DLL name 'BooleanOp_PolyTree64'; - -function BooleanOpD(cliptype: UInt8; fillrule: UInt8; - const subjects: CPathsD; const subjects_open: CPathsD; - const clips: CPathsD; out solution: CPathsD; out solution_open: CPathsD; - precision: integer = 2; - preserve_collinear: boolean = true; - reverse_solution: boolean = false): integer; cdecl; - external CLIPPER2_DLL name 'BooleanOpD'; -function BooleanOp_PolyTreeD(cliptype: UInt8; fillrule: UInt8; - const subjects: CPathsD; const subjects_open: CPathsD; - const clips: CPathsD; out solution: CPolyTreeD; out solution_open: CPathsD; - precision: integer = 2; - preserve_collinear: boolean = true; - reverse_solution: boolean = false): integer; cdecl; - external CLIPPER2_DLL name 'BooleanOp_PolyTreeD'; -function InflatePaths64(const paths: CPaths64; - delta: double; jointype, endtype: UInt8; miter_limit: double = 2.0; - arc_tolerance: double = 0.0; - reverse_solution: Boolean = false): CPaths64; cdecl; - external CLIPPER2_DLL name 'InflatePaths64'; -function InflatePathsD(const paths: CPathsD; - delta: double; jointype, endtype: UInt8; precision: integer = 2; - miter_limit: double = 2.0; arc_tolerance: double = 0.0; - reverse_solution: Boolean = false): CPathsD; cdecl; - external CLIPPER2_DLL name 'InflatePathsD'; - -function RectClip64(const rect: TRect64; const paths: CPaths64; - convexOnly: Boolean = false): CPaths64; cdecl; - external CLIPPER2_DLL name 'RectClip64'; -function RectClipD(const rect: TRectD; const paths: CPathsD; - precision: integer = 2; convexOnly: Boolean = false): CPathsD; cdecl; - external CLIPPER2_DLL name 'RectClipD'; -function RectClipLines64(const rect: TRect64; - const paths: CPaths64): CPaths64; cdecl; - external CLIPPER2_DLL name 'RectClipLines64'; -function RectClipLinesD(const rect: TRectD; - const paths: CPathsD; precision: integer = 2): CPathsD; cdecl; - external CLIPPER2_DLL name 'RectClipLinesD'; - -const - Intersection = 1; Union = 2; Difference =3; Xor_ = 4; - EvenOdd = 0; NonZero = 1; Positive = 2; Negative = 3; - -//////////////////////////////////////////////////////// -// functions related to Clipper2 DLL structures -//////////////////////////////////////////////////////// - -procedure DisposeLocalArray64(cp: PCInt64arr); -begin - FreeMem(cp); -end; - -procedure DisposeLocalArrayD(cp: PCDblarr); -begin - FreeMem(cp); -end; - -//////////////////////////////////////////////////////// -// path format conversion functions -//////////////////////////////////////////////////////// - -function CreateCPaths64(const pp: TPaths64): CPaths64; -var - i,j, len, len2: integer; - v: PInt64; -begin - len := Length(pp); - len2 := 2; - for i := 0 to len -1 do - if Length(pp[i]) > 0 then - inc(len2, Length(pp[i]) *2 + 2); - GetMem(Result, len2 * sizeof(Int64)); - Result[0] := len2; - Result[1] := len; - v := @Result[2]; - for i := 0 to len -1 do - begin - len2 := Length(pp[i]); - if len2 = 0 then continue; - v^ := len2; inc(v); - v^ := 0; inc(v); - for j := 0 to len2 -1 do - begin - v^ := pp[i][j].X; inc(v); - v^ := pp[i][j].Y; inc(v); - end; - end; -end; - -function CreateCPathsD(const pp: TPathsD): CPathsD; -var - i,j, len, len2: integer; - v: PDouble; -begin - len := Length(pp); - len2 := 2; - for i := 0 to len -1 do - if Length(pp[i]) > 0 then - inc(len2, Length(pp[i]) *2 + 2); - GetMem(Result, len2 * sizeof(double)); - Result[0] := len2; - Result[1] := len; - v := @Result[2]; - for i := 0 to len -1 do - begin - len2 := Length(pp[i]); - if len2 = 0 then continue; - v^ := len2; inc(v); - v^ := 0; inc(v); - for j := 0 to len2 -1 do - begin - v^ := pp[i][j].X; inc(v); - v^ := pp[i][j].Y; inc(v); - end; - end; -end; - -function ConvertToTPaths64(cp: CPaths64): TPaths64; -var - i, j, len, len2: integer; - v: PInt64; -begin - Result := nil; - v := PInt64(cp); - inc(v); // ignore array length - len := v^; inc(v); - SetLength(Result, len); - for i := 0 to len -1 do - begin - len2 := v^; inc(v, 2); - SetLength(Result[i], len2); - for j := 0 to len2 -1 do - begin - Result[i][j].X := v^; inc(v); - Result[i][j].Y := v^; inc(v); - end; - end; -end; - -function ConvertToTPathsD(cp: CPathsD): TPathsD; -var - i, j, len, len2: integer; - v: PDouble; -begin - Result := nil; - v := PDouble(cp); - inc(v); // ignore array length - len := Round(cp[1]); inc(v); - SetLength(Result, len); - for i := 0 to len -1 do - begin - len2 := Round(v^); inc(v, 2); - SetLength(Result[i], len2); - for j := 0 to len2 -1 do - begin - Result[i][j].X := v^; inc(v); - Result[i][j].Y := v^; inc(v); - end; - end; -end; - -function GetPolyPath64ArrayLen(const pp: TPolyPath64): integer; -var - i: integer; -begin - Result := 2; // poly_length + child_count - inc(Result, Length(pp.Polygon) * 2); - for i := 0 to pp.Count -1 do - Inc(Result, GetPolyPath64ArrayLen(pp.Child[i])); -end; - -procedure GetPolytreeCountAndCStorageSize(const tree: TPolyTree64; - out cnt: integer; out arrayLen: integer); -begin - cnt := tree.Count; // nb: top level count only - arrayLen := GetPolyPath64ArrayLen(tree); -end; - -procedure CreateCPolyPathD(const pp: TPolyPath64; - var v: PDouble; scale: double); -var - i, len: integer; -begin - len := Length(pp.Polygon); - v^ := len; inc(v); - v^ := pp.Count; inc(v); - for i := 0 to len -1 do - begin - v^ := pp.Polygon[i].x * scale; - v^ := pp.Polygon[i].y * scale; - end; - for i := 0 to pp.Count -1 do - CreateCPolyPathD(pp.Child[i], v, scale); -end; - - -function CreateCPolyTreeD(const tree: TPolyTree64; scale: double): CPolyTreeD; -var - i, cnt, arrayLen: integer; - v: PDouble; -begin - Result := nil; - GetPolytreeCountAndCStorageSize(tree, cnt, arrayLen); - if cnt = 0 then Exit; - // allocate storage - GetMem(Result, arrayLen * SizeOf(double)); - - v := PDouble(Result); - v^ := arrayLen; inc(v); - v^ := tree.Count; inc(v); - for i := 0 to tree.Count - 1 do - CreateCPolyPathD(tree.Child[i], v, scale); -end; - -function CreatePolyPath64FromCPolyPath(var v: PInt64; owner: TPolyPath64): Boolean; -var - i, childCount, len: integer; - path: TPath64; - newOwner: TPolyPath64; -begin - Result := false; - len := v^; inc(v); //polygon length - childCount := v^; inc(v); - if (len = 0) then Exit; - SetLength(path, len); - for i := 0 to len -1 do - begin - path[i].X := v^; inc(v); - path[i].Y := v^; inc(v); - end; - newOwner := TPolyPath64(owner.AddChild(path)); - for i := 0 to childCount -1 do - if not CreatePolyPath64FromCPolyPath(v, newOwner) then Exit; - Result := true; -end; - -function BuildPolyTree64FromCPolyTree(tree: CPolyTree64; outTree: TPolyTree64): Boolean; -var - v: PInt64; - i, childCount: integer; -begin - Result := false; - outTree.Clear(); - v := PInt64(tree); - inc(v); //skip array size - childCount := v^; inc(v); - for i := 0 to childCount -1 do - if not CreatePolyPath64FromCPolyPath(v, outTree) then Exit; - Result := true; -end; - -function CreatePolyPathDFromCPolyPath(var v: PDouble; owner: TPolyPathD): Boolean; -var - i, len, childCount: integer; - path: TPathD; - newOwner: TPolyPathD; -begin - Result := false; - len := Round(v^); inc(v); - childCount := Round(v^); inc(v); - if (len = 0) then Exit; - SetLength(path, len); - for i := 0 to len -1 do - begin - path[i].X := v^; inc(v); - path[i].Y := v^; inc(v); - end; - newOwner := TPolyPathD(owner.AddChild(path)); - for i := 0 to childCount -1 do - if not CreatePolyPathDFromCPolyPath(v, newOwner) then Exit; - Result := true; -end; - -function BuildPolyTreeDFromCPolyTree(tree: CPolyTreeD; outTree: TPolyTreeD): Boolean; -var - v: PDouble; - i, childCount: integer; -begin - Result := false; - outTree.Clear(); - v := PDouble(tree); - inc(v); // ignore array size - childCount := Round(v^); inc(v); - for i := 0 to childCount -1 do - if not CreatePolyPathDFromCPolyPath(v, outTree) then Exit; - Result := true; -end; - -//////////////////////////////////////////////////////// -// miscellaneous functions -//////////////////////////////////////////////////////// - -function MakePath64(vals: array of Int64): TPath64; -var - i, len: integer; -begin - len := Length(vals) div 2; - SetLength(Result, len); - for i := 0 to len -1 do - begin - Result[i].X := vals[i*2]; - Result[i].Y := vals[i*2 +1]; - end; -end; - -function MakePathD(vals: array of double): TPathD; -var - i, len: integer; -begin - len := Length(vals) div 2; - SetLength(Result, len); - for i := 0 to len -1 do - begin - Result[i].X := vals[i*2]; - Result[i].Y := vals[i*2 +1]; - end; -end; - -function MakeRandomPath(maxWidth, maxHeight, count: Integer; - margin: Integer = 10): TPath64; -var - i: Integer; -begin - setlength(Result, count); - for i := 0 to count -1 do with Result[i] do - begin - X := Random(maxWidth - 2 * margin) + margin; - Y := Random(maxHeight - 2 * margin) + margin; - end; -end; - -function MakeRandomPathD(maxWidth, maxHeight, count: Integer; - margin: Integer = 10): TPathD; -var - i: Integer; -begin - setlength(Result, count); - for i := 0 to count -1 do with Result[i] do - begin - X := Random(maxWidth - 2 * margin) + margin; - Y := Random(maxHeight - 2 * margin) + margin; - end; -end; - -procedure ShowSvgImage(const svgFilename: string); -begin - ShellExecute(0, 'open',PChar(svgFilename), nil, nil, SW_SHOW); -end; - -const - displayWidth = 600; - displayHeight = 400; - -procedure DisplaySVG(const sub, subo, clp, sol, solo: TPathsD; - const svgName: string; width: integer = displayWidth; - height: integer = displayHeight); overload; -var - svg: TSvgWriter; -begin - svg := TSvgWriter.Create(frNonZero); - try - AddSubject(svg, sub); - AddOpenSubject(svg, subo); - AddClip(svg, clp); - AddSolution(svg, sol); - AddOpenSolution(svg, solo); - SaveSvg(svg, svgName, width, height); - ShowSvgImage(svgName); - finally - svg.Free; - end; -end; - -procedure DisplaySVG(const sub, subo, clp, sol, solo: TPaths64; - const svgName: string; width: integer = displayWidth; - height: integer = displayHeight); overload; -var - svg: TSvgWriter; -begin - svg := TSvgWriter.Create(frNonZero); - try - AddSubject(svg, sub); - AddOpenSubject(svg, subo); - AddClip(svg, clp); - - AddSolution(svg, sol); - AddOpenSolution(svg, solo); - SaveSvg(svg, svgName, width, height); - ShowSvgImage(svgName); - finally - svg.Free; - end; -end; - -//////////////////////////////////////////////////////// -// test procedures -//////////////////////////////////////////////////////// - -procedure Test_Version(); -begin - Write(#10'Clipper2 DLL version: '); - WriteLn(Version); -end; - -procedure Test_BooleanOp64(edgeCnt: integer); -var - sub, clp: TPaths64; - csub_local, cclp_local: CPaths64; - csol_extern, csolo_extern: CPaths64; -begin - // setup - csolo_extern := nil; - WriteLn(#10'Testing BooleanOp64'); - SetLength(sub, 1); - sub[0] := MakeRandomPath(displayWidth, displayHeight, edgeCnt); - SetLength(clp, 1); - clp[0] := MakeRandomPath(displayWidth, displayHeight, edgeCnt); - // convert paths into DLL structures (will require local clean up) - csub_local := CreateCPaths64(sub); - cclp_local := CreateCPaths64(clp); - - // do the DLL operation - BooleanOp64(Intersection, NonZero, - csub_local, nil, cclp_local, - csol_extern, csolo_extern); - - DisplaySVG(sub, nil, clp, - ConvertToTPaths64(csol_extern), nil, 'BooleanOp64.svg'); - - // clean up - DisposeLocalArray64(csub_local); - DisposeLocalArray64(cclp_local); - DisposeExportedArray64(csol_extern); - DisposeExportedArray64(csolo_extern); -end; - -procedure Test_BooleanOpD(edgeCnt: integer); -var - sub, clp: TPathsD; - csub_local, cclp_local: CPathsD; - csol_extern, csolo_extern: CPathsD; -begin - // setup - csolo_extern := nil; - WriteLn(#10'Testing BooleanOpD'); - SetLength(sub, 1); - sub[0] := MakeRandomPathD(displayWidth, displayHeight, edgeCnt); - SetLength(clp, 1); - clp[0] := MakeRandomPathD(displayWidth, displayHeight, edgeCnt); - // convert paths into DLL structures (will require local clean up) - csub_local := CreateCPathsD(sub); - cclp_local := CreateCPathsD(clp); - - // do the DLL operation - BooleanOpD(Uint8(TClipType.ctIntersection), - Uint8(TFillRule.frNonZero), - csub_local, nil, cclp_local, - csol_extern, csolo_extern); - - // optionally display result on the console - //WriteCPaths64(csol_extern); - - DisplaySVG(sub, nil, clp, - ConvertToTPathsD(csol_extern), nil, 'BooleanOpD.svg'); - - DisposeLocalArrayD(csub_local); - DisposeLocalArrayD(cclp_local); - DisposeExportedArrayD(csol_extern); - DisposeExportedArrayD(csolo_extern); -end; - -procedure Test_BooleanOp_Polytree64(edgeCnt: integer); -var - sub, clp, sol: TPaths64; - csub_local, cclp_local: CPaths64; - csol_extern: CPolyTree64; - tree: TPolyTree64; - csol_open_extern: CPaths64; -begin - // setup - WriteLn(#10'Testing BooleanOp_PolyTree64'); - SetLength(sub, 1); - sub[0] := MakeRandomPath(displayWidth, displayHeight, edgeCnt); - SetLength(clp, 1); - clp[0] := MakeRandomPath(displayWidth, displayHeight, edgeCnt); - // convert paths into DLL structures (will require local clean up) - csub_local := CreateCPaths64(sub); - cclp_local := CreateCPaths64(clp); - - // do the DLL operation - BooleanOp_PolyTree64(Intersection, NonZero, - csub_local, nil, cclp_local, csol_extern, csol_open_extern); - - tree := TPolyTree64.Create; - try - BuildPolyTree64FromCPolyTree(csol_extern, tree); - sol := PolyTreeToPaths64(tree); - finally - tree.Free; - end; - DisposeExportedArray64(csol_extern); - DisposeExportedArray64(csol_open_extern); - - DisposeLocalArray64(csub_local); - DisposeLocalArray64(cclp_local); - - // finally, display and clean up - DisplaySVG(sub, nil, clp, sol, nil, 'BooleanOp_PolyTree64.svg'); -end; - -procedure Test_BooleanOp_PolytreeD(edgeCnt: integer); -var - sub, clp, sol: TPathsD; - csub_local, cclp_local: CPathsD; - csol_extern: CPolyTreeD; - tree: TPolyTreeD; - csol_open_extern: CPathsD; -begin - // setup - WriteLn(#10'Testing BooleanOp_PolyTreeD'); - SetLength(sub, 1); - sub[0] := MakeRandomPathD(displayWidth, displayHeight, edgeCnt); - SetLength(clp, 1); - clp[0] := MakeRandomPathD(displayWidth, displayHeight, edgeCnt); - // convert paths into DLL structures (will require local clean up) - csub_local := CreateCPathsD(sub); - cclp_local := CreateCPathsD(clp); - - // do the DLL operation - BooleanOp_PolyTreeD(Intersection, NonZero, - csub_local, nil, cclp_local, csol_extern, csol_open_extern); - - tree := TPolyTreeD.Create; - try - BuildPolyTreeDFromCPolyTree(csol_extern, tree); - sol := PolyTreeToPathsD(tree); - finally - tree.Free; - end; - DisposeExportedArrayD(csol_extern); - DisposeExportedArrayD(csol_open_extern); - - DisposeLocalArrayD(csub_local); - DisposeLocalArrayD(cclp_local); - - // finally, display and clean up - DisplaySVG(sub, nil, clp, sol, nil, 'BooleanOp_PolyTreeD.svg'); -end; - -procedure Test_InflatePathsD(edgeCnt: integer; delta: double); -var - sub: TPathsD; - csub_local: CPathsD; - csol_extern: CPathsD; - csolo_extern: CPathsD; -begin - // setup - WriteLn(#10'Testing InflatePaths64'); - SetLength(sub, 1); - sub[0] := MakeRandomPathD(displayWidth, displayHeight, edgeCnt); - // convert path into required DLL structure (also requires local clean up) - csub_local := CreateCPathsD(sub); - - // and because offsetting self-intersecting paths is unpredictable - // we must remove self-intersection via a union operation - BooleanOpD(Uint8(TClipType.ctUnion), - Uint8(TFillRule.frNonZero), csub_local, nil, nil, - csol_extern, csolo_extern); - - // now do the DLL operation - csol_extern := InflatePathsD(csol_extern, delta, - UInt8(TJoinType.jtRound), UInt8(TEndType.etPolygon), 2, 4); - - // optionally display result on the console - //WriteCPaths64(csol_extern); - - DisplaySVG(sub, nil, nil, - ConvertToTPathsD(csol_extern), nil, 'InflatePathsD.svg'); - - DisposeLocalArrayD(csub_local); - DisposeExportedArrayD(csol_extern); - DisposeExportedArrayD(csolo_extern); -end; - -procedure Test_RectClipD(edgeCount: integer); -var - rec_margin: Integer; - sub, clp, sol: TPathsD; - csub_local: CPathsD; - csol_extern: CPathsD; - rec: TRectD; -begin - WriteLn(#10'Testing RectClipD:'); - - rec_margin := Min(displayWidth,displayHeight) div 4; - rec.Left := rec_margin; - rec.Top := rec_margin; - rec.Right := displayWidth - rec_margin; - rec.Bottom := displayHeight -rec_margin; - - SetLength(sub, 1); - sub[0] := MakeRandomPathD(displayWidth, displayHeight, edgeCount); - csub_local := CreateCPathsD(sub); - - csol_extern := RectClipD(rec, csub_local, 2, true); - sol := ConvertToTPathsD(csol_extern); - DisposeLocalArrayD(csub_local); - DisposeExportedArrayD(csol_extern); - - SetLength(clp, 1); - clp[0] := rec.AsPath; - DisplaySVG(sub, nil, clp, sol, - nil, 'RectClipD.svg', displayWidth,displayHeight); -end; - -procedure Test_RectClipLines64(edgeCnt: integer); -var - sub, clp: TPaths64; - csub_local: CPaths64; - csolo_extern: CPaths64; - rec: TRect64; -begin - // setup - WriteLn(#10'Testing RectClipLines64:'); - SetLength(sub, 1); - - sub[0] := MakeRandomPath(displayWidth, displayHeight, edgeCnt); - csub_local := CreateCPaths64(sub); - - rec.Left := 80; - rec.Top := 80; - rec.Right := displayWidth - 80; - rec.Bottom := displayHeight -80; - - // do the DLL operation - csolo_extern := RectClipLines64(rec, csub_local); - - SetLength(clp, 1); - clp[0] := rec.AsPath; - - DisplaySVG(nil, sub, clp, nil, - ConvertToTPaths64(csolo_extern), 'RectClipLines64.svg'); - - DisposeLocalArray64(csub_local); - DisposeExportedArray64(csolo_extern); -end; - -//////////////////////////////////////////////////////// -// main entry here -//////////////////////////////////////////////////////// - -//var -// s: string; -begin - Randomize; - Test_Version(); - Test_BooleanOp64(25); - Test_BooleanOpD(25); - Test_BooleanOp_Polytree64(15); - Test_BooleanOp_PolytreeD(25); - Test_InflatePathsD(20, -10); // edgeCount, offsetDist - Test_RectClipD(7); - Test_RectClipLines64(25); - -// WriteLn(#10'Press Enter to quit.'); -// ReadLn(s); -end. diff --git a/DLL/Delphi_TestApp/Test_DLL.dproj b/DLL/Delphi_TestApp/Test_DLL.dproj deleted file mode 100644 index c84a1231..00000000 --- a/DLL/Delphi_TestApp/Test_DLL.dproj +++ /dev/null @@ -1,138 +0,0 @@ - - - {E2CCB41D-87F4-433F-9DAA-E38EF73B29B3} - Test_DLL.dpr - True - Debug - 2 - Console - None - 19.2 - Win64 - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - false - false - false - false - false - 00400000 - Test_DLL - 3081 - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= - System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) - $(BDS)\bin\delphi_PROJECTICON.ico - $(BDS)\bin\delphi_PROJECTICNS.icns - - - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - Debug - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - 1033 - - - RELEASE;$(DCC_Define) - 0 - false - 0 - - - DEBUG;$(DCC_Define) - false - true - - - - MainSource - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - Delphi.Personality.12 - - - - - Test_DLL.dpr - - - - True - - - - - Test_DLL.exe - true - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - 12 - - - - - diff --git a/DLL/CSharp_TestApp/COMPILE AND ADD Clipper2_64.dll HERE b/DLL/TEST_APPS/CSharp_TestApps/COPY Clipper2 DLLs HERE similarity index 100% rename from DLL/CSharp_TestApp/COMPILE AND ADD Clipper2_64.dll HERE rename to DLL/TEST_APPS/CSharp_TestApps/COPY Clipper2 DLLs HERE diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/CSharp_TestApp.csproj b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/CSharp_TestApp.csproj new file mode 100644 index 00000000..2bfe6ff7 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/CSharp_TestApp.csproj @@ -0,0 +1,19 @@ + + + + Exe + net6.0 + enable + enable + x64 + + + + $(DefineConstants);USINGZ + + + + $(DefineConstants);USINGZ + + + diff --git a/DLL/CSharp_TestApp/CSharp_TestApp.sln b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/CSharp_TestApp.sln similarity index 100% rename from DLL/CSharp_TestApp/CSharp_TestApp.sln rename to DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/CSharp_TestApp.sln diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllCore.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllCore.cs new file mode 100644 index 00000000..df8124b4 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllCore.cs @@ -0,0 +1,392 @@ +using System.Drawing; +using System.Runtime.InteropServices; +using static Clipper2Dll.Clipper2DllCore; + +using ztype = System.Int64; +using Point64 = Clipper2Dll.Clipper2DllCore.Point; +using PointD = Clipper2Dll.Clipper2DllCore.Point; +using Rect64 = Clipper2Dll.Clipper2DllCore.Rect; +using RectD = Clipper2Dll.Clipper2DllCore.Rect; + +namespace Clipper2Dll +{ + + public static class Clipper2DllCore + { + ///////////////////////////////////////////////////////////////////////// + // (Very abbreviated) Clipper2 structures + ///////////////////////////////////////////////////////////////////////// + public enum FillRule { EvenOdd, NonZero, Positive, Negative }; + public enum ClipType { None, Intersection, Union, Difference, Xor }; + +#if USINGZ + public static long VERTEX_FIELD_CNT = 3; + + public struct Point + { + public T X; + public T Y; + public ztype Z; + + public Point(Point pt) + { + X = pt.X; + Y = pt.Y; + Z = pt.Z; + } + + public Point(T x, T y, ztype z = 0) + { + X = x; + Y = y; + Z = z; + } + } +#else + + public static long VERTEX_FIELD_CNT = 2; + + public struct Point + { + public T X; + public T Y; + + public Point(Point pt) + { + X = pt.X; + Y = pt.Y; + } + + public Point(T x, T y) + { + X = x; + Y = y; + } + } +#endif + + public struct Rect where T : IComparable + { + public T left; + public T top; + public T right; + public T bottom; + public Rect(T l, T t, T r, T b) + { + left = l; + top = t; + right = r; + bottom = b; + } + public bool IsEmpty() + { + return left.CompareTo(right) >= 0 || top.CompareTo(bottom) >= 0; + } + public double Width() + { + if (IsEmpty()) return 0; + dynamic r = right; + dynamic l = left; + return r - l; + } + public double Height() + { + if (IsEmpty()) return 0; + dynamic b = bottom; + dynamic t = top; + return b - t; + } + } + + public static RectD InvalidRectD = + new RectD(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue); + + ///////////////////////////////////////////////////////////////////////// + // Clipper2 DLL - exported functions + ///////////////////////////////////////////////////////////////////////// + +#if USINGZ + const string clipperDll = @"..\..\..\..\..\Clipper2_Z_64.dll"; +#else + const string clipperDll = @"..\..\..\..\..\Clipper2_64.dll"; +#endif + + [DllImport(clipperDll, EntryPoint = + "Version", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Version(); + + [DllImport(clipperDll, EntryPoint = + "BooleanOp64", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOp64(byte clipType, byte fillRule, + long[] subjects, long[]? openSubs, long[]? clips, + out IntPtr solution, out IntPtr openSol, bool preserveCollinear, bool reverseSolution); + + [DllImport(clipperDll, EntryPoint = + "BooleanOpD", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOpD(byte clipType, byte fillRule, + double[] subjects, double[]? openSubs, double[]? clips, + out IntPtr solution, out IntPtr openSol, Int32 precision, bool preserveCollinear, bool reverseSolution); + + [DllImport(clipperDll, EntryPoint = + "DisposeArray64", CallingConvention = CallingConvention.Cdecl)] + public static extern void DisposeArray64(ref IntPtr intptr); + + // DisposeExported(): since all these functions behave identically ... + [DllImport(clipperDll, EntryPoint = + "DisposeArrayD", CallingConvention = CallingConvention.Cdecl)] + public static extern void DisposeArrayD(ref IntPtr intptr); + + [DllImport(clipperDll, EntryPoint = + "BooleanOp_PolyTree64", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOp_PolyTree64(byte cliptype, + byte fillrule, long[] subjects, long[]? openSubs, long[]? clips, + out IntPtr solTree, out IntPtr openSol, + bool preserve_collinear, bool reverse_solution); + + [DllImport(clipperDll, EntryPoint = + "BooleanOp_PolyTreeD", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOp_PolyTreeD(byte cliptype, + byte fillrule, double[] subjects, double[]? openSubs, double[]? clips, + out IntPtr solTree, out IntPtr openSol, Int32 precision, + bool preserve_collinear, bool reverse_solution); + + +#if USINGZ + public delegate void DLLZCallback64(Point64 e1bot, Point64 e1top, Point64 e2bot, Point64 e2top, ref Point64 ip); + + [DllImport(clipperDll, EntryPoint = + "SetZCallback64", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetZCallback64(DLLZCallback64 callback); + + public delegate void DLLZCallbackD(PointD e1bot, PointD e1top, PointD e2bot, PointD e2top, ref PointD ip); + + [DllImport(clipperDll, EntryPoint = + "SetZCallbackD", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetZCallbackD(DLLZCallbackD callback); +#endif + + ///////////////////////////////////////////////////////////////////////// + // Clipper2 DLL support functions + ///////////////////////////////////////////////////////////////////////// + + // CreateCPaths: The CPaths structure is defined in + // clipper.export.h and is a simple array of long[] or + // double[] that represents a number of path contours. + + public static T[]? CreateCPath(T[] coords) + { + long pathLen = coords.Length / VERTEX_FIELD_CNT; + if (pathLen == 0) return null; + long arrayLen = pathLen * VERTEX_FIELD_CNT + 2; + T[] result = new T[arrayLen]; + result[0] = (T)Convert.ChangeType(pathLen, typeof(T)); + result[1] = (T)Convert.ChangeType(0, typeof(T)); + coords.CopyTo(result, 2); + return result; + } + + public static T[] CreateCPathsFromCPathList(List listOfCPath) + { + int pathCount = listOfCPath.Count(); + int arrayLen = 2; + foreach (T[] path in listOfCPath) + arrayLen += path.Length; + T[] result = new T[arrayLen]; + + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(pathCount, typeof(T)); + + int idx = 2; + foreach (T[] cpath in listOfCPath) + { + cpath.CopyTo(result, idx); + idx += cpath.Length; + } + return result; + } + + // or create a cpaths array that contains just 1 path + public static T[] CreateCPathsFromCoords(T[] coords) + { + long pathLen = coords.Length / VERTEX_FIELD_CNT; + long arrayLen = pathLen * VERTEX_FIELD_CNT + 4; + T[] result = new T[arrayLen]; + + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(1, typeof(T)); // 1 path + result[2] = (T)Convert.ChangeType(pathLen, typeof(T)); + result[3] = (T)Convert.ChangeType(0, typeof(T)); + + coords.CopyTo(result, 4); + return result; + } + +#if USINGZ + public static string VertexAsString(T X, T Y, T Z, int precision) + { + if (typeof(T) == typeof(long)) // ignore precision + return $"{X},{Y},{Z} "; + else + return string.Format($"{{0:F{precision}}},{{1:F{precision}}},{{2:F{precision}}} ", X, Y, Z); + } +#else + public static string VertexAsString(T X, T Y, int precision) + { + if (typeof(T) == typeof(long)) // ignore precision + return $"{X},{Y} "; + else + return string.Format($"{{0:F{precision}}},{{1:F{precision}}} ", X, Y); + } +#endif + + public static void LogCPath(T[] cpaths, ref int idx, string spaceIndent) + { + int vertexCnt = Convert.ToInt32(cpaths[idx]); + idx += 2; + for (int i = 0; i < vertexCnt; i++) +#if USINGZ + Console.Write(spaceIndent + VertexAsString(cpaths[idx++], cpaths[idx++], cpaths[idx++], 2)); +#else + Console.Write(spaceIndent + VertexAsString(cpaths[idx++], cpaths[idx++], 2)); +#endif + Console.Write("\n"); + } + + public static void LogCPaths(T[]? cpaths, string spaceIndent) + { + if (cpaths == null) return; + int pathCnt = Convert.ToInt32(cpaths[1]); + int idx = 2; + for (int i = 0; i < pathCnt; i++) + LogCPath(cpaths, ref idx, spaceIndent); + } + + // Note: The CPolyTree structure defined in clipper.export.h is + // a simple array of T that contains any number of nested path contours. + + private static void CPolypathCounter(T[] cpolypath, + ref long idx, ref int pathCount, ref long arrayLen) + { + // returns the vertex count of each contained polygon + int vertexCount = Convert.ToInt32(cpolypath[idx++]); + int childCount = Convert.ToInt32(cpolypath[idx++]); + arrayLen += vertexCount * VERTEX_FIELD_CNT + 2; + pathCount++; + idx += vertexCount * VERTEX_FIELD_CNT; + for (int i = 0; i < childCount; i++) + CPolypathCounter(cpolypath, ref idx, ref pathCount, ref arrayLen); + } + private static void ConvertCPolypathToCPaths(T[] cpolypath, ref long idx, + T[] result, ref int resultIdx) + { + T vertexCount = cpolypath[idx++]!; + int vertCnt = (int)Convert.ChangeType(vertexCount, typeof(int)); + int childCount = Convert.ToInt32(cpolypath[idx++]); + result[resultIdx++] = vertexCount; + result[resultIdx++] = (T)Convert.ChangeType(0, typeof(T)); + // parse path + for (int i = 0; i < vertCnt; i++) + { + result[resultIdx++] = cpolypath[idx++]; // x + result[resultIdx++] = cpolypath[idx++]; // y +#if USINGZ + result[resultIdx++] = cpolypath[idx++]; // z +#endif + } + // parse children + for (int i = 0; i < childCount; i++) + ConvertCPolypathToCPaths(cpolypath, ref idx, result, ref resultIdx); + } + + public static void ConvertCPolytreeToCPaths(T[] cpolytree, out T[] result) + { + if (cpolytree == null) + { + result = new T[0]; + return; + } + int topPathsCount = Convert.ToInt32(cpolytree[1]); + long idx = 2; + int pathCount = 0; + long arrayLen = 2; + for (int i = 0; i < topPathsCount; i++) + CPolypathCounter(cpolytree, ref idx, ref pathCount, ref arrayLen); + result = new T[arrayLen]; + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(pathCount, typeof(T)); + int resultIdx = 2; + idx = 2; + for (int i = 0; i < topPathsCount; i++) + ConvertCPolypathToCPaths(cpolytree, ref idx, result, ref resultIdx); + } + + public static void LogPolyPath(T[] polypath, + ref int idx, bool isHole, string spaceIndent, int precision) + { + int polyCnt = Convert.ToInt32(polypath[idx++]); + int childCnt = Convert.ToInt32(polypath[idx++]); + string preamble = isHole ? "Hole: " : (spaceIndent == "") ? + "Polygon: " : "Nested Polygon: "; + Console.Write(spaceIndent + preamble); + spaceIndent += " "; + for (int i = 0; i < polyCnt; i++) +#if USINGZ + Console.Write(VertexAsString(polypath[idx++], polypath[idx++], polypath[idx++], precision)); +#else + Console.Write(VertexAsString(polypath[idx++], polypath[idx++], precision)); +#endif + Console.Write("\n"); + for (int i = 0; i < childCnt; i++) + LogPolyPath(polypath, ref idx, !isHole, spaceIndent, precision); + } + + public static void LogPolytree(T[] polytree, int precision) + { + int cnt = Convert.ToInt32(polytree[1]); + int idx = 2; + for (int i = 0; i < cnt; i++) + LogPolyPath(polytree, ref idx, false, " ", precision); + } + + public static T[]? GetArrayFromIntPtr(IntPtr paths) + { + if (paths == IntPtr.Zero) return null; + if (typeof(T) == typeof(long)) + { + long[] len = new long[1]; + Marshal.Copy(paths, len, 0, 1); + long[] res = new long[(int)len[0]]; + Marshal.Copy(paths, res, 0, (int)len[0]); + return res as T[]; + } + else if (typeof(T) == typeof(double)) + { + double[] len = new double[1]; + Marshal.Copy(paths, len, 0, 1); + double[] res = new double[(int)len[0]]; + Marshal.Copy(paths, res, 0, (int)len[0]); + return res as T[]; + } + else return null; + } + + public static void ConvertArrayOfLongs(long[] longs, out double[] result) + { + result = new double[longs.Length]; + for (int i = 0; i < longs.Length; i++) + result[i] = longs[i]; + } + + public static double StaticCastLongToDouble(long val) + { + byte[] b = BitConverter.GetBytes(val); + return BitConverter.ToDouble(b, 0); + } + public static long StaticCastDoubleToLong(double val) + { + byte[] b = BitConverter.GetBytes(val); + return BitConverter.ToInt64(b, 0); + } + + } +} diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllSvg.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllSvg.cs new file mode 100644 index 00000000..84bb74b6 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Clipper2DllSvg.cs @@ -0,0 +1,469 @@ +using System.Diagnostics; +using System.Globalization; +using static Clipper2Dll.Clipper2DllCore; + +using RectD = Clipper2Dll.Clipper2DllCore.Rect; + +namespace Clipper2Dll +{ + public class SvgWriter + { + private static RectD rectMax = InvalidRectD; + + public static RectD RectEmpty = new RectD(0, 0, 0, 0); + internal static bool IsValidRect(RectD rec) + { + return rec.right >= rec.left && rec.bottom >= rec.top; + } + + private readonly struct CoordStyle + { + public readonly string FontName; + public readonly int FontSize; + public readonly uint FontColor; + public CoordStyle(string fontname, int fontsize, uint fontcolor) + { + FontName = fontname; + FontSize = fontsize; + FontColor = fontcolor; + } + } + private readonly struct TextInfo + { + public readonly string text; + public readonly int fontSize; + public readonly uint fontColor; + public readonly double posX; + public readonly double posY; + public TextInfo(string text, double x, double y, + int fontsize = 12, uint fontcolor = 0xFF000000) + { + this.text = text; + posX = x; + posY = y; + fontSize = fontsize; + fontColor = fontcolor; + } + } + + private readonly struct CircleInfo + { + public readonly double centerX; + public readonly double centerY; + public readonly double radius; + public readonly uint brushClr; + public readonly uint penClr; + public readonly uint penWidth; + public CircleInfo(double centerX, double centerY, + double radius, uint brushClr, uint penClr, uint penWidth) + { + this.centerX = centerX; + this.centerY = centerY; + this.radius = radius; + this.brushClr = brushClr; + this.penClr = penClr; + this.penWidth = penWidth; + } + } + + private readonly struct PolyInfo + { + public readonly double[] paths; + public readonly uint BrushClr; + public readonly uint PenClr; + public readonly double PenWidth; + public readonly bool ShowCoords; + public readonly bool IsOpen; + public PolyInfo(double[] paths, uint brushcolor, uint pencolor, + double penwidth, bool showcoords = false, bool isopen = false) + { + this.paths = new double[paths.Length]; + paths.CopyTo(this.paths, 0); + BrushClr = brushcolor; + PenClr = pencolor; + PenWidth = penwidth; + ShowCoords = showcoords; + IsOpen = isopen; + } + } + + public FillRule FillRule { get; set; } + private readonly List PolyInfoList = new List(); + private readonly List textInfos = new List(); + private readonly List circleInfos = new List(); + private readonly CoordStyle coordStyle; + + private const string svg_header = "\n" + + "\n\n"; + private const string svg_path_format = "\"\n style=\"fill:{0};" + + " fill-opacity:{1:f2}; fill-rule:{2}; stroke:{3};" + + " stroke-opacity:{4:f2}; stroke-width:{5:f2};\"/>\n\n"; + private const string svg_path_format2 = "\"\n style=\"fill:none; stroke:{0};" + + "stroke-opacity:{1:f2}; stroke-width:{2:f2};\"/>\n\n"; + + public SvgWriter(FillRule fillrule = FillRule.EvenOdd, + string coordFontName = "Verdana", int coordFontsize = 9, uint coordFontColor = 0xFF000000) + { + coordStyle = new CoordStyle(coordFontName, coordFontsize, coordFontColor); + FillRule = fillrule; + } + + public void ClearPaths() + { + PolyInfoList.Clear(); + } + + public void ClearText() + { + textInfos.Clear(); + } + + public void ClearCircles() + { + circleInfos.Clear(); + } + + public void ClearAll() + { + PolyInfoList.Clear(); + textInfos.Clear(); + circleInfos.Clear(); + } + public void AddClosedPath(long[] path, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + long[] tmp = new long[path.Count() +2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddClosedPaths(tmp, brushColor, penColor, penWidth, showCoords); + } + + public void AddClosedPath(double[] path, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + double[] tmp = new double[path.Length + 2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddClosedPaths(tmp, brushColor, penColor, penWidth, showCoords); + } + + public void AddClosedPaths(long[] paths, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + double[] pathsD; + ConvertArrayOfLongs(paths, out pathsD); + PolyInfoList.Add(new PolyInfo(pathsD, brushColor, penColor, penWidth, showCoords, false)); + } + + public void AddClosedPaths(double[] paths, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + PolyInfoList.Add(new PolyInfo(paths, brushColor, penColor, penWidth, showCoords, false)); + } + + public void AddOpenPath(long[] path, uint penColor, + double penWidth, bool showCoords = false) + { + long[] tmp = new long[path.Length + 2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddOpenPaths(tmp, penColor, penWidth, showCoords); + } + + public void AddOpenPath(double[] path, uint penColor, + double penWidth, bool showCoords = false) + { + double[] tmp = new double[path.Length + 2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddOpenPaths(tmp, penColor, penWidth, showCoords); + } + + public void AddOpenPaths(long[] paths, + uint penColor, double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + double[] pathsD; + ConvertArrayOfLongs(paths, out pathsD); + PolyInfoList.Add(new PolyInfo(pathsD, 0x0, penColor, penWidth, showCoords, true)); + } + + public void AddOpenPaths(double[] paths, uint penColor, + double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + PolyInfoList.Add(new PolyInfo(paths, + 0x0, penColor, penWidth, showCoords, true)); + } + + + public void AddText(string cap, double posX, double posY, int fontSize, uint fontClr = 0xFF000000) + { + textInfos.Add(new TextInfo(cap, posX, posY, fontSize, fontClr)); + } + + public void AddCircle(double centerX, double centerY, + double radius, uint brushClr, uint penClr, uint penWidth) + { + circleInfos.Add(new CircleInfo(centerX, centerY, radius, brushClr, penClr, penWidth)); + } + + private RectD GetBounds() + { + RectD bounds = new RectD(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue); + foreach (PolyInfo pi in PolyInfoList) + { + int idx = 2; + long pathsCnt = Convert.ToInt64(pi.paths[1]); + for (int i = 0; i < pathsCnt; i++) + { + long pathLen = Convert.ToInt64(pi.paths[idx]); + idx += 2; + for (int j = 0; j < pathLen; j++) + { + double x = pi.paths[idx++]; + double y = pi.paths[idx++]; +#if USINGZ + idx++; +#endif + if (x < bounds.left) bounds.left = x; + if (x > bounds.right) bounds.right = x; + if (y < bounds.top) bounds.top = y; + if (y > bounds.bottom) bounds.bottom = y; + } + + } + } + if (!IsValidRect(bounds)) return RectEmpty; + return bounds; + } + + private static string ColorToHtml(uint clr) + { + return '#' + (clr & 0xFFFFFF).ToString("X6"); + } + + private static float GetAlpha(uint clr) + { + return ((float)(clr >> 24) / 255); + } + + public bool SaveToFile(string filename, int maxWidth = 0, int maxHeight = 0, int margin = -1) + { + if (margin < 0) margin = 20; + RectD bounds = GetBounds(); + if (bounds.IsEmpty()) return false; + + double scale = 1.0; + if (maxWidth > 0 && maxHeight > 0) + scale = Math.Min( + (maxWidth - margin * 2) / bounds.Width(), + (maxHeight - margin * 2) / bounds.Height()); + + long offsetX = margin - (long)(bounds.left * scale); + long offsetY = margin - (long)(bounds.top * scale); + + StreamWriter writer; + try + { + writer = new StreamWriter(filename); + } + catch + { + return false; + } + + if (maxWidth <= 0 || maxHeight <= 0) + writer.Write(svg_header, (bounds.right - bounds.left) + margin * 2, + (bounds.bottom - bounds.top) + margin * 2); + else + writer.Write(svg_header, maxWidth, maxHeight); + + foreach (PolyInfo pi in PolyInfoList) + { + writer.Write(" \n", + coordStyle.FontName, coordStyle.FontSize, ColorToHtml(coordStyle.FontColor)); + + idx = 2; + pathsCnt = Convert.ToInt64(pi.paths[1]); + for (int i = 0; i < pathsCnt; i++) + { + long pathLen = Convert.ToInt64(pi.paths[idx]); + idx += 2; + for (int j = 1; j < pathLen; j++) + { + double x = pi.paths[idx++]; + double y = pi.paths[idx++]; +#if USINGZ + double z = pi.paths[idx++]; + writer.Write("{2:f2},{3:f2},{4}\n", + (x * scale + offsetX), (y * scale + offsetY), x, y, z); +#else + writer.Write("{2:f2},{3:f2}\n", + (x * scale + offsetX), (y * scale + offsetY), x, y); +#endif + } + } + writer.Write("\n\n"); + } + } + + foreach (CircleInfo circInfo in circleInfos) + { + writer.Write("\n \n\n", + circInfo.centerX * scale + offsetX, circInfo.centerY * scale + offsetY, + circInfo.radius * scale, ColorToHtml(circInfo.penClr), circInfo.penWidth, + ColorToHtml(circInfo.brushClr), GetAlpha(circInfo.brushClr)); + } + + + foreach (TextInfo captionInfo in textInfos) + { + writer.Write("\n", + captionInfo.fontSize, ColorToHtml(captionInfo.fontColor)); + writer.Write("{2}\n\n", + captionInfo.posX * scale + offsetX, captionInfo.posY * scale + offsetY, captionInfo.text); + } + + writer.Write("\n"); + writer.Close(); + return true; + } + } //end SvgWriter + +public static class SvgWriterUtils + { + public static void AddCaption(SvgWriter svg, string caption, int x, int y) + { + svg.AddText(caption, x, y, 14); + } + + public static void AddSubject(SvgWriter svg, long[] path) + { + svg.AddClosedPath(path, 0x1800009C, 0xAAB3B3DA, 0.8); + } + public static void AddSubject(SvgWriter svg, double[] path) + { + svg.AddClosedPath(path, 0x1800009C, 0xAAB3B3DA, 0.8); + } + + public static void AddSubjects(SvgWriter svg, long[] paths) + { + svg.AddClosedPaths(paths, 0x1800009C, 0xAAB3B3DA, 0.8); + } + public static void AddOpenSubjects(SvgWriter svg, long[] paths) + { + svg.AddOpenPaths(paths, 0xAAB3B3DA, 0.8); + } + + public static void AddSubjects(SvgWriter svg, double[] paths) + { + svg.AddClosedPaths(paths, 0x1800009C, 0xAAB3B3DA, 0.8); + } + + public static void AddOpenSubjects(SvgWriter svg, double[] paths) + { + svg.AddOpenPaths(paths, 0xAAB3B3DA, 1.2); + } + + public static void AddClip(SvgWriter svg, long[] path) + { + svg.AddClosedPath(path, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddClip(SvgWriter svg, double[] path) + { + svg.AddClosedPath(path, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddClips(SvgWriter svg, long[] paths) + { + svg.AddClosedPaths(paths, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddClips(SvgWriter svg, double[] paths) + { + svg.AddClosedPaths(paths, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddSolution(SvgWriter svg, long[] paths, + bool show_coords, bool is_closed = true, bool is_joined = true) + { + svg.AddClosedPaths(paths, 0x4080ff9C, 0xFF003300, 1.5, show_coords); + } + + public static void AddOpenSolution(SvgWriter svg, long[] paths, bool show_coords) + { + svg.AddOpenPaths(paths, 0xFF003300, 2.2, show_coords); + } + + public static void AddSolution(SvgWriter svg, double[] paths, bool show_coords) + { + svg.AddClosedPaths(paths, 0x4080ff9C, 0xFF003300, 1.5, show_coords); + } + + public static void AddOpenSolution(SvgWriter svg, double[] paths, bool show_coords) + { + svg.AddOpenPaths(paths, 0xFF003300, 2.2, show_coords); + } + + public static void OpenFileWithDefaultApp(string filename) + { + string path = Path.GetFullPath(filename); + if (!File.Exists(path)) return; + Process p = new Process() { StartInfo = new ProcessStartInfo(path) { UseShellExecute = true } }; + p.Start(); + } + + } //end SvgWriterUtils +} diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs new file mode 100644 index 00000000..f1d08ca9 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs @@ -0,0 +1,253 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 29 October 2023 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2023 * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +using Clipper2Dll; +using System.Globalization; +using System.Runtime.InteropServices; +using static Clipper2Dll.Clipper2DllCore; +using static Clipper2Dll.SvgWriterUtils; + +using ztype = System.Int64; +using Point64 = Clipper2Dll.Clipper2DllCore.Point; +using PointD = Clipper2Dll.Clipper2DllCore.Point; + +namespace ClipperDllDemo +{ + + public class Application + { + +#if USINGZ + private static long counter = 0; + private static void MyCallBack64(Point64 e1bot, Point64 e1top, Point64 e2bot, Point64 e2top, ref Point64 ip) + { + ip.Z = counter++; + } + + private static void MyCallBackD(PointD e1bot, PointD e1top, PointD e2bot, PointD e2top, ref PointD ip) + { + ip.Z = StaticCastDoubleToLong((double)counter++); + } +#endif + + private static void DrawSVG(string filename, long[]? cSubjects, long[]? cClips, long[]? cSolution) + { + SvgWriter svg = new SvgWriter(); + if (cSubjects != null) + AddSubjects(svg, cSubjects); + if (cClips != null) + AddClips(svg, cClips); + if (cSolution != null) + { + AddSolution(svg, cSolution, false); +#if USINGZ + long pathsCnt = cSolution[1]; + long idx = 2; + for (long i = 0; i < pathsCnt; i++) + { + long pathLen = cSolution[idx]; + idx += 2; + for (long j = 0; j < pathLen; j++) + { + long x = cSolution[idx++]; + long y = cSolution[idx++]; + long z = cSolution[idx++]; + if (z <= 10) continue; + svg.AddText(string.Format(NumberFormatInfo.InvariantInfo, "{0}", z), x +1, y - 3, 9); + svg.AddCircle(x, y, 3, 0x40FFFF00, 0xFF000000, 2); + } + } +#endif + } + svg.SaveToFile(filename, 400, 400); + OpenFileWithDefaultApp(filename); + } + + private static void DrawSVG(string filename, double[] cSubjects, double[] cClips, double[] cSolution) + { + SvgWriter svg = new SvgWriter(); + AddSubjects(svg, cSubjects); + AddClips(svg, cClips); + if (cSolution.Length > 0) + { + AddSolution(svg, cSolution, false); +#if USINGZ + long pathsCnt = Convert.ToInt64(cSolution[1]); + long idx = 2; + for (long i = 0; i < pathsCnt; i++) + { + long pathLen = Convert.ToInt64(cSolution[idx]); + idx += 2; + for (long j = 0; j < pathLen; j++) + { + double x = cSolution[idx++]; + double y = cSolution[idx++]; + double z = cSolution[idx++]; + if (z <= 10) continue; + svg.AddText(string.Format(NumberFormatInfo.InvariantInfo, "{0}", z), x + 1, y - 3, 9); + svg.AddCircle(x, y, 3, 0x40FFFF00, 0xFF000000, 2); + } + } +#endif + } + svg.SaveToFile(filename, 400, 400); + OpenFileWithDefaultApp(filename); + } + + /// Main Entry //////////////////////////////////////////////////////////// + public static void Main() + { + //string? ver = Marshal.PtrToStringAnsi(Version()); + //Console.WriteLine(ver + "\n"); + +#if USINGZ + SetZCallback64(MyCallBack64); + SetZCallbackD(MyCallBackD); +#endif + + ///////////////////////////////////////////////////////////////////////// + // test BooleanOp64() /////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + +#if USINGZ + long[] cSubject = CreateCPathsFromCoords(new long[] { 0,0,1, 100,0,2, 100,100,3, 0,100,4 }); + long[] cClip = CreateCPathsFromCoords(new long[] { 20,20,1, 120,20,2, 120,120,3, 20,120,4 }); + counter = 11; +#else + long[] cSubject = CreateCPathsFromCoords(new long[] { 0,0, 100,0, 100,100, 0,100 }); + long[] cClip = CreateCPathsFromCoords(new long[] { 20,20, 120,20, 120,120, 20,120 }); +#endif + + if (BooleanOp64((int)ClipType.Intersection, (int)FillRule.NonZero, cSubject, + null, cClip, out IntPtr cSol, out IntPtr cSolOpen, false, false) != 0) return; + + long[]? cSolution = GetArrayFromIntPtr(cSol); + + //Console.WriteLine("BooleanOp64:"); + //LogCPaths(cSolution, " "); + + // clean up unmanaged memory + DisposeArray64(ref cSol); + DisposeArray64(ref cSolOpen); + + DrawSVG(@"..\..\..\rectangles.svg", cSubject, cClip, cSolution); + + ///////////////////////////////////////////////////////////////////////// + // test BooleanOpD() //////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + +#if USINGZ + double[] cSubjectD = CreateCPathsFromCoords(new double[] { 0,0,1, 100,0,2, 100,100,3, 0,100,4 }); + double[] cClipD = CreateCPathsFromCoords(new double[] { 20,20,1, 120,20,2, 120,120,3, 20,120,4 }); + counter = 21; +#else + double[] cSubjectD = CreateCPathsFromCoords(new double[] { 0,0, 100,0, 100,100, 0,100 }); + double[] cClipD = CreateCPathsFromCoords(new double[] { 20,20, 120,20, 120,120, 20,120 }); +#endif + int resultD = BooleanOpD((int)ClipType.Intersection, (int)FillRule.NonZero, cSubjectD, + null, cClipD, out IntPtr cSolD, out IntPtr cSolOpenD, 2, false, false); + if (resultD != 0) return; + double[]? cSolutionD = GetArrayFromIntPtr(cSolD); + + //Console.WriteLine("BooleanOpD:"); + //LogCPaths(cSolutionD, " "); + + // clean up unmanaged memory + DisposeArrayD(ref cSolD); + DisposeArrayD(ref cSolOpenD); + + DrawSVG(@"..\..\..\rectangles2.svg", cSubjectD, cClipD, cSolutionD!); + + ///////////////////////////////////////////////////////////////////////// + // test BooleanOp_PolyTree64() ////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + // create arrays of x,y coords that define 5 successively + // larger rectangles centered on the origin ... + List cRectangles = new(5); +#if USINGZ + for (int i = 1; i < 6; ++i) + cRectangles.Add(CreateCPath(new long[] { + -i*20,-i*20,1, i*20,-i*20,2, i*20,i*20,3, -i*20,i*20,4 })!); + long[] cSubjects = CreateCPathsFromCPathList(cRectangles); + long[] cClips = CreateCPathsFromCoords( + new long[] { -90,-120,11, 90,-120,12, 90,120,13, -90,120,14 }); + counter = 31; +#else + for (int i = 1; i < 6; ++i) + cRectangles.Add(CreateCPath(new long[] { + -i*20,-i*20, i*20,-i*20, i*20,i*20, -i*20,i*20 })!); + long[] cSubjects = CreateCPathsFromCPathList(cRectangles); + long[] cClips = CreateCPathsFromCoords(new long[] { -90,-120, 90,-120, 90,120, -90,120 }); +#endif + + int result3 = BooleanOp_PolyTree64((int)ClipType.Intersection, + (int)FillRule.EvenOdd, cSubjects, null, cClips, + out IntPtr cSol_pt64, out IntPtr cSolOpen_pt64, false, false); + if (result3 != 0) return; + + long[]? cPolyTree64 = GetArrayFromIntPtr(cSol_pt64); + // clean up unmanaged memory + DisposeArray64(ref cSol_pt64); + DisposeArray64(ref cSolOpen_pt64); + + if (cPolyTree64 == null) return; + //Console.WriteLine("BooleanOp_PolyTree64:"); + //LogPolytree(cPolyTree64, 2); + + ConvertCPolytreeToCPaths(cPolyTree64, out long[] solution5); + DrawSVG(@"..\..\..\polytree64.svg", cSubjects, cClips, solution5); + + ///////////////////////////////////////////////////////////////////////// + // test BooleanOp_PolyTreeD() /////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + List subList2 = new(5); +#if USINGZ + for (int i = 1; i < 6; ++i) + subList2.Add(CreateCPath(new double[] { + -i*20,-i*20,1, i*20,-i*20,2, i*20,i*20,3, -i*20,i*20,4 })!); + double[] cSubject4 = CreateCPathsFromCPathList(subList2); + double[] cClip4 = CreateCPathsFromCoords(new double[] { -90, -120, 11, 90, -120, 12, 90, 120, 13, -90, 120, 14 }); + counter = 41; +#else + for (int i = 1; i < 6; ++i) + subList2.Add(CreateCPath(new double[] { + -i*20,-i*20, i*20,-i*20, i*20,i*20, -i*20,i*20 })!); + double[] cSubject4 = CreateCPathsFromCPathList(subList2); + double[] cClip4 = CreateCPathsFromCoords(new double[] { -90,-120, 90,-120, 90,120, -90,120 }); +#endif + + int result4 = BooleanOp_PolyTreeD((int)ClipType.Intersection, + (int)FillRule.EvenOdd, cSubject4, null, cClip4, + out IntPtr cSol_ptD, out IntPtr cSolOpen_ptD, 2, false, false); + if (result4 != 0) return; + + double[]? cPolyTreeD = GetArrayFromIntPtr(cSol_ptD); + + // clean up unmanaged memory + DisposeArrayD(ref cSol_ptD); + DisposeArrayD(ref cSolOpen_ptD); + + if (cPolyTreeD == null) return; + + //Console.WriteLine("BooleanOp_PolyTreeD:"); + //LogPolytree(cPolyTreeD, 2); + + ConvertCPolytreeToCPaths(cPolyTreeD, out double[] solution6); + DrawSVG(@"..\..\..\polytreeD.svg", cSubject4, cClip4, solution6); + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + //Console.WriteLine("\nPress any key to exit ... "); + //Console.ReadKey(); + } + + } //end Application +} //end namespace diff --git a/DLL/CSharp_TestApp/polytree_sample.png b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/polytree_sample.png similarity index 100% rename from DLL/CSharp_TestApp/polytree_sample.png rename to DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/polytree_sample.png diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.csproj b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.csproj new file mode 100644 index 00000000..553504d4 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.csproj @@ -0,0 +1,23 @@ + + + + Exe + net6.0 + enable + enable + x64 + + + + $(DefineConstants);USINGZ + + + + $(DefineConstants);USINGZ + + + + + + + diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.sln b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.sln new file mode 100644 index 00000000..7df98794 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/CSharp_TestApp2.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33801.468 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp_TestApp2", "CSharp_TestApp2.csproj", "{3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Clipper2Lib", "..\..\..\..\CSharp\Clipper2Lib\Clipper2Lib.csproj", "{6170CB42-C763-4C12-A9E5-7D90E6953615}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|Any CPU.ActiveCfg = Debug|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x64.ActiveCfg = Debug|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x64.Build.0 = Debug|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x86.ActiveCfg = Debug|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Debug|x86.Build.0 = Debug|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|Any CPU.ActiveCfg = Release|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x64.ActiveCfg = Release|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x64.Build.0 = Release|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x86.ActiveCfg = Release|x64 + {3D611FB4-7D15-4FD4-9EE4-D02E0D37F424}.Release|x86.Build.0 = Release|x64 + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Debug|x64.ActiveCfg = Debug|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Debug|x64.Build.0 = Debug|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Debug|x86.ActiveCfg = Debug|x86 + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Debug|x86.Build.0 = Debug|x86 + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Release|Any CPU.Build.0 = Release|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Release|x64.ActiveCfg = Release|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Release|x64.Build.0 = Release|Any CPU + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Release|x86.ActiveCfg = Release|x86 + {6170CB42-C763-4C12-A9E5-7D90E6953615}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DAE344E0-A107-49C8-B269-1E1896665E6B} + EndGlobalSection +EndGlobal diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllCore.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllCore.cs new file mode 100644 index 00000000..df8124b4 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllCore.cs @@ -0,0 +1,392 @@ +using System.Drawing; +using System.Runtime.InteropServices; +using static Clipper2Dll.Clipper2DllCore; + +using ztype = System.Int64; +using Point64 = Clipper2Dll.Clipper2DllCore.Point; +using PointD = Clipper2Dll.Clipper2DllCore.Point; +using Rect64 = Clipper2Dll.Clipper2DllCore.Rect; +using RectD = Clipper2Dll.Clipper2DllCore.Rect; + +namespace Clipper2Dll +{ + + public static class Clipper2DllCore + { + ///////////////////////////////////////////////////////////////////////// + // (Very abbreviated) Clipper2 structures + ///////////////////////////////////////////////////////////////////////// + public enum FillRule { EvenOdd, NonZero, Positive, Negative }; + public enum ClipType { None, Intersection, Union, Difference, Xor }; + +#if USINGZ + public static long VERTEX_FIELD_CNT = 3; + + public struct Point + { + public T X; + public T Y; + public ztype Z; + + public Point(Point pt) + { + X = pt.X; + Y = pt.Y; + Z = pt.Z; + } + + public Point(T x, T y, ztype z = 0) + { + X = x; + Y = y; + Z = z; + } + } +#else + + public static long VERTEX_FIELD_CNT = 2; + + public struct Point + { + public T X; + public T Y; + + public Point(Point pt) + { + X = pt.X; + Y = pt.Y; + } + + public Point(T x, T y) + { + X = x; + Y = y; + } + } +#endif + + public struct Rect where T : IComparable + { + public T left; + public T top; + public T right; + public T bottom; + public Rect(T l, T t, T r, T b) + { + left = l; + top = t; + right = r; + bottom = b; + } + public bool IsEmpty() + { + return left.CompareTo(right) >= 0 || top.CompareTo(bottom) >= 0; + } + public double Width() + { + if (IsEmpty()) return 0; + dynamic r = right; + dynamic l = left; + return r - l; + } + public double Height() + { + if (IsEmpty()) return 0; + dynamic b = bottom; + dynamic t = top; + return b - t; + } + } + + public static RectD InvalidRectD = + new RectD(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue); + + ///////////////////////////////////////////////////////////////////////// + // Clipper2 DLL - exported functions + ///////////////////////////////////////////////////////////////////////// + +#if USINGZ + const string clipperDll = @"..\..\..\..\..\Clipper2_Z_64.dll"; +#else + const string clipperDll = @"..\..\..\..\..\Clipper2_64.dll"; +#endif + + [DllImport(clipperDll, EntryPoint = + "Version", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Version(); + + [DllImport(clipperDll, EntryPoint = + "BooleanOp64", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOp64(byte clipType, byte fillRule, + long[] subjects, long[]? openSubs, long[]? clips, + out IntPtr solution, out IntPtr openSol, bool preserveCollinear, bool reverseSolution); + + [DllImport(clipperDll, EntryPoint = + "BooleanOpD", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOpD(byte clipType, byte fillRule, + double[] subjects, double[]? openSubs, double[]? clips, + out IntPtr solution, out IntPtr openSol, Int32 precision, bool preserveCollinear, bool reverseSolution); + + [DllImport(clipperDll, EntryPoint = + "DisposeArray64", CallingConvention = CallingConvention.Cdecl)] + public static extern void DisposeArray64(ref IntPtr intptr); + + // DisposeExported(): since all these functions behave identically ... + [DllImport(clipperDll, EntryPoint = + "DisposeArrayD", CallingConvention = CallingConvention.Cdecl)] + public static extern void DisposeArrayD(ref IntPtr intptr); + + [DllImport(clipperDll, EntryPoint = + "BooleanOp_PolyTree64", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOp_PolyTree64(byte cliptype, + byte fillrule, long[] subjects, long[]? openSubs, long[]? clips, + out IntPtr solTree, out IntPtr openSol, + bool preserve_collinear, bool reverse_solution); + + [DllImport(clipperDll, EntryPoint = + "BooleanOp_PolyTreeD", CallingConvention = CallingConvention.Cdecl)] + public static extern Int32 BooleanOp_PolyTreeD(byte cliptype, + byte fillrule, double[] subjects, double[]? openSubs, double[]? clips, + out IntPtr solTree, out IntPtr openSol, Int32 precision, + bool preserve_collinear, bool reverse_solution); + + +#if USINGZ + public delegate void DLLZCallback64(Point64 e1bot, Point64 e1top, Point64 e2bot, Point64 e2top, ref Point64 ip); + + [DllImport(clipperDll, EntryPoint = + "SetZCallback64", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetZCallback64(DLLZCallback64 callback); + + public delegate void DLLZCallbackD(PointD e1bot, PointD e1top, PointD e2bot, PointD e2top, ref PointD ip); + + [DllImport(clipperDll, EntryPoint = + "SetZCallbackD", CallingConvention = CallingConvention.Cdecl)] + public static extern void SetZCallbackD(DLLZCallbackD callback); +#endif + + ///////////////////////////////////////////////////////////////////////// + // Clipper2 DLL support functions + ///////////////////////////////////////////////////////////////////////// + + // CreateCPaths: The CPaths structure is defined in + // clipper.export.h and is a simple array of long[] or + // double[] that represents a number of path contours. + + public static T[]? CreateCPath(T[] coords) + { + long pathLen = coords.Length / VERTEX_FIELD_CNT; + if (pathLen == 0) return null; + long arrayLen = pathLen * VERTEX_FIELD_CNT + 2; + T[] result = new T[arrayLen]; + result[0] = (T)Convert.ChangeType(pathLen, typeof(T)); + result[1] = (T)Convert.ChangeType(0, typeof(T)); + coords.CopyTo(result, 2); + return result; + } + + public static T[] CreateCPathsFromCPathList(List listOfCPath) + { + int pathCount = listOfCPath.Count(); + int arrayLen = 2; + foreach (T[] path in listOfCPath) + arrayLen += path.Length; + T[] result = new T[arrayLen]; + + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(pathCount, typeof(T)); + + int idx = 2; + foreach (T[] cpath in listOfCPath) + { + cpath.CopyTo(result, idx); + idx += cpath.Length; + } + return result; + } + + // or create a cpaths array that contains just 1 path + public static T[] CreateCPathsFromCoords(T[] coords) + { + long pathLen = coords.Length / VERTEX_FIELD_CNT; + long arrayLen = pathLen * VERTEX_FIELD_CNT + 4; + T[] result = new T[arrayLen]; + + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(1, typeof(T)); // 1 path + result[2] = (T)Convert.ChangeType(pathLen, typeof(T)); + result[3] = (T)Convert.ChangeType(0, typeof(T)); + + coords.CopyTo(result, 4); + return result; + } + +#if USINGZ + public static string VertexAsString(T X, T Y, T Z, int precision) + { + if (typeof(T) == typeof(long)) // ignore precision + return $"{X},{Y},{Z} "; + else + return string.Format($"{{0:F{precision}}},{{1:F{precision}}},{{2:F{precision}}} ", X, Y, Z); + } +#else + public static string VertexAsString(T X, T Y, int precision) + { + if (typeof(T) == typeof(long)) // ignore precision + return $"{X},{Y} "; + else + return string.Format($"{{0:F{precision}}},{{1:F{precision}}} ", X, Y); + } +#endif + + public static void LogCPath(T[] cpaths, ref int idx, string spaceIndent) + { + int vertexCnt = Convert.ToInt32(cpaths[idx]); + idx += 2; + for (int i = 0; i < vertexCnt; i++) +#if USINGZ + Console.Write(spaceIndent + VertexAsString(cpaths[idx++], cpaths[idx++], cpaths[idx++], 2)); +#else + Console.Write(spaceIndent + VertexAsString(cpaths[idx++], cpaths[idx++], 2)); +#endif + Console.Write("\n"); + } + + public static void LogCPaths(T[]? cpaths, string spaceIndent) + { + if (cpaths == null) return; + int pathCnt = Convert.ToInt32(cpaths[1]); + int idx = 2; + for (int i = 0; i < pathCnt; i++) + LogCPath(cpaths, ref idx, spaceIndent); + } + + // Note: The CPolyTree structure defined in clipper.export.h is + // a simple array of T that contains any number of nested path contours. + + private static void CPolypathCounter(T[] cpolypath, + ref long idx, ref int pathCount, ref long arrayLen) + { + // returns the vertex count of each contained polygon + int vertexCount = Convert.ToInt32(cpolypath[idx++]); + int childCount = Convert.ToInt32(cpolypath[idx++]); + arrayLen += vertexCount * VERTEX_FIELD_CNT + 2; + pathCount++; + idx += vertexCount * VERTEX_FIELD_CNT; + for (int i = 0; i < childCount; i++) + CPolypathCounter(cpolypath, ref idx, ref pathCount, ref arrayLen); + } + private static void ConvertCPolypathToCPaths(T[] cpolypath, ref long idx, + T[] result, ref int resultIdx) + { + T vertexCount = cpolypath[idx++]!; + int vertCnt = (int)Convert.ChangeType(vertexCount, typeof(int)); + int childCount = Convert.ToInt32(cpolypath[idx++]); + result[resultIdx++] = vertexCount; + result[resultIdx++] = (T)Convert.ChangeType(0, typeof(T)); + // parse path + for (int i = 0; i < vertCnt; i++) + { + result[resultIdx++] = cpolypath[idx++]; // x + result[resultIdx++] = cpolypath[idx++]; // y +#if USINGZ + result[resultIdx++] = cpolypath[idx++]; // z +#endif + } + // parse children + for (int i = 0; i < childCount; i++) + ConvertCPolypathToCPaths(cpolypath, ref idx, result, ref resultIdx); + } + + public static void ConvertCPolytreeToCPaths(T[] cpolytree, out T[] result) + { + if (cpolytree == null) + { + result = new T[0]; + return; + } + int topPathsCount = Convert.ToInt32(cpolytree[1]); + long idx = 2; + int pathCount = 0; + long arrayLen = 2; + for (int i = 0; i < topPathsCount; i++) + CPolypathCounter(cpolytree, ref idx, ref pathCount, ref arrayLen); + result = new T[arrayLen]; + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(pathCount, typeof(T)); + int resultIdx = 2; + idx = 2; + for (int i = 0; i < topPathsCount; i++) + ConvertCPolypathToCPaths(cpolytree, ref idx, result, ref resultIdx); + } + + public static void LogPolyPath(T[] polypath, + ref int idx, bool isHole, string spaceIndent, int precision) + { + int polyCnt = Convert.ToInt32(polypath[idx++]); + int childCnt = Convert.ToInt32(polypath[idx++]); + string preamble = isHole ? "Hole: " : (spaceIndent == "") ? + "Polygon: " : "Nested Polygon: "; + Console.Write(spaceIndent + preamble); + spaceIndent += " "; + for (int i = 0; i < polyCnt; i++) +#if USINGZ + Console.Write(VertexAsString(polypath[idx++], polypath[idx++], polypath[idx++], precision)); +#else + Console.Write(VertexAsString(polypath[idx++], polypath[idx++], precision)); +#endif + Console.Write("\n"); + for (int i = 0; i < childCnt; i++) + LogPolyPath(polypath, ref idx, !isHole, spaceIndent, precision); + } + + public static void LogPolytree(T[] polytree, int precision) + { + int cnt = Convert.ToInt32(polytree[1]); + int idx = 2; + for (int i = 0; i < cnt; i++) + LogPolyPath(polytree, ref idx, false, " ", precision); + } + + public static T[]? GetArrayFromIntPtr(IntPtr paths) + { + if (paths == IntPtr.Zero) return null; + if (typeof(T) == typeof(long)) + { + long[] len = new long[1]; + Marshal.Copy(paths, len, 0, 1); + long[] res = new long[(int)len[0]]; + Marshal.Copy(paths, res, 0, (int)len[0]); + return res as T[]; + } + else if (typeof(T) == typeof(double)) + { + double[] len = new double[1]; + Marshal.Copy(paths, len, 0, 1); + double[] res = new double[(int)len[0]]; + Marshal.Copy(paths, res, 0, (int)len[0]); + return res as T[]; + } + else return null; + } + + public static void ConvertArrayOfLongs(long[] longs, out double[] result) + { + result = new double[longs.Length]; + for (int i = 0; i < longs.Length; i++) + result[i] = longs[i]; + } + + public static double StaticCastLongToDouble(long val) + { + byte[] b = BitConverter.GetBytes(val); + return BitConverter.ToDouble(b, 0); + } + public static long StaticCastDoubleToLong(double val) + { + byte[] b = BitConverter.GetBytes(val); + return BitConverter.ToInt64(b, 0); + } + + } +} diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllSvg.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllSvg.cs new file mode 100644 index 00000000..84bb74b6 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Clipper2DllSvg.cs @@ -0,0 +1,469 @@ +using System.Diagnostics; +using System.Globalization; +using static Clipper2Dll.Clipper2DllCore; + +using RectD = Clipper2Dll.Clipper2DllCore.Rect; + +namespace Clipper2Dll +{ + public class SvgWriter + { + private static RectD rectMax = InvalidRectD; + + public static RectD RectEmpty = new RectD(0, 0, 0, 0); + internal static bool IsValidRect(RectD rec) + { + return rec.right >= rec.left && rec.bottom >= rec.top; + } + + private readonly struct CoordStyle + { + public readonly string FontName; + public readonly int FontSize; + public readonly uint FontColor; + public CoordStyle(string fontname, int fontsize, uint fontcolor) + { + FontName = fontname; + FontSize = fontsize; + FontColor = fontcolor; + } + } + private readonly struct TextInfo + { + public readonly string text; + public readonly int fontSize; + public readonly uint fontColor; + public readonly double posX; + public readonly double posY; + public TextInfo(string text, double x, double y, + int fontsize = 12, uint fontcolor = 0xFF000000) + { + this.text = text; + posX = x; + posY = y; + fontSize = fontsize; + fontColor = fontcolor; + } + } + + private readonly struct CircleInfo + { + public readonly double centerX; + public readonly double centerY; + public readonly double radius; + public readonly uint brushClr; + public readonly uint penClr; + public readonly uint penWidth; + public CircleInfo(double centerX, double centerY, + double radius, uint brushClr, uint penClr, uint penWidth) + { + this.centerX = centerX; + this.centerY = centerY; + this.radius = radius; + this.brushClr = brushClr; + this.penClr = penClr; + this.penWidth = penWidth; + } + } + + private readonly struct PolyInfo + { + public readonly double[] paths; + public readonly uint BrushClr; + public readonly uint PenClr; + public readonly double PenWidth; + public readonly bool ShowCoords; + public readonly bool IsOpen; + public PolyInfo(double[] paths, uint brushcolor, uint pencolor, + double penwidth, bool showcoords = false, bool isopen = false) + { + this.paths = new double[paths.Length]; + paths.CopyTo(this.paths, 0); + BrushClr = brushcolor; + PenClr = pencolor; + PenWidth = penwidth; + ShowCoords = showcoords; + IsOpen = isopen; + } + } + + public FillRule FillRule { get; set; } + private readonly List PolyInfoList = new List(); + private readonly List textInfos = new List(); + private readonly List circleInfos = new List(); + private readonly CoordStyle coordStyle; + + private const string svg_header = "\n" + + "\n\n"; + private const string svg_path_format = "\"\n style=\"fill:{0};" + + " fill-opacity:{1:f2}; fill-rule:{2}; stroke:{3};" + + " stroke-opacity:{4:f2}; stroke-width:{5:f2};\"/>\n\n"; + private const string svg_path_format2 = "\"\n style=\"fill:none; stroke:{0};" + + "stroke-opacity:{1:f2}; stroke-width:{2:f2};\"/>\n\n"; + + public SvgWriter(FillRule fillrule = FillRule.EvenOdd, + string coordFontName = "Verdana", int coordFontsize = 9, uint coordFontColor = 0xFF000000) + { + coordStyle = new CoordStyle(coordFontName, coordFontsize, coordFontColor); + FillRule = fillrule; + } + + public void ClearPaths() + { + PolyInfoList.Clear(); + } + + public void ClearText() + { + textInfos.Clear(); + } + + public void ClearCircles() + { + circleInfos.Clear(); + } + + public void ClearAll() + { + PolyInfoList.Clear(); + textInfos.Clear(); + circleInfos.Clear(); + } + public void AddClosedPath(long[] path, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + long[] tmp = new long[path.Count() +2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddClosedPaths(tmp, brushColor, penColor, penWidth, showCoords); + } + + public void AddClosedPath(double[] path, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + double[] tmp = new double[path.Length + 2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddClosedPaths(tmp, brushColor, penColor, penWidth, showCoords); + } + + public void AddClosedPaths(long[] paths, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + double[] pathsD; + ConvertArrayOfLongs(paths, out pathsD); + PolyInfoList.Add(new PolyInfo(pathsD, brushColor, penColor, penWidth, showCoords, false)); + } + + public void AddClosedPaths(double[] paths, uint brushColor, + uint penColor, double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + PolyInfoList.Add(new PolyInfo(paths, brushColor, penColor, penWidth, showCoords, false)); + } + + public void AddOpenPath(long[] path, uint penColor, + double penWidth, bool showCoords = false) + { + long[] tmp = new long[path.Length + 2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddOpenPaths(tmp, penColor, penWidth, showCoords); + } + + public void AddOpenPath(double[] path, uint penColor, + double penWidth, bool showCoords = false) + { + double[] tmp = new double[path.Length + 2]; + path.CopyTo(tmp, 2); + tmp[0] = tmp.Length; + tmp[1] = 1; + AddOpenPaths(tmp, penColor, penWidth, showCoords); + } + + public void AddOpenPaths(long[] paths, + uint penColor, double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + double[] pathsD; + ConvertArrayOfLongs(paths, out pathsD); + PolyInfoList.Add(new PolyInfo(pathsD, 0x0, penColor, penWidth, showCoords, true)); + } + + public void AddOpenPaths(double[] paths, uint penColor, + double penWidth, bool showCoords = false) + { + if (paths.Length == 0) return; + PolyInfoList.Add(new PolyInfo(paths, + 0x0, penColor, penWidth, showCoords, true)); + } + + + public void AddText(string cap, double posX, double posY, int fontSize, uint fontClr = 0xFF000000) + { + textInfos.Add(new TextInfo(cap, posX, posY, fontSize, fontClr)); + } + + public void AddCircle(double centerX, double centerY, + double radius, uint brushClr, uint penClr, uint penWidth) + { + circleInfos.Add(new CircleInfo(centerX, centerY, radius, brushClr, penClr, penWidth)); + } + + private RectD GetBounds() + { + RectD bounds = new RectD(double.MaxValue, double.MaxValue, -double.MaxValue, -double.MaxValue); + foreach (PolyInfo pi in PolyInfoList) + { + int idx = 2; + long pathsCnt = Convert.ToInt64(pi.paths[1]); + for (int i = 0; i < pathsCnt; i++) + { + long pathLen = Convert.ToInt64(pi.paths[idx]); + idx += 2; + for (int j = 0; j < pathLen; j++) + { + double x = pi.paths[idx++]; + double y = pi.paths[idx++]; +#if USINGZ + idx++; +#endif + if (x < bounds.left) bounds.left = x; + if (x > bounds.right) bounds.right = x; + if (y < bounds.top) bounds.top = y; + if (y > bounds.bottom) bounds.bottom = y; + } + + } + } + if (!IsValidRect(bounds)) return RectEmpty; + return bounds; + } + + private static string ColorToHtml(uint clr) + { + return '#' + (clr & 0xFFFFFF).ToString("X6"); + } + + private static float GetAlpha(uint clr) + { + return ((float)(clr >> 24) / 255); + } + + public bool SaveToFile(string filename, int maxWidth = 0, int maxHeight = 0, int margin = -1) + { + if (margin < 0) margin = 20; + RectD bounds = GetBounds(); + if (bounds.IsEmpty()) return false; + + double scale = 1.0; + if (maxWidth > 0 && maxHeight > 0) + scale = Math.Min( + (maxWidth - margin * 2) / bounds.Width(), + (maxHeight - margin * 2) / bounds.Height()); + + long offsetX = margin - (long)(bounds.left * scale); + long offsetY = margin - (long)(bounds.top * scale); + + StreamWriter writer; + try + { + writer = new StreamWriter(filename); + } + catch + { + return false; + } + + if (maxWidth <= 0 || maxHeight <= 0) + writer.Write(svg_header, (bounds.right - bounds.left) + margin * 2, + (bounds.bottom - bounds.top) + margin * 2); + else + writer.Write(svg_header, maxWidth, maxHeight); + + foreach (PolyInfo pi in PolyInfoList) + { + writer.Write(" \n", + coordStyle.FontName, coordStyle.FontSize, ColorToHtml(coordStyle.FontColor)); + + idx = 2; + pathsCnt = Convert.ToInt64(pi.paths[1]); + for (int i = 0; i < pathsCnt; i++) + { + long pathLen = Convert.ToInt64(pi.paths[idx]); + idx += 2; + for (int j = 1; j < pathLen; j++) + { + double x = pi.paths[idx++]; + double y = pi.paths[idx++]; +#if USINGZ + double z = pi.paths[idx++]; + writer.Write("{2:f2},{3:f2},{4}\n", + (x * scale + offsetX), (y * scale + offsetY), x, y, z); +#else + writer.Write("{2:f2},{3:f2}\n", + (x * scale + offsetX), (y * scale + offsetY), x, y); +#endif + } + } + writer.Write("\n\n"); + } + } + + foreach (CircleInfo circInfo in circleInfos) + { + writer.Write("\n \n\n", + circInfo.centerX * scale + offsetX, circInfo.centerY * scale + offsetY, + circInfo.radius * scale, ColorToHtml(circInfo.penClr), circInfo.penWidth, + ColorToHtml(circInfo.brushClr), GetAlpha(circInfo.brushClr)); + } + + + foreach (TextInfo captionInfo in textInfos) + { + writer.Write("\n", + captionInfo.fontSize, ColorToHtml(captionInfo.fontColor)); + writer.Write("{2}\n\n", + captionInfo.posX * scale + offsetX, captionInfo.posY * scale + offsetY, captionInfo.text); + } + + writer.Write("\n"); + writer.Close(); + return true; + } + } //end SvgWriter + +public static class SvgWriterUtils + { + public static void AddCaption(SvgWriter svg, string caption, int x, int y) + { + svg.AddText(caption, x, y, 14); + } + + public static void AddSubject(SvgWriter svg, long[] path) + { + svg.AddClosedPath(path, 0x1800009C, 0xAAB3B3DA, 0.8); + } + public static void AddSubject(SvgWriter svg, double[] path) + { + svg.AddClosedPath(path, 0x1800009C, 0xAAB3B3DA, 0.8); + } + + public static void AddSubjects(SvgWriter svg, long[] paths) + { + svg.AddClosedPaths(paths, 0x1800009C, 0xAAB3B3DA, 0.8); + } + public static void AddOpenSubjects(SvgWriter svg, long[] paths) + { + svg.AddOpenPaths(paths, 0xAAB3B3DA, 0.8); + } + + public static void AddSubjects(SvgWriter svg, double[] paths) + { + svg.AddClosedPaths(paths, 0x1800009C, 0xAAB3B3DA, 0.8); + } + + public static void AddOpenSubjects(SvgWriter svg, double[] paths) + { + svg.AddOpenPaths(paths, 0xAAB3B3DA, 1.2); + } + + public static void AddClip(SvgWriter svg, long[] path) + { + svg.AddClosedPath(path, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddClip(SvgWriter svg, double[] path) + { + svg.AddClosedPath(path, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddClips(SvgWriter svg, long[] paths) + { + svg.AddClosedPaths(paths, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddClips(SvgWriter svg, double[] paths) + { + svg.AddClosedPaths(paths, 0x129C0000, 0xCCFFA07A, 0.8); + } + + public static void AddSolution(SvgWriter svg, long[] paths, + bool show_coords, bool is_closed = true, bool is_joined = true) + { + svg.AddClosedPaths(paths, 0x4080ff9C, 0xFF003300, 1.5, show_coords); + } + + public static void AddOpenSolution(SvgWriter svg, long[] paths, bool show_coords) + { + svg.AddOpenPaths(paths, 0xFF003300, 2.2, show_coords); + } + + public static void AddSolution(SvgWriter svg, double[] paths, bool show_coords) + { + svg.AddClosedPaths(paths, 0x4080ff9C, 0xFF003300, 1.5, show_coords); + } + + public static void AddOpenSolution(SvgWriter svg, double[] paths, bool show_coords) + { + svg.AddOpenPaths(paths, 0xFF003300, 2.2, show_coords); + } + + public static void OpenFileWithDefaultApp(string filename) + { + string path = Path.GetFullPath(filename); + if (!File.Exists(path)) return; + Process p = new Process() { StartInfo = new ProcessStartInfo(path) { UseShellExecute = true } }; + p.Start(); + } + + } //end SvgWriterUtils +} diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs new file mode 100644 index 00000000..6413cd15 --- /dev/null +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs @@ -0,0 +1,123 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 14 August 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************/ + +using System.Diagnostics; +using static Clipper2Dll.Clipper2DllCore; +using static Clipper2Dll.SvgWriterUtils; +using Clipper2Lib; + +namespace ClipperDllDemo +{ + public class Application + { + private static void MakeRandomCPaths(int width, int height, int count, + Random rand, out T[] result) + { + T[] coords = new T[count * VERTEX_FIELD_CNT]; + int idx = 0; + for (int i = 0; i < count; i++) + { + coords[idx++] = (T)Convert.ChangeType(rand.Next(width), typeof(T)); + coords[idx++] = (T)Convert.ChangeType(rand.Next(height), typeof(T)); +#if USINGZ + coords[idx++] = (T)Convert.ChangeType(0, typeof(T)); +#endif + } + + long arrayLen = count * VERTEX_FIELD_CNT + 4; + result = new T[arrayLen]; + result[0] = (T)Convert.ChangeType(arrayLen, typeof(T)); + result[1] = (T)Convert.ChangeType(1, typeof(T)); + result[2] = (T)Convert.ChangeType(count, typeof(T)); + result[3] = (T)Convert.ChangeType(0, typeof(T)); + coords.CopyTo(result, 4); + } + + private static void ConvertCPathsToPaths64(long[] cpaths, out Paths64 result) + { + if (cpaths[1] != 1) + throw new Exception("This function assumes cpaths contains only a single path"); + result = new Paths64(); + long pathLen = cpaths[2]; + Path64 path = new Path64((int)pathLen); + int idx = 4; + for (int i = 0; i < pathLen; i++) + { + long x = cpaths[idx++]; + long y = cpaths[idx++]; +#if USINGZ + long z = cpaths[idx++]; + path.Add(new Point64(x, y, z)); +#else + path.Add(new Point64(x, y)); +#endif + } + result.Add(path); + } + + public static void Main() + { + + //string? ver = Marshal.PtrToStringAnsi(Version()); + //Console.WriteLine(ver + "\n"); + + long timeMsec; + Random rand = new(); + const int edgeCount = 2500; + + long[] cSubjects; + MakeRandomCPaths(600, 400, edgeCount, rand, out cSubjects); + long[] cClips; + MakeRandomCPaths(600, 400, edgeCount, rand, out cClips); + + Paths64 solution; + ConvertCPathsToPaths64(cSubjects, out Paths64 subjects); + ConvertCPathsToPaths64(cClips, out Paths64 clips); + + ////////////////////////////////////////////////////////////////////// + // Use Dynamically Linked C++ compiled library (ie use the DLL) + ////////////////////////////////////////////////////////////////////// + Stopwatch sw1 = Stopwatch.StartNew(); + if (BooleanOp64((int)Clipper2Dll.Clipper2DllCore.ClipType.Intersection, + (int)Clipper2Dll.Clipper2DllCore.FillRule.NonZero, + cSubjects, null, cClips, out IntPtr cSol, out IntPtr cSolOpen, false, false) != 0) + return; + + long[]? cSolution = GetArrayFromIntPtr(cSol); + if (cSolution == null) return; + DisposeArray64(ref cSol); + DisposeArray64(ref cSolOpen); + sw1.Stop(); + timeMsec = sw1.ElapsedMilliseconds; + Console.WriteLine($"Time using DLL (C++ code): {timeMsec} ms"); + + string fileName = "../../../clipper2_dll.svg"; + Clipper2Dll.SvgWriter svg = new(Clipper2Dll.Clipper2DllCore.FillRule.NonZero); + AddSubjects(svg, cSubjects); + AddClips(svg, cClips); + AddSolution(svg, cSolution, false); + svg.SaveToFile(fileName, 800, 600, 20); + OpenFileWithDefaultApp(fileName); + + ////////////////////////////////////////////////////////////////////// + // Use Clipper2's statically linked C# compiled library + ////////////////////////////////////////////////////////////////////// + + Stopwatch sw2 = Stopwatch.StartNew(); + solution = Clipper.Intersect(subjects, clips, Clipper2Lib.FillRule.NonZero); + sw2.Stop(); + timeMsec = sw2.ElapsedMilliseconds; + Console.WriteLine($"Time using C# code : {timeMsec} ms"); + ////////////////////////////////////////////////////////////////////// + + //Console.WriteLine("Press any key to exit ... "); + //Console.ReadKey(); + } + + } //end Application +} //namespace diff --git a/DLL/CSharp_TestApp2/COMPILE AND ADD Clipper2_64.dll HERE b/DLL/TEST_APPS/Delphi_TestApps/Output/COPY Clipper2 DLLs HERE similarity index 100% rename from DLL/CSharp_TestApp2/COMPILE AND ADD Clipper2_64.dll HERE rename to DLL/TEST_APPS/Delphi_TestApps/Output/COPY Clipper2 DLLs HERE diff --git a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas new file mode 100644 index 00000000..33a9b395 --- /dev/null +++ b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas @@ -0,0 +1,329 @@ +unit Clipper2DllCore; + +(******************************************************************************* +* Author : Angus Johnson * +* Date : 12 August 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************) + +interface + +uses + SysUtils, Classes, Math; + +type +{$IFDEF USINGZ} + Ztype = type Int64; //double; // + PZtype = ^Ztype; +{$ENDIF} + + PPoint64 = ^TPoint64; + TPoint64 = record + X, Y: Int64; +{$IFDEF USINGZ} + Z: Ztype; +{$ENDIF} + end; + + PPointD = ^TPointD; + TPointD = record + X, Y: double; +{$IFDEF USINGZ} + Z: Ztype; +{$ENDIF} + end; + + // The most commonly used filling rules for polygons are EvenOdd and NonZero. + // https://en.wikipedia.org/wiki/Even-odd_rule + // https://en.wikipedia.org/wiki/Nonzero-rule + TFillRule = (frEvenOdd, frNonZero, frPositive, frNegative); + TClipType = (ctNone, ctIntersection, ctUnion, ctDifference, ctXor); + + TArrayOfBoolean = array of Boolean; + TArrayOfInt64 = array of Int64; + TArrayOfDouble = array of double; + + TRect64 = record + public + Left : Int64; + Top : Int64; + Right : Int64; + Bottom : Int64; + end; + + TRectD = record + public + Left : double; + Top : double; + Right : double; + Bottom : double; + end; + + CInt64arr = array[0..$FFFF] of Int64; + PCInt64arr = ^CInt64arr; + CPath64 = PCInt64arr; + CPaths64 = PCInt64arr; + CPolyPath64 = PCInt64arr; + CPolytree64 = PCInt64arr; + + CDblarr = array[0..$FFFF] of Double; + PCDblarr = ^CDblarr; + CPathD = PCDblarr; + CPathsD = PCDblarr; + CPolyPathD = PCDblarr; + CPolytreeD = PCDblarr; + +{$IFDEF USINGZ} +type + TZCallback64_DLL = procedure (const bot1, top1, bot2, top2: TPoint64; + var intersectPt: TPoint64); + TZCallbackD_DLL = procedure (const bot1, top1, bot2, top2: TPointD; + var intersectPt: TPointD); +{$ENDIF} + +const +{$IFDEF WIN64} + {$IFDEF USINGZ} + CLIPPER2_DLL = 'Clipper2_Z_64.dll'; + {$ELSE} + CLIPPER2_DLL = 'Clipper2_64.dll'; + {$ENDIF} +{$ELSE} + {$IFDEF USINGZ} + CLIPPER2_DLL = 'Clipper2_Z_32.dll'; + {$ELSE} + CLIPPER2_DLL = 'Clipper2_32.dll'; + {$ENDIF} +{$ENDIF} + +{$IFDEF USINGZ} + VERTEX_FIELD_CNT = 3; +{$ELSE} + VERTEX_FIELD_CNT = 2; +{$ENDIF} + + Intersection = 1; Union = 2; Difference =3; Xor_ = 4; + EvenOdd = 0; NonZero = 1; Positive = 2; Negative = 3; + POINT_FIELD_CNT = {$IFDEF USINGZ} 3 {$ELSE} 2 {$ENDIF}; + +//////////////////////////////////////////////////////// +// Clipper2 DLL functions +//////////////////////////////////////////////////////// + +function Version(): PAnsiChar; cdecl; + external CLIPPER2_DLL name 'Version'; + +procedure DisposeExportedArray64(var cps: PCInt64arr); cdecl; + external CLIPPER2_DLL name 'DisposeArray64'; +procedure DisposeExportedArrayD(var cp: PCDblarr); cdecl; + external CLIPPER2_DLL name 'DisposeArrayD'; + +function BooleanOp64(cliptype: UInt8; fillrule: UInt8; + const subjects: CPaths64; const subjects_open: CPaths64; + const clips: CPaths64; out solution: CPaths64; + out solution_open: CPaths64; + preserve_collinear: boolean = true; + reverse_solution: boolean = false): integer; cdecl; + external CLIPPER2_DLL name 'BooleanOp64'; +function BooleanOp_PolyTree64(cliptype: UInt8; fillrule: UInt8; + const subjects: CPaths64; const subjects_open: CPaths64; + const clips: CPaths64; out solution: CPolyTree64; + out solution_open: CPaths64; + preserve_collinear: boolean = true; + reverse_solution: boolean = false): integer; cdecl; + external CLIPPER2_DLL name 'BooleanOp_PolyTree64'; + +function BooleanOpD(cliptype: UInt8; fillrule: UInt8; + const subjects: CPathsD; const subjects_open: CPathsD; + const clips: CPathsD; out solution: CPathsD; out solution_open: CPathsD; + precision: integer = 2; + preserve_collinear: boolean = true; + reverse_solution: boolean = false): integer; cdecl; + external CLIPPER2_DLL name 'BooleanOpD'; +function BooleanOp_PolyTreeD(cliptype: UInt8; fillrule: UInt8; + const subjects: CPathsD; const subjects_open: CPathsD; + const clips: CPathsD; out solution: CPolyTreeD; out solution_open: CPathsD; + precision: integer = 2; + preserve_collinear: boolean = true; + reverse_solution: boolean = false): integer; cdecl; + external CLIPPER2_DLL name 'BooleanOp_PolyTreeD'; +function InflatePaths64(const paths: CPaths64; + delta: double; jointype, endtype: UInt8; miter_limit: double = 2.0; + arc_tolerance: double = 0.0; + reverse_solution: Boolean = false): CPaths64; cdecl; + external CLIPPER2_DLL name 'InflatePaths64'; +function InflatePathsD(const paths: CPathsD; + delta: double; jointype, endtype: UInt8; precision: integer = 2; + miter_limit: double = 2.0; arc_tolerance: double = 0.0; + reverse_solution: Boolean = false): CPathsD; cdecl; + external CLIPPER2_DLL name 'InflatePathsD'; + +function RectClip64(const rect: TRect64; const paths: CPaths64; + convexOnly: Boolean = false): CPaths64; cdecl; + external CLIPPER2_DLL name 'RectClip64'; +function RectClipD(const rect: TRectD; const paths: CPathsD; + precision: integer = 2; convexOnly: Boolean = false): CPathsD; cdecl; + external CLIPPER2_DLL name 'RectClipD'; +function RectClipLines64(const rect: TRect64; + const paths: CPaths64): CPaths64; cdecl; + external CLIPPER2_DLL name 'RectClipLines64'; +function RectClipLinesD(const rect: TRectD; + const paths: CPathsD; precision: integer = 2): CPathsD; cdecl; + external CLIPPER2_DLL name 'RectClipLinesD'; + +{$IFDEF USINGZ} +procedure SetZCallback64(zCallback64: TZCallback64_DLL); cdecl; + external CLIPPER2_DLL name 'SetZCallback64'; +procedure SetZCallbackD(zCallbackD: TZCallbackD_DLL); cdecl; + external CLIPPER2_DLL name 'SetZCallbackD'; +{$ENDIF} + +function Rect64(const left, top, right, bottom: Int64): TRect64; +function RectD(const left, top, right, bottom: double): TRectD; +function IsValidRect64(const rec: TRect64): Boolean; +function IsValidRectD(const rec: TRectD): Boolean; + +function GetBounds(paths: CPathsD): TRectD; +function MakeCPaths64(const coords: array of Int64): CPaths64; +function MakeCPathsD(const coords: array of double): CPathsD; + +{$IFDEF USINGZ} +function PointD(const X, Y: Double; Z: ZType = 0): TPointD; +{$ELSE} +function PointD(const X, Y: Double): TPointD; +{$ENDIF} + +procedure DisposeLocalArray64(cp: PCInt64arr); +procedure DisposeLocalArrayD(cp: PCDblarr); + +implementation + +{$IFDEF USINGZ} +function PointD(const X, Y: Double; Z: ZType = 0): TPointD; +begin + Result.X := X; + Result.Y := Y; + Result.Z := Z; +end; +{$ELSE} +function PointD(const X, Y: Double): TPointD; +begin + Result.X := X; + Result.Y := Y; +end; +{$ENDIF} +//------------------------------------------------------------------------------ + +function Rect64(const left, top, right, bottom: Int64): TRect64; +begin + Result.Left := left; + Result.Top := top; + Result.Right := right; + Result.Bottom := bottom; +end; +//------------------------------------------------------------------------------ + +function RectD(const left, top, right, bottom: double): TRectD; +begin + Result.Left := left; + Result.Top := top; + Result.Right := right; + Result.Bottom := bottom; +end; +//------------------------------------------------------------------------------ + +function IsValidRect64(const rec: TRect64): Boolean; +begin + Result := (rec.Left <= rec.Right) and (rec.Top < rec.Bottom); +end; +//------------------------------------------------------------------------------ + +function IsValidRectD(const rec: TRectD): Boolean; +begin + Result := (rec.Left <= rec.Right) and (rec.Top < rec.Bottom); +end; +//------------------------------------------------------------------------------ + +function GetBounds(paths: CPathsD): TRectD; +var + i,j, idx : Integer; + pathsCnt, pathLen : Integer; + x,y: double; +begin + Result := RectD(MaxDouble, MaxDouble, -MaxDouble, -MaxDouble); + pathsCnt := Round(paths[1]); + idx := 2; + for i := 0 to pathsCnt -1 do + begin + pathLen := Round(paths[idx]); + inc(idx, 2); + for j := 0 to pathLen -1 do + begin + x := paths[idx]; inc(idx); + y := paths[idx]; inc(idx); +{$IFDEF USINGZ} + inc(idx); +{$ENDIF} + if (x < Result.left) then Result.left := x; + if (x > Result.right) then Result.right := x; + if (y < Result.top) then Result.top := y; + if (y > Result.bottom) then Result.bottom := y; + end; + end; + if not IsValidRectD(Result) then + Result := RectD(0,0,0,0); +end; +//------------------------------------------------------------------------------ + +function MakeCPaths64(const coords: array of Int64): CPaths64; +var + i, arrayLen, pathLen: integer; +begin + pathLen := length(coords) div VERTEX_FIELD_CNT; + arrayLen := length(coords) + 4; + GetMem(Result, arrayLen * sizeOf(Int64)); + Result[0] := arrayLen; + Result[1] := 1; + Result[2] := pathLen; + Result[3] := 0; + if pathLen > 0 then + Move(coords[0], Result[4], length(coords) * SizeOf(Int64)); +end; +//------------------------------------------------------------------------------ + +function MakeCPathsD(const coords: array of double): CPathsD; +var + i, arrayLen, pathLen: integer; +begin + pathLen := length(coords) div VERTEX_FIELD_CNT; + arrayLen := length(coords) + 4; + GetMem(Result, arrayLen * SizeOf(double)); + Result[0] := arrayLen; + Result[1] := 1; + Result[2] := pathLen; + Result[3] := 0; + if pathLen > 0 then + Move(coords[0], Result[4], length(coords) * SizeOf(double)); +end; +//------------------------------------------------------------------------------ + +//////////////////////////////////////////////////////// +// functions related to Clipper2 DLL structures +// including path format conversion functions +//////////////////////////////////////////////////////// + +procedure DisposeLocalArray64(cp: PCInt64arr); +begin + FreeMem(cp); +end; +//----------------------------------------------------- + +procedure DisposeLocalArrayD(cp: PCDblarr); +begin + FreeMem(cp); +end; +//----------------------------------------------------- + +end. diff --git a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas new file mode 100644 index 00000000..39b8519c --- /dev/null +++ b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas @@ -0,0 +1,774 @@ + unit Clipper2DllSVG; + +(******************************************************************************* +* Author : Angus Johnson * +* Date : 12 August 2024 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2024 * +* License : http://www.boost.org/LICENSE_1_0.txt * +*******************************************************************************) + +interface + +uses + Windows, ShellApi, + Classes, SysUtils, Math, Clipper2DllCore; + +const + black = $FF000000; + white = $FFFFFFFF; + maroon = $FF800000; + navy = $FF000080; + blue = $FF0000FF; + red = $FFFF0000; + green = $FF008000; + yellow = $FFFFFF00; + lime = $FF00FF00; + fuscia = $FFFF00FF; + aqua = $FF00FFFF; + + displayWidth = 400; + displayHeight = 400; + +type + +{$IFDEF RECORD_METHODS} + TCoordStyle = record +{$ELSE} + TCoordStyle = object +{$ENDIF} + FontName: string; + FontSize: integer; + FontColor: cardinal; + constructor Create(const afontname: string; + afontsize: integer; afontcolor: Cardinal); + end; + + TArrayOfString = array of string; + TArrayOfInteger = array of Integer; + + PTextInfo = ^TTextInfo; +{$IFDEF RECORD_METHODS} + TTextInfo = record +{$ELSE} + TTextInfo = object +{$ENDIF} + x,y : double; + text: string; + fontSize: integer; + fontColor: Cardinal; + Bold: Boolean; + constructor Create(atext: string; _x, _y: double; + afontsize: integer = 12; + afontcolor: Cardinal = black; + aBold: Boolean = false); + end; + + PPolyInfo = ^TPolyInfo; + TPolyInfo = record + paths : CPathsD; + BrushClr : Cardinal; + PenClr : Cardinal; + PenWidth : double; + ShowCoords: Boolean; + ShowFrac : Boolean; + IsOpen : Boolean; + dashes : TArrayOfInteger; + end; + + PCircleInfo = ^TCircleInfo; + TCircleInfo = record + center : TPointD; + radius : double; + BrushClr : Cardinal; + PenClr : Cardinal; + PenWidth : double; + end; + + TSvgWriter = class + private + fFillRule : TFillRule; + fCoordStyle : TCoordStyle; + fPolyInfos : TList; + fCircleInfos: TList; + fTextInfos : TList; + function GetBounds: TRectD; + public + constructor Create(fillRule: TFillRule; + const coordFontName: string = 'Verdana'; + coordFontSize: integer = 9; + coordFontColor: Cardinal = black); + destructor Destroy; override; + + procedure AddPaths(const paths: CPaths64; isOpen: Boolean; + brushColor, penColor: Cardinal; + penWidth: double; showCoords: Boolean = false); overload; + + procedure AddPaths(const paths: CPathsD; isOpen: Boolean; + brushColor, penColor: Cardinal; + penWidth: double; showCoords: Boolean = false); overload; + + procedure AddCircle(const center: TPoint64; radius: double; + brushColor, penColor: Cardinal; penWidth: double); overload; + procedure AddCircle(const center: TPointD; radius: double; + brushColor, penColor: Cardinal; penWidth: double); overload; + procedure AddText(text: string; x,y: double; + fontSize: integer = 14; fontClr: Cardinal = black; bold: Boolean = false); + + function SaveToFile(const filename: string; + maxWidth: integer = 0; maxHeight: integer = 0; + margin: integer = 20): Boolean; + procedure ClearPaths; + procedure ClearText; + procedure ClearAll; + end; + + procedure AddSubjects(svg: TSvgWriter; const paths: CPaths64; showCoords: Boolean = false); overload; + procedure AddOpenSubjects(svg: TSvgWriter; const paths: CPaths64); overload; + procedure AddClips(svg: TSvgWriter; const paths: CPaths64; showCoords: Boolean = false); overload; + procedure AddSolution(svg: TSvgWriter; const paths: CPaths64; showCoords: Boolean = false); overload; + procedure AddOpenSolution(svg: TSvgWriter; const paths: CPaths64); overload; + + procedure SaveSvg(svg: TSvgWriter; const filename: string; + width: integer = 0; height: integer = 0; margin: integer = 0); overload; + + procedure AddSubjects(svg: TSvgWriter; const paths: CPathsD; showCoords: Boolean = false); overload; + procedure AddOpenSubjects(svg: TSvgWriter; const paths: CPathsD); overload; + procedure AddClips(svg: TSvgWriter; const paths: CPathsD; showCoords: Boolean = false); overload; + procedure AddSolution(svg: TSvgWriter; const paths: CPathsD; showCoords: Boolean= false); overload; + procedure AddOpenSolution(svg: TSvgWriter; const paths: CPathsD); overload; + + procedure DisplaySVG(const sub, subo, clp, sol, solo: CPaths64; + const svgName: string; width: integer = displayWidth; + height: integer = displayHeight; showCoords: Boolean = false); overload; + procedure DisplaySVG(const sub, subo, clp, sol, solo: CPathsD; + const svgName: string; width: integer = displayWidth; + height: integer = displayHeight; showCoords: Boolean = false); overload; + + +implementation + +const + MaxRect: TRectD = (left: MaxDouble; + Top: MaxDouble; Right: -MaxDouble; Bottom: -MaxDouble); + + svg_header: string = + ''; + svg_path_format: string = '"'+#10+' style="fill:%s;' + + ' fill-opacity:%1.2f; fill-rule:%s; stroke:%s;' + + ' stroke-opacity:%1.2f; stroke-width:%1.2f;"/>'#10; + svg_path_format2: string = '"'+#10+ + ' style="fill:none; stroke:%s; ' + + 'stroke-opacity:%1.2f; stroke-width:%1.2f; %s"/>'#10; + +function ColorToHtml(color: Cardinal): string; +begin + Result := Format('#%6.6x', [color and $FFFFFF]);; +end; +//------------------------------------------------------------------------------ + +function GetAlpha(clr: Cardinal): double; +begin + Result := (clr shr 24) / 255; +end; +//------------------------------------------------------------------------------ + +function CopyCPaths64(cpaths: CPaths64): CPaths64; +var + arrayLen, byteSpace: int64; +begin + arrayLen := cpaths[0]; + byteSpace := arrayLen * SizeOf(Int64); + GetMem(Result, byteSpace); + Move(cpaths[0], Result[0], byteSpace); +end; +//------------------------------------------------------------------------------ + +function CopyCPathsD(cpaths: CPathsD): CPathsD; +var + arrayLen, byteSpace: int64; +begin + arrayLen := Round(cpaths[0]); + byteSpace := arrayLen * SizeOf(double); + GetMem(Result, byteSpace); + Move(cpaths[0], Result[0], byteSpace); +end; +//------------------------------------------------------------------------------ + +function CopyConvertCPath64(cpaths: CPaths64): CPathsD; +var + i: integer; + arrayLen, byteSpace: int64; +begin + arrayLen := cpaths[0]; + byteSpace := arrayLen * SizeOf(double); + GetMem(Result, byteSpace); + for i := 0 to arrayLen -1 do Result[i] := cpaths[i]; +end; +//------------------------------------------------------------------------------ + +constructor TCoordStyle.Create(const afontname: string; + afontsize: integer; afontcolor: Cardinal); +begin + Self.FontName := afontname; + Self.FontSize := afontsize; + Self.FontColor := afontcolor; +end; +//------------------------------------------------------------------------------ + +constructor TTextInfo.Create(atext: string; _x, _y: double; + afontsize: integer = 12; afontcolor: Cardinal = black; + aBold: Boolean = false); +begin + self.x := _x; + self.y := _y; + self.text := text; + self.fontSize := afontsize; + self.fontColor := afontcolor; + Self.Bold := aBold; +end; +//------------------------------------------------------------------------------ + +constructor TSvgWriter.Create(fillRule: TFillRule; + const coordFontName: string = 'Verdana'; + coordFontSize: integer = 9; + coordFontColor: Cardinal = black); +begin + fFillRule := fillRule; + fCoordStyle.FontName := coordFontName; + fCoordStyle.FontSize := coordFontSize; + fCoordStyle.FontColor := coordFontColor; + fPolyInfos := TList.Create; + fCircleInfos := TList.Create; + fTextInfos := TList.Create; +end; +//------------------------------------------------------------------------------ + +destructor TSvgWriter.Destroy; +begin + ClearAll; + fPolyInfos.Free; + fCircleInfos.Free; + fTextInfos.Free; + inherited; +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.AddPaths(const paths: CPaths64; + isOpen: Boolean; brushColor, penColor: Cardinal; + penWidth: double; showCoords: Boolean = false); +var + pi: PPolyInfo; +begin + if paths = nil then Exit; + new(pi); + pi.paths := CopyConvertCPath64(paths); + pi.BrushClr := brushColor; + pi.PenClr := penColor; + pi.PenWidth := penWidth; + pi.ShowCoords := showCoords; + pi.ShowFrac := false; + pi.IsOpen := isOpen; + fPolyInfos.Add(pi); +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.AddPaths(const paths: CPathsD; isOpen: Boolean; + brushColor, penColor: Cardinal; + penWidth: double; showCoords: Boolean = false); +var + pi: PPolyInfo; +begin + if paths = nil then Exit; + new(pi); + pi.paths := CopyCPathsD(paths); + pi.BrushClr := brushColor; + pi.PenClr := penColor; + pi.PenWidth := penWidth; + pi.ShowCoords := showCoords; + pi.ShowFrac := true; + pi.IsOpen := isOpen; + fPolyInfos.Add(pi); +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.AddCircle(const center: TPoint64; + radius: double; brushColor, penColor: Cardinal; penWidth: double); +var + ci: PCircleInfo; +begin + new(ci); + ci.center := PointD(center.X, center.Y); + ci.radius := radius; + ci.BrushClr := brushColor; + ci.PenClr := penColor; + ci.PenWidth := penWidth; + fCircleInfos.Add(ci); +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.AddCircle(const center: TPointD; + radius: double; brushColor, penColor: Cardinal; penWidth: double); +var + ci: PCircleInfo; +begin + new(ci); + ci.center := center; + ci.radius := radius; + ci.BrushClr := brushColor; + ci.PenClr := penColor; + ci.PenWidth := penWidth; + fCircleInfos.Add(ci); +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.AddText(text: string; x,y: double; + fontSize: integer; fontClr: Cardinal; bold: Boolean); +var + ti: PTextInfo; +begin + new(ti); + ti.x := x; + ti.y := y; + ti.text := text; + ti.fontSize := fontSize; + ti.fontColor := fontClr; + ti.Bold := bold; + fTextInfos.Add(ti); +end; +//------------------------------------------------------------------------------ + +function TSvgWriter.GetBounds: TRectD; +var + i: integer; + bounds: TRectD; +begin + Result := MaxRect; + for i := 0 to fPolyInfos.Count -1 do + with PPolyInfo(fPolyInfos[i])^ do + begin + bounds := Clipper2DllCore.GetBounds(paths); + if not IsValidRectD(bounds) then Continue; + if (bounds.left < Result.Left) then Result.Left := bounds.Left; + if (bounds.right> Result.Right) then Result.Right := bounds.Right; + if (bounds.top < Result.Top) then Result.Top := bounds.Top; + if (bounds.bottom > Result.Bottom) then Result.Bottom := bounds.Bottom; + end; +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.ClearPaths; +var + i: integer; +begin + for i := 0 to fPolyInfos.Count -1 do + begin + Dispose(PPolyInfo(fPolyInfos[i]).paths); + Dispose(PPolyInfo(fPolyInfos[i])); + end; + fPolyInfos.Clear; + + for i := 0 to fCircleInfos.Count -1 do + Dispose(PCircleInfo(fCircleInfos[i])); + fCircleInfos.Clear; +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.ClearText; +var + i: integer; +begin + for i := 0 to fTextInfos.Count -1 do + Dispose(PTextInfo(fTextInfos[i])); + fTextInfos.Clear; +end; +//------------------------------------------------------------------------------ + +procedure TSvgWriter.ClearAll; +begin + ClearText; + ClearPaths; +end; +//------------------------------------------------------------------------------ + +function TSvgWriter.SaveToFile(const filename: string; + maxWidth: integer = 0; maxHeight: integer = 0; margin: integer = 20): Boolean; +var + i, j, k, idx : integer; + frac : integer; + showCo : boolean; + showFr : boolean; + x,y : double; +{$IFDEF USINGZ} + z : double; +{$ENDIF} + bounds : TRectD; + scale : double; + offsetX, offsetY : integer; + s : string; + sInline, dashStr : string; + sl : TStringList; + pathsCnt, pathLen : integer; + formatSettings: TFormatSettings; +const + fillRuleStr: array[boolean] of string = ('evenodd', 'nonzero'); + boldFont: array[boolean] of string = ('normal', '700'); + gap: integer = 5; + + procedure Add(const s: string); + begin + sl.Add( sInline + s); + sInline := ''; + end; + + procedure AddInline(const s: string); + begin + sInline := sInline + s; + end; + +begin + Result := false; + +{$IF NOT Defined(fpc) AND (CompilerVersion > 19)} //Delphi XE + + formatSettings := TFormatSettings.Create; +{$IFEND} + formatSettings.DecimalSeparator := '.'; + + // adjust margin + if (margin < 20) then margin := 20; + showCo := false; + showFr := false; + for i := 0 to fPolyInfos.Count -1 do + with PPolyInfo(fPolyInfos[i])^ do + if ShowCoords then + begin + showCo := true; + if showFrac then showFr := true; + + end; + if showCo then + begin + if showFr then + inc(margin, Abs(fCoordStyle.FontSize *4)) else + inc(margin, Abs(fCoordStyle.FontSize *2)); + end; + + // get scale and offset + bounds := GetBounds; + if (bounds.Left >= bounds.Right) or + (bounds.Top >= bounds.Bottom) then Exit; + + scale := 1.0; + if (maxWidth > 0) and (maxHeight > 0) then + scale := 1.0 / Max((bounds.right - bounds.left) / + (maxWidth - margin * 2), (bounds.bottom - bounds.top) / + (maxHeight - margin * 2)); + + offsetX := margin - Round(bounds.left * scale); + offsetY := margin - Round(bounds.top * scale); + + // write SVG + sl := TStringList.Create; + try + if (maxWidth <= 0) or (maxHeight <= 0) then + Add(Format(svg_header, + [Round(bounds.right - bounds.left) + margin * 2, + Round(bounds.bottom - bounds.top) + margin * 2], formatSettings)) + else + Add(Format(svg_header, [maxWidth, maxHeight], formatSettings)); + + for i := 0 to fPolyInfos.Count -1 do + with PPolyInfo(fPolyInfos[i])^ do + begin + AddInline(' ', + [FontName, FontSize, ColorToHtml(FontColor)], formatSettings)); + + idx := 2; + for j := 0 to pathsCnt -1 do + begin + pathLen := Round(paths[idx]); inc(idx, 2); + for k := 0 to pathLen -1 do + begin + x := paths[idx]; inc(idx); + y := paths[idx]; inc(idx); + {$IFDEF USINGZ} + z := paths[idx]; inc(idx); + Add(Format(' %1.*f,%1.*f,%1.*f', + [x * scale + offsetX, y * scale + offsetY, frac, x, frac, y, frac, z], + formatSettings)); + {$ELSE} + Add(Format(' %1.*f,%1.*f', + [x * scale + offsetX, y * scale + offsetY, frac, x, frac, y], + formatSettings)); + {$ENDIF} + end; + Add(''#10); + end; + end; + + end; + + + for i := 0 to fCircleInfos.Count -1 do + with PCircleInfo(fCircleInfos[i])^ do + begin + if GetAlpha(BrushClr) > 0.1 then + Add(Format(' ', + [center.X * scale + offsetX, center.Y * scale + offsetY, + radius, ColorToHtml(PenClr), PenWidth, + ColorToHtml(BrushClr), GetAlpha(BrushClr)], formatSettings)) + else + Add(Format(' ', + [center.X * scale + offsetX, center.Y * scale + offsetY, + radius, ColorToHtml(PenClr), PenWidth, + GetAlpha(PenClr)], formatSettings)); + end; + + for i := 0 to fTextInfos.Count -1 do + with PTextInfo(fTextInfos[i])^ do + begin + Add(Format( + ' ' + + '%s', + [boldFont[Bold], Round(fontSize * scale), + ColorToHtml(fontColor), + (x) * scale + offsetX, + (y) * scale + offsetY, text], formatSettings)); + end; + + Add(''#10); + sl.SaveToFile(filename); + Result := true; + finally + sl.free; + end; +end; +//------------------------------------------------------------------------------ + +procedure AddSubjects(svg: TSvgWriter; const paths: CPaths64; showCoords: Boolean = false); +begin + svg.AddPaths(paths, false, $200099FF, $800066FF, 1.0, showCoords); +end; +//------------------------------------------------------------------------------ + +procedure AddOpenSubjects(svg: TSvgWriter; const paths: CPaths64); +begin + svg.AddPaths(paths, true, $0, $800066FF, 2.2); +end; +//------------------------------------------------------------------------------ + +procedure AddClips(svg: TSvgWriter; const paths: CPaths64; showCoords: Boolean = false); +begin + svg.AddPaths(paths, false, $10FF9900, $80FF6600, 1.0, showCoords); +end; +//------------------------------------------------------------------------------ + +procedure AddSolution(svg: TSvgWriter; const paths: CPaths64; showCoords: Boolean); +begin + svg.AddPaths(paths, false, $8066FF66, $FF006600, 1.0, showCoords); +end; +//------------------------------------------------------------------------------ + +procedure AddOpenSolution(svg: TSvgWriter; const paths: CPaths64); +begin + svg.AddPaths(paths, true, $0, $FF006600, 1.5); +end; +//------------------------------------------------------------------------------ + +procedure SaveSvg(svg: TSvgWriter; const filename: string; + width: integer = 0; height: integer = 0; margin: integer = 0); +begin + svg.SaveToFile(filename, width, height, margin); +end; +//------------------------------------------------------------------------------ + +procedure AddSubjects(svg: TSvgWriter; const paths: CPathsD; showCoords: Boolean = false); +begin + svg.AddPaths(paths, false, $200099FF, $800066FF, 1.0, showCoords); +end; +//------------------------------------------------------------------------------ + +procedure AddOpenSubjects(svg: TSvgWriter; const paths: CPathsD); +begin + svg.AddPaths(paths, true, $0, $400066FF, 2.2); +end; +//------------------------------------------------------------------------------ + +procedure AddClips(svg: TSvgWriter; const paths: CPathsD; showCoords: Boolean = false); +begin + svg.AddPaths(paths, false, $10FF9900, $80FF6600, 1.0, showCoords); +end; +//------------------------------------------------------------------------------ + +procedure AddSolution(svg: TSvgWriter; const paths: CPathsD; showCoords: Boolean); +begin + svg.AddPaths(paths, false, $8066FF66, $FF006600, 1.0, showCoords); +end; +//------------------------------------------------------------------------------ + +procedure AddOpenSolution(svg: TSvgWriter; const paths: CPathsD); +begin + svg.AddPaths(paths, true, $0, $FF006600, 1.5); +end; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +procedure ShowSvg(const svgFilename: string); +begin + ShellExecute(0, 'open',PChar(svgFilename), nil, nil, SW_SHOW); +end; +//------------------------------------------------------------------------------ + +procedure DisplaySVG(const sub, subo, clp, sol, solo: CPathsD; + const svgName: string; width: integer = displayWidth; + height: integer = displayHeight; showCoords: Boolean = false); +var + i,j, idx, pathsCnt, pathLen: integer; + x,y,z: double; + svg: TSvgWriter; +begin + svg := TSvgWriter.Create(TFillRule.frNonZero); + try + AddSubjects(svg, sub); + AddOpenSubjects(svg, subo); + AddClips(svg, clp); + AddSolution(svg, sol, showCoords); +{$IFDEF USINGZ} + if Assigned(sol) then + begin + pathsCnt := Round(sol[1]); + idx := 2; + for i := 0 to pathsCnt -1 do + begin + pathLen := Round(sol[idx]); inc(idx, 2); + for j := 0 to pathLen -1 do + begin + x := sol[idx]; inc(idx); + y := sol[idx]; inc(idx); + z := sol[idx]; inc(idx); + if z <= 0 then Continue; + svg.AddCircle(PointD(x,y), 3, $FFFFFF00, $FF000000, 1); + svg.AddText(format('%1.0n',[z]), X +1, Y, 3); + end; + end; + end; +{$ENDIF} + AddOpenSolution(svg, solo); + SaveSvg(svg, svgName, width, height); + ShowSvg(svgName); + finally + svg.Free; + end; +end; +//------------------------------------------------------------------------------ + +procedure DisplaySVG(const sub, subo, clp, sol, solo: CPaths64; + const svgName: string; width: integer = displayWidth; + height: integer = displayHeight; showCoords: Boolean = false); +var + i,j, idx, pathsCnt, pathLen: integer; + x,y,z: Int64; + svg: TSvgWriter; +begin + svg := TSvgWriter.Create(frNonZero); + try + AddSubjects(svg, sub); + AddOpenSubjects(svg, subo); + AddClips(svg, clp); + AddSolution(svg, sol, showCoords); +{$IFDEF USINGZ} + if Assigned(sol) then + begin + pathsCnt := sol[1]; + idx := 2; + for i := 0 to pathsCnt -1 do + begin + pathLen := sol[idx]; inc(idx, 2); + for j := 0 to pathLen -1 do + begin + x := sol[idx]; inc(idx); + y := sol[idx]; inc(idx); + z := sol[idx]; inc(idx); + if z <= 0 then Continue; + svg.AddCircle(PointD(x,y), 3, $FFFFFF00, $FF000000, 1); + svg.AddText(format('%d',[z]), X +1, Y, 3); + end; + end; + end; +{$ENDIF} + AddOpenSolution(svg, solo); + SaveSvg(svg, svgName, width, height); + ShowSvg(svgName); + finally + svg.Free; + end; +end; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +end. + diff --git a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dpr b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dpr new file mode 100644 index 00000000..a359386c --- /dev/null +++ b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dpr @@ -0,0 +1,135 @@ +program Test1_DLL; + +{$APPTYPE CONSOLE} +{$R *.res} + +uses + Windows, + Math, + SysUtils, + Clipper2DllCore in 'Clipper2DllCore.pas', + Clipper2DllSVG in 'Clipper2DllSVG.pas'; + +//////////////////////////////////////////////////////// +// Z callback functions +//////////////////////////////////////////////////////// + +{$IFDEF USINGZ} +var + counter64: Int64; + counterD: double; + +procedure MyZCallback64(const bot1, top1, bot2, top2: TPoint64; + var intersectPt: TPoint64); +begin + // when ztype = double, we would need to perform + // a static cast from Int64 to double + //if TypeHandle(ztype) = TypeHandle(double) then + // intersectPt.Z := PDouble(Pointer(@counter64))^ else + intersectPt.Z := counter64; + + counter64 := counter64 +1; +end; + +procedure MyZCallbackD(const bot1, top1, bot2, top2: TPointD; + var intersectPt: TPointD); +begin + // when ztype = Int64 then we need to perform + // a static cast from double to Int64 + //if (TypeHandle(ztype) = TypeHandle(double)) then + // intersectPt.Z := counterD else + intersectPt.Z := PInt64(Pointer(@counterD))^; + + counterD := counterD +1; +end; +{$ENDIF} + +//////////////////////////////////////////////////////// +// test procedures +//////////////////////////////////////////////////////// + +procedure Test_BooleanOp64; +var + csub, cclp: CPaths64; + csol_extern, csolo_extern: CPaths64; +begin + // setup + csolo_extern := nil; + WriteLn(#10'Testing BooleanOp64'); + +{$IFDEF USINGZ} + csub := MakeCPaths64([100,50,0, 10,79,0, 65,2,0, 65,98,0, 10,21,0]); + cclp := MakeCPaths64([80,50,0, 59,79,0, 26,68,0, 26,32,0, 59,21,0]); + SetZCallback64(MyZCallback64); + counter64 := 10; +{$ELSE} + csub := MakeCPaths64([100,50, 10,79, 65,2, 65,98, 10,21]); + cclp := MakeCPaths64([80,50, 59,79, 26,68, 26,32, 59,21]); +{$ENDIF} + + // do the DLL operation + BooleanOp64(Intersection, NonZero, + csub, nil, cclp, csol_extern, csolo_extern); + + DisplaySVG(csub, nil, cclp, csol_extern, nil, + 'BooleanOp64.svg', 400,400, false); + + // clean up + DisposeLocalArray64(csub); + DisposeLocalArray64(cclp); + DisposeExportedArray64(csol_extern); + DisposeExportedArray64(csolo_extern); +end; + +procedure Test_BooleanOpD; +var + csub, cclp: CPathsD; + csol_extern, csolo_extern: CPathsD; +begin + // setup + csolo_extern := nil; + WriteLn(#10'Testing BooleanOpD'); +{$IFDEF USINGZ} + csub := MakeCPathsD([100,50,0, 10,79,0, 65,2,0, 65,98,0, 10,21,0]); + cclp := MakeCPathsD([80,50,0, 59,79,0, 26,68,0, 26,32,0, 59,21,0]); + SetZCallbackD(MyZCallbackD); + counterD := 10; +{$ELSE} + csub := MakeCPathsD([100,50, 10,79, 65,2, 65,98, 10,21]); + cclp := MakeCPathsD([80,50, 59,79, 26,68, 26,32, 59,21]); +{$ENDIF} + + // do the DLL operation + BooleanOpD(Uint8(TClipType.ctIntersection), Uint8(TFillRule.frNonZero), + csub, nil, cclp, csol_extern, csolo_extern); + + // optionally display result on the console + //WriteCPaths64(csol_extern); + + DisplaySVG(csub, nil, cclp, csol_extern, nil, + 'BooleanOpD.svg', 400,400, false); + + DisposeLocalArrayD(csub); + DisposeLocalArrayD(cclp); + DisposeExportedArrayD(csol_extern); + DisposeExportedArrayD(csolo_extern); +end; + +//////////////////////////////////////////////////////// +// main entry here +//////////////////////////////////////////////////////// + +var + s: string; +begin + Randomize; + + Write(#10'Clipper2 DLL version: '); + WriteLn(Version); + + Test_BooleanOp64; + Test_BooleanOpD; + + WriteLn(#10'Press Enter to quit.'); + ReadLn(s); +end. diff --git a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dproj b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dproj new file mode 100644 index 00000000..5c913e9a --- /dev/null +++ b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Test1_DLL.dproj @@ -0,0 +1,189 @@ + + + {09E2139C-6702-4081-A629-5FF300006048} + Test1_DLL.dpr + True + Debug + Test1_DLL + 2 + Console + None + 20.1 + Win64 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + Test1_DLL + 3081 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar + + + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_192x192.png + activity-1.7.2.dex.jar;annotation-experimental-1.3.0.dex.jar;annotation-jvm-1.6.0.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-6.0.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-1.1.0.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.10.1.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.10.1.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.1.dex.jar;lifecycle-livedata-2.6.1.dex.jar;lifecycle-livedata-core-2.6.1.dex.jar;lifecycle-runtime-2.6.1.dex.jar;lifecycle-service-2.6.1.dex.jar;lifecycle-viewmodel-2.6.1.dex.jar;lifecycle-viewmodel-savedstate-2.6.1.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.1.0.dex.jar;play-services-basement-18.1.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.0.2.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.0.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + Test1_DLL_Icon.ico + + + Test1_DLL_Icon.ico + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + USINGZ;$(DCC_Define) + (None) + none + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + DEBUG;$(DCC_Define) + false + true + true + true + + + Debug + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Test1_DLL_Icon.ico + (None) + none + + + + MainSource + + + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + + + + Delphi.Personality.12 + + + + + Test1_DLL.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + + + 12 + + + + From 97906fea2bed3a16bc46ce01ecf1607c366908b2 Mon Sep 17 00:00:00 2001 From: angusj Date: Tue, 17 Sep 2024 08:00:44 +1000 Subject: [PATCH 10/31] Renamed ClipType.None to ClipType.NoClip (#885) Trivial code tidy in TestExportHeaders.cpp --- .../include/clipper2/clipper.engine.h | 16 ++++----- .../include/clipper2/clipper.export.h | 4 +-- CPP/Clipper2Lib/src/clipper.engine.cpp | 36 +++++++++---------- CPP/Tests/TestExportHeaders.cpp | 4 +-- CPP/Tests/TestOffsets.cpp | 2 +- CPP/Tests/TestPolytreeHoles.cpp | 4 +-- CPP/Utils/ClipFileSave.cpp | 30 ++++++++-------- CSharp/Clipper2Lib/Clipper.Core.cs | 4 +-- Delphi/Clipper2Lib/Clipper.Core.pas | 4 +-- Delphi/Clipper2Lib/Clipper.pas | 2 +- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index f6108832..559f2479 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 5 July 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -32,13 +32,13 @@ namespace Clipper2Lib { struct HorzSegment; //Note: all clipping operations except for Difference are commutative. - enum class ClipType { None, Intersection, Union, Difference, Xor }; + enum class ClipType { NoClip, Intersection, Union, Difference, Xor }; enum class PathType { Subject, Clip }; - enum class JoinWith { None, Left, Right }; + enum class JoinWith { NoJoin, Left, Right }; enum class VertexFlags : uint32_t { - None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 + Empty = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 }; constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) @@ -55,7 +55,7 @@ namespace Clipper2Lib { Point64 pt; Vertex* next = nullptr; Vertex* prev = nullptr; - VertexFlags flags = VertexFlags::None; + VertexFlags flags = VertexFlags::Empty; }; struct OutPt { @@ -131,7 +131,7 @@ namespace Clipper2Lib { Vertex* vertex_top = nullptr; LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti) bool is_left_bound = false; - JoinWith join_with = JoinWith::None; + JoinWith join_with = JoinWith::NoJoin; }; struct LocalMinima { @@ -197,7 +197,7 @@ namespace Clipper2Lib { class ClipperBase { private: - ClipType cliptype_ = ClipType::None; + ClipType cliptype_ = ClipType::NoClip; FillRule fillrule_ = FillRule::EvenOdd; FillRule fillpos = FillRule::Positive; int64_t bot_y_ = 0; @@ -210,7 +210,7 @@ namespace Clipper2Lib { std::vector vertex_lists_; std::priority_queue scanline_list_; IntersectNodeList intersect_nodes_; - HorzSegmentList horz_seg_list_; + HorzSegmentList horz_seg_list_; std::vector horz_join_list_; void Reset(); inline void InsertScanline(int64_t y); diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index 3dfd2618..5afbba17 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 16 August 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * @@ -10,7 +10,7 @@ /* Boolean clipping: - cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4 + cliptype: NoClip=0, Intersection=1, Union=2, Difference=3, Xor=4 fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3 Polygon offsetting (inflate/deflate): diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index 8f120267..d3a146b4 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 27 April 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -85,7 +85,7 @@ namespace Clipper2Lib { inline bool IsOpenEnd(const Vertex& v) { return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) != - VertexFlags::None; + VertexFlags::Empty; } @@ -220,7 +220,7 @@ namespace Clipper2Lib { inline bool IsMaxima(const Vertex& v) { - return ((v.flags & VertexFlags::LocalMax) != VertexFlags::None); + return ((v.flags & VertexFlags::LocalMax) != VertexFlags::Empty); } @@ -235,12 +235,12 @@ namespace Clipper2Lib { if (e.wind_dx > 0) while ((result->next->pt.y == result->pt.y) && ((result->flags & (VertexFlags::OpenEnd | - VertexFlags::LocalMax)) == VertexFlags::None)) + VertexFlags::LocalMax)) == VertexFlags::Empty)) result = result->next; else while (result->prev->pt.y == result->pt.y && ((result->flags & (VertexFlags::OpenEnd | - VertexFlags::LocalMax)) == VertexFlags::None)) + VertexFlags::LocalMax)) == VertexFlags::Empty)) result = result->prev; if (!IsMaxima(*result)) result = nullptr; // not a maxima return result; @@ -478,7 +478,7 @@ namespace Clipper2Lib { inline bool IsJoined(const Active& e) { - return e.join_with != JoinWith::None; + return e.join_with != JoinWith::NoJoin; } inline void SetOwner(OutRec* outrec, OutRec* new_owner) @@ -608,7 +608,7 @@ namespace Clipper2Lib { Vertex& vert, PathType polytype, bool is_open) { //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); list.push_back(std::make_unique (&vert, polytype, is_open)); @@ -643,7 +643,7 @@ namespace Clipper2Lib { } curr_v->prev = prev_v; curr_v->pt = pt; - curr_v->flags = VertexFlags::None; + curr_v->flags = VertexFlags::Empty; prev_v = curr_v++; cnt++; } @@ -725,7 +725,7 @@ namespace Clipper2Lib { void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open) { //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); @@ -907,7 +907,7 @@ namespace Clipper2Lib { void ClipperBase::AddLocMin(Vertex& vert, PathType polytype, bool is_open) { //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); @@ -932,7 +932,7 @@ namespace Clipper2Lib { switch (cliptype_) { - case ClipType::None: + case ClipType::NoClip: return false; case ClipType::Intersection: switch (fillrule_) @@ -1208,7 +1208,7 @@ namespace Clipper2Lib { while (PopLocalMinima(bot_y, local_minima)) { - if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::None) + if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::Empty) { left_bound = nullptr; } @@ -1224,7 +1224,7 @@ namespace Clipper2Lib { SetDx(*left_bound); } - if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::None) + if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::Empty) { right_bound = nullptr; } @@ -2125,7 +2125,7 @@ namespace Clipper2Lib { using_polytree_ = use_polytrees; Reset(); int64_t y; - if (ct == ClipType::None || !PopScanline(y)) return true; + if (ct == ClipType::NoClip || !PopScanline(y)) return true; while (succeeded_) { @@ -2789,14 +2789,14 @@ namespace Clipper2Lib { { if (e.join_with == JoinWith::Right) { - e.join_with = JoinWith::None; - e.next_in_ael->join_with = JoinWith::None; + e.join_with = JoinWith::NoJoin; + e.next_in_ael->join_with = JoinWith::NoJoin; AddLocalMinPoly(e, *e.next_in_ael, pt, true); } else { - e.join_with = JoinWith::None; - e.prev_in_ael->join_with = JoinWith::None; + e.join_with = JoinWith::NoJoin; + e.prev_in_ael->join_with = JoinWith::NoJoin; AddLocalMinPoly(*e.prev_in_ael, e, pt, true); } } diff --git a/CPP/Tests/TestExportHeaders.cpp b/CPP/Tests/TestExportHeaders.cpp index 423d46ee..037ff64f 100644 --- a/CPP/Tests/TestExportHeaders.cpp +++ b/CPP/Tests/TestExportHeaders.cpp @@ -15,7 +15,7 @@ static bool CreatePolyPath64FromCPolyPath(CPolyPath64& v, PolyPath64& owner) { int64_t x = *v++, y = *v++; #ifdef USINGZ - auto z = Reinterpret(*v++); + z_type z = Reinterpret(*v++); path.push_back(Point64(x, y, z)); #else path.push_back(Point64(x, y)); @@ -48,7 +48,7 @@ static bool CreatePolyPathDFromCPolyPath(CPolyPathD& v, PolyPathD& owner) { double x = *v++, y = *v++; #ifdef USINGZ - auto z = Reinterpret(*v++); + z_type z = Reinterpret(*v++); path.push_back(PointD(x, y, z)); #else path.push_back(PointD(x, y)); diff --git a/CPP/Tests/TestOffsets.cpp b/CPP/Tests/TestOffsets.cpp index 006e4b6a..d1af4e74 100644 --- a/CPP/Tests/TestOffsets.cpp +++ b/CPP/Tests/TestOffsets.cpp @@ -12,7 +12,7 @@ TEST(Clipper2Tests, TestOffsets) { ClipperOffset co; Paths64 subject, subject_open, clip; Paths64 solution, solution_open; - ClipType ct = ClipType::None; + ClipType ct = ClipType::NoClip; FillRule fr = FillRule::NonZero; int64_t stored_area = 0, stored_count = 0; ASSERT_TRUE(LoadTestNum(ifs, test_number, subject, subject_open, clip, stored_area, stored_count, ct, fr)); diff --git a/CPP/Tests/TestPolytreeHoles.cpp b/CPP/Tests/TestPolytreeHoles.cpp index 891094ba..901521a0 100644 --- a/CPP/Tests/TestPolytreeHoles.cpp +++ b/CPP/Tests/TestPolytreeHoles.cpp @@ -9,7 +9,7 @@ TEST(Clipper2Tests, TestPolytreeHoles1) Paths64 subject, subject_open, clip; PolyTree64 solution; Paths64 solution_open; - ClipType ct = ClipType::None; + ClipType ct = ClipType::NoClip; FillRule fr = FillRule::EvenOdd; int64_t area = 0, count = 0; bool success = false; @@ -61,7 +61,7 @@ TEST(Clipper2Tests, TestPolytreeHoles2) ASSERT_TRUE(ifs); ASSERT_TRUE(ifs.good()); Paths64 subject, subject_open, clip; - ClipType ct = ClipType::None; + ClipType ct = ClipType::NoClip; FillRule fr = FillRule::EvenOdd; int64_t area = 0, count = 0; ASSERT_TRUE(LoadTestNum(ifs, 1, subject, subject_open, clip, area, count, ct, fr)); diff --git a/CPP/Utils/ClipFileSave.cpp b/CPP/Utils/ClipFileSave.cpp index 305484c6..139e03a7 100644 --- a/CPP/Utils/ClipFileSave.cpp +++ b/CPP/Utils/ClipFileSave.cpp @@ -14,7 +14,7 @@ namespace Clipper2Lib { // Boyer Moore Horspool Search //------------------------------------------------------------------------------ -class BMH_Search +class BMH_Search { private: uint8_t case_table[256]; @@ -51,7 +51,7 @@ class BMH_Search } void Init() - { + { case_sensitive_ = false; current = nullptr; end = nullptr; last_found = nullptr; } @@ -85,7 +85,7 @@ class BMH_Search while (current < end) { uint8_t i = shift[case_table[static_cast(*current)]]; - if (!i) + if (!i) { char* j = current - needle_len_less1; while (i < needle_len_less1 && @@ -107,7 +107,7 @@ class BMH_Search public: - explicit BMH_Search(std::ifstream& stream, + explicit BMH_Search(std::ifstream& stream, const std::string& needle = "") { //case blind table @@ -121,7 +121,7 @@ class BMH_Search if (needle.size() > 0) SetNeedle(needle); } - BMH_Search(const char* haystack, size_t length, + BMH_Search(const char* haystack, size_t length, const std::string& needle = "") { Init(); @@ -137,7 +137,7 @@ class BMH_Search void Reset() { - current = haystack_; + current = haystack_; last_found = nullptr; } @@ -152,7 +152,7 @@ class BMH_Search needle_.clear(); needle_.reserve(needle_len_); for (const char& c : needle) needle_.push_back(static_cast(c)); - + //case insensitive needle needle_ic_ = needle_; for (std::vector< uint8_t>::iterator ui = needle_ic_.begin(); ui != needle_ic_.end(); ++ui) @@ -199,10 +199,10 @@ class BMH_Search inline size_t LastFoundOffset() { return last_found - haystack_; } inline char* FindNextEndLine() - { + { current = last_found + needle_len_; - while (current < end && - *current != char(13) && *current != char(10)) + while (current < end && + *current != char(13) && *current != char(10)) ++current; return current; } @@ -212,7 +212,7 @@ class BMH_Search void PathsToStream(const Paths64& paths, std::ostream& stream) { - for (Paths64::const_iterator paths_it = paths.cbegin(); + for (Paths64::const_iterator paths_it = paths.cbegin(); paths_it != paths.cend(); ++paths_it) { //watch out for empty paths @@ -247,7 +247,7 @@ static bool GetInt(string::const_iterator& s_it, } bool SaveTest(const std::string& filename, bool append, - const Paths64* subj, const Paths64* subj_open, const Paths64* clip, + const Paths64* subj, const Paths64* subj_open, const Paths64* clip, int64_t area, int64_t count, ClipType ct, FillRule fr) { string line; @@ -267,8 +267,8 @@ bool SaveTest(const std::string& filename, bool append, string::const_iterator s_it = line.cbegin(), s_end = line.cend(); GetInt(s_it, s_end, last_test_no); } - } - else if (FileExists(filename)) + } + else if (FileExists(filename)) remove(filename.c_str()); ++last_test_no; @@ -282,7 +282,7 @@ bool SaveTest(const std::string& filename, bool append, string cliptype_string; switch (ct) { - case ClipType::None: cliptype_string = "NONE"; break; + case ClipType::NoClip: cliptype_string = "NOCLIP"; break; case ClipType::Intersection: cliptype_string = "INTERSECTION"; break; case ClipType::Union: cliptype_string = "UNION"; break; case ClipType::Difference: cliptype_string = "DIFFERENCE"; break; diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index 6358aef8..dc2ee7bd 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 13 May 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Core structures and functions for the Clipper Library * @@ -532,7 +532,7 @@ public string ToString(int precision = 2) // Note: all clipping operations except for Difference are commutative. public enum ClipType { - None, + NoClip, Intersection, Union, Difference, diff --git a/Delphi/Clipper2Lib/Clipper.Core.pas b/Delphi/Clipper2Lib/Clipper.Core.pas index a6ee145c..d265e05d 100644 --- a/Delphi/Clipper2Lib/Clipper.Core.pas +++ b/Delphi/Clipper2Lib/Clipper.Core.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 13 May 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Core Clipper Library module * @@ -137,7 +137,7 @@ TListEx = class property Item[idx: integer]: Pointer read UnsafeGet; default; end; - TClipType = (ctNone, ctIntersection, ctUnion, ctDifference, ctXor); + TClipType = (ctNoClip, ctIntersection, ctUnion, ctDifference, ctXor); TPointInPolygonResult = (pipOn, pipInside, pipOutside); diff --git a/Delphi/Clipper2Lib/Clipper.pas b/Delphi/Clipper2Lib/Clipper.pas index 0c2fe87e..1520c671 100644 --- a/Delphi/Clipper2Lib/Clipper.pas +++ b/Delphi/Clipper2Lib/Clipper.pas @@ -52,7 +52,7 @@ interface etSquare = Clipper.Offset.etSquare; etRound = Clipper.Offset.etRound; - ctNone = Clipper.Core.ctNone; + ctNone = Clipper.Core.ctNoClip; ctIntersection = Clipper.Core.ctIntersection; ctUnion = Clipper.Core.ctUnion; ctDifference = Clipper.Core.ctDifference; From 20bd69475f48f55730df09c4ce2dfa5051f6fe02 Mon Sep 17 00:00:00 2001 From: angusj Date: Tue, 17 Sep 2024 08:08:42 +1000 Subject: [PATCH 11/31] Missed uploading clipper.engine.cs during the previous revision --- CSharp/Clipper2Lib/Clipper.Engine.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index c33ff708..d227b166 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 27 April 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -1840,7 +1840,7 @@ private void AdjustCurrXAndCopyToSEL(long topY) protected void ExecuteInternal(ClipType ct, FillRule fillRule) { - if (ct == ClipType.None) return; + if (ct == ClipType.NoClip) return; _fillrule = fillRule; _cliptype = ct; Reset(); From 80ef1fd656bc83db962fabb0c5f9f81c0028f6b9 Mon Sep 17 00:00:00 2001 From: philstopford Date: Mon, 30 Sep 2024 01:46:19 -0500 Subject: [PATCH 12/31] Proposed clean-ups (#894) 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 304235b0d25cbbd034e34233a7b666605d098680 Mon Sep 17 00:00:00 2001 From: Andre Popovitch Date: Mon, 30 Sep 2024 08:30:02 -0500 Subject: [PATCH 13/31] Fix warnings (#896) --- CPP/Clipper2Lib/include/clipper2/clipper.core.h | 3 +++ CPP/Clipper2Lib/src/clipper.engine.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index 40124804..624de903 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -88,6 +88,9 @@ namespace Clipper2Lib throw Clipper2Exception(undefined_error); case range_error_i: throw Clipper2Exception(range_error); + // Should never happen, but adding this to stop a compiler warning + default: + throw Clipper2Exception("Unknown error"); } #else ++error_code; // only to stop compiler warning diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index d3a146b4..97717322 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -928,6 +928,9 @@ namespace Clipper2Lib { case FillRule::Negative: if (e.wind_cnt != -1) return false; break; + // Should never happen, but adding this to stop a compiler warning + default: + break; } switch (cliptype_) @@ -978,6 +981,9 @@ namespace Clipper2Lib { break; case ClipType::Xor: return true; break; + // Should never happen, but adding this to stop a compiler warning + default: + break; } return false; // we should never get here } From 907d1fdf077473f770b8a3fbe23dea1538a910bf Mon Sep 17 00:00:00 2001 From: philstopford Date: Mon, 30 Sep 2024 18:06:35 -0500 Subject: [PATCH 14/31] And linq (#895) * 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. * 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 * Oops - roll back the net version for this project to work on GitHub --- 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 +++-- 8 files changed, 95 insertions(+), 189 deletions(-) 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 2b665ac7e3e4ede7c32ce89e608f5163ea4d2756 Mon Sep 17 00:00:00 2001 From: angusj Date: Thu, 10 Oct 2024 08:41:44 +1000 Subject: [PATCH 15/31] Reversed LINQ dependency in C# code --- CSharp/Clipper2Lib/Clipper.Core.cs | 21 +++- CSharp/Clipper2Lib/Clipper.Engine.cs | 40 +++++-- CSharp/Clipper2Lib/Clipper.Minkowski.cs | 11 +- CSharp/Clipper2Lib/Clipper.Offset.cs | 27 +++-- CSharp/Clipper2Lib/Clipper.RectClip.cs | 28 +++-- CSharp/Clipper2Lib/Clipper.cs | 130 ++++++++++++++-------- CSharp/Utils/ClipFileIO/Clipper.FileIO.cs | 4 +- CSharp/Utils/SVG/Clipper.SVG.cs | 29 ++--- 8 files changed, 192 insertions(+), 98 deletions(-) diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index 3b8e18b1..dd422ed7 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 September 2024 * +* Date : 10 October 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Core structures and functions for the Clipper Library * @@ -10,7 +10,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -479,7 +478,9 @@ public Path64(int capacity = 0) : base(capacity) { } public Path64(IEnumerable path) : base(path) { } public override string ToString() { - string s = this.Aggregate("", (current, p) => current + p.ToString() + ", "); + string s = ""; + foreach (Point64 p in this) + s = s + p.ToString() + ", "; if (s != "") s = s.Remove(s.Length - 2); return s; } @@ -492,7 +493,10 @@ public Paths64(int capacity = 0) : base(capacity) { } public Paths64(IEnumerable paths) : base(paths) { } public override string ToString() { - return this.Aggregate("", (current, p) => current + p + "\n"); + string s = ""; + foreach (Path64 p in this) + s = s + p + "\n"; + return s; } } @@ -503,7 +507,9 @@ public PathD(int capacity = 0) : base(capacity) { } public PathD(IEnumerable path) : base(path) { } public string ToString(int precision = 2) { - string s = this.Aggregate("", (current, p) => current + p.ToString(precision) + ", "); + string s = ""; + foreach (PointD p in this) + s = s + p.ToString(precision) + ", "; if (s != "") s = s.Remove(s.Length - 2); return s; } @@ -516,7 +522,10 @@ public PathsD(int capacity = 0) : base(capacity) { } public PathsD(IEnumerable paths) : base(paths) { } public string ToString(int precision = 2) { - return this.Aggregate("", (current, p) => current + p.ToString(precision) + "\n"); + string s = ""; + foreach (PathD p in this) + s = s + p.ToString(precision) + "\n"; + return s; } } diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index c1dc41df..7cfc0b5f 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 September 2024 * +* Date : 10 October 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -13,7 +13,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -236,7 +235,8 @@ 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 = paths.Sum(path => path.Count); + int totalVertCnt = 0; + foreach (Path64 path in paths) totalVertCnt += path.Count; vertexList.EnsureCapacity(vertexList.Count + totalVertCnt); foreach (Path64 path in paths) @@ -2528,7 +2528,9 @@ private static int HorzSegSort(HorzSegment? hs1, HorzSegment? hs2) private void ConvertHorzSegsToJoins() { - int k = _horzSegList.Count(UpdateHorzSegment); + int k = 0; + foreach (HorzSegment hs in _horzSegList) + if (UpdateHorzSegment(hs)) k++; if (k < 2) return; _horzSegList.Sort(HorzSegSort); @@ -3032,8 +3034,10 @@ private bool CheckBounds(OutRec outrec) private bool CheckSplitOwner(OutRec outrec, List? splits) { - foreach (OutRec? split in splits!.Select(i => GetRealOutRec(_outrecList[i])).OfType().Where(split => split != outrec && split.recursiveSplit != outrec)) + foreach (int i in splits!) { + 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) || @@ -3043,7 +3047,6 @@ private bool CheckSplitOwner(OutRec outrec, List? splits) outrec.owner = split; //found in split return true; } - return false; } private void RecursiveCheckOwners(OutRec outrec, PolyPathBase polypath) @@ -3343,9 +3346,11 @@ public bool Execute(ClipType clipType, FillRule fillRule, if (!success) return false; solutionClosed.EnsureCapacity(solClosed64.Count); - solutionClosed.AddRange(solClosed64.Select(path => Clipper.ScalePathD(path, _invScale))); + foreach (Path64 path in solClosed64) + solutionClosed.Add(Clipper.ScalePathD(path, _invScale)); solutionOpen.EnsureCapacity(solOpen64.Count); - solutionOpen.AddRange(solOpen64.Select(path => Clipper.ScalePathD(path, _invScale))); + foreach (Path64 path in solOpen64) + solutionOpen.Add(Clipper.ScalePathD(path, _invScale)); return true; } @@ -3380,7 +3385,8 @@ public bool Execute(ClipType clipType, FillRule fillRule, PolyTreeD polytree, Pa if (!success) return false; if (oPaths.Count <= 0) return true; openPaths.EnsureCapacity(oPaths.Count); - openPaths.AddRange(oPaths.Select(path => Clipper.ScalePathD(path, _invScale))); + foreach (Path64 path in oPaths) + openPaths.Add(Clipper.ScalePathD(path, _invScale)); return true; } @@ -3534,7 +3540,13 @@ public PolyPath64 Child(int index) [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Area() { - return (Polygon == null ? 0 : Clipper.Area(Polygon)) + _childs.Cast().Sum(child => child.Area()); + double result = Polygon == null ? 0 : Clipper.Area(Polygon); + foreach (PolyPathBase polyPathBase in _childs) + { + PolyPath64 child = (PolyPath64) polyPathBase; + result += child.Area(); + } + return result; } } public class PolyPathD : PolyPathBase @@ -3577,7 +3589,13 @@ public PolyPathD this[int index] [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Area() { - return (Polygon == null ? 0 : Clipper.Area(Polygon)) + _childs.Cast().Sum(child => child.Area()); + double result = Polygon == null ? 0 : Clipper.Area(Polygon); + foreach (PolyPathBase polyPathBase in _childs) + { + PolyPathD child = (PolyPathD) polyPathBase; + result += child.Area(); + } + return result; } } diff --git a/CSharp/Clipper2Lib/Clipper.Minkowski.cs b/CSharp/Clipper2Lib/Clipper.Minkowski.cs index 91412b14..afad586f 100644 --- a/CSharp/Clipper2Lib/Clipper.Minkowski.cs +++ b/CSharp/Clipper2Lib/Clipper.Minkowski.cs @@ -1,15 +1,14 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 10 October 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : Minkowski Sum and Difference * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #nullable enable using System; -using System.Linq; namespace Clipper2Lib { @@ -26,11 +25,13 @@ private static Paths64 MinkowskiInternal(Path64 pattern, Path64 path, bool isSum Path64 path2 = new Path64(patLen); if (isSum) { - path2.AddRange(pattern.Select(basePt => pathPt + basePt)); + foreach (Point64 basePt in pattern) + path2.Add(pathPt + basePt); } else { - path2.AddRange(pattern.Select(basePt => pathPt - basePt)); + foreach (Point64 basePt in pattern) + path2.Add(pathPt - basePt); } tmp.Add(path2); } diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 11d6dc01..8741322a 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -141,12 +140,22 @@ public void AddPaths(Paths64 paths, JoinType joinType, EndType endType) private int CalcSolutionCapacity() { - return _groupList.Sum(g => (g.endType == EndType.Joined) ? g.inPaths.Count * 2 : g.inPaths.Count); + int result = 0; + foreach (Group g in _groupList) + result += (g.endType == EndType.Joined) ? g.inPaths.Count * 2 : g.inPaths.Count; + return result; } internal bool CheckPathsReversed() { - return (from g in _groupList where g.endType == EndType.Polygon select g.pathsReversed).FirstOrDefault(); + bool result = false; + foreach (Group g in _groupList) + if (g.endType == EndType.Polygon) + { + result = g.pathsReversed; + break; + } + return result; } private void ExecuteInternal(double delta) @@ -157,8 +166,9 @@ private void ExecuteInternal(double delta) // make sure the offset delta is significant if (Math.Abs(delta) < 0.5) { - foreach (Path64 path in _groupList.SelectMany(group => group.inPaths)) - _solution.Add(path); + foreach (Group group in _groupList) + foreach (Path64 path in group.inPaths) + _solution.Add(path); return; } @@ -230,9 +240,10 @@ 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].Where(pt => (pt.Y >= botPt.Y) && - ((pt.Y != botPt.Y) || (pt.X < botPt.X)))) - { + foreach (Point64 pt in paths[i]) + { + if ((pt.Y < botPt.Y) || + ((pt.Y == botPt.Y) && (pt.X >= botPt.X))) continue; 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 ff10c0b1..02e637f1 100644 --- a/CSharp/Clipper2Lib/Clipper.RectClip.cs +++ b/CSharp/Clipper2Lib/Clipper.RectClip.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 5 July 2024 * +* Date : 10 October 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * @@ -10,7 +10,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -92,8 +91,10 @@ 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 (PointInPolygonResult pip in path2.Select(pt => InternalClipper.PointInPolygon(pt, path1))) + foreach (Point64 pt in path2) { + PointInPolygonResult pip = + InternalClipper.PointInPolygon(pt, path1); switch(pip) { case PointInPolygonResult.IsInside: @@ -636,8 +637,9 @@ private void ExecuteInternal(Path64 path) if (startLocs.Count > 0) { prev = loc; - foreach (Location loc2 in startLocs.Where(loc2 => prev != loc2)) + foreach (Location loc2 in startLocs) { + if (prev == loc2) continue; AddCorner(ref prev, HeadingClockwise(prev, loc2)); prev = loc2; } @@ -652,8 +654,9 @@ public Paths64 Execute(Paths64 paths) { Paths64 result = new Paths64(); if (rect_.IsEmpty()) return result; - foreach (Path64 path in paths.Where(path => path.Count >= 3)) + foreach (Path64 path in paths) { + if (path.Count < 3) continue; pathBounds_ = Clipper.GetBounds(path); if (!rect_.Intersects(pathBounds_)) continue; // the path must be completely outside fRect @@ -668,7 +671,11 @@ public Paths64 Execute(Paths64 paths) for (int i = 0; i < 4; ++i) TidyEdgePair(i, edges_[i * 2], edges_[i * 2 + 1]); - result.AddRange(results_.Select(GetPath).Where(tmp => tmp.Count > 0)); + foreach (OutPt2? op in results_) + { + Path64 tmp = GetPath(op); + if (tmp.Count > 0) result.Add(tmp); + } //clean up after every loop results_.Clear(); @@ -955,8 +962,9 @@ internal RectClipLines64(Rect64 rect) : base(rect) { } { Paths64 result = new Paths64(); if (rect_.IsEmpty()) return result; - foreach (Path64 path in paths.Where(path => path.Count >= 2)) + foreach (Path64 path in paths) { + if (path.Count < 2) continue; pathBounds_ = Clipper.GetBounds(path); if (!rect_.Intersects(pathBounds_)) continue; // the path must be completely outside fRect @@ -965,7 +973,11 @@ internal RectClipLines64(Rect64 rect) : base(rect) { } // fRect, simply by comparing path bounds with fRect. ExecuteInternal(path); - result.AddRange(results_.Select(GetPath).Where(tmp => tmp.Count > 0)); + foreach (OutPt2? op in results_) + { + Path64 tmp = GetPath(op); + if (tmp.Count > 0) result.Add(tmp); + } //clean up after every loop results_.Clear(); diff --git a/CSharp/Clipper2Lib/Clipper.cs b/CSharp/Clipper2Lib/Clipper.cs index b72f4bb1..90efb98f 100644 --- a/CSharp/Clipper2Lib/Clipper.cs +++ b/CSharp/Clipper2Lib/Clipper.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 10 May 2024 * +* Date : 10 October 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This module contains simple functions that will likely cover * @@ -14,7 +14,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; namespace Clipper2Lib @@ -261,7 +260,10 @@ public static double Area(Path64 path) public static double Area(Paths64 paths) { - return paths.Sum(Area); + double a = 0.0; + foreach (Path64 path in paths) + a += Area(path); + return a; } public static double Area(PathD path) @@ -280,7 +282,10 @@ public static double Area(PathD path) public static double Area(PathsD paths) { - return paths.Sum(Area); + double a = 0.0; + foreach (PathD path in paths) + a += Area(path); + return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -297,26 +302,37 @@ public static bool IsPositive(PathD poly) public static string Path64ToString(Path64 path) { - string result = path.Aggregate("", (current, pt) => current + pt.ToString()); + string result = ""; + foreach (Point64 pt in path) + result += pt.ToString(); return result + '\n'; } public static string Paths64ToString(Paths64 paths) { - return paths.Aggregate("", (current, path) => current + Path64ToString(path)); + string result = ""; + foreach (Path64 path in paths) + result += Path64ToString(path); + return result; } public static string PathDToString(PathD path) { - string result = path.Aggregate("", (current, pt) => current + pt.ToString()); + string result = ""; + foreach (PointD pt in path) + result += pt.ToString(); return result + '\n'; } public static string PathsDToString(PathsD paths) { - return paths.Aggregate("", (current, path) => current + PathDToString(path)); + string result = ""; + foreach (PathD path in paths) + result += PathDToString(path); + return result; } public static Path64 OffsetPath(Path64 path, long dx, long dy) { Path64 result = new Path64(path.Count); - result.AddRange(path.Select(pt => new Point64(pt.X + dx, pt.Y + dy))); + foreach (Point64 pt in path) + result.Add(new Point64(pt.X + dx, pt.Y + dy)); return result; } @@ -366,11 +382,12 @@ 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; } @@ -379,7 +396,8 @@ public static Paths64 ScalePaths(Paths64 paths, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return paths; Paths64 result = new Paths64(paths.Count); - result.AddRange(paths.Select(path => ScalePath(path, scale))); + foreach (Path64 path in paths) + result.Add(ScalePath(path, scale)); return result; } @@ -387,7 +405,8 @@ public static PathD ScalePath(PathD path, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return path; PathD result = new PathD(path.Count); - result.AddRange(path.Select(pt => new PointD(pt, scale))); + foreach (PointD pt in path) + result.Add(new PointD(pt, scale)); return result; } @@ -395,7 +414,8 @@ public static PathsD ScalePaths(PathsD paths, double scale) { if (InternalClipper.IsAlmostZero(scale - 1)) return paths; PathsD result = new PathsD(paths.Count); - result.AddRange(paths.Select(path => ScalePath(path, scale))); + foreach (PathD path in paths) + result.Add(ScalePath(path, scale)); return result; } @@ -404,7 +424,8 @@ public static Path64 ScalePath64(PathD path, double scale) { int cnt = path.Count; Path64 res = new Path64(cnt); - res.AddRange(path.Select(pt => new Point64(pt, scale))); + foreach (PointD pt in path) + res.Add(new Point64(pt, scale)); return res; } @@ -412,7 +433,8 @@ public static Paths64 ScalePaths64(PathsD paths, double scale) { int cnt = paths.Count; Paths64 res = new Paths64(cnt); - res.AddRange(paths.Select(path => ScalePath64(path, scale))); + foreach (PathD path in paths) + res.Add(ScalePath64(path, scale)); return res; } @@ -420,7 +442,8 @@ public static PathD ScalePathD(Path64 path, double scale) { int cnt = path.Count; PathD res = new PathD(cnt); - res.AddRange(path.Select(pt => new PointD(pt, scale))); + foreach (Point64 pt in path) + res.Add(new PointD(pt, scale)); return res; } @@ -428,7 +451,8 @@ public static PathsD ScalePathsD(Paths64 paths, double scale) { int cnt = paths.Count; PathsD res = new PathsD(cnt); - res.AddRange(paths.Select(path => ScalePathD(path, scale))); + foreach (Path64 path in paths) + res.Add(ScalePathD(path, scale)); return res; } @@ -436,56 +460,64 @@ public static PathsD ScalePathsD(Paths64 paths, double scale) public static Path64 Path64(PathD path) { Path64 result = new Path64(path.Count); - result.AddRange(path.Select(pt => new Point64(pt))); + foreach (PointD pt in path) + result.Add(new Point64(pt)); return result; } public static Paths64 Paths64(PathsD paths) { Paths64 result = new Paths64(paths.Count); - result.AddRange(paths.Select(Path64)); + foreach (PathD path in paths) + result.Add(Path64(path)); return result; } public static PathsD PathsD(Paths64 paths) { PathsD result = new PathsD(paths.Count); - result.AddRange(paths.Select(PathD)); + foreach (Path64 path in paths) + result.Add(PathD(path)); return result; } public static PathD PathD(Path64 path) { PathD result = new PathD(path.Count); - result.AddRange(path.Select(pt => new PointD(pt))); + foreach (Point64 pt in path) + result.Add(new PointD(pt)); return result; } public static Path64 TranslatePath(Path64 path, long dx, long dy) { Path64 result = new Path64(path.Count); - result.AddRange(path.Select(pt => new Point64(pt.X + dx, pt.Y + dy))); + foreach (Point64 pt in path) + result.Add(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); - result.AddRange(paths.Select(path => OffsetPath(path, dx, dy))); + foreach (Path64 path in paths) + result.Add(OffsetPath(path, dx, dy)); return result; } public static PathD TranslatePath(PathD path, double dx, double dy) { PathD result = new PathD(path.Count); - result.AddRange(path.Select(pt => new PointD(pt.x + dx, pt.y + dy))); + foreach (PointD pt in path) + result.Add(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); - result.AddRange(paths.Select(path => TranslatePath(path, dx, dy))); + foreach (PathD path in paths) + result.Add(TranslatePath(path, dx, dy)); return result; } @@ -506,7 +538,8 @@ public static PathD ReversePath(PathD path) public static Paths64 ReversePaths(Paths64 paths) { Paths64 result = new Paths64(paths.Count); - result.AddRange(paths.Select(ReversePath)); + foreach (Path64 t in paths) + result.Add(ReversePath(t)); return result; } @@ -514,7 +547,8 @@ public static Paths64 ReversePaths(Paths64 paths) public static PathsD ReversePaths(PathsD paths) { PathsD result = new PathsD(paths.Count); - result.AddRange(paths.Select(ReversePath)); + foreach (PathD path in paths) + result.Add(ReversePath(path)); return result; } @@ -534,13 +568,14 @@ public static Rect64 GetBounds(Path64 path) public static Rect64 GetBounds(Paths64 paths) { Rect64 result = InvalidRect64; - 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; - } + 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; + } return result.left == long.MaxValue ? new Rect64() : result; } @@ -560,13 +595,14 @@ public static RectD GetBounds(PathD path) public static RectD GetBounds(PathsD paths) { RectD result = InvalidRectD; - 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; - } + 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; + } return Math.Abs(result.left - double.MaxValue) < InternalClipper.floatingPointTolerance ? new RectD() : result; } @@ -819,7 +855,8 @@ public static Path64 RamerDouglasPeucker(Path64 path, double epsilon) public static Paths64 RamerDouglasPeucker(Paths64 paths, double epsilon) { Paths64 result = new Paths64(paths.Count); - result.AddRange(paths.Select(path => RamerDouglasPeucker(path, epsilon))); + foreach (Path64 path in paths) + result.Add(RamerDouglasPeucker(path, epsilon)); return result; } @@ -867,7 +904,8 @@ public static PathD RamerDouglasPeucker(PathD path, double epsilon) public static PathsD RamerDouglasPeucker(PathsD paths, double epsilon) { PathsD result = new PathsD(paths.Count); - result.AddRange(paths.Select(path => RamerDouglasPeucker(path, epsilon))); + foreach (PathD path in paths) + result.Add(RamerDouglasPeucker(path, epsilon)); return result; } @@ -964,7 +1002,8 @@ public static Paths64 SimplifyPaths(Paths64 paths, double epsilon, bool isClosedPaths = true) { Paths64 result = new Paths64(paths.Count); - result.AddRange(paths.Select(path => SimplifyPath(path, epsilon, isClosedPaths))); + foreach (Path64 path in paths) + result.Add(SimplifyPath(path, epsilon, isClosedPaths)); return result; } @@ -1036,7 +1075,8 @@ public static PathsD SimplifyPaths(PathsD paths, double epsilon, bool isClosedPath = true) { PathsD result = new PathsD(paths.Count); - result.AddRange(paths.Select(path => SimplifyPath(path, epsilon, isClosedPath))); + foreach (PathD path in paths) + result.Add(SimplifyPath(path, epsilon, isClosedPath)); return result; } diff --git a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs index 5fef3ad9..f5cdc471 100644 --- a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs +++ b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs @@ -9,7 +9,6 @@ using System; using System.IO; using System.Diagnostics; -using System.Linq; namespace Clipper2Lib { @@ -265,7 +264,8 @@ public static Paths64 AffineTranslatePaths(Paths64 paths, long dx, long dy) foreach (Path64 path in paths) { Path64 p = new Path64(path.Count); - p.AddRange(path.Select(pt => new Point64(pt.X + dx, pt.Y + dy))); + foreach (Point64 pt in path) + p.Add(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 188dc2ed..4d101ba2 100644 --- a/CSharp/Utils/SVG/Clipper.SVG.cs +++ b/CSharp/Utils/SVG/Clipper.SVG.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; namespace Clipper2Lib { @@ -199,13 +198,15 @@ public void AddText(string cap, double posX, double posY, int fontSize, uint fon private RectD GetBounds() { RectD bounds = new RectD(RectMax); - 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; - } + 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; + } return !IsValidRect(bounds) ? RectEmpty : bounds; } @@ -253,16 +254,18 @@ 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)) + foreach (PathD path in pi.paths) { + 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 ed98928c66707988d4eb2c49a31c41380a08931c Mon Sep 17 00:00:00 2001 From: zed Date: Mon, 14 Oct 2024 22:05:53 +0300 Subject: [PATCH 16/31] fix after type renaming (#901) --- Delphi/Clipper2Lib/Clipper.Engine.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Delphi/Clipper2Lib/Clipper.Engine.pas b/Delphi/Clipper2Lib/Clipper.Engine.pas index 950e847c..56863818 100644 --- a/Delphi/Clipper2Lib/Clipper.Engine.pas +++ b/Delphi/Clipper2Lib/Clipper.Engine.pas @@ -2842,7 +2842,7 @@ procedure TClipperBase.ExecuteInternal(clipType: TClipType; Y: Int64; e: PActive; begin - if clipType = ctNone then Exit; + if clipType = ctNoClip then Exit; FFillRule := fillRule; FClipType := clipType; Reset; From fe5f6b7faa166cca2d20a8d7698361ec2bf6bbee Mon Sep 17 00:00:00 2001 From: angusj Date: Fri, 22 Nov 2024 10:17:08 +1000 Subject: [PATCH 17/31] Clipper.Offset: fixed a bug seen with excessive deflation (#916) --- CPP/Clipper2Lib/src/clipper.offset.cpp | 17 +++---- CSharp/Clipper2Lib/Clipper.Offset.cs | 7 +-- Delphi/Clipper2Lib/Clipper.Core.pas | 38 +++++++++++----- Delphi/Clipper2Lib/Clipper.Engine.pas | 63 ++++++++++++++------------ Delphi/Clipper2Lib/Clipper.Offset.pas | 21 ++++----- 5 files changed, 78 insertions(+), 68 deletions(-) diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 834bfd6c..6fdcc667 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 July 2024 * +* Date : 22 November 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -326,16 +326,11 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size // to ensure that path reversals (ie over-shrunk paths) are removed. #ifdef USINGZ path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); -#else - path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); -#endif - - // when the angle is almost flat (cos_a ~= 1), it's safe to skip this middle point - if (cos_a < 0.999) path_out.push_back(path[j]); // (#405, #873) - -#ifdef USINGZ + path_out.push_back(path[j]); // (#405, #873, #916) path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); #else + path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); + path_out.push_back(path[j]); // (#405, #873, #916) path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); #endif } @@ -594,7 +589,8 @@ void ClipperOffset::ExecuteInternal(double delta) if (!solution->size()) return; - bool paths_reversed = CheckReverseOrientation(); + bool paths_reversed = CheckReverseOrientation(); + //clean up self-intersections ... Clipper64 c; c.PreserveCollinear(false); @@ -621,6 +617,7 @@ void ClipperOffset::ExecuteInternal(double delta) else c.Execute(ClipType::Union, FillRule::Positive, *solution); } + } void ClipperOffset::Execute(double delta, Paths64& paths) diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 8741322a..5fc73b3b 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 July 2024 * +* Date : 22 November 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -551,10 +551,7 @@ private void OffsetPoint(Group group, Path64 path, int j, ref int k) // will be removed later by the finishing union operation. This is also the best way // to ensure that path reversals (ie over-shrunk paths) are removed. pathOut.Add(GetPerpendic(path[j], _normals[k])); - - // when the angle is almost flat (cos_a ~= 1), it's safe to skip this middle point - if (cosA < 0.999) pathOut.Add(path[j]); // (#405, #873) - + pathOut.Add(path[j]); // (#405, #873, #916) pathOut.Add(GetPerpendic(path[j], _normals[j])); } else if ((cosA > 0.999) && (_joinType != JoinType.Round)) diff --git a/Delphi/Clipper2Lib/Clipper.Core.pas b/Delphi/Clipper2Lib/Clipper.Core.pas index d265e05d..c726bec3 100644 --- a/Delphi/Clipper2Lib/Clipper.Core.pas +++ b/Delphi/Clipper2Lib/Clipper.Core.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 17 September 2024 * +* Date : 22 November 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Core Clipper Library module * @@ -18,12 +18,15 @@ interface SysUtils, Classes, Math; type +{$IFDEF USINGZ} + ZType = Int64; // or alternatively, ZType = double +{$ENDIF} PPoint64 = ^TPoint64; TPoint64 = record X, Y: Int64; {$IFDEF USINGZ} - Z: Int64; + Z: ZType; {$ENDIF} end; @@ -31,7 +34,7 @@ TPoint64 = record TPointD = record X, Y: double; {$IFDEF USINGZ} - Z: Int64; + Z: ZType; {$ENDIF} end; @@ -121,6 +124,7 @@ TListEx = class fCount : integer; fCapacity : integer; fList : TPointerList; + fSorted : Boolean; protected function UnsafeGet(idx: integer): Pointer; // no range checking procedure UnsafeSet(idx: integer; val: Pointer); @@ -130,10 +134,12 @@ TListEx = class destructor Destroy; override; procedure Clear; virtual; function Add(item: Pointer): integer; + procedure DeleteLast; procedure Swap(idx1, idx2: integer); - procedure Sort(Compare: TListSortCompare); + procedure Sort(Compare: TListSortCompareFunc); procedure Resize(count: integer); property Count: integer read fCount; + property Sorted: Boolean read fSorted; property Item[idx: integer]: Pointer read UnsafeGet; default; end; @@ -186,11 +192,11 @@ function PointsNearEqual(const pt1, pt2: TPointD; distanceSqrd: double): Boolean {$IFDEF INLINING} inline; {$ENDIF} {$IFDEF USINGZ} -function Point64(const X, Y: Int64; Z: Int64 = 0): TPoint64; overload; +function Point64(const X, Y: Int64; Z: ZType = 0): TPoint64; overload; {$IFDEF INLINING} inline; {$ENDIF} -function Point64(const X, Y: Double; Z: Int64 = 0): TPoint64; overload; +function Point64(const X, Y: Double; Z: ZType = 0): TPoint64; overload; {$IFDEF INLINING} inline; {$ENDIF} -function PointD(const X, Y: Double; Z: Int64 = 0): TPointD; overload; +function PointD(const X, Y: Double; Z: ZType = 0): TPointD; overload; {$IFDEF INLINING} inline; {$ENDIF} {$ELSE} function Point64(const X, Y: Int64): TPoint64; overload; {$IFDEF INLINING} inline; {$ENDIF} @@ -540,6 +546,7 @@ procedure TListEx.Clear; fList := nil; fCount := 0; fCapacity := 0; + fSorted := false; end; //------------------------------------------------------------------------------ @@ -555,6 +562,13 @@ function TListEx.Add(item: Pointer): integer; fList[fCount] := item; Result := fCount; inc(fCount); + fSorted := false; +end; +//------------------------------------------------------------------------------ + +procedure TListEx.DeleteLast; +begin + dec(fCount); end; //------------------------------------------------------------------------------ @@ -611,10 +625,11 @@ procedure QuickSort(SortList: TPointerList; L, R: Integer; end; //------------------------------------------------------------------------------ -procedure TListEx.Sort(Compare: TListSortCompare); +procedure TListEx.Sort(Compare: TListSortCompareFunc); begin if fCount < 2 then Exit; QuickSort(FList, 0, fCount - 1, Compare); + fSorted := true; end; //------------------------------------------------------------------------------ @@ -654,6 +669,7 @@ procedure TListEx.Swap(idx1, idx2: integer); p := fList[idx1]; fList[idx1] := fList[idx2]; fList[idx2] := p; + fSorted := false; end; //------------------------------------------------------------------------------ @@ -1383,7 +1399,7 @@ function ArrayOfPathsToPaths(const ap: TArrayOfPaths): TPaths64; //------------------------------------------------------------------------------ {$IFDEF USINGZ} -function Point64(const X, Y: Int64; Z: Int64): TPoint64; +function Point64(const X, Y: Int64; Z: ZType): TPoint64; begin Result.X := X; Result.Y := Y; @@ -1391,7 +1407,7 @@ function Point64(const X, Y: Int64; Z: Int64): TPoint64; end; //------------------------------------------------------------------------------ -function Point64(const X, Y: Double; Z: Int64): TPoint64; +function Point64(const X, Y: Double; Z: ZType): TPoint64; begin Result.X := Round(X); Result.Y := Round(Y); @@ -1399,7 +1415,7 @@ function Point64(const X, Y: Double; Z: Int64): TPoint64; end; //------------------------------------------------------------------------------ -function PointD(const X, Y: Double; Z: Int64): TPointD; +function PointD(const X, Y: Double; Z: ZType): TPointD; begin Result.X := X; Result.Y := Y; diff --git a/Delphi/Clipper2Lib/Clipper.Engine.pas b/Delphi/Clipper2Lib/Clipper.Engine.pas index 950e847c..29034030 100644 --- a/Delphi/Clipper2Lib/Clipper.Engine.pas +++ b/Delphi/Clipper2Lib/Clipper.Engine.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 27 April 2024 * +* Date : 22 November 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -219,7 +219,7 @@ TClipperBase = class FSucceeded : Boolean; FReverseSolution : Boolean; {$IFDEF USINGZ} - fDefaultZ : Int64; + fDefaultZ : Ztype; fZCallback : TZCallback64; {$ENDIF} procedure Reset; @@ -287,7 +287,7 @@ TClipperBase = class {$IFDEF USINGZ} procedure SetZ( e1, e2: PActive; var intersectPt: TPoint64); property ZCallback : TZCallback64 read fZCallback write fZCallback; - property DefaultZ : Int64 read fDefaultZ write fDefaultZ; + property DefaultZ : Ztype read fDefaultZ write fDefaultZ; {$ENDIF} property Succeeded : Boolean read FSucceeded; public @@ -372,8 +372,7 @@ TClipperD = class(TClipperBase) // for floating point coordinates FInvScale: double; {$IFDEF USINGZ} fZCallback : TZCallbackD; - procedure ZCB(const bot1, top1, bot2, top2: TPoint64; - var intersectPt: TPoint64); + procedure ZCB(const bot1, top1, bot2, top2: TPoint64; var intersectPt: TPoint64); procedure CheckCallback; {$ENDIF} public @@ -1017,6 +1016,11 @@ procedure AddPathsToVertexList(const paths: TPaths64; GetMem(v, sizeof(TVertex) * totalVerts); vertexList.Add(v); + {$IF not defined(FPC) and (CompilerVersion <= 26.0)} + // Delphi 7-XE5 have a problem with "continue" and the + // code analysis, marking "ascending" as "not initialized" + ascending := False; + {$IFEND} for i := 0 to High(paths) do begin len := Length(paths[i]); @@ -2559,9 +2563,8 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); var e1WindCnt, e2WindCnt, e1WindCnt2, e2WindCnt2: Integer; e3: PActive; - resultOp, op2: POutPt; + op, op2: POutPt; begin - resultOp := nil; // MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... if FHasOpenPaths and (IsOpen(e1) or IsOpen(e2)) then begin @@ -2586,7 +2589,7 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); // toggle contribution ... if IsHotEdge(e1) then begin - resultOp := AddOutPt(e1, pt); + op := AddOutPt(e1, pt); if IsFront(e1) then e1.outrec.frontE := nil else e1.outrec.backE := nil; @@ -2610,12 +2613,12 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); SetSides(e3.outrec, e3, e1); Exit; end else - resultOp := StartOpenPath(e1, pt); + op := StartOpenPath(e1, pt); end else - resultOp := StartOpenPath(e1, pt); + op := StartOpenPath(e1, pt); {$IFDEF USINGZ} - SetZ(e1, e2, resultOp.pt); + SetZ(e1, e2, op.pt); {$ENDIF} Exit; end; @@ -2679,9 +2682,9 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); if not (e1WindCnt in [0,1]) or not (e2WindCnt in [0,1]) or (not IsSamePolyType(e1, e2) and (fClipType <> ctXor)) then begin - resultOp := AddLocalMaxPoly(e1, e2, pt); + op := AddLocalMaxPoly(e1, e2, pt); {$IFDEF USINGZ} - if Assigned(Result) then SetZ(e1, e2, Result.pt); + if Assigned(op) then SetZ(e1, e2, op.pt); {$ENDIF} end else if IsFront(e1) or (e1.outrec = e2.outrec) then @@ -2689,10 +2692,10 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); // this 'else if' condition isn't strictly needed but // it's sensible to split polygons that ony touch at // a common vertex (not at common edges). - resultOp := AddLocalMaxPoly(e1, e2, pt); + op := AddLocalMaxPoly(e1, e2, pt); {$IFDEF USINGZ} op2 := AddLocalMinPoly(e1, e2, pt); - if Assigned(Result) then SetZ(e1, e2, Result.pt); + if Assigned(op) then SetZ(e1, e2, op.pt); SetZ(e1, e2, op2.pt); {$ELSE} AddLocalMinPoly(e1, e2, pt); @@ -2700,10 +2703,10 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); end else begin // can't treat as maxima & minima - resultOp := AddOutPt(e1, pt); + op := AddOutPt(e1, pt); {$IFDEF USINGZ} op2 := AddOutPt(e2, pt); - SetZ(e1, e2, Result.pt); + SetZ(e1, e2, op.pt); SetZ(e1, e2, op2.pt); {$ELSE} AddOutPt(e2, pt); @@ -2715,17 +2718,17 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); // if one or other edge is 'hot' ... else if IsHotEdge(e1) then begin - resultOp := AddOutPt(e1, pt); + op := AddOutPt(e1, pt); {$IFDEF USINGZ} - SetZ(e1, e2, Result.pt); + SetZ(e1, e2, op.pt); {$ENDIF} SwapOutRecs(e1, e2); end else if IsHotEdge(e2) then begin - resultOp := AddOutPt(e2, pt); + op := AddOutPt(e2, pt); {$IFDEF USINGZ} - SetZ(e1, e2, Result.pt); + SetZ(e1, e2, op.pt); {$ENDIF} SwapOutRecs(e1, e2); end @@ -2753,32 +2756,32 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); if not IsSamePolyType(e1, e2) then begin - resultOp := AddLocalMinPoly(e1, e2, pt, false); + op := AddLocalMinPoly(e1, e2, pt, false); {$IFDEF USINGZ} - SetZ(e1, e2, Result.pt); + SetZ(e1, e2, op.pt); {$ENDIF} end else if (e1WindCnt = 1) and (e2WindCnt = 1) then begin - resultOp := nil; + op := nil; case FClipType of ctIntersection: if (e1WindCnt2 <= 0) or (e2WindCnt2 <= 0) then Exit - else resultOp := AddLocalMinPoly(e1, e2, pt, false); + else op := AddLocalMinPoly(e1, e2, pt, false); ctUnion: if (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0) then - resultOp := AddLocalMinPoly(e1, e2, pt, false); + op := AddLocalMinPoly(e1, e2, pt, false); ctDifference: if ((GetPolyType(e1) = ptClip) and (e1WindCnt2 > 0) and (e2WindCnt2 > 0)) or ((GetPolyType(e1) = ptSubject) and (e1WindCnt2 <= 0) and (e2WindCnt2 <= 0)) then - resultOp := AddLocalMinPoly(e1, e2, pt, false); + op := AddLocalMinPoly(e1, e2, pt, false); else // xOr - resultOp := AddLocalMinPoly(e1, e2, pt, false); + op := AddLocalMinPoly(e1, e2, pt, false); end; {$IFDEF USINGZ} - if assigned(Result) then SetZ(e1, e2, Result.pt); + if assigned(op) then SetZ(e1, e2, op.pt); {$ENDIF} end; end; @@ -2842,7 +2845,7 @@ procedure TClipperBase.ExecuteInternal(clipType: TClipType; Y: Int64; e: PActive; begin - if clipType = ctNone then Exit; + if clipType = ctNoClip then Exit; FFillRule := fillRule; FClipType := clipType; Reset; diff --git a/Delphi/Clipper2Lib/Clipper.Offset.pas b/Delphi/Clipper2Lib/Clipper.Offset.pas index e0cab16b..77590979 100644 --- a/Delphi/Clipper2Lib/Clipper.Offset.pas +++ b/Delphi/Clipper2Lib/Clipper.Offset.pas @@ -2,7 +2,7 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 24 July 2024 * +* Date : 22 November 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * @@ -72,13 +72,14 @@ TClipperOffset = class fZCallback64 : TZCallback64; procedure ZCB(const bot1, top1, bot2, top2: TPoint64; var intersectPt: TPoint64); - procedure AddPoint(x,y: double; z: Int64); overload; + procedure AddPoint(x,y: double; z: ZType); overload; procedure AddPoint(const pt: TPoint64); overload; {$IFDEF INLINING} inline; {$ENDIF} - procedure AddPoint(const pt: TPoint64; newZ: Int64); overload; + procedure AddPoint(const pt: TPoint64; newZ: ZType); overload; {$IFDEF INLINING} inline; {$ENDIF} {$ELSE} procedure AddPoint(x,y: double); overload; + procedure AddPoint(const pt: TPoint64); overload; {$IFDEF INLINING} inline; {$ENDIF} {$ENDIF} @@ -695,7 +696,7 @@ procedure TClipperOffset.ZCB(const bot1, top1, bot2, top2: TPoint64; //------------------------------------------------------------------------------ {$IFDEF USINGZ} -procedure TClipperOffset.AddPoint(x,y: double; z: Int64); +procedure TClipperOffset.AddPoint(x,y: double; z: ZType); {$ELSE} procedure TClipperOffset.AddPoint(x,y: double); {$ENDIF} @@ -719,7 +720,7 @@ procedure TClipperOffset.AddPoint(x,y: double); //------------------------------------------------------------------------------ {$IFDEF USINGZ} -procedure TClipperOffset.AddPoint(const pt: TPoint64; newZ: Int64); +procedure TClipperOffset.AddPoint(const pt: TPoint64; newZ: ZType); begin AddPoint(pt.X, pt.Y, newZ); end; @@ -996,15 +997,11 @@ procedure TClipperOffset.DoRound(j, k: Integer; angle: double); // (ie over-shrunk paths) are removed. {$IFDEF USINGZ} AddPoint(GetPerpendic(fInPath[j], fNorms[k], fGroupDelta), fInPath[j].Z); -{$ELSE} - AddPoint(GetPerpendic(fInPath[j], fNorms[k], fGroupDelta)); -{$ENDIF} - // when the angle is almost flat (cos_a ~= 1), - // it's safe to skip inserting this middle point - if (cosA < 0.999) then AddPoint(fInPath[j]); // (#405, #873) -{$IFDEF USINGZ} + AddPoint(fInPath[j]); // (#405, #873) AddPoint(GetPerpendic(fInPath[j], fNorms[j], fGroupDelta), fInPath[j].Z); {$ELSE} + AddPoint(GetPerpendic(fInPath[j], fNorms[k], fGroupDelta)); + AddPoint(fInPath[j]); // (#405, #873) AddPoint(GetPerpendic(fInPath[j], fNorms[j], fGroupDelta)); {$ENDIF} end From c1b13f6aba7f45dbe8977ede0d1882a591d6edf7 Mon Sep 17 00:00:00 2001 From: angusj Date: Sat, 21 Dec 2024 17:05:05 +1000 Subject: [PATCH 18/31] CI update - Ubuntu non-default clang changed to 17 (was 13). --- .github/workflows/actions_cpp.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/actions_cpp.yml b/.github/workflows/actions_cpp.yml index c8f0e486..cef13a0f 100644 --- a/.github/workflows/actions_cpp.yml +++ b/.github/workflows/actions_cpp.yml @@ -80,22 +80,22 @@ jobs: run: | cd CPP/build env CTEST_OUTPUT_ON_FAILURE=1 make test - ubuntu-latest-clang-13: + ubuntu-latest-clang-17: runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '16' - - name: Install clang 13 + - name: Install clang 17 run: | wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh - sudo ./llvm.sh 13 + sudo ./llvm.sh 17 - name: Build run: | - export CC=/usr/bin/clang-13 - export CXX=/usr/bin/clang++-13 + export CC=/usr/bin/clang-17 + export CXX=/usr/bin/clang++-17 mkdir CPP/build cd CPP/build cmake .. -DCLIPPER2_TESTS=ON From 7598be97d333837c460c778dd16ba8d945fefdba Mon Sep 17 00:00:00 2001 From: Pavel Kretov Date: Sat, 21 Dec 2024 09:09:30 +0200 Subject: [PATCH 19/31] Clipper.Core.cs: make USINGZ as local as possible (minor refactoring) (#924) Keeping USINGZ-enable and USINGS-disabled code close together is going to help in preventing accidental code divergences. --- CSharp/Clipper2Lib/Clipper.Core.cs | 175 +++++++++++------------------ 1 file changed, 63 insertions(+), 112 deletions(-) diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index dd422ed7..a864ce23 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -21,47 +21,66 @@ public struct Point64 #if USINGZ public long Z; +#endif public Point64(Point64 pt) { X = pt.X; Y = pt.Y; +#if USINGZ Z = pt.Z; +#endif } public Point64(Point64 pt, double scale) { X = (long) Math.Round(pt.X * scale, MidpointRounding.AwayFromZero); Y = (long) Math.Round(pt.Y * scale, MidpointRounding.AwayFromZero); +#if USINGZ Z = (long) Math.Round(pt.Z * scale, MidpointRounding.AwayFromZero); +#endif } - public Point64(long x, long y, long z = 0) - { + public Point64(long x, long y +#if USINGZ + , long z = 0 +#endif + ) { X = x; Y = y; +#if USINGZ Z = z; +#endif } - public Point64(double x, double y, double z = 0.0) - { + public Point64(double x, double y +#if USINGZ + , double z = 0.0 +#endif + ) { X = (long) Math.Round(x, MidpointRounding.AwayFromZero); Y = (long) Math.Round(y, MidpointRounding.AwayFromZero); +#if USINGZ Z = (long) Math.Round(z, MidpointRounding.AwayFromZero); +#endif } public Point64(PointD pt) { X = (long) Math.Round(pt.x, MidpointRounding.AwayFromZero); Y = (long) Math.Round(pt.y, MidpointRounding.AwayFromZero); +#if USINGZ Z = pt.z; +#endif } public Point64(PointD pt, double scale) { X = (long) Math.Round(pt.x * scale, MidpointRounding.AwayFromZero); Y = (long) Math.Round(pt.y * scale, MidpointRounding.AwayFromZero); +#if USINGZ Z = pt.z; +#endif } public static bool operator ==(Point64 lhs, Point64 rhs) @@ -76,81 +95,33 @@ public Point64(PointD pt, double scale) public static Point64 operator +(Point64 lhs, Point64 rhs) { - return new Point64(lhs.X + rhs.X, lhs.Y + rhs.Y, lhs.Z + rhs.Z); + return new Point64(lhs.X + rhs.X, lhs.Y + rhs.Y +#if USINGZ + , lhs.Z + rhs.Z +#endif + ); } public static Point64 operator -(Point64 lhs, Point64 rhs) { - return new Point64(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z); + return new Point64(lhs.X - rhs.X, lhs.Y - rhs.Y +#if USINGZ + , lhs.Z - rhs.Z +#endif + ); } public readonly override string ToString() { - return $"{X},{Y},{Z} "; // nb: trailing space - } - + // nb: trailing space +#if USINGZ + return $"{X},{Y},{Z} "; #else - public Point64(Point64 pt) - { - X = pt.X; - Y = pt.Y; - } - - public Point64(long x, long y) - { - X = x; - Y = y; - } - - public Point64(double x, double y) - { - X = (long) Math.Round(x, MidpointRounding.AwayFromZero); - Y = (long) Math.Round(y, MidpointRounding.AwayFromZero); - } - - public Point64(PointD pt) - { - X = (long) Math.Round(pt.x, MidpointRounding.AwayFromZero); - Y = (long) Math.Round(pt.y, MidpointRounding.AwayFromZero); - } - - public Point64(Point64 pt, double scale) - { - X = (long) Math.Round(pt.X * scale, MidpointRounding.AwayFromZero); - Y = (long) Math.Round(pt.Y * scale, MidpointRounding.AwayFromZero); - } - - public Point64(PointD pt, double scale) - { - X = (long) Math.Round(pt.x * scale, MidpointRounding.AwayFromZero); - Y = (long) Math.Round(pt.y * scale, MidpointRounding.AwayFromZero); - } - - public static bool operator ==(Point64 lhs, Point64 rhs) - { - return lhs.X == rhs.X && lhs.Y == rhs.Y; - } - - public static bool operator !=(Point64 lhs, Point64 rhs) - { - return lhs.X != rhs.X || lhs.Y != rhs.Y; - } - - public static Point64 operator +(Point64 lhs, Point64 rhs) - { - return new Point64(lhs.X + rhs.X, lhs.Y + rhs.Y); - } + return $"{X},{Y} "; +#endif - public static Point64 operator -(Point64 lhs, Point64 rhs) - { - return new Point64(lhs.X - rhs.X, lhs.Y - rhs.Y); - } - public readonly override string ToString() - { - return $"{X},{Y} "; // nb: trailing space } -#endif public readonly override bool Equals(object? obj) { if (obj != null && obj is Point64 p) @@ -172,97 +143,77 @@ public struct PointD #if USINGZ public long z; +#endif public PointD(PointD pt) { x = pt.x; y = pt.y; +#if USINGZ z = pt.z; +#endif } public PointD(Point64 pt) { x = pt.X; y = pt.Y; +#if USINGZ z = pt.Z; +#endif } public PointD(Point64 pt, double scale) { x = pt.X * scale; y = pt.Y * scale; +#if USINGZ z = pt.Z; +#endif } public PointD(PointD pt, double scale) { x = pt.x * scale; y = pt.y * scale; +#if USINGZ z = pt.z; +#endif } - public PointD(long x, long y, long z = 0) - { + public PointD(long x, long y +#if USINGZ + , long z = 0 +#endif + ) { this.x = x; this.y = y; +#if USINGZ this.z = z; +#endif } - public PointD(double x, double y, long z = 0) - { + public PointD(double x, double y +#if USINGZ + , long z = 0 +#endif + ) { this.x = x; this.y = y; +#if USINGZ this.z = z; +#endif } public readonly string ToString(int precision = 2) { +#if USINGZ return string.Format($"{{0:F{precision}}},{{1:F{precision}}},{{2:D}}", x,y,z); - } - #else - public PointD(PointD pt) - { - x = pt.x; - y = pt.y; - } - - public PointD(Point64 pt) - { - x = pt.X; - y = pt.Y; - } - - public PointD(PointD pt, double scale) - { - x = pt.x * scale; - y = pt.y * scale; - } - - public PointD(Point64 pt, double scale) - { - x = pt.X * scale; - y = pt.Y * scale; - } - - public PointD(long x, long y) - { - this.x = x; - this.y = y; - } - - public PointD(double x, double y) - { - this.x = x; - this.y = y; - } - - public readonly string ToString(int precision = 2) - { return string.Format($"{{0:F{precision}}},{{1:F{precision}}}", x,y); +#endif } -#endif public static bool operator ==(PointD lhs, PointD rhs) { return InternalClipper.IsAlmostZero(lhs.x - rhs.x) && From 4ab4da0f98d02b86fcc841af348dcddcea45775d Mon Sep 17 00:00:00 2001 From: angusj Date: Wed, 22 Jan 2025 09:40:29 +1000 Subject: [PATCH 20/31] Clipper.Offset - changed arc_tolerance default --- .../include/clipper2/clipper.offset.h | 6 +- CPP/Clipper2Lib/src/clipper.offset.cpp | 43 +++++---- CPP/Examples/Inflate/Inflate.cpp | 92 +++++++++++-------- .../Clipper2Lib.Examples/InflateDemo/Main.cs | 58 ++++++------ CSharp/Clipper2Lib/Clipper.Core.cs | 5 +- CSharp/Clipper2Lib/Clipper.Offset.cs | 31 ++++--- Delphi/Clipper2Lib/Clipper.Offset.pas | 29 ++++-- 7 files changed, 153 insertions(+), 111 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h index bb075a6d..c0ec99dd 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 March 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -96,7 +96,7 @@ class ClipperOffset { void AddPaths(const Paths64& paths, JoinType jt_, EndType et_); void Clear() { groups_.clear(); norms.clear(); }; - void Execute(double delta, Paths64& paths); + void Execute(double delta, Paths64& sols_64); void Execute(double delta, PolyTree64& polytree); void Execute(DeltaCallback64 delta_cb, Paths64& paths); diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 6fdcc667..7f54f28a 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 22 November 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -13,9 +13,23 @@ namespace Clipper2Lib { -const double default_arc_tolerance = 0.25; const double floating_point_tolerance = 1e-12; +// Clipper2 approximates arcs by using series of relatively short straight +//line segments. And logically, shorter line segments will produce better arc +// approximations. But very short segments can degrade performance, usually +// with little or no discernable improvement in curve quality. Very short +// segments can even detract from curve quality, due to the effects of integer +// rounding. Since there isn't an optimal number of line segments for any given +// arc radius (that perfectly balances curve approximation with performance), +// arc tolerance is user defined. Nevertheless, when the user doesn't define +// an arc tolerance (ie leaves alone the 0 default value), the calculated +// default arc tolerance (offset_radius / 500) generally produces good (smooth) +// arc approximations without producing excessively small segment lengths. +// See also: https://www.angusj.com/clipper2/Docs/Trigonometry.htm +const double arc_const = 0.002; // <-- 1/500 + + //------------------------------------------------------------------------------ // Miscellaneous methods //------------------------------------------------------------------------------ @@ -50,11 +64,10 @@ inline double Hypot(double x, double y) static PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) { - double dx, dy, inverse_hypot; if (pt1 == pt2) return PointD(0.0, 0.0); - dx = static_cast(pt2.x - pt1.x); - dy = static_cast(pt2.y - pt1.y); - inverse_hypot = 1.0 / Hypot(dx, dy); + double dx = static_cast(pt2.x - pt1.x); + double dy = static_cast(pt2.y - pt1.y); + double inverse_hypot = 1.0 / Hypot(dx, dy); dx *= inverse_hypot; dy *= inverse_hypot; return PointD(dy, -dx); @@ -260,8 +273,7 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle // so we'll need to do the following calculations for *every* vertex. double abs_delta = std::fabs(group_delta_); double arcTol = (arc_tolerance_ > floating_point_tolerance ? - std::min(abs_delta, arc_tolerance_) : - std::log10(2 + abs_delta) * default_arc_tolerance); + std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const); double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); step_sin_ = std::sin(2 * PI / steps_per_360); step_cos_ = std::cos(2 * PI / steps_per_360); @@ -459,9 +471,8 @@ void ClipperOffset::DoGroupOffset(Group& group) // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision // will be relative to the size of the offset (delta). Obviously very //large offsets will almost always require much less precision. - double arcTol = (arc_tolerance_ > floating_point_tolerance ? - std::min(abs_delta, arc_tolerance_) : - std::log10(2 + abs_delta) * default_arc_tolerance); + double arcTol = (arc_tolerance_ > floating_point_tolerance) ? + std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const; double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); step_sin_ = std::sin(2 * PI / steps_per_360); @@ -590,7 +601,6 @@ void ClipperOffset::ExecuteInternal(double delta) if (!solution->size()) return; bool paths_reversed = CheckReverseOrientation(); - //clean up self-intersections ... Clipper64 c; c.PreserveCollinear(false); @@ -617,13 +627,12 @@ void ClipperOffset::ExecuteInternal(double delta) else c.Execute(ClipType::Union, FillRule::Positive, *solution); } - } -void ClipperOffset::Execute(double delta, Paths64& paths) +void ClipperOffset::Execute(double delta, Paths64& paths64) { - paths.clear(); - solution = &paths; + paths64.clear(); + solution = &paths64; solution_tree = nullptr; ExecuteInternal(delta); } diff --git a/CPP/Examples/Inflate/Inflate.cpp b/CPP/Examples/Inflate/Inflate.cpp index b7a7dcc7..3880c169 100644 --- a/CPP/Examples/Inflate/Inflate.cpp +++ b/CPP/Examples/Inflate/Inflate.cpp @@ -20,71 +20,83 @@ int main(int argc, char* argv[]) //std::getchar(); } -void DoSimpleShapes() +void DoSimpleShapes() { - Paths64 op1, op2; + // OPEN_PATHS SVG: + + PathsD op1, op2; FillRule fr2 = FillRule::EvenOdd; SvgWriter svg2; - - op1.push_back(MakePath({ 80,60, 20,20, 180,20, 180,70, 25,150, 20,180, 180,180 })); + op1.push_back(MakePathD({ 80,60, 20,20, 180,20, 180,70, 25,150, 20,180, 180,180 })); op2 = InflatePaths(op1, 15, JoinType::Miter, EndType::Square, 3); SvgAddOpenSubject(svg2, op1, fr2, false); - SvgAddSolution(svg2, TransformPaths(op2), fr2, false); + SvgAddSolution(svg2, op2, fr2, false); SvgAddCaption(svg2, "Miter Joins; Square Ends", 20, 210); - - op1 = TranslatePaths(op1, 210, 0); + op1 = TranslatePaths(op1, 210, 0); op2 = InflatePaths(op1, 15, JoinType::Square, EndType::Square); SvgAddOpenSubject(svg2, op1, fr2, false); - SvgAddSolution(svg2, TransformPaths(op2), fr2, false); + SvgAddSolution(svg2, op2, fr2, false); SvgAddCaption(svg2, "Square Joins; Square Ends", 230, 210); - - op1 = TranslatePaths(op1, 210, 0); + op1 = TranslatePaths(op1, 210, 0); op2 = InflatePaths(op1, 15, JoinType::Bevel, EndType::Butt, 3); SvgAddOpenSubject(svg2, op1, fr2, false); - SvgAddSolution(svg2, TransformPaths(op2), fr2, false); + SvgAddSolution(svg2, op2, fr2, false); SvgAddCaption(svg2, "Bevel Joins; Butt Ends", 440, 210); - - op1 = TranslatePaths(op1, 210, 0); + op1 = TranslatePaths(op1, 210, 0); op2 = InflatePaths(op1, 15, JoinType::Round, EndType::Round); SvgAddOpenSubject(svg2, op1, fr2, false); - SvgAddSolution(svg2, TransformPaths(op2), fr2, false); + SvgAddSolution(svg2, op2, fr2, false); SvgAddCaption(svg2, "Round Joins; Round Ends", 650, 210); - SvgSaveToFile(svg2, "open_paths.svg", 800, 600, 20); System("open_paths.svg"); - //triangle offset - with large miter - Paths64 p, pp; - p.push_back(MakePath({ 30, 150, 60, 350, 0, 350 })); - pp.insert(pp.end(), p.begin(), p.end()); + // POLYGON JOINTYPES SVG: + // 1. triangle offset - with large miter + int err, scale = 100; + PathsD p, solution; + p.push_back(MakePathD({ 30,150, 60,350, 0,350 })); + solution.insert(solution.end(), p.begin(), p.end()); for (int i = 0; i < 5; ++i) { - //note the relatively large miter limit set here (10) - p = InflatePaths(p, 5, - JoinType::Miter, EndType::Polygon, 10); - pp.insert(pp.end(), p.begin(), p.end()); + p = InflatePaths(p, 5, JoinType::Miter, EndType::Polygon, 10); + solution.insert(solution.end(), p.begin(), p.end()); } + + // 2. open rectangles offset bevelled, squared & rounded ... - //rectangle offset - both squared and rounded p.clear(); - p.push_back(MakePath({ 100,30, 340,30, 340,230, 100,230 })); - pp.insert(pp.end(), p.begin(), p.end()); - //nb: using the ClipperOffest class directly here to control - //different join types within the same offset operation - ClipperOffset co; - co.AddPaths(p, JoinType::Miter, EndType::Joined); - p = TranslatePaths(p, 120, 100); - pp.insert(pp.end(), p.begin(), p.end()); - co.AddPaths(p, JoinType::Round, EndType::Joined); - co.Execute(10, p); - pp.insert(pp.end(), p.begin(), p.end()); - - FillRule fr3 = FillRule::EvenOdd; + p.push_back(MakePathD({ 100,30, 340,30, 340,230, 100,230 })); + p.push_back(TranslatePath(p[0], 60, 50)); + p.push_back(TranslatePath(p[1], 60, 50)); + SvgWriter svg; - SvgAddSolution(svg, TransformPaths(pp), fr3, false); - SvgSaveToFile(svg, "solution_off.svg", 800, 600, 20); - System("solution_off.svg"); + SvgAddOpenSubject(svg, p); + SvgAddCaption(svg, "Bevelled", 100, 15); + SvgAddCaption(svg, "Squared", 160, 65); + SvgAddCaption(svg, "Rounded", 220, 115); + + // nb: we must use the ClipperOffest class directly if we want to + // perform different join types within the same offset operation + // ClipperOffset only supports int64_t coords so, if we want better than unit + // precision, we have to scale manually. (InflatePaths does this scaling internally) + ClipperOffset co; + p = ScalePaths(p, scale, err); + // AddPaths - paths must be int64_t paths + co.AddPath(TransformPath(p[0]), JoinType::Bevel, EndType::Joined); + co.AddPath(TransformPath(p[1]), JoinType::Square, EndType::Joined); + co.AddPath(TransformPath(p[2]), JoinType::Round, EndType::Joined); + Paths64 sol64; + co.Execute(scale * 10, sol64); // ClipperOffset solutions must be int64_t + + // de-scale and append to solution ... + p = ScalePaths(sol64, 1.0 / scale, err); + solution.insert(solution.end(), p.begin(), p.end()); + + string filename = "polygon_jointypes.svg"; + SvgAddSolution(svg, solution, FillRule::EvenOdd, false); + SvgSaveToFile(svg, filename, 800, 600, 20); + System(filename); } void DoRabbit() diff --git a/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs b/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs index 2820048c..a5a73fee 100644 --- a/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs +++ b/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs @@ -26,42 +26,48 @@ public static void Main() public static void DoSimpleShapes() { SvgWriter svg = new(); - ClipperOffset co = new(); - //triangle offset - with large miter - Paths64 p0 = new() { Clipper.MakePath(new [] { 30,150, 60,350, 0,350 }) }; - Paths64 p = new(); + //TRIANGLE OFFSET - WITH LARGE MITER + + PathsD pp = new() { Clipper.MakePath(new double[] { 30,150, 60,350, 0,350 }) }; + PathsD solution = new(); for (int i = 0; i < 5; ++i) { //nb: the last parameter here (10) greatly increases miter limit - p0 = Clipper.InflatePaths(p0, 5, JoinType.Miter, EndType.Polygon, 10); - p.AddRange(p0); + pp = Clipper.InflatePaths(pp, 5, JoinType.Miter, EndType.Polygon, 10); + solution.AddRange(pp); } - SvgUtils.AddSolution(svg, p, false); - p.Clear(); + SvgUtils.AddSolution(svg, solution, false); - //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 [] { 100,0, 340,0, 340,200, 100,200, 100, 0 })); - SvgUtils.AddOpenSubject(svg, p); - co.AddPaths(p, JoinType.Bevel, EndType.Joined); + // RECTANGLE OFFSET - BEVEL, SQUARED AND ROUNDED - p = Clipper.TranslatePaths(p, 60, 50); - SvgUtils.AddOpenSubject(svg, p); - co.AddPaths(p, JoinType.Square, EndType.Joined); - p = Clipper.TranslatePaths(p, 60, 50); - SvgUtils.AddOpenSubject(svg, p); - co.AddPaths(p, JoinType.Round, EndType.Joined); + solution.Clear(); + solution.Add(Clipper.MakePath(new double[] { 100, 0, 340, 0, 340, 200, 100, 200 })); + solution.Add(Clipper.TranslatePath(solution[0], 60, 50)); + solution.Add(Clipper.TranslatePath(solution[1], 60, 50)); + SvgUtils.AddOpenSubject(svg, solution); - co.Execute(10, p); + // nb: rather than using InflatePaths(), we have to use the + // ClipperOffest class directly because we want to perform + // different join types in a single offset operation + ClipperOffset co = new(); + // because ClipperOffset only accepts Int64 paths, scale them + // so the de-scaled offset result will have greater precision + double scale = 100; + Paths64 pp64 = Clipper.ScalePaths64(solution, scale); + co.AddPath(pp64[0], JoinType.Bevel, EndType.Joined); + co.AddPath(pp64[1], JoinType.Square, EndType.Joined); + co.AddPath(pp64[2], JoinType.Round, EndType.Joined); + co.Execute(10 * scale, pp64); + // now de-scale the offset solution + solution = Clipper.ScalePathsD(pp64, 1 / scale); const string filename = "../../../inflate.svg"; - SvgUtils.AddSolution(svg, p, false); - SvgUtils.AddCaption(svg, "Beveled join", 100, -27); - SvgUtils.AddCaption(svg, "Squared join", 160, 23); - SvgUtils.AddCaption(svg, "Rounded join", 220, 73); - SvgUtils.SaveToFile(svg, filename, FillRule.EvenOdd, 800, 600, 20); + SvgUtils.AddSolution(svg, solution, false); + SvgUtils.AddCaption(svg, "Beveled join", 100, -17); + SvgUtils.AddCaption(svg, "Squared join", 160, 33); + SvgUtils.AddCaption(svg, "Rounded join", 220, 83); + SvgUtils.SaveToFile(svg, filename, FillRule.EvenOdd, 800, 600, 40); ClipperFileIO.OpenFileWithDefaultApp(filename); } diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index a864ce23..13f89ff4 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 10 October 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Core structures and functions for the Clipper Library * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -523,7 +523,6 @@ public static class InternalClipper internal const double min_coord = -MaxCoord; internal const long Invalid64 = MaxInt64; - internal const double defaultArcTolerance = 0.25; internal const double floatingPointTolerance = 1E-12; internal const double defaultMinimumEdgeLength = 0.1; diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 5fc73b3b..82cbe4e6 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 22 November 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -69,6 +69,20 @@ public Group(Paths64 paths, JoinType joinType, EndType endType = EndType.Polygon private const double Tolerance = 1.0E-12; + // Clipper2 approximates arcs by using series of relatively short straight + //line segments. And logically, shorter line segments will produce better arc + // approximations. But very short segments can degrade performance, usually + // with little or no discernable improvement in curve quality. Very short + // segments can even detract from curve quality, due to the effects of integer + // rounding. Since there isn't an optimal number of line segments for any given + // arc radius (that perfectly balances curve approximation with performance), + // arc tolerance is user defined. Nevertheless, when the user doesn't define + // an arc tolerance (ie leaves alone the 0 default value), the calculated + // default arc tolerance (offset_radius / 500) generally produces good (smooth) + // arc approximations without producing excessively small segment lengths. + // See also: https://www.angusj.com/clipper2/Docs/Trigonometry.htm + const double arc_const = 0.002; // <-- 1/500 + private readonly List _groupList = new List(); private Path64 pathOut = new Path64(); private readonly PathD _normals = new PathD(); @@ -474,9 +488,7 @@ private void DoRound(Path64 path, int j, int k, double angle) // when DeltaCallback is assigned, _groupDelta won't be constant, // so we'll need to do the following calculations for *every* vertex. double absDelta = Math.Abs(_groupDelta); - double arcTol = ArcTolerance > 0.01 ? - ArcTolerance : - Math.Log10(2 + absDelta) * InternalClipper.defaultArcTolerance; + double arcTol = ArcTolerance > 0.01 ? ArcTolerance : absDelta * arc_const; double stepsPer360 = Math.PI / Math.Acos(1 - arcTol / absDelta); _stepSin = Math.Sin((2 * Math.PI) / stepsPer360); _stepCos = Math.Cos((2 * Math.PI) / stepsPer360); @@ -680,14 +692,7 @@ private void DoGroupOffset(Group group) if (group.joinType == JoinType.Round || group.endType == EndType.Round) { - // calculate the number of steps required to approximate a circle - // (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm) - // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision - // will be relative to the size of the offset (delta). Obviously very - //large offsets will almost always require much less precision. - double arcTol = ArcTolerance > 0.01 ? - ArcTolerance : - Math.Log10(2 + absDelta) * InternalClipper.defaultArcTolerance; + double arcTol = ArcTolerance > 0.01 ? ArcTolerance : absDelta * arc_const; double stepsPer360 = Math.PI / Math.Acos(1 - arcTol / absDelta); _stepSin = Math.Sin((2 * Math.PI) / stepsPer360); _stepCos = Math.Cos((2 * Math.PI) / stepsPer360); diff --git a/Delphi/Clipper2Lib/Clipper.Offset.pas b/Delphi/Clipper2Lib/Clipper.Offset.pas index 77590979..b487f270 100644 --- a/Delphi/Clipper2Lib/Clipper.Offset.pas +++ b/Delphi/Clipper2Lib/Clipper.Offset.pas @@ -2,9 +2,9 @@ (******************************************************************************* * Author : Angus Johnson * -* Date : 22 November 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) @@ -142,6 +142,20 @@ implementation TwoPi : Double = 2 * PI; InvTwoPi : Double = 1/(2 * PI); +// Clipper2 approximates arcs by using series of relatively short straight +//line segments. And logically, shorter line segments will produce better arc +// approximations. But very short segments can degrade performance, usually +// with little or no discernable improvement in curve quality. Very short +// segments can even detract from curve quality, due to the effects of integer +// rounding. Since there isn't an optimal number of line segments for any given +// arc radius (that perfectly balances curve approximation with performance), +// arc tolerance is user defined. Nevertheless, when the user doesn't define +// an arc tolerance (ie leaves alone the 0 default value), the calculated +// default arc tolerance (offset_radius / 500) generally produces good (smooth) +// arc approximations without producing excessively small segment lengths. +// See also: https://www.angusj.com/clipper2/Docs/Trigonometry.htm +const arc_const = 0.002; // <-- 1/500 + //------------------------------------------------------------------------------ // Miscellaneous offset support functions //------------------------------------------------------------------------------ @@ -368,9 +382,8 @@ procedure TClipperOffset.DoGroupOffset(group: TGroup); // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision // will be relative to the size of the offset (delta). Obviously very //large offsets will almost always require much less precision. - arcTol := Iif(fArcTolerance > 0.01, - Min(absDelta, fArcTolerance), - Log10(2 + absDelta) * 0.25); // empirically derived + arcTol := Iif(fArcTolerance > 0.0, + Min(absDelta, fArcTolerance), absDelta * arc_const); stepsPer360 := Pi / ArcCos(1 - arcTol / absDelta); if (stepsPer360 > absDelta * Pi) then @@ -917,10 +930,8 @@ procedure TClipperOffset.DoRound(j, k: Integer; angle: double); // when fDeltaCallback64 is assigned, fGroupDelta won't be constant, // so we'll need to do the following calculations for *every* vertex. absDelta := Abs(fGroupDelta); - arcTol := Iif(fArcTolerance > 0.01, - Min(absDelta, fArcTolerance), - Log10(2 + absDelta) * 0.25); // empirically derived - //http://www.angusj.com/clipper2/Docs/Trigonometry.htm + arcTol := Iif(fArcTolerance > 0.0, + Min(absDelta, fArcTolerance), absDelta * arc_const); stepsPer360 := Pi / ArcCos(1 - arcTol / absDelta); if (stepsPer360 > absDelta * Pi) then stepsPer360 := absDelta * Pi; // avoid excessive precision From d10ad598fcbaa697337c117552f6762de4f5feff Mon Sep 17 00:00:00 2001 From: angusj Date: Fri, 24 Jan 2025 08:52:47 +1000 Subject: [PATCH 21/31] InflatePaths function (C# only) - added missing arcTolerance parameter. Updated clipper.export.h documention. --- .../include/clipper2/clipper.export.h | 54 ++++++++++--------- CSharp/Clipper2Lib/Clipper.cs | 12 ++--- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index 5afbba17..1642d1f5 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 September 2024 * +* Date : 24 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -19,27 +19,31 @@ The path structures used extensively in other parts of this library are all based on std::vector classes. Since C++ classes can't be accessed by other -languages, these paths are converted here into very simple array data -structures that can be parsed by just about any programming language. +languages, these paths are exported here as very simple array structures +(either of int64_t or double) that can be parsed by just about any +programming language. These 2D paths are defined by series of x and y coordinates together with an optional user-defined 'z' value (see Z-values below). Hence, a vertex refers -to a single x and y coordinate (+/- a user-defined value). These values will -be either type int64_t or type double. Data structures have names that -indicate the array type by a suffixed '64' or a 'D'. For example, the data -structure CPath64 contains an array of int64_t values, whereas the data -structure CPathD contains an array of double. Where documentation omits -the type suffix (eg CPath), it is simply agnostic to the array's data type. +to a single x and y coordinate (+/- a user-defined value). Data structures +have names with suffixes that indicate the array type (either int64_t or +double). For example, the data structure CPath64 contains an array of int64_t +values, whereas the data structure CPathD contains an array of double. +Where documentation omits the type suffix (eg CPath), it is referring to an +array whose data type could be either int64_t or double. For conciseness, the following letters are used in the diagrams below: N: Number of vertices in a given path -C: Count of structure's paths -A: Array size (as distinct from the size in memory) +C: Count (ie number) of paths (or PolyPaths) in the structure +A: Number of elements in an array CPath64 and CPathD: -These are arrays of consecutive vertices preceeded by a pair of values -containing the number of vertices (N) in the path, and a 0 value. +These are arrays of either int64_t or double values. Apart from +the first two elements, these arrays are a series of vertices +that together define a path. The very first element contains the +number of vertices (N) in the path, while second element should +contain a 0 value. _______________________________________________________________ | counters | vertex1 | vertex2 | ... | vertexN | | N, 0 | x1, y1, (z1) | x2, y2, (z2) | ... | xN, yN, (zN) | @@ -47,10 +51,11 @@ _______________________________________________________________ CPaths64 and CPathsD: -These are also arrays containing any number of consecutive CPath -structures. Preceeding these consecutive paths, there is a pair of -values that contain the length of the array structure (A) and -the count of following CPath structures (C). +These are also arrays of either int64_t or double values that +contain any number of consecutive CPath structures. However, +preceeding the first path is a pair of values. The first value +contains the length of the entire array structure (A), and the +second contains the number (ie count) of contained paths (C). Memory allocation for CPaths64 = A * sizeof(int64_t) Memory allocation for CPathsD = A * sizeof(double) __________________________________________ @@ -60,12 +65,13 @@ __________________________________________ CPolytree64 and CPolytreeD: -These structures consist of two values followed by a series of CPolyPath -structures. The first value indicates the total length of the array (A). -The second value indicates the number of following CPolyPath structures -that are the top level CPolyPath in the CPolytree (C). These CPolyPath -may, in turn, contain their own nested CPolyPath children that -collectively make a tree structure. +The entire polytree structure is an array of int64_t or double. The +first element in the array indicates the array's total length (A). +The second element indicates the number (C) of CPolyPath structures +that are the TOP LEVEL CPolyPath in the polytree, and these top +level CPolyPath immediately follow these first two array elements. +These top level CPolyPath structures may, in turn, contain nested +CPolyPath children, and these collectively make a tree structure. _________________________________________________________ | counters | CPolyPath1 | CPolyPath2 | ... | CPolyPathC | | A, C | | | ... | | diff --git a/CSharp/Clipper2Lib/Clipper.cs b/CSharp/Clipper2Lib/Clipper.cs index 90efb98f..99283f05 100644 --- a/CSharp/Clipper2Lib/Clipper.cs +++ b/CSharp/Clipper2Lib/Clipper.cs @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 10 October 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : This module contains simple functions that will likely cover * * most polygon boolean and offsetting needs, while also avoiding * * the inherent complexities of the other modules. * @@ -138,9 +138,9 @@ public static void BooleanOp(ClipType clipType, } public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinType, - EndType endType, double miterLimit = 2.0) + EndType endType, double miterLimit = 2.0, double arcTolerance = 0.0) { - ClipperOffset co = new ClipperOffset(miterLimit); + ClipperOffset co = new ClipperOffset(miterLimit, arcTolerance); co.AddPaths(paths, joinType, endType); Paths64 solution = new Paths64(); co.Execute(delta, solution); @@ -148,12 +148,12 @@ public static Paths64 InflatePaths(Paths64 paths, double delta, JoinType joinTyp } public static PathsD InflatePaths(PathsD paths, double delta, JoinType joinType, - EndType endType, double miterLimit = 2.0, int precision = 2) + EndType endType, double miterLimit = 2.0, int precision = 2, double arcTolerance = 0.0) { InternalClipper.CheckPrecision(precision); double scale = Math.Pow(10, precision); Paths64 tmp = ScalePaths64(paths, scale); - ClipperOffset co = new ClipperOffset(miterLimit); + ClipperOffset co = new ClipperOffset(miterLimit, arcTolerance); co.AddPaths(tmp, joinType, endType); co.Execute(delta * scale, tmp); // reuse 'tmp' to receive (scaled) solution return ScalePathsD(tmp, 1 / scale); From ef6dbfdcaba251ef4363ec121216ba168f694b68 Mon Sep 17 00:00:00 2001 From: angusj Date: Fri, 24 Jan 2025 22:09:37 +1000 Subject: [PATCH 22/31] Clipper2 ver 1.5.0 --- CPP/CMakeLists.txt | 2 +- CPP/Clipper2Lib/include/clipper2/clipper.engine.h | 2 +- CPP/Clipper2Lib/include/clipper2/clipper.version.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CPP/CMakeLists.txt b/CPP/CMakeLists.txt index 0e68fa9f..6f40531a 100644 --- a/CPP/CMakeLists.txt +++ b/CPP/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(Clipper2 VERSION 1.4.0 LANGUAGES C CXX) +project(Clipper2 VERSION 1.5.0 LANGUAGES C CXX) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 17) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index 559f2479..8129beb0 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -167,7 +167,7 @@ namespace Clipper2Lib { }; #ifdef USINGZ - typedef std::function ZCallback64; typedef std::function Date: Sun, 26 Jan 2025 15:04:02 +0100 Subject: [PATCH 23/31] Fix typos (#935) --- CPP/BenchMark/PointInPolygonBenchmark.cpp | 2 +- CPP/Clipper2Lib/include/clipper2/clipper.export.h | 2 +- CPP/Clipper2Lib/src/clipper.engine.cpp | 6 +++--- CPP/Utils/ClipFileSave.cpp | 2 +- CSharp/Clipper2Lib/Clipper.Engine.cs | 4 ++-- Delphi/Clipper2Lib/Clipper.Engine.pas | 6 +++--- Delphi/Clipper2Lib/Clipper.inc | 2 +- Delphi/Clipper2Lib/Clipper.pas | 2 +- README.md | 6 +++--- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CPP/BenchMark/PointInPolygonBenchmark.cpp b/CPP/BenchMark/PointInPolygonBenchmark.cpp index 33e7a924..38795e2a 100644 --- a/CPP/BenchMark/PointInPolygonBenchmark.cpp +++ b/CPP/BenchMark/PointInPolygonBenchmark.cpp @@ -195,7 +195,7 @@ inline PointInPolygonResult PIP2(const Point64& pt, const Path64& polygon) } ///////////////////////////////////////////////////////// -// PIP3: An entirely different algorithm for comparision. +// PIP3: An entirely different algorithm for comparison. // "Optimal Reliable Point-in-Polygon Test and // Differential Coding Boolean Operations on Polygons" // by Jianqiang Hao et al. diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index 1642d1f5..66361f55 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -53,7 +53,7 @@ _______________________________________________________________ CPaths64 and CPathsD: These are also arrays of either int64_t or double values that contain any number of consecutive CPath structures. However, -preceeding the first path is a pair of values. The first value +preceding the first path is a pair of values. The first value contains the length of the entire array structure (A), and the second contains the number (ie count) of contained paths (C). Memory allocation for CPaths64 = A * sizeof(int64_t) diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index 97717322..99abdf46 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -517,7 +517,7 @@ namespace Clipper2Lib { while (op2 != op && op2->pt.y > pt.y) op2 = op2->next; if (op2 == op) break; - // must have touched or crossed the pt.Y horizonal + // must have touched or crossed the pt.Y horizontal // and this must happen an even number of times if (op2->pt.y == pt.y) // touching the horizontal @@ -1961,7 +1961,7 @@ namespace Clipper2Lib { else if (IsFront(e1) || (e1.outrec == e2.outrec)) { //this 'else if' condition isn't strictly needed but - //it's sensible to split polygons that ony touch at + //it's sensible to split polygons that only touch at //a common vertex (not at common edges). #ifdef USINGZ @@ -2661,7 +2661,7 @@ namespace Clipper2Lib { if (horz.outrec) { - //nb: The outrec containining the op returned by IntersectEdges + //nb: The outrec containing the op returned by IntersectEdges //above may no longer be associated with horzEdge. AddTrialHorzJoin(GetLastOp(horz)); } diff --git a/CPP/Utils/ClipFileSave.cpp b/CPP/Utils/ClipFileSave.cpp index 139e03a7..f7dfc70b 100644 --- a/CPP/Utils/ClipFileSave.cpp +++ b/CPP/Utils/ClipFileSave.cpp @@ -254,7 +254,7 @@ bool SaveTest(const std::string& filename, bool append, bool found = false; int last_cap_pos = 0, curr_cap_pos = 0; int64_t last_test_no = 0; - if (append && FileExists(filename)) //get the number of the preceeding test + if (append && FileExists(filename)) //get the number of the preceding test { ifstream file; file.open(filename, std::ios::binary); diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index 7cfc0b5f..0f3a916e 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -1691,7 +1691,7 @@ private void IntersectEdges(Active ae1, Active ae2, Point64 pt) else if (IsFront(ae1) || (ae1.outrec == ae2.outrec)) { // this 'else if' condition isn't strictly needed but - // it's sensible to split polygons that ony touch at + // it's sensible to split polygons that only touch at // a common vertex (not at common edges). resultOp = AddLocalMaxPoly(ae1, ae2, pt); #if USINGZ @@ -2628,7 +2628,7 @@ private static PointInPolygonResult PointInOpPolygon(Point64 pt, OutPt op) while (op2 != op && op2.pt.Y > pt.Y) op2 = op2.next!; if (op2 == op) break; - // must have touched or crossed the pt.Y horizonal + // must have touched or crossed the pt.Y horizontal // and this must happen an even number of times if (op2.pt.Y == pt.Y) // touching the horizontal diff --git a/Delphi/Clipper2Lib/Clipper.Engine.pas b/Delphi/Clipper2Lib/Clipper.Engine.pas index 29034030..fa6bcf75 100644 --- a/Delphi/Clipper2Lib/Clipper.Engine.pas +++ b/Delphi/Clipper2Lib/Clipper.Engine.pas @@ -899,7 +899,7 @@ function PointInOpPolygon(const pt: TPoint64; op: POutPt): TPointInPolygonResult while (op2 <> op) and (op2.pt.Y > pt.Y) do op2 := op2.next; if (op2 = op) then break; - // must have touched or crossed the pt.Y horizonal + // must have touched or crossed the pt.Y horizontal // and this must happen an even number of times if (op2.pt.Y = pt.Y) then // touching the horizontal @@ -2690,7 +2690,7 @@ procedure TClipperBase.IntersectEdges(e1, e2: PActive; pt: TPoint64); end else if IsFront(e1) or (e1.outrec = e2.outrec) then begin // this 'else if' condition isn't strictly needed but - // it's sensible to split polygons that ony touch at + // it's sensible to split polygons that only touch at // a common vertex (not at common edges). op := AddLocalMaxPoly(e1, e2, pt); {$IFDEF USINGZ} @@ -3528,7 +3528,7 @@ procedure TClipperBase.DoHorizontal(horzEdge: PActive); end; if IsHotEdge(horzEdge) then begin - //nb: The outrec containining the op returned by IntersectEdges + //nb: The outrec containing the op returned by IntersectEdges //above may no longer be associated with horzEdge. FHorzSegList.Add(GetLastOp(horzEdge)); end; diff --git a/Delphi/Clipper2Lib/Clipper.inc b/Delphi/Clipper2Lib/Clipper.inc index 5b15f920..17da40df 100644 --- a/Delphi/Clipper2Lib/Clipper.inc +++ b/Delphi/Clipper2Lib/Clipper.inc @@ -7,7 +7,7 @@ {.$DEFINE USINGZ} /////////////////////////////////////////////////////////////////////////////// -//COMPILER DIFINED PREPROCESSOR DIRECTIVES (ie. do not touch ;)) +//COMPILER DEFINED PREPROCESSOR DIRECTIVES (ie. do not touch ;)) /////////////////////////////////////////////////////////////////////////////// {$IFDEF FPC} diff --git a/Delphi/Clipper2Lib/Clipper.pas b/Delphi/Clipper2Lib/Clipper.pas index 1520c671..06aa03ff 100644 --- a/Delphi/Clipper2Lib/Clipper.pas +++ b/Delphi/Clipper2Lib/Clipper.pas @@ -821,7 +821,7 @@ function DistanceSqrd(const pt1, pt2: TPoint64): double; var x1,y1,x2,y2: double; begin - // nb: older versions of Delphi don't allow explicit typcasting + // nb: older versions of Delphi don't allow explicit typecasting x1 := pt1.X; y1 := pt1.Y; x2 := pt2.X; y2 := pt2.Y; result := Sqr(x1 - x2) + Sqr(y1 - y2); diff --git a/README.md b/README.md index 2a7dbc54..2d6be184 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ### A Polygon Clipping and Offsetting library (in C++, C# & Delphi)
[![GitHub Actions C++ status](https://github.com/AngusJohnson/Clipper2/actions/workflows/actions_cpp.yml/badge.svg)](https://github.com/AngusJohnson/Clipper2/actions/workflows/actions_cpp.yml) [![C#](https://github.com/AngusJohnson/Clipper2/actions/workflows/actions_csharp.yml/badge.svg)](https://github.com/AngusJohnson/Clipper2/actions/workflows/actions_csharp.yml) [![License](https://img.shields.io/badge/License-Boost_1.0-lightblue.svg)](https://www.boost.org/LICENSE_1_0.txt) [![Nuget](https://img.shields.io/nuget/v/Clipper2?color=green)](https://www.nuget.org/packages/Clipper2) -[![documentation](https://user-images.githubusercontent.com/5280692/187832279-b2a43890-da80-4888-95fe-793f092be372.svg)](http://www.angusj.com/clipper2/Docs/Overview.htm) +[![documentation](https://user-images.githubusercontent.com/5280692/187832279-b2a43890-da80-4888-95fe-793f092be372.svg)](https://www.angusj.com/clipper2/Docs/Overview.htm) -The Clipper2 library performs **intersection**, **union**, **difference** and **XOR** boolean operations on both simple and complex polygons. It also performs polygon offsetting. This is a major update of my original Clipper library that was written over 10 years ago. That library I'm now calling Clipper1, and while it still works very well, Clipper2 is [better](http://www.angusj.com/clipper2/Docs/Changes.htm) in just about every way. +The Clipper2 library performs **intersection**, **union**, **difference** and **XOR** boolean operations on both simple and complex polygons. It also performs polygon offsetting. This is a major update of my original Clipper library that was written over 10 years ago. That library I'm now calling Clipper1, and while it still works very well, Clipper2 is [better](https://www.angusj.com/clipper2/Docs/Changes.htm) in just about every way. ### Compilers Clipper2 can be compiled using either C++, or C#, or Delphi Pascal. The library can also be accessed from other programming languages by dynamically linking to exported functions in the [C++ compiled Clipper2 library](https://github.com/AngusJohnson/Clipper2/tree/main/DLL). (Since the C++ compiled code is [measurably](https://www.angusj.com/clipper2/Docs/Changes.htm) faster, C# and Delphi developers may also prefer this approach in applications where the library's performance is critical.) @@ -16,7 +16,7 @@ The Clipper2 library performs **intersection**, **union**, **difference** ### Documentation - Extensive HTML documentation + Extensive HTML documentation

### Examples From 41b5f0199f72f125e0fd602c400040e447fe24bb Mon Sep 17 00:00:00 2001 From: Andreas Deininger Date: Sun, 26 Jan 2025 16:47:38 +0100 Subject: [PATCH 24/31] Fix broken links, convert links from http to https protocol (#936) --- CPP/Clipper2Lib/include/clipper2/clipper.core.h | 6 +++--- CPP/Clipper2Lib/include/clipper2/clipper.engine.h | 4 ++-- CPP/Clipper2Lib/include/clipper2/clipper.export.h | 4 ++-- CPP/Clipper2Lib/include/clipper2/clipper.h | 4 ++-- CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h | 4 ++-- CPP/Clipper2Lib/include/clipper2/clipper.offset.h | 4 ++-- CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h | 4 ++-- CPP/Clipper2Lib/src/clipper.engine.cpp | 4 ++-- CPP/Clipper2Lib/src/clipper.offset.cpp | 6 +++--- CPP/Clipper2Lib/src/clipper.rectclip.cpp | 4 ++-- CPP/Utils/clipper.svg.cpp | 4 ++-- CPP/Utils/clipper.svg.h | 4 ++-- CPP/Utils/clipper.svg.utils.h | 4 ++-- CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs | 4 ++-- CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs | 4 ++-- CSharp/Clipper2Lib.Examples/RectClip/Main.cs | 4 ++-- CSharp/Clipper2Lib/Clipper.Core.cs | 4 ++-- CSharp/Clipper2Lib/Clipper.Engine.cs | 4 ++-- CSharp/Clipper2Lib/Clipper.Minkowski.cs | 4 ++-- CSharp/Clipper2Lib/Clipper.Offset.cs | 4 ++-- CSharp/Clipper2Lib/Clipper.RectClip.cs | 4 ++-- CSharp/Clipper2Lib/Clipper.cs | 4 ++-- CSharp/Clipper2Lib/Clipper2Lib.csproj | 2 +- CSharp/Clipper2Lib/HashCode.cs | 2 +- CSharp/USINGZ.TestApp/Program.cs | 4 ++-- CSharp/Utils/ClipFileIO/Clipper.FileIO.cs | 4 ++-- CSharp/Utils/Colors/Clipper.Colors.cs | 4 ++-- CSharp/Utils/SVG/Clipper.SVG.Utils.cs | 4 ++-- CSharp/Utils/SVG/Clipper.SVG.cs | 4 ++-- DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs | 4 ++-- DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs | 4 ++-- .../Delphi_TestApps/TestApp1/Clipper2DllCore.pas | 4 ++-- DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas | 4 ++-- Delphi/Clipper2Lib/Clipper.Core.pas | 4 ++-- Delphi/Clipper2Lib/Clipper.Engine.pas | 4 ++-- Delphi/Clipper2Lib/Clipper.Minkowski.pas | 2 +- Delphi/Clipper2Lib/Clipper.Offset.pas | 8 ++++---- Delphi/Clipper2Lib/Clipper.RectClip.pas | 4 ++-- Delphi/Clipper2Lib/Clipper.pas | 4 ++-- Delphi/Utils/Clipper.SVG.pas | 4 ++-- 40 files changed, 81 insertions(+), 81 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index 624de903..59372f55 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 12 May 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Core Clipper Library structures and functions * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef CLIPPER_CORE_H @@ -787,7 +787,7 @@ namespace Clipper2Lib const Point& line1, const Point& line2) { //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - //see http://en.wikipedia.org/wiki/Perpendicular_distance + //see https://en.wikipedia.org/wiki/Perpendicular_distance double a = static_cast(pt.x - line1.x); double b = static_cast(pt.y - line1.y); double c = static_cast(line2.x - line1.x); diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index 8129beb0..76317f2d 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 17 September 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef CLIPPER_ENGINE_H diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index 66361f55..c942862b 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 24 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.h b/CPP/Clipper2Lib/include/clipper2/clipper.h index a2fe5c3c..e3873f30 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 27 April 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This module provides a simple interface to the Clipper Library * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef CLIPPER_H diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h b/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h index a3ddcf86..0e1462a9 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 1 November 2023 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : Minkowski Sum and Difference * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef CLIPPER_MINKOWSKI_H diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h index c0ec99dd..0125dc2d 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 22 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef CLIPPER_OFFSET_H_ diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h index bfcfacf2..40cf3632 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 5 July 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef CLIPPER_RECTCLIP_H diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index 99abdf46..c9fae4c6 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 17 September 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #include diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 7f54f28a..5263cc07 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 22 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #include @@ -467,7 +467,7 @@ void ClipperOffset::DoGroupOffset(Group& group) if (group.join_type == JoinType::Round || group.end_type == EndType::Round) { // calculate the number of steps required to approximate a circle - // (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm) + // (see https://www.angusj.com/clipper2/Docs/Trigonometry.htm) // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision // will be relative to the size of the offset (delta). Obviously very //large offsets will almost always require much less precision. diff --git a/CPP/Clipper2Lib/src/clipper.rectclip.cpp b/CPP/Clipper2Lib/src/clipper.rectclip.cpp index 23809b5e..b7227204 100644 --- a/CPP/Clipper2Lib/src/clipper.rectclip.cpp +++ b/CPP/Clipper2Lib/src/clipper.rectclip.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 5 July 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #include diff --git a/CPP/Utils/clipper.svg.cpp b/CPP/Utils/clipper.svg.cpp index 1206bbb1..ab6f8be3 100644 --- a/CPP/Utils/clipper.svg.cpp +++ b/CPP/Utils/clipper.svg.cpp @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 24 March 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #include diff --git a/CPP/Utils/clipper.svg.h b/CPP/Utils/clipper.svg.h index 2c718137..1abf61bf 100644 --- a/CPP/Utils/clipper.svg.h +++ b/CPP/Utils/clipper.svg.h @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 24 March 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef svglib_h diff --git a/CPP/Utils/clipper.svg.utils.h b/CPP/Utils/clipper.svg.utils.h index dbfe5c55..2764b3b9 100644 --- a/CPP/Utils/clipper.svg.utils.h +++ b/CPP/Utils/clipper.svg.utils.h @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 16 June 2022 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #ifndef svgutillib_h diff --git a/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs b/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs index b52330c9..56e7dc50 100644 --- a/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs +++ b/CSharp/Clipper2Lib.Examples/ConsoleDemo/Main.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 1 January 2023 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System.Reflection; diff --git a/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs b/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs index a5a73fee..9a2b08c0 100644 --- a/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs +++ b/CSharp/Clipper2Lib.Examples/InflateDemo/Main.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 24 September 2023 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System.IO; diff --git a/CSharp/Clipper2Lib.Examples/RectClip/Main.cs b/CSharp/Clipper2Lib.Examples/RectClip/Main.cs index 2ec8426e..c60e5d01 100644 --- a/CSharp/Clipper2Lib.Examples/RectClip/Main.cs +++ b/CSharp/Clipper2Lib.Examples/RectClip/Main.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 11 February 2023 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System; diff --git a/CSharp/Clipper2Lib/Clipper.Core.cs b/CSharp/Clipper2Lib/Clipper.Core.cs index 13f89ff4..bfc9b19d 100644 --- a/CSharp/Clipper2Lib/Clipper.Core.cs +++ b/CSharp/Clipper2Lib/Clipper.Core.cs @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 22 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : Core structures and functions for the Clipper Library * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #nullable enable diff --git a/CSharp/Clipper2Lib/Clipper.Engine.cs b/CSharp/Clipper2Lib/Clipper.Engine.cs index 0f3a916e..f9015376 100644 --- a/CSharp/Clipper2Lib/Clipper.Engine.cs +++ b/CSharp/Clipper2Lib/Clipper.Engine.cs @@ -1,12 +1,12 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 10 October 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * * Thanks : Special thanks to Thong Nguyen, Guus Kuiper, Phil Stopford, * * : and Daniel Gosnell for their invaluable assistance with C#. * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #nullable enable diff --git a/CSharp/Clipper2Lib/Clipper.Minkowski.cs b/CSharp/Clipper2Lib/Clipper.Minkowski.cs index afad586f..d683d7d0 100644 --- a/CSharp/Clipper2Lib/Clipper.Minkowski.cs +++ b/CSharp/Clipper2Lib/Clipper.Minkowski.cs @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 10 October 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Minkowski Sum and Difference * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #nullable enable diff --git a/CSharp/Clipper2Lib/Clipper.Offset.cs b/CSharp/Clipper2Lib/Clipper.Offset.cs index 82cbe4e6..65fac1a2 100644 --- a/CSharp/Clipper2Lib/Clipper.Offset.cs +++ b/CSharp/Clipper2Lib/Clipper.Offset.cs @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 22 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System; diff --git a/CSharp/Clipper2Lib/Clipper.RectClip.cs b/CSharp/Clipper2Lib/Clipper.RectClip.cs index 02e637f1..f1c36efe 100644 --- a/CSharp/Clipper2Lib/Clipper.RectClip.cs +++ b/CSharp/Clipper2Lib/Clipper.RectClip.cs @@ -1,10 +1,10 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 10 October 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #nullable enable diff --git a/CSharp/Clipper2Lib/Clipper.cs b/CSharp/Clipper2Lib/Clipper.cs index 99283f05..9ee4c803 100644 --- a/CSharp/Clipper2Lib/Clipper.cs +++ b/CSharp/Clipper2Lib/Clipper.cs @@ -1,14 +1,14 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 22 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : This module contains simple functions that will likely cover * * most polygon boolean and offsetting needs, while also avoiding * * the inherent complexities of the other modules. * * Thanks : Special thanks to Thong Nguyen, Guus Kuiper, Phil Stopford, * * : and Daniel Gosnell for their invaluable assistance with C#. * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ #nullable enable diff --git a/CSharp/Clipper2Lib/Clipper2Lib.csproj b/CSharp/Clipper2Lib/Clipper2Lib.csproj index 9a4f2c5f..7966c056 100644 --- a/CSharp/Clipper2Lib/Clipper2Lib.csproj +++ b/CSharp/Clipper2Lib/Clipper2Lib.csproj @@ -9,7 +9,7 @@ Polygon Clipping and Offsetting Library Clipper2 Clipper2 - http://www.angusj.com/clipper2/Docs/Overview.htm + https://www.angusj.com/clipper2/Docs/Overview.htm Copyright © 2010-2023 git https://github.com/AngusJohnson/Clipper2 diff --git a/CSharp/Clipper2Lib/HashCode.cs b/CSharp/Clipper2Lib/HashCode.cs index 62889210..e8df2f2d 100644 --- a/CSharp/Clipper2Lib/HashCode.cs +++ b/CSharp/Clipper2Lib/HashCode.cs @@ -42,7 +42,7 @@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You can contact the author at : - - xxHash homepage: http://www.xxhash.com + - xxHash homepage: https://www.xxhash.com - xxHash source repository : https://github.com/Cyan4973/xxHash */ diff --git a/CSharp/USINGZ.TestApp/Program.cs b/CSharp/USINGZ.TestApp/Program.cs index a86af66e..58cfaacd 100644 --- a/CSharp/USINGZ.TestApp/Program.cs +++ b/CSharp/USINGZ.TestApp/Program.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 24 March 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System.Collections.Generic; diff --git a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs index f5cdc471..75356a62 100644 --- a/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs +++ b/CSharp/Utils/ClipFileIO/Clipper.FileIO.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 16 September 2022 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System; diff --git a/CSharp/Utils/Colors/Clipper.Colors.cs b/CSharp/Utils/Colors/Clipper.Colors.cs index 87f3174c..2c1566ab 100644 --- a/CSharp/Utils/Colors/Clipper.Colors.cs +++ b/CSharp/Utils/Colors/Clipper.Colors.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 11 February 2023 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System; diff --git a/CSharp/Utils/SVG/Clipper.SVG.Utils.cs b/CSharp/Utils/SVG/Clipper.SVG.Utils.cs index 3908abe8..92017171 100644 --- a/CSharp/Utils/SVG/Clipper.SVG.Utils.cs +++ b/CSharp/Utils/SVG/Clipper.SVG.Utils.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 16 September 2022 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System.IO; diff --git a/CSharp/Utils/SVG/Clipper.SVG.cs b/CSharp/Utils/SVG/Clipper.SVG.cs index 4d101ba2..601fa8b9 100644 --- a/CSharp/Utils/SVG/Clipper.SVG.cs +++ b/CSharp/Utils/SVG/Clipper.SVG.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 24 March 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System; diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs index f1d08ca9..05fbe00b 100644 --- a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp/Program.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 29 October 2023 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using Clipper2Dll; diff --git a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs index 6413cd15..753f1b92 100644 --- a/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs +++ b/DLL/TEST_APPS/CSharp_TestApps/CSharp_TestApp2/Program.cs @@ -1,9 +1,9 @@ /******************************************************************************* * Author : Angus Johnson * * Date : 14 August 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ using System.Diagnostics; diff --git a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas index 33a9b395..e5d372e7 100644 --- a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas +++ b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllCore.pas @@ -3,9 +3,9 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 12 August 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) interface diff --git a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas index 39b8519c..7559fc26 100644 --- a/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas +++ b/DLL/TEST_APPS/Delphi_TestApps/TestApp1/Clipper2DllSVG.pas @@ -3,9 +3,9 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 12 August 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) interface diff --git a/Delphi/Clipper2Lib/Clipper.Core.pas b/Delphi/Clipper2Lib/Clipper.Core.pas index c726bec3..77bc2761 100644 --- a/Delphi/Clipper2Lib/Clipper.Core.pas +++ b/Delphi/Clipper2Lib/Clipper.Core.pas @@ -3,11 +3,11 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 22 November 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : Core Clipper Library module * * Contains structures and functions used throughout the library * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) {$I Clipper.inc} diff --git a/Delphi/Clipper2Lib/Clipper.Engine.pas b/Delphi/Clipper2Lib/Clipper.Engine.pas index fa6bcf75..e66db765 100644 --- a/Delphi/Clipper2Lib/Clipper.Engine.pas +++ b/Delphi/Clipper2Lib/Clipper.Engine.pas @@ -3,10 +3,10 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 22 November 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) interface diff --git a/Delphi/Clipper2Lib/Clipper.Minkowski.pas b/Delphi/Clipper2Lib/Clipper.Minkowski.pas index bacb3ea2..d2e97055 100644 --- a/Delphi/Clipper2Lib/Clipper.Minkowski.pas +++ b/Delphi/Clipper2Lib/Clipper.Minkowski.pas @@ -5,7 +5,7 @@ * Date : 21 December 2023 * * Copyright : Angus Johnson 2010-2022 * * Purpose : Minkowski Addition and Difference * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) {$I Clipper.inc} diff --git a/Delphi/Clipper2Lib/Clipper.Offset.pas b/Delphi/Clipper2Lib/Clipper.Offset.pas index b487f270..e9c474ba 100644 --- a/Delphi/Clipper2Lib/Clipper.Offset.pas +++ b/Delphi/Clipper2Lib/Clipper.Offset.pas @@ -3,10 +3,10 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 22 January 2025 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) {$I Clipper.inc} @@ -378,7 +378,7 @@ procedure TClipperOffset.DoGroupOffset(group: TGroup); if (group.joinType = jtRound) or (group.endType = etRound) then begin // calculate the number of steps required to approximate a circle - // (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm) + // (see https://www.angusj.com/clipper2/Docs/Trigonometry.htm) // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision // will be relative to the size of the offset (delta). Obviously very //large offsets will almost always require much less precision. @@ -758,7 +758,7 @@ function IntersectPoint(const ln1a, ln1b, ln2a, ln2b: TPointD): TPointD; m1,b1,m2,b2: double; begin result := NullPointD; - //see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + //see https://paulbourke.net/geometry/pointlineplane/#i2l if (ln1B.X = ln1A.X) then begin if (ln2B.X = ln2A.X) then exit; //parallel lines diff --git a/Delphi/Clipper2Lib/Clipper.RectClip.pas b/Delphi/Clipper2Lib/Clipper.RectClip.pas index 91fbba66..a6898ba8 100644 --- a/Delphi/Clipper2Lib/Clipper.RectClip.pas +++ b/Delphi/Clipper2Lib/Clipper.RectClip.pas @@ -3,10 +3,10 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 5 July 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) interface diff --git a/Delphi/Clipper2Lib/Clipper.pas b/Delphi/Clipper2Lib/Clipper.pas index 06aa03ff..09a33f43 100644 --- a/Delphi/Clipper2Lib/Clipper.pas +++ b/Delphi/Clipper2Lib/Clipper.pas @@ -3,10 +3,10 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 7 May 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This module provides a simple interface to the Clipper Library * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) interface diff --git a/Delphi/Utils/Clipper.SVG.pas b/Delphi/Utils/Clipper.SVG.pas index 279d1211..146c54c8 100644 --- a/Delphi/Utils/Clipper.SVG.pas +++ b/Delphi/Utils/Clipper.SVG.pas @@ -3,10 +3,10 @@ (******************************************************************************* * Author : Angus Johnson * * Date : 21 March 2024 * -* Website : http://www.angusj.com * +* Website : https://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This module provides a very simple SVG Writer for Clipper2 * -* License : http://www.boost.org/LICENSE_1_0.txt * +* License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************) interface From 51bb2841fd716d1cdd6a8bd7f64a249253f8503b Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Sun, 26 Jan 2025 15:33:02 -0800 Subject: [PATCH 25/31] Installed Clipper2.pc:: Use canonical way to refer to inc and libdir (#937) Cmake already provides variables where the installed libraries and include directories end up, so use these for most compatible way of generating the package config. --- CPP/Clipper2.pc.cmakein | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CPP/Clipper2.pc.cmakein b/CPP/Clipper2.pc.cmakein index 5632c936..0a5d6bab 100644 --- a/CPP/Clipper2.pc.cmakein +++ b/CPP/Clipper2.pc.cmakein @@ -1,7 +1,7 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} -libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ -includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: Clipper2@PCFILE_LIB_SUFFIX@ Description: A Polygon Clipping and Offsetting library in C++ From baa7014321d32896e0a3aea689e57ff35dccfede Mon Sep 17 00:00:00 2001 From: nonwill <46510529+nonwill@users.noreply.github.com> Date: Mon, 27 Jan 2025 07:36:14 +0800 Subject: [PATCH 26/31] std::vector: Using emplace_back to replace push_back to improve performance (#939) --- .../include/clipper2/clipper.core.h | 14 ++-- .../include/clipper2/clipper.export.h | 20 +++--- CPP/Clipper2Lib/include/clipper2/clipper.h | 26 ++++---- .../include/clipper2/clipper.minkowski.h | 14 ++-- CPP/Clipper2Lib/src/clipper.engine.cpp | 50 +++++++------- CPP/Clipper2Lib/src/clipper.offset.cpp | 66 +++++++++---------- CPP/Clipper2Lib/src/clipper.rectclip.cpp | 26 ++++---- 7 files changed, 106 insertions(+), 110 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index 59372f55..cee2946f 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -331,10 +331,10 @@ namespace Clipper2Lib { Path result; result.reserve(4); - result.push_back(Point(left, top)); - result.push_back(Point(right, top)); - result.push_back(Point(right, bottom)); - result.push_back(Point(left, bottom)); + result.emplace_back(left, top); + result.emplace_back(right, top); + result.emplace_back(right, bottom); + result.emplace_back(left, bottom); return result; } @@ -618,13 +618,13 @@ namespace Clipper2Lib result.reserve(path.size()); typename Path::const_iterator path_iter = path.cbegin(); Point first_pt = *path_iter++, last_pt = first_pt; - result.push_back(first_pt); + result.emplace_back(first_pt); for (; path_iter != path.cend(); ++path_iter) { if (!NearEqual(*path_iter, last_pt, max_dist_sqrd)) { last_pt = *path_iter; - result.push_back(last_pt); + result.emplace_back(last_pt); } } if (!is_closed_path) return result; @@ -642,7 +642,7 @@ namespace Clipper2Lib for (typename Paths::const_iterator paths_citer = paths.cbegin(); paths_citer != paths.cend(); ++paths_citer) { - result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path)); + result.emplace_back(std::move(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path))); } return result; } diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index c942862b..6fa57bc4 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -392,9 +392,9 @@ static Path ConvertCPathToPathT(T* path) T x = *v++, y = *v++; #ifdef USINGZ z_type z = Reinterpret(*v++); - result.push_back(Point(x, y, z)); + result.emplace_back(x, y, z); #else - result.push_back(Point(x, y)); + result.emplace_back(x, y); #endif } return result; @@ -419,12 +419,12 @@ static Paths ConvertCPathsToPathsT(T* paths) T x = *v++, y = *v++; #ifdef USINGZ z_type z = Reinterpret(*v++); - path.push_back(Point(x, y, z)); + path.emplace_back(x, y, z); #else - path.push_back(Point(x, y)); + path.emplace_back(x, y); #endif } - result.push_back(path); + result.emplace_back(std::move(path)); } return result; } @@ -443,9 +443,9 @@ static Path64 ConvertCPathDToPath64WithScale(const CPathD path, double scale) double y = *v++ * scale; #ifdef USINGZ z_type z = Reinterpret(*v++); - result.push_back(Point64(x, y, z)); + result.emplace_back(x, y, z); #else - result.push_back(Point64(x, y)); + result.emplace_back(x, y); #endif } return result; @@ -471,12 +471,12 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) double y = *v++ * scale; #ifdef USINGZ z_type z = Reinterpret(*v++); - path.push_back(Point64(x, y, z)); + path.emplace_back(x, y, z); #else - path.push_back(Point64(x, y)); + path.emplace_back(x, y); #endif } - result.push_back(path); + result.emplace_back(std::move(path)); } return result; } diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.h b/CPP/Clipper2Lib/include/clipper2/clipper.h index e3873f30..b857c945 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.h @@ -272,14 +272,14 @@ namespace Clipper2Lib { inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths) { - paths.push_back(polypath.Polygon()); + paths.emplace_back(polypath.Polygon()); for (const auto& child : polypath) PolyPathToPaths64(*child, paths); } inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths) { - paths.push_back(polypath.Polygon()); + paths.emplace_back(polypath.Polygon()); for (const auto& child : polypath) PolyPathToPathsD(*child, paths); } @@ -348,9 +348,9 @@ namespace Clipper2Lib { result.reserve(array_size / 2); for (size_t i = 0; i < array_size; i +=2) #ifdef USINGZ - result.push_back( U{ an_array[i], an_array[i + 1], 0} ); + result.emplace_back( an_array[i], an_array[i + 1], 0 ); #else - result.push_back( U{ an_array[i], an_array[i + 1]} ); + result.emplace_back( an_array[i], an_array[i + 1] ); #endif } @@ -518,20 +518,20 @@ namespace Clipper2Lib { } prevIt = srcIt++; - dst.push_back(*prevIt); + dst.emplace_back(*prevIt); for (; srcIt != stop; ++srcIt) { if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1))) { prevIt = srcIt; - dst.push_back(*prevIt); + dst.emplace_back(*prevIt); } } if (is_open_path) - dst.push_back(*srcIt); + dst.emplace_back(*srcIt); else if (!IsCollinear(*prevIt, *stop, dst[0])) - dst.push_back(*stop); + dst.emplace_back(*stop); else { while (dst.size() > 2 && @@ -603,10 +603,10 @@ namespace Clipper2Lib { double dx = co, dy = si; Path result; result.reserve(steps); - result.push_back(Point(center.x + radiusX, static_cast(center.y))); + result.emplace_back(center.x + radiusX, static_cast(center.y)); for (size_t i = 1; i < steps; ++i) { - result.push_back(Point(center.x + radiusX * dx, center.y + radiusY * dy)); + result.emplace_back(center.x + radiusX * dx, center.y + radiusY * dy); double x = dx * co - dy * si; dy = dy * co + dx * si; dx = x; @@ -700,7 +700,7 @@ namespace Clipper2Lib { Path result; result.reserve(len); for (typename Path::size_type i = 0; i < len; ++i) - if (!flags[i]) result.push_back(path[i]); + if (!flags[i]) result.emplace_back(path[i]); return result; } @@ -711,7 +711,7 @@ namespace Clipper2Lib { Paths result; result.reserve(paths.size()); for (const auto& path : paths) - result.push_back(SimplifyPath(path, epsilon, isClosedPath)); + result.emplace_back(std::move(SimplifyPath(path, epsilon, isClosedPath))); return result; } @@ -749,7 +749,7 @@ namespace Clipper2Lib { result.reserve(len); for (typename Path::size_type i = 0; i < len; ++i) if (flags[i]) - result.push_back(path[i]); + result.emplace_back(path[i]); return result; } diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h b/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h index 0e1462a9..5962e4fa 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h @@ -35,7 +35,7 @@ namespace Clipper2Lib Path64 path2(pattern.size()); std::transform(pattern.cbegin(), pattern.cend(), path2.begin(), [p](const Point64& pt2) {return p + pt2; }); - tmp.push_back(path2); + tmp.emplace_back(std::move(path2)); } } else @@ -45,7 +45,7 @@ namespace Clipper2Lib Path64 path2(pattern.size()); std::transform(pattern.cbegin(), pattern.cend(), path2.begin(), [p](const Point64& pt2) {return p - pt2; }); - tmp.push_back(path2); + tmp.emplace_back(std::move(path2)); } } @@ -59,14 +59,14 @@ namespace Clipper2Lib Path64 quad; quad.reserve(4); { - quad.push_back(tmp[g][h]); - quad.push_back(tmp[i][h]); - quad.push_back(tmp[i][j]); - quad.push_back(tmp[g][j]); + quad.emplace_back(tmp[g][h]); + quad.emplace_back(tmp[i][h]); + quad.emplace_back(tmp[i][j]); + quad.emplace_back(tmp[g][j]); }; if (!IsPositive(quad)) std::reverse(quad.begin(), quad.end()); - result.push_back(quad); + result.emplace_back(std::move(quad)); h = j; } g = i; diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index c9fae4c6..1da5aad7 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -564,7 +564,7 @@ namespace Clipper2Lib { while (op2->next != op && ((op2->pt.x == op2->next->pt.x && op2->pt.x == op2->prev->pt.x) || (op2->pt.y == op2->next->pt.y && op2->pt.y == op2->prev->pt.y))) op2 = op2->next; - result.push_back(op2->pt); + result.emplace_back(op2->pt); OutPt* prevOp = op2; op2 = op2->next; while (op2 != op) @@ -572,7 +572,7 @@ namespace Clipper2Lib { if ((op2->pt.x != op2->next->pt.x || op2->pt.x != prevOp->pt.x) && (op2->pt.y != op2->next->pt.y || op2->pt.y != prevOp->pt.y)) { - result.push_back(op2->pt); + result.emplace_back(op2->pt); prevOp = op2; } op2 = op2->next; @@ -611,7 +611,7 @@ namespace Clipper2Lib { if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); - list.push_back(std::make_unique (&vert, polytype, is_open)); + list.emplace_back(std::make_unique (&vert, polytype, is_open)); } void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, @@ -728,7 +728,7 @@ namespace Clipper2Lib { if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); - minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); + minima_list_.emplace_back(std::make_unique (&vert, polytype, is_open)); } void ReuseableDataContainer64::AddPaths(const Paths64& paths, @@ -836,9 +836,7 @@ namespace Clipper2Lib { void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open) { - Paths64 tmp; - tmp.push_back(path); - AddPaths(tmp, polytype, is_open); + AddPaths(Paths64(1, path), polytype, is_open); } void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open) @@ -857,7 +855,7 @@ namespace Clipper2Lib { LocalMinimaList::const_iterator i; for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i) { - minima_list_.push_back(std::make_unique ((*i)->vertex, (*i)->polytype, (*i)->is_open)); + minima_list_.emplace_back(std::make_unique ((*i)->vertex, (*i)->polytype, (*i)->is_open)); if ((*i)->is_open) has_open_paths_ = true; } } @@ -910,7 +908,7 @@ namespace Clipper2Lib { if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); - minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); + minima_list_.emplace_back(std::make_unique (&vert, polytype, is_open)); } bool ClipperBase::IsContributingClosed(const Active& e) const @@ -1494,7 +1492,7 @@ namespace Clipper2Lib { { OutRec* result = new OutRec(); result->idx = outrec_list_.size(); - outrec_list_.push_back(result); + outrec_list_.emplace_back(result); result->pts = nullptr; result->owner = nullptr; result->polypath = nullptr; @@ -1637,12 +1635,12 @@ namespace Clipper2Lib { if (Path1InsidePath2(prevOp, newOp)) { newOr->splits = new OutRecList(); - newOr->splits->push_back(outrec); + newOr->splits->emplace_back(outrec); } else { if (!outrec->splits) outrec->splits = new OutRecList(); - outrec->splits->push_back(newOr); + outrec->splits->emplace_back(newOr); } } } @@ -2245,7 +2243,7 @@ namespace Clipper2Lib { HorzJoin join = HorzJoin( DuplicateOp(hs1->left_op, true), DuplicateOp(hs2->left_op, false)); - horz_join_list_.push_back(join); + horz_join_list_.emplace_back(join); } else { @@ -2258,7 +2256,7 @@ namespace Clipper2Lib { HorzJoin join = HorzJoin( DuplicateOp(hs2->left_op, true), DuplicateOp(hs1->left_op, false)); - horz_join_list_.push_back(join); + horz_join_list_.emplace_back(join); } } } @@ -2270,7 +2268,7 @@ namespace Clipper2Lib { if (!toOr->splits) toOr->splits = new OutRecList(); OutRecList::iterator orIter = fromOr->splits->begin(); for (; orIter != fromOr->splits->end(); ++orIter) - toOr->splits->push_back(*orIter); + toOr->splits->emplace_back(*orIter); fromOr->splits->clear(); } @@ -2323,7 +2321,7 @@ namespace Clipper2Lib { or2->owner = or1->owner; if (!or1->splits) or1->splits = new OutRecList(); - or1->splits->push_back(or2); + or1->splits->emplace_back(or2); } else or2->owner = or1; @@ -2382,7 +2380,7 @@ namespace Clipper2Lib { else ip.x = TopX(e2, ip.y); } } - intersect_nodes_.push_back(IntersectNode(&e1, &e2, ip)); + intersect_nodes_.emplace_back(&e1, &e2, ip); } bool ClipperBase::BuildIntersectList(const int64_t top_y) @@ -2503,7 +2501,7 @@ namespace Clipper2Lib { void ClipperBase::AddTrialHorzJoin(OutPt* op) { if (op->outrec->is_open) return; - horz_seg_list_.push_back(HorzSegment(op)); + horz_seg_list_.emplace_back(op); } bool ClipperBase::ResetHorzDirection(const Active& horz, @@ -2905,14 +2903,14 @@ namespace Clipper2Lib { lastPt = op->pt; op2 = op->next; } - path.push_back(lastPt); + path.emplace_back(lastPt); while (op2 != op) { if (op2->pt != lastPt) { lastPt = op2->pt; - path.push_back(lastPt); + path.emplace_back(lastPt); } if (reverse) op2 = op2->prev; @@ -3037,7 +3035,7 @@ namespace Clipper2Lib { { Path64 path; if (BuildPath64(outrec->pts, reverse_solution_, true, path)) - open_paths.push_back(path); + open_paths.emplace_back(std::move(path)); continue; } @@ -3066,9 +3064,9 @@ namespace Clipper2Lib { op2 = op->next; } #ifdef USINGZ - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z)); + path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z); #else - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); + path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale); #endif while (op2 != op) @@ -3077,9 +3075,9 @@ namespace Clipper2Lib { { lastPt = op2->pt; #ifdef USINGZ - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z)); + path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z); #else - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); + path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale); #endif } @@ -3143,7 +3141,7 @@ namespace Clipper2Lib { { PathD path; if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_)) - open_paths.push_back(path); + open_paths.emplace_back(std::move(path)); continue; } diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 5263cc07..0c30d700 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -161,15 +161,13 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_) { - Paths64 paths; - paths.push_back(path); - AddPaths(paths, jt_, et_); + groups_.emplace_back(Paths64(1, path), jt_, et_); } void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_) { if (paths.size() == 0) return; - groups_.push_back(Group(paths, jt_, et_)); + groups_.emplace_back(paths, jt_, et_); } void ClipperOffset::BuildNormals(const Path64& path) @@ -179,8 +177,8 @@ void ClipperOffset::BuildNormals(const Path64& path) if (path.size() == 0) return; Path64::const_iterator path_iter, path_stop_iter = --path.cend(); for (path_iter = path.cbegin(); path_iter != path_stop_iter; ++path_iter) - norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1))); - norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin()))); + norms.emplace_back(GetUnitNormal(*path_iter,*(path_iter +1))); + norms.emplace_back(GetUnitNormal(*path_stop_iter, *(path.cbegin()))); } void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k) @@ -207,8 +205,8 @@ void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k) pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y); #endif } - path_out.push_back(Point64(pt1)); - path_out.push_back(Point64(pt2)); + path_out.emplace_back(pt1); + path_out.emplace_back(pt2); } void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k) @@ -237,17 +235,17 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k) PointD pt = ptQ; GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt); //get the second intersect point through reflecion - path_out.push_back(Point64(ReflectPoint(pt, ptQ))); - path_out.push_back(Point64(pt)); + path_out.emplace_back(ReflectPoint(pt, ptQ)); + path_out.emplace_back(pt); } else { PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_); PointD pt = ptQ; GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt); - path_out.push_back(Point64(pt)); + path_out.emplace_back(pt); //get the second intersect point through reflecion - path_out.push_back(Point64(ReflectPoint(pt, ptQ))); + path_out.emplace_back(ReflectPoint(pt, ptQ)); } } @@ -255,14 +253,14 @@ void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a { double q = group_delta_ / (cos_a + 1); #ifdef USINGZ - path_out.push_back(Point64( + path_out.emplace_back( path[j].x + (norms[k].x + norms[j].x) * q, path[j].y + (norms[k].y + norms[j].y) * q, - path[j].z)); + path[j].z); #else - path_out.push_back(Point64( + path_out.emplace_back( path[j].x + (norms[k].x + norms[j].x) * q, - path[j].y + (norms[k].y + norms[j].y) * q)); + path[j].y + (norms[k].y + norms[j].y) * q); #endif } @@ -286,9 +284,9 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle if (j == k) offsetVec.Negate(); #ifdef USINGZ - path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z)); + path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z); #else - path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y)); + path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y); #endif int steps = static_cast(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456 for (int i = 1; i < steps; ++i) // ie 1 less than steps @@ -296,12 +294,12 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y, offsetVec.x * step_sin_ + offsetVec.y * step_cos_); #ifdef USINGZ - path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z)); + path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z); #else - path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y)); + path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y); #endif } - path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); + path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_)); } void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size_t k) @@ -325,7 +323,7 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size } if (std::fabs(group_delta_) <= floating_point_tolerance) { - path_out.push_back(path[j]); + path_out.emplace_back(path[j]); return; } @@ -337,13 +335,13 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size // will be removed later by the finishing union operation. This is also the best way // to ensure that path reversals (ie over-shrunk paths) are removed. #ifdef USINGZ - path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); - path_out.push_back(path[j]); // (#405, #873, #916) - path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); + path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_), path[j].z); + path_out.emplace_back(path[j]); // (#405, #873, #916) + path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_), path[j].z); #else - path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); - path_out.push_back(path[j]); // (#405, #873, #916) - path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); + path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_)); + path_out.emplace_back(path[j]); // (#405, #873, #916) + path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_)); #endif } else if (cos_a > 0.999 && join_type_ != JoinType::Round) @@ -370,7 +368,7 @@ void ClipperOffset::OffsetPolygon(Group& group, const Path64& path) path_out.clear(); for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j) OffsetPoint(group, path, j, k); - solution->push_back(path_out); + solution->emplace_back(path_out); } void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path) @@ -381,7 +379,7 @@ void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path) //rebuild normals std::reverse(norms.begin(), norms.end()); - norms.push_back(norms[0]); + norms.emplace_back(norms[0]); norms.erase(norms.begin()); NegatePath(norms); @@ -394,7 +392,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0); if (std::fabs(group_delta_) <= floating_point_tolerance) - path_out.push_back(path[0]); + path_out.emplace_back(path[0]); else { switch (end_type_) @@ -426,7 +424,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) group_delta_ = deltaCallback64_(path, norms, highI, highI); if (std::fabs(group_delta_) <= floating_point_tolerance) - path_out.push_back(path[highI]); + path_out.emplace_back(path[highI]); else { switch (end_type_) @@ -445,7 +443,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) for (size_t j = highI -1, k = highI; j > 0; k = j, --j) OffsetPoint(group, path, j, k); - solution->push_back(path_out); + solution->emplace_back(path_out); } void ClipperOffset::DoGroupOffset(Group& group) @@ -519,7 +517,7 @@ void ClipperOffset::DoGroupOffset(Group& group) #endif } - solution->push_back(path_out); + solution->emplace_back(path_out); continue; } // end of offsetting a single point diff --git a/CPP/Clipper2Lib/src/clipper.rectclip.cpp b/CPP/Clipper2Lib/src/clipper.rectclip.cpp index b7227204..620e2814 100644 --- a/CPP/Clipper2Lib/src/clipper.rectclip.cpp +++ b/CPP/Clipper2Lib/src/clipper.rectclip.cpp @@ -282,7 +282,7 @@ namespace Clipper2Lib { { if (op->edge) return; op->edge = &edge; - edge.push_back(op); + edge.emplace_back(op); } inline void UncoupleEdge(OutPt2* op) @@ -328,7 +328,7 @@ namespace Clipper2Lib { result->pt = pt; result->next = result; result->prev = result; - results_.push_back(result); + results_.emplace_back(result); } else { @@ -489,7 +489,7 @@ namespace Clipper2Lib { { bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_); do { - start_locs_.push_back(prev); + start_locs_.emplace_back(prev); prev = GetAdjacentLocation(prev, isClockw); } while (prev != loc); crossing_loc = crossing_prev; // still not crossed @@ -514,7 +514,7 @@ namespace Clipper2Lib { if (first_cross_ == Location::Inside) { first_cross_ = crossing_loc; - start_locs_.push_back(prev); + start_locs_.emplace_back(prev); } else if (prev != crossing_loc) { @@ -536,7 +536,7 @@ namespace Clipper2Lib { if (first_cross_ == Location::Inside) { first_cross_ = loc; - start_locs_.push_back(prev); + start_locs_.emplace_back(prev); } loc = crossing_loc; @@ -750,7 +750,7 @@ namespace Clipper2Lib { if (!isRejoining) { size_t new_idx = results_.size(); - results_.push_back(p1a); + results_.emplace_back(p1a); SetNewOwner(p1a, new_idx); } @@ -861,11 +861,11 @@ namespace Clipper2Lib { if (!op2) return Path64(); Path64 result; - result.push_back(op->pt); + result.emplace_back(op->pt); op2 = op->next; while (op2 != op) { - result.push_back(op2->pt); + result.emplace_back(op2->pt); op2 = op2->next; } return result; @@ -885,7 +885,7 @@ namespace Clipper2Lib { else if (rect_.Contains(path_bounds_)) { // the path must be completely inside rect_ - result.push_back(path); + result.emplace_back(path); continue; } @@ -898,7 +898,7 @@ namespace Clipper2Lib { { Path64 tmp = GetPath(op); if (!tmp.empty()) - result.emplace_back(tmp); + result.emplace_back(std::move(tmp)); } //clean up after every loop @@ -930,7 +930,7 @@ namespace Clipper2Lib { { Path64 tmp = GetPath(op); if (!tmp.empty()) - result.emplace_back(tmp); + result.emplace_back(std::move(tmp)); } results_.clear(); @@ -1015,11 +1015,11 @@ namespace Clipper2Lib { Path64 result; if (!op || op == op->next) return result; op = op->next; // starting at path beginning - result.push_back(op->pt); + result.emplace_back(op->pt); OutPt2 *op2 = op->next; while (op2 != op) { - result.push_back(op2->pt); + result.emplace_back(op2->pt); op2 = op2->next; } return result; From e39a44f51606369725e9ce6830573290863bea91 Mon Sep 17 00:00:00 2001 From: angusj Date: Mon, 27 Jan 2025 10:00:03 +1000 Subject: [PATCH 27/31] Version 1.5.1 --- CPP/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPP/CMakeLists.txt b/CPP/CMakeLists.txt index 6f40531a..6fc97953 100644 --- a/CPP/CMakeLists.txt +++ b/CPP/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(Clipper2 VERSION 1.5.0 LANGUAGES C CXX) +project(Clipper2 VERSION 1.5.1 LANGUAGES C CXX) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 17) From d6baa5229734d2174909ddb018c4b9d59557e646 Mon Sep 17 00:00:00 2001 From: nonwill <46510529+nonwill@users.noreply.github.com> Date: Mon, 27 Jan 2025 08:01:27 +0800 Subject: [PATCH 28/31] Fix header including to avoid confusion: header only included where they are needed (#940) --- CPP/Clipper2Lib/include/clipper2/clipper.core.h | 7 ++----- CPP/Clipper2Lib/include/clipper2/clipper.engine.h | 8 +------- CPP/Clipper2Lib/include/clipper2/clipper.export.h | 3 +-- CPP/Clipper2Lib/include/clipper2/clipper.h | 5 +---- CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h | 3 --- CPP/Clipper2Lib/include/clipper2/clipper.offset.h | 1 + CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h | 4 +--- CPP/Clipper2Lib/src/clipper.engine.cpp | 8 +------- CPP/Clipper2Lib/src/clipper.offset.cpp | 1 - CPP/Clipper2Lib/src/clipper.rectclip.cpp | 1 - 10 files changed, 8 insertions(+), 33 deletions(-) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.core.h b/CPP/Clipper2Lib/include/clipper2/clipper.core.h index cee2946f..ab71aeb0 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.core.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.core.h @@ -10,17 +10,14 @@ #ifndef CLIPPER_CORE_H #define CLIPPER_CORE_H +#include "clipper2/clipper.version.h" #include -#include -#include #include #include #include #include -#include #include -#include -#include "clipper2/clipper.version.h" +#include namespace Clipper2Lib { diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h index 76317f2d..f4e1e183 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.engine.h @@ -10,17 +10,11 @@ #ifndef CLIPPER_ENGINE_H #define CLIPPER_ENGINE_H -#include -#include //#541 -#include +#include "clipper2/clipper.core.h" #include -#include #include -#include #include -#include "clipper2/clipper.core.h" - namespace Clipper2Lib { struct Scanline; diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.export.h b/CPP/Clipper2Lib/include/clipper2/clipper.export.h index 6fa57bc4..79856e2e 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.export.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.export.h @@ -112,12 +112,11 @@ the four vertices that define the two segments that are intersecting. #ifndef CLIPPER2_EXPORT_H #define CLIPPER2_EXPORT_H -#include -#include #include "clipper2/clipper.core.h" #include "clipper2/clipper.engine.h" #include "clipper2/clipper.offset.h" #include "clipper2/clipper.rectclip.h" +#include namespace Clipper2Lib { diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.h b/CPP/Clipper2Lib/include/clipper2/clipper.h index b857c945..b75bbd35 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.h @@ -10,15 +10,12 @@ #ifndef CLIPPER_H #define CLIPPER_H -#include -#include -#include - #include "clipper2/clipper.core.h" #include "clipper2/clipper.engine.h" #include "clipper2/clipper.offset.h" #include "clipper2/clipper.minkowski.h" #include "clipper2/clipper.rectclip.h" +#include namespace Clipper2Lib { diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h b/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h index 5962e4fa..3a85ba5d 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.minkowski.h @@ -10,9 +10,6 @@ #ifndef CLIPPER_MINKOWSKI_H #define CLIPPER_MINKOWSKI_H -#include -#include -#include #include "clipper2/clipper.core.h" namespace Clipper2Lib diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h index 0125dc2d..90ccd5ec 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.offset.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.offset.h @@ -12,6 +12,7 @@ #include "clipper.core.h" #include "clipper.engine.h" +#include namespace Clipper2Lib { diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h index 40cf3632..e7cf2f45 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.rectclip.h @@ -10,10 +10,8 @@ #ifndef CLIPPER_RECTCLIP_H #define CLIPPER_RECTCLIP_H -#include -#include -#include #include "clipper2/clipper.core.h" +#include namespace Clipper2Lib { diff --git a/CPP/Clipper2Lib/src/clipper.engine.cpp b/CPP/Clipper2Lib/src/clipper.engine.cpp index 1da5aad7..927e7a75 100644 --- a/CPP/Clipper2Lib/src/clipper.engine.cpp +++ b/CPP/Clipper2Lib/src/clipper.engine.cpp @@ -7,15 +7,9 @@ * License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ -#include -#include -#include -#include -#include -#include - #include "clipper2/clipper.engine.h" #include "clipper2/clipper.h" +#include // https://github.com/AngusJohnson/Clipper2/discussions/334 // #discussioncomment-4248602 diff --git a/CPP/Clipper2Lib/src/clipper.offset.cpp b/CPP/Clipper2Lib/src/clipper.offset.cpp index 0c30d700..23e405ed 100644 --- a/CPP/Clipper2Lib/src/clipper.offset.cpp +++ b/CPP/Clipper2Lib/src/clipper.offset.cpp @@ -7,7 +7,6 @@ * License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ -#include #include "clipper2/clipper.h" #include "clipper2/clipper.offset.h" diff --git a/CPP/Clipper2Lib/src/clipper.rectclip.cpp b/CPP/Clipper2Lib/src/clipper.rectclip.cpp index 620e2814..a5d66df4 100644 --- a/CPP/Clipper2Lib/src/clipper.rectclip.cpp +++ b/CPP/Clipper2Lib/src/clipper.rectclip.cpp @@ -7,7 +7,6 @@ * License : https://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ -#include #include "clipper2/clipper.h" #include "clipper2/clipper.rectclip.h" From 6901921c4be75126d1de60bfd24bd86a61319fd0 Mon Sep 17 00:00:00 2001 From: angusj Date: Mon, 27 Jan 2025 10:10:58 +1000 Subject: [PATCH 29/31] Version 1.5.2 --- CPP/CMakeLists.txt | 2 +- CPP/Clipper2Lib/include/clipper2/clipper.version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CPP/CMakeLists.txt b/CPP/CMakeLists.txt index 6fc97953..fe9e518b 100644 --- a/CPP/CMakeLists.txt +++ b/CPP/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.15) -project(Clipper2 VERSION 1.5.1 LANGUAGES C CXX) +project(Clipper2 VERSION 1.5.2 LANGUAGES C CXX) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 17) diff --git a/CPP/Clipper2Lib/include/clipper2/clipper.version.h b/CPP/Clipper2Lib/include/clipper2/clipper.version.h index 130b4c97..661b0f1c 100644 --- a/CPP/Clipper2Lib/include/clipper2/clipper.version.h +++ b/CPP/Clipper2Lib/include/clipper2/clipper.version.h @@ -1,6 +1,6 @@ #ifndef CLIPPER_VERSION_H #define CLIPPER_VERSION_H -constexpr auto CLIPPER2_VERSION = "1.5.0"; +constexpr auto CLIPPER2_VERSION = "1.5.2"; #endif // CLIPPER_VERSION_H From 97533dd94327d2fc02ac918fa2af85134f6aaab6 Mon Sep 17 00:00:00 2001 From: syunya_tolkunaga Date: Wed, 29 Jan 2025 16:18:18 +0900 Subject: [PATCH 30/31] Rename namespace --- CSharp/Clipper2Lib.Tests/Tests1/Tests/TestOffset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CSharp/Clipper2Lib.Tests/Tests1/Tests/TestOffset.cs b/CSharp/Clipper2Lib.Tests/Tests1/Tests/TestOffset.cs index 54522c74..cd9bfef0 100644 --- a/CSharp/Clipper2Lib.Tests/Tests1/Tests/TestOffset.cs +++ b/CSharp/Clipper2Lib.Tests/Tests1/Tests/TestOffset.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Clipper2Lib.UnitTests +namespace Drecom.Clipper2Lib.UnitTests { [TestClass] From c2905b3c6ddeaa963beade27f72b708f092047de Mon Sep 17 00:00:00 2001 From: syunya_tolkunaga Date: Wed, 29 Jan 2025 16:20:02 +0900 Subject: [PATCH 31/31] Update action --- .github/workflows/actions_csharp.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/actions_csharp.yml b/.github/workflows/actions_csharp.yml index 6a54caa2..0f9555f4 100644 --- a/.github/workflows/actions_csharp.yml +++ b/.github/workflows/actions_csharp.yml @@ -11,15 +11,15 @@ jobs: run: working-directory: ./CSharp steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Copy Clipper2Lib to USINGZ directory run: cp Clipper2Lib/*.cs USINGZ/ - name: Setup .NET Core - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | - 3.1.x 6.0.x + 8.0.x - name: Restore run: dotnet restore - name: Build