diff --git a/resources/images/param_2dhoneycomb.svg b/resources/images/param_2dhoneycomb.svg new file mode 100644 index 0000000000..5c9112b38a --- /dev/null +++ b/resources/images/param_2dhoneycomb.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resources/profiles/OrcaFilamentLibrary.json b/resources/profiles/OrcaFilamentLibrary.json index 690eaded8f..9ed681e6a9 100644 --- a/resources/profiles/OrcaFilamentLibrary.json +++ b/resources/profiles/OrcaFilamentLibrary.json @@ -376,6 +376,22 @@ "name": "Overture ABS Basic @base", "sub_path": "filament/Overture/Overture ABS Basic @base.json" }, + { + "name": "Valment PLA @base", + "sub_path": "filament/Valment/Valment PLA @base.json" + }, + { + "name": "Valment PLA Silk @base", + "sub_path": "filament/Valment/Valment PLA Silk @base.json" + }, + { + "name": "Valment PLA-CF @base", + "sub_path": "filament/Valment/Valment PLA-CF @base.json" + }, + { + "name": "Valment PLA Galaxy @base", + "sub_path": "filament/Valment/Valment PLA Galaxy @base.json" + }, { "name": "PolyLite PLA @base", "sub_path": "filament/Polymaker/PolyLite PLA @base.json" @@ -700,6 +716,22 @@ "name": "Overture ABS Basic @System", "sub_path": "filament/Overture/Overture ABS Basic @System.json" }, + { + "name": "Valment PLA @System", + "sub_path": "filament/Valment/Valment PLA @System.json" + }, + { + "name": "Valment PLA Silk @System", + "sub_path": "filament/Valment/Valment PLA Silk @System.json" + }, + { + "name": "Valment PLA-CF @System", + "sub_path": "filament/Valment/Valment PLA-CF @System.json" + }, + { + "name": "Valment PLA Galaxy @System", + "sub_path": "filament/Valment/Valment PLA Galaxy @System.json" + }, { "name": "PolyLite Dual PLA @System", "sub_path": "filament/Polymaker/PolyLite Dual PLA @System.json" diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA @System.json new file mode 100644 index 0000000000..ff2ce6a1c2 --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA @System.json @@ -0,0 +1,9 @@ +{ + "type": "filament", + "name": "Valment PLA @System", + "inherits": "Valment PLA @base", + "from": "system", + "setting_id": "VLMNT_01", + "instantiation": "true", + "compatible_printers": [] +} \ No newline at end of file diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA @base.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA @base.json new file mode 100644 index 0000000000..fb7e29e3fb --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA @base.json @@ -0,0 +1,27 @@ +{ + "type": "filament", + "name": "Valment PLA @base", + "inherits": "fdm_filament_pla", + "from": "system", + "filament_id": "VLMNT01", + "instantiation": "false", + "filament_cost": ["670"], + "filament_type": ["PLA"], + "default_filament_colour": ["#124943"], + "filament_density": ["1.24"], + "filament_flow_ratio": ["0.98"], + "filament_max_volumetric_speed": ["12"], + "filament_vendor": ["Valment"], + "slow_down_layer_time": ["6"], + "nozzle_temperature": ["220"], + "nozzle_temperature_initial_layer": ["225"], + "nozzle_temperature_range_low": ["200"], + "nozzle_temperature_range_high": ["250"], + "filament_notes": [ + "Bu filament ayarları GlauTech tarafından oluşturulmuştur. Filamentin daha verimli çalışması için, slicerdaki kalibrasyon ayarlarının tek tek yapılması önemlidir. Kalibrasyon ayarları için, GlauTech yotube kanalından destek alabilirsiniz.\n\nBoş Makara Ağırlığı: 150gr" + ], + "slow_down_min_speed": ["20"], + "complete_print_exhaust_fan_speed": ["80"], + "during_print_exhaust_fan_speed": ["60"], + "additional_cooling_fan_speed": ["100"] +} diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Galaxy @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Galaxy @System.json new file mode 100644 index 0000000000..b2e053d2b3 --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Galaxy @System.json @@ -0,0 +1,9 @@ +{ + "type": "filament", + "name": "Valment PLA Galaxy @System", + "inherits": "Valment PLA Galaxy @base", + "from": "system", + "setting_id": "VLMNT_04", + "instantiation": "true", + "compatible_printers": [] +} \ No newline at end of file diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Galaxy @base.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Galaxy @base.json new file mode 100644 index 0000000000..e6dc2548a3 --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Galaxy @base.json @@ -0,0 +1,27 @@ +{ + "type": "filament", + "name": "Valment PLA Galaxy @base", + "inherits": "fdm_filament_pla", + "from": "system", + "filament_id": "VLMNT04", + "instantiation": "false", + "filament_cost": ["850"], + "filament_type": ["PLA"], + "default_filament_colour": ["#124943"], + "filament_density": ["1.24"], + "filament_flow_ratio": ["0.98"], + "filament_max_volumetric_speed": ["12"], + "filament_vendor": ["Valment"], + "slow_down_layer_time": ["6"], + "nozzle_temperature": ["240"], + "nozzle_temperature_initial_layer": ["245"], + "nozzle_temperature_range_low": ["220"], + "nozzle_temperature_range_high": ["250"], + "filament_notes": [ + "Bu filament ayarları GlauTech tarafından oluşturulmuştur. Filamentin daha verimli çalışması için, slicerdaki kalibrasyon ayarlarının tek tek yapılması önemlidir. Kalibrasyon ayarları için, GlauTech yotube kanalından destek alabilirsiniz.\n\nGalaxy filamentlerimizde; \n0.2mm nozul kullanılmaması,\n0.16mm katman yüksekliği altına inilmemesi önerilir.\n\nBoş Makara Ağırlığı: 150gr" + ], + "slow_down_min_speed": ["20"], + "complete_print_exhaust_fan_speed": ["80"], + "during_print_exhaust_fan_speed": ["60"], + "additional_cooling_fan_speed": ["100"] +} diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Silk @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Silk @System.json new file mode 100644 index 0000000000..8c842d388b --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Silk @System.json @@ -0,0 +1,9 @@ +{ + "type": "filament", + "name": "Valment PLA Silk @System", + "inherits": "Valment PLA Silk @base", + "from": "system", + "setting_id": "VLMNT_02", + "instantiation": "true", + "compatible_printers": [] +} \ No newline at end of file diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Silk @base.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Silk @base.json new file mode 100644 index 0000000000..11fce51d0c --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA Silk @base.json @@ -0,0 +1,27 @@ +{ + "type": "filament", + "name": "Valment PLA Silk @base", + "inherits": "fdm_filament_pla", + "from": "system", + "filament_id": "VLMNT02", + "instantiation": "false", + "filament_cost": ["710"], + "filament_type": ["PLA"], + "filament_density": ["1.24"], + "default_filament_colour": ["#124943"], + "filament_flow_ratio": ["0.98"], + "filament_max_volumetric_speed": ["7.5"], + "filament_vendor": ["Valment"], + "slow_down_layer_time": ["6"], + "nozzle_temperature": ["240"], + "nozzle_temperature_initial_layer": ["245"], + "nozzle_temperature_range_low": ["220"], + "nozzle_temperature_range_high": ["250"], + "filament_notes": [ + "Bu filament ayarları GlauTech tarafından oluşturulmuştur. Filamentin daha verimli çalışması için, slicerdaki kalibrasyon ayarlarının tek tek yapılması önemlidir. Kalibrasyon ayarları için, GlauTech yotube kanalından destek alabilirsiniz.\n\nBoş Makara Ağırlığı: 150gr" + ], + "slow_down_min_speed": ["20"], + "complete_print_exhaust_fan_speed": ["80"], + "during_print_exhaust_fan_speed": ["60"], + "additional_cooling_fan_speed": ["100"] +} diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA-CF @System.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA-CF @System.json new file mode 100644 index 0000000000..23980efd55 --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA-CF @System.json @@ -0,0 +1,9 @@ +{ + "type": "filament", + "name": "Valment PLA-CF @System", + "inherits": "Valment PLA-CF @base", + "from": "system", + "setting_id": "VLMNT_03", + "instantiation": "true", + "compatible_printers": [] +} \ No newline at end of file diff --git a/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA-CF @base.json b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA-CF @base.json new file mode 100644 index 0000000000..cc9f0448fb --- /dev/null +++ b/resources/profiles/OrcaFilamentLibrary/filament/Valment/Valment PLA-CF @base.json @@ -0,0 +1,27 @@ +{ + "type": "filament", + "name": "Valment PLA-CF @base", + "inherits": "fdm_filament_pla", + "from": "system", + "filament_id": "VLMNT03", + "instantiation": "false", + "filament_cost": ["1400"], + "filament_type": ["PLA-CF"], + "default_filament_colour": ["#124943"], + "filament_density": ["1.24"], + "filament_flow_ratio": ["0.98"], + "filament_max_volumetric_speed": ["12"], + "filament_vendor": ["Valment"], + "slow_down_layer_time": ["6"], + "nozzle_temperature": ["225"], + "nozzle_temperature_initial_layer": ["230"], + "nozzle_temperature_range_low": ["200"], + "nozzle_temperature_range_high": ["250"], + "filament_notes": [ + "Bu filament ayarları GlauTech tarafından oluşturulmuştur. Filamentin daha verimli çalışması için, slicerdaki kalibrasyon ayarlarının tek tek yapılması önemlidir. Kalibrasyon ayarları için, GlauTech yotube kanalından destek alabilirsiniz.\n\nBoş Makara Ağırlığı: 150gr" + ], + "slow_down_min_speed": ["20"], + "complete_print_exhaust_fan_speed": ["80"], + "during_print_exhaust_fan_speed": ["60"], + "additional_cooling_fan_speed": ["100"] +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 91eaee8465..60259d3704 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,11 +1,13 @@ cmake_minimum_required(VERSION 3.13) project(OrcaSlicer-native) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + add_subdirectory(build-utils) add_subdirectory(admesh) # add_subdirectory(avrdude) add_subdirectory(clipper) -add_subdirectory(clipper2) add_subdirectory(miniz) add_subdirectory(minilzo) add_subdirectory(glu-libtess) diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt index f625088209..1c1cfd5a7c 100644 --- a/src/clipper/CMakeLists.txt +++ b/src/clipper/CMakeLists.txt @@ -9,6 +9,4 @@ add_library(clipper STATIC clipper_z.hpp ) -if(SLIC3R_PROFILE) - target_link_libraries(clipper Shiny) -endif() +target_link_libraries(clipper TBB::tbb TBB::tbbmalloc) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index 370bf934d3..1f16446ac8 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.4.2 * +* Date : 27 February 2017 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * +* Copyright : Angus Johnson 2010-2017 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -50,17 +50,6 @@ #include #include -// Profiling support using the Shiny intrusive profiler -//#define CLIPPERLIB_PROFILE -#if defined(SLIC3R_PROFILE) && defined(CLIPPERLIB_PROFILE) - #include - #define CLIPPERLIB_PROFILE_FUNC() PROFILE_FUNC() - #define CLIPPERLIB_PROFILE_BLOCK(name) PROFILE_BLOCK(name) -#else - #define CLIPPERLIB_PROFILE_FUNC() - #define CLIPPERLIB_PROFILE_BLOCK(name) -#endif - #ifdef CLIPPERLIB_NAMESPACE_PREFIX namespace CLIPPERLIB_NAMESPACE_PREFIX { #endif // CLIPPERLIB_NAMESPACE_PREFIX @@ -84,25 +73,6 @@ static int const Skip = -2; //edge that would otherwise close a path #define TOLERANCE (1.0e-20) #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) -// Output polygon. -struct OutRec { - int Idx; - bool IsHole; - bool IsOpen; - //The 'FirstLeft' field points to another OutRec that contains or is the - //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is - //parsed left from the current edge (owning OutRec) until the owner OutRec - //is found. This field simplifies sorting the polygons into a tree structure - //which reflects the parent/child relationships of all polygons. - //This field should be renamed Parent, and will be later. - OutRec *FirstLeft; - // Used only by void Clipper::BuildResult2(PolyTree& polytree) - PolyNode *PolyNd; - // Linked list of output points, dynamically allocated. - OutPt *Pts; - OutPt *BottomPt; -}; - //------------------------------------------------------------------------------ inline IntPoint IntPoint2d(cInt x, cInt y) @@ -114,9 +84,25 @@ inline IntPoint IntPoint2d(cInt x, cInt y) ); } -inline cInt Round(double val) +// Fast rounding upwards. +inline double FRound(double a) { - return static_cast((val < 0) ? (val - 0.5) : (val + 0.5)); + // Why does Java Math.round(0.49999999999999994) return 1? + // https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1 + return a == 0.49999999999999994 ? 0 : floor(a + 0.5); +} + +template +inline IType Round(double val) +{ + double v = FRound(val); +#if defined(CLIPPERLIB_INT32) && ! defined(NDEBUG) + static_assert(sizeof(IType) == 4 || sizeof(IType) == 8, "IType must be int32 or int64"); + static constexpr const double hi = 65536. * 16383. * (sizeof(IType) == 4 ? 1 : 65536. * 65536.); + if (v > hi || -v > hi) + throw clipperException("Coordinate outside allowed range"); +#endif + return static_cast(v); } // Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional. @@ -142,7 +128,7 @@ int PolyTree::Total() const void PolyNode::AddChild(PolyNode& child) { unsigned cnt = (unsigned)Childs.size(); - Childs.push_back(&child); + Childs.emplace_back(&child); child.Parent = this; child.Index = cnt; } @@ -246,7 +232,7 @@ int PointInPolygon(const IntPoint &pt, const Path &path) if (ipNext.x() > pt.x()) result = 1 - result; else { - double d = (double)(ip.x() - pt.x()) * (ipNext.y() - pt.y()) - (double)(ipNext.x() - pt.x()) * (ip.y() - pt.y()); + auto d = CrossProductType(ip.x() - pt.x()) * CrossProductType(ipNext.y() - pt.y()) - CrossProductType(ipNext.x() - pt.x()) * CrossProductType(ip.y() - pt.y()); if (!d) return -1; if ((d > 0) == (ipNext.y() > ip.y())) result = 1 - result; } @@ -254,7 +240,7 @@ int PointInPolygon(const IntPoint &pt, const Path &path) { if (ipNext.x() > pt.x()) { - double d = (double)(ip.x() - pt.x()) * (ipNext.y() - pt.y()) - (double)(ipNext.x() - pt.x()) * (ip.y() - pt.y()); + auto d = CrossProductType(ip.x() - pt.x()) * CrossProductType(ipNext.y() - pt.y()) - CrossProductType(ipNext.x() - pt.x()) * CrossProductType(ip.y() - pt.y()); if (!d) return -1; if ((d > 0) == (ipNext.y() > ip.y())) result = 1 - result; } @@ -267,7 +253,7 @@ int PointInPolygon(const IntPoint &pt, const Path &path) //------------------------------------------------------------------------------ // Called by Poly2ContainsPoly1() -int PointInPolygon (const IntPoint &pt, OutPt *op) +int PointInPolygon(const IntPoint &pt, OutPt *op) { //returns 0 if false, +1 if true, -1 if pt ON polygon boundary int result = 0; @@ -286,7 +272,7 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) if (op->Next->Pt.x() > pt.x()) result = 1 - result; else { - double d = (double)(op->Pt.x() - pt.x()) * (op->Next->Pt.y() - pt.y()) - (double)(op->Next->Pt.x() - pt.x()) * (op->Pt.y() - pt.y()); + auto d = CrossProductType(op->Pt.x() - pt.x()) * CrossProductType(op->Next->Pt.y() - pt.y()) - CrossProductType(op->Next->Pt.x() - pt.x()) * CrossProductType(op->Pt.y() - pt.y()); if (!d) return -1; if ((d > 0) == (op->Next->Pt.y() > op->Pt.y())) result = 1 - result; } @@ -294,7 +280,7 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) { if (op->Next->Pt.x() > pt.x()) { - double d = (double)(op->Pt.x() - pt.x()) * (op->Next->Pt.y() - pt.y()) - (double)(op->Next->Pt.x() - pt.x()) * (op->Pt.y() - pt.y()); + auto d = CrossProductType(op->Pt.x() - pt.x()) * CrossProductType(op->Next->Pt.y() - pt.y()) - CrossProductType(op->Next->Pt.x() - pt.x()) * CrossProductType(op->Pt.y() - pt.y()); if (!d) return -1; if ((d > 0) == (op->Next->Pt.y() > op->Pt.y())) result = 1 - result; } @@ -309,7 +295,6 @@ int PointInPolygon (const IntPoint &pt, OutPt *op) // This is potentially very expensive! O(n^2)! bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) { - CLIPPERLIB_PROFILE_FUNC(); OutPt* op = OutPt1; do { @@ -340,7 +325,7 @@ inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cI #endif inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) - { return SlopesEqual(e1.Delta.x(), e1.Delta.y(), e2.Delta.x(), e2.Delta.y(), UseFullInt64Range); } + { return SlopesEqual(e1.Top.x() - e1.Bot.x(), e1.Top.y() - e1.Bot.y(), e2.Top.x() - e2.Bot.x(), e2.Top.y() - e2.Bot.y(), UseFullInt64Range); } inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range) { return SlopesEqual(pt1.x()-pt2.x(), pt1.y()-pt2.y(), pt2.x()-pt3.x(), pt2.y()-pt3.y(), UseFullInt64Range); } inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, const IntPoint &pt4, bool UseFullInt64Range) @@ -350,7 +335,7 @@ inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint inline bool IsHorizontal(TEdge &e) { - return e.Delta.y() == 0; + return e.Dx == HORIZONTAL; } //------------------------------------------------------------------------------ @@ -365,7 +350,7 @@ inline cInt TopX(TEdge &edge, const cInt currentY) { return (currentY == edge.Top.y()) ? edge.Top.x() : - edge.Bot.x() + Round(edge.Dx *(currentY - edge.Bot.y())); + edge.Bot.x() + Round(edge.Dx *(currentY - edge.Bot.y())); } //------------------------------------------------------------------------------ @@ -375,65 +360,53 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.z() = 0; #endif - double b1, b2; if (Edge1.Dx == Edge2.Dx) { ip.y() = Edge1.Curr.y(); ip.x() = TopX(Edge1, ip.y()); return; } - else if (Edge1.Delta.x() == 0) + + int64_t y; + if (Edge1.Dx == 0) { ip.x() = Edge1.Bot.x(); - if (IsHorizontal(Edge2)) - ip.y() = Edge2.Bot.y(); - else - { - b2 = Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx); - ip.y() = Round(ip.x() / Edge2.Dx + b2); - } + y = IsHorizontal(Edge2) ? + Edge2.Bot.y() : + Round(ip.x() / Edge2.Dx + Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx)); + } - else if (Edge2.Delta.x() == 0) + else if (Edge2.Dx == 0) { ip.x() = Edge2.Bot.x(); - if (IsHorizontal(Edge1)) - ip.y() = Edge1.Bot.y(); - else - { - b1 = Edge1.Bot.y() - (Edge1.Bot.x() / Edge1.Dx); - ip.y() = Round(ip.x() / Edge1.Dx + b1); - } - } - else + y = IsHorizontal(Edge1) ? + Edge1.Bot.y() : + Round(ip.x() / Edge1.Dx + Edge1.Bot.y() - (Edge1.Bot.x() / Edge1.Dx)); + } + else { - b1 = double(Edge1.Bot.x()) - double(Edge1.Bot.y()) * Edge1.Dx; - b2 = double(Edge2.Bot.x()) - double(Edge2.Bot.y()) * Edge2.Dx; + double b1 = double(Edge1.Bot.x()) - double(Edge1.Bot.y()) * Edge1.Dx; + double b2 = double(Edge2.Bot.x()) - double(Edge2.Bot.y()) * Edge2.Dx; double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.y() = Round(q); - ip.x() = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? - Round(Edge1.Dx * q + b1) : - Round(Edge2.Dx * q + b2); + y = Round(q); + ip.x() = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? + Round(Edge1.Dx * q + b1) : + Round(Edge2.Dx * q + b2); } - if (ip.y() < Edge1.Top.y() || ip.y() < Edge2.Top.y()) + ip.y() = cInt(y); + if (y < Edge1.Top.y() || y < Edge2.Top.y()) { - if (Edge1.Top.y() > Edge2.Top.y()) - ip.y() = Edge1.Top.y(); - else - ip.y() = Edge2.Top.y(); - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.x() = TopX(Edge1, ip.y()); - else - ip.x() = TopX(Edge2, ip.y()); - } + ip.y() = (Edge1.Top.y() > Edge2.Top.y() ? Edge1 : Edge2).Top.y(); + y = ip.y(); + ip.x() = TopX(std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx) ? Edge1 : Edge2, ip.y()); + } //finally, don't allow 'ip' to be BELOW curr.y() (ie bottom of scanbeam) ... - if (ip.y() > Edge1.Curr.y()) + if (y > Edge1.Curr.y()) { ip.y() = Edge1.Curr.y(); //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.x() = TopX(Edge2, ip.y()); else - ip.x() = TopX(Edge1, ip.y()); + ip.x() = TopX(std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx) ? Edge2 : Edge1, ip.y()); } } //------------------------------------------------------------------------------ @@ -475,11 +448,9 @@ void InitEdge2(TEdge& e, PolyType Pt) e.Bot = e.Next->Curr; } - e.Delta.x() = (e.Top.x() - e.Bot.x()); - e.Delta.y() = (e.Top.y() - e.Bot.y()); - - if (e.Delta.y() == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Delta.x()) / e.Delta.y(); + cInt dy = (e.Top.y() - e.Bot.y()); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.x() - e.Bot.x()) / dy; e.PolyTyp = Pt; } @@ -618,9 +589,18 @@ bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) // ClipperBase class methods ... //------------------------------------------------------------------------------ -#ifndef CLIPPERLIB_INT32 +#ifdef CLIPPERLIB_INT32 +static inline void RangeTest(const IntPoint &pt) +{ +#ifndef NDEBUG + static constexpr const int32_t hi = 65536 * 16383; + if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi) + throw clipperException("Coordinate outside allowed range"); +#endif // NDEBUG +} +#else // CLIPPERLIB_INT32 // Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates. -inline void RangeTest(const IntPoint& Pt, bool& useFullRange) +static inline void RangeTest(const IntPoint& Pt, bool& useFullRange) { if (useFullRange) { @@ -696,7 +676,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) locMin.RightBound = E; E->WindDelta = 0; Result = ProcessBound(E, NextIsForward); - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); } return Result; } @@ -775,7 +755,6 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) { - CLIPPERLIB_PROFILE_FUNC(); // Remove duplicate end point from a closed input path. // Remove duplicate points from the end of the input path. int highI = (int)pg.size() -1; @@ -788,7 +767,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return false; // Allocate a new edge array. - std::vector edges(highI + 1); + Edges edges(highI + 1); // Fill in the edge array. bool result = AddPathInternal(pg, highI, PolyTyp, Closed, edges.data()); if (result) @@ -799,7 +778,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges) { - CLIPPERLIB_PROFILE_FUNC(); #ifdef use_lines if (!Closed && PolyTyp == ptClip) throw clipperException("AddPath: Open paths must be subject."); @@ -814,7 +792,10 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b try { edges[1].Curr = pg[1]; -#ifndef CLIPPERLIB_INT32 +#ifdef CLIPPERLIB_INT32 + RangeTest(pg[0]); + RangeTest(pg[highI]); +#else RangeTest(pg[0], m_UseFullRange); RangeTest(pg[highI], m_UseFullRange); #endif // CLIPPERLIB_INT32 @@ -822,7 +803,9 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); for (int i = highI - 1; i >= 1; --i) { -#ifndef CLIPPERLIB_INT32 +#ifdef CLIPPERLIB_INT32 + RangeTest(pg[i]); +#else RangeTest(pg[i], m_UseFullRange); #endif // CLIPPERLIB_INT32 InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); @@ -915,7 +898,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b E->NextInLML = E->Next; E = E->Next; } - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); return true; } @@ -949,8 +932,6 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b locMin.RightBound = E->Prev; leftBoundIsForward = true; //Q.nextInLML = Q.next } - locMin.LeftBound->Side = esLeft; - locMin.RightBound->Side = esRight; if (!Closed) locMin.LeftBound->WindDelta = 0; else if (locMin.LeftBound->Next == locMin.RightBound) @@ -968,7 +949,7 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b locMin.LeftBound = 0; else if (locMin.RightBound->OutIdx == Skip) locMin.RightBound = 0; - m_MinimaList.push_back(locMin); + m_MinimaList.emplace_back(locMin); if (!leftBoundIsForward) E = E2; } return true; @@ -977,7 +958,6 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b void ClipperBase::Clear() { - CLIPPERLIB_PROFILE_FUNC(); m_MinimaList.clear(); m_edges.clear(); #ifndef CLIPPERLIB_INT32 @@ -991,7 +971,6 @@ void ClipperBase::Clear() // Sort the LML entries, initialize the left / right bound edges of each Local Minima. void ClipperBase::Reset() { - CLIPPERLIB_PROFILE_FUNC(); if (m_MinimaList.empty()) return; //ie nothing to process std::sort(m_MinimaList.begin(), m_MinimaList.end(), [](const LocalMinimum& lm1, const LocalMinimum& lm2){ return lm1.Y < lm2.Y; }); @@ -1020,7 +999,6 @@ void ClipperBase::Reset() // Returns (0,0,0,0) for an empty rectangle. IntRect ClipperBase::GetBounds() { - CLIPPERLIB_PROFILE_FUNC(); IntRect result; auto lm = m_MinimaList.begin(); if (lm == m_MinimaList.end()) @@ -1064,8 +1042,7 @@ IntRect ClipperBase::GetBounds() Clipper::Clipper(int initOptions) : ClipperBase(), m_OutPtsFree(nullptr), - m_OutPtsChunkSize(32), - m_OutPtsChunkLast(32), + m_OutPtsChunkLast(m_OutPtsChunkSize), m_ActiveEdges(nullptr), m_SortedEdges(nullptr) { @@ -1081,9 +1058,8 @@ Clipper::Clipper(int initOptions) : void Clipper::Reset() { - CLIPPERLIB_PROFILE_FUNC(); ClipperBase::Reset(); - m_Scanbeam = std::priority_queue(); + m_Scanbeam = std::priority_queue{}; m_Maxima.clear(); m_ActiveEdges = 0; m_SortedEdges = 0; @@ -1096,7 +1072,6 @@ void Clipper::Reset() bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType subjFillType, PolyFillType clipFillType) { - CLIPPERLIB_PROFILE_FUNC(); if (m_HasOpenPaths) throw clipperException("Error: PolyTree struct is needed for open path clipping."); solution.clear(); @@ -1114,7 +1089,6 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, bool Clipper::Execute(ClipType clipType, PolyTree& polytree, PolyFillType subjFillType, PolyFillType clipFillType) { - CLIPPERLIB_PROFILE_FUNC(); m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; @@ -1128,10 +1102,8 @@ bool Clipper::Execute(ClipType clipType, PolyTree& polytree, bool Clipper::ExecuteInternal() { - CLIPPERLIB_PROFILE_FUNC(); bool succeeded = true; try { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Process); Reset(); if (m_MinimaList.empty()) return true; cInt botY = m_Scanbeam.top(); @@ -1156,31 +1128,28 @@ bool Clipper::ExecuteInternal() if (succeeded) { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix); //fix orientations ... //FIXME Vojtech: Does it not invalidate the loop hierarchy maintained as OutRec::FirstLeft pointers? //FIXME Vojtech: The area is calculated with floats, it may not be numerically stable! { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_orientations); - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts && !outRec->IsOpen && (outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) - ReversePolyPtLinks(outRec->Pts); + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts && !outRec.IsOpen && (outRec.IsHole ^ m_ReverseOutput) == (Area(outRec) > 0)) + ReversePolyPtLinks(outRec.Pts); } JoinCommonEdges(); //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() { - CLIPPERLIB_PROFILE_BLOCK(Clipper_ExecuteInternal_Fix_fixup); - for (OutRec *outRec : m_PolyOuts) - if (outRec->Pts) { - if (outRec->IsOpen) + for (OutRec &outRec : m_PolyOuts) + if (outRec.Pts) { + if (outRec.IsOpen) // Removes duplicate points. - FixupOutPolyline(*outRec); + FixupOutPolyline(outRec); else // Removes duplicate points and simplifies consecutive parallel edges by removing the middle vertex. - FixupOutPolygon(*outRec); + FixupOutPolygon(outRec); } } // For each polygon, search for exactly duplicate non-successive points. @@ -1205,22 +1174,18 @@ OutPt* Clipper::AllocateOutPt() m_OutPtsFree = pt->Next; } else if (m_OutPtsChunkLast < m_OutPtsChunkSize) { // Get a point from the last chunk. - pt = m_OutPts.back() + (m_OutPtsChunkLast ++); + pt = &m_OutPts.back()[m_OutPtsChunkLast ++]; } else { // The last chunk is full. Allocate a new one. - m_OutPts.push_back(new OutPt[m_OutPtsChunkSize]); + m_OutPts.emplace_back(); m_OutPtsChunkLast = 1; - pt = m_OutPts.back(); + pt = &m_OutPts.back().front(); } return pt; } void Clipper::DisposeAllOutRecs() { - for (OutPt *pts : m_OutPts) - delete[] pts; - for (OutRec *rec : m_PolyOuts) - delete rec; m_OutPts.clear(); m_OutPtsFree = nullptr; m_OutPtsChunkLast = m_OutPtsChunkSize; @@ -1235,7 +1200,13 @@ void Clipper::SetWindingCount(TEdge &edge) const while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; if (!e) { - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; edge.WindCnt2 = 0; e = m_ActiveEdges; //ie get ready to calc WindCnt2 } @@ -1426,7 +1397,6 @@ bool Clipper::IsContributing(const TEdge& edge) const // Called from Clipper::InsertLocalMinimaIntoAEL() and Clipper::IntersectEdges(). OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) { - CLIPPERLIB_PROFILE_FUNC(); OutPt* result; TEdge *e, *prevE; if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) @@ -1453,13 +1423,16 @@ OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) prevE = e->PrevInAEL; } - if (prevE && prevE->OutIdx >= 0 && - (TopX(*prevE, Pt.y()) == TopX(*e, Pt.y())) && - SlopesEqual(*e, *prevE, m_UseFullRange) && - (e->WindDelta != 0) && (prevE->WindDelta != 0)) + if (prevE && prevE->OutIdx >= 0 && prevE->Top.y() < Pt.y() && e->Top.y() < Pt.y()) { - OutPt* outPt = AddOutPt(prevE, Pt); - m_Joins.emplace_back(Join(result, outPt, e->Top)); + cInt xPrev = TopX(*prevE, Pt.y()); + cInt xE = TopX(*e, Pt.y()); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint2d(xPrev, Pt.y()), prevE->Top, IntPoint2d(xE, Pt.y()), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + m_Joins.emplace_back(Join(result, outPt, e->Top)); + } } return result; } @@ -1518,7 +1491,6 @@ void Clipper::CopyAELToSEL() // Called from Clipper::ExecuteInternal() void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - CLIPPERLIB_PROFILE_FUNC(); while (!m_MinimaList.empty() && m_MinimaList.back().Y == botY) { TEdge* lb = m_MinimaList.back().LeftBound; @@ -1556,7 +1528,12 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (rb) { - if(IsHorizontal(*rb)) AddEdgeToSEL(rb); + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + m_Scanbeam.push(rb->NextInLML->Top.y()); + } else m_Scanbeam.push(rb->Top.y()); } @@ -1576,7 +1553,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (lb->OutIdx >= 0 && lb->PrevInAEL && lb->PrevInAEL->Curr.x() == lb->Bot.x() && lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) { OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); @@ -1587,7 +1564,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) { OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); @@ -1845,21 +1822,29 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) } //------------------------------------------------------------------------------ -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) { - bool IsHole = false; TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; while (e2) { if (e2->OutIdx >= 0 && e2->WindDelta != 0) { - IsHole = !IsHole; - if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; } e2 = e2->PrevInAEL; } - if (IsHole) outrec->IsHole = true; + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = &m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } } //------------------------------------------------------------------------------ @@ -1883,7 +1868,7 @@ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) } //------------------------------------------------------------------------------ -bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) { do { @@ -1896,23 +1881,23 @@ bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) OutRec* Clipper::GetOutRec(int Idx) { - OutRec* outrec = m_PolyOuts[Idx]; - while (outrec != m_PolyOuts[outrec->Idx]) - outrec = m_PolyOuts[outrec->Idx]; + OutRec* outrec = &m_PolyOuts[Idx]; + while (outrec != &m_PolyOuts[outrec->Idx]) + outrec = &m_PolyOuts[outrec->Idx]; return outrec; } //------------------------------------------------------------------------------ -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) { //get the start and ends of both output polygons ... - OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + OutRec *outRec1 = &m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = &m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; - if (Param1RightOfParam2(outRec1, outRec2)) + if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); @@ -1925,7 +1910,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const OutPt* p2_lft = outRec2->Pts; OutPt* p2_rt = p2_lft->Prev; - EdgeSide Side; //join e2 poly onto e1 poly and delete pointers to e2 ... if( e1->Side == esLeft ) { @@ -1947,7 +1931,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const p1_rt->Next = p2_lft; outRec1->Pts = p2_lft; } - Side = esLeft; } else { if( e2->Side == esRight ) @@ -1966,7 +1949,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const p1_lft->Prev = p2_rt; p2_rt->Next = p1_lft; } - Side = esRight; } outRec1->BottomPt = 0; @@ -1992,7 +1974,7 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const if( e->OutIdx == ObsoleteIdx ) { e->OutIdx = OKIdx; - e->Side = Side; + e->Side = e1->Side; break; } e = e->NextInAEL; @@ -2004,16 +1986,16 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const OutRec* Clipper::CreateOutRec() { - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; + m_PolyOuts.emplace_back(); + OutRec &result = m_PolyOuts.back(); + result.IsHole = false; + result.IsOpen = false; + result.FirstLeft = 0; + result.Pts = 0; + result.BottomPt = 0; + result.PolyNd = 0; + result.Idx = (int)m_PolyOuts.size()-1; + return &result; } //------------------------------------------------------------------------------ @@ -2035,7 +2017,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) return newOp; } else { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' OutPt* op = outRec->Pts; @@ -2058,7 +2040,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) OutPt* Clipper::GetLastOutPt(TEdge *e) { - OutRec *outRec = m_PolyOuts[e->OutIdx]; + OutRec *outRec = &m_PolyOuts[e->OutIdx]; if (e->Side == esLeft) return outRec->Pts; else @@ -2068,7 +2050,6 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { - CLIPPERLIB_PROFILE_FUNC(); TEdge* horzEdge = m_SortedEdges; while(horzEdge) { @@ -2093,17 +2074,21 @@ inline bool IsIntermediate(TEdge *e, const cInt Y) inline TEdge *GetMaximaPair(TEdge *e) { - TEdge* result = 0; if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - result = e->Next; + return e->Next; else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - result = e->Prev; + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ - if (result && (result->OutIdx == Skip || - //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) - return 0; - return result; +inline TEdge *GetMaximaPairEx(TEdge *e) +{ + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; } //------------------------------------------------------------------------------ @@ -2230,7 +2215,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); + bool IsOpen = (horzEdge->WindDelta == 0); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); @@ -2240,8 +2225,8 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (!eLastHorz->NextInLML) eMaxPair = GetMaximaPair(eLastHorz); - std::vector::const_iterator maxIt; - std::vector::const_reverse_iterator maxRit; + cInts::const_iterator maxIt; + cInts::const_reverse_iterator maxRit; if (!m_Maxima.empty()) { //get the first maxima in range (X) ... @@ -2307,10 +2292,12 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times { #ifdef CLIPPERLIB_USE_XYZ - if (dir == dLeftToRight) SetZ(e->Curr, *horzEdge, *e); - else SetZ(e->Curr, *e, *horzEdge); -#endif - op1 = AddOutPt(horzEdge, e->Curr); + if (dir == dLeftToRight) + SetZ(e->Curr, *horzEdge, *e); + else + SetZ(e->Curr, *e, *horzEdge); +#endif + op1 = AddOutPt(horzEdge, e->Curr); TEdge* eNextHorz = m_SortedEdges; while (eNextHorz) { @@ -2443,7 +2430,6 @@ void Clipper::UpdateEdgeIntoAEL(TEdge *&e) bool Clipper::ProcessIntersections(const cInt topY) { - CLIPPERLIB_PROFILE_FUNC(); if( !m_ActiveEdges ) return true; try { BuildIntersectList(topY); @@ -2497,6 +2483,8 @@ void Clipper::BuildIntersectList(const cInt topY) if(e->Curr.x() > eNext->Curr.x()) { IntersectPoint(*e, *eNext, Pt); + + if (Pt.y() < topY) Pt = IntPoint2d(TopX(*e, topY), topY); m_IntersectList.emplace_back(IntersectNode(e, eNext, Pt)); SwapPositionsInSEL(e, eNext); isModified = true; @@ -2546,7 +2534,7 @@ bool Clipper::FixupIntersectionOrder() void Clipper::DoMaxima(TEdge *e) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); if (!eMaxPair) { if (e->OutIdx >= 0) @@ -2598,7 +2586,6 @@ void Clipper::DoMaxima(TEdge *e) void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) { - CLIPPERLIB_PROFILE_FUNC(); TEdge* e = m_ActiveEdges; while( e ) { @@ -2608,13 +2595,13 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); } if(IsMaximaEdge) { - if (m_StrictSimple) m_Maxima.push_back(e->Top.x()); + if (m_StrictSimple) m_Maxima.emplace_back(e->Top.x()); TEdge* ePrev = e->PrevInAEL; DoMaxima(e); if( !ePrev ) e = m_ActiveEdges; @@ -2637,7 +2624,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) #ifdef CLIPPERLIB_USE_XYZ e->Curr.z() = topY == e->Top.y() ? e->Top.z() : (topY == e->Bot.y() ? e->Bot.z() : 0); #endif - } + } //When StrictlySimple and 'e' is being touched by another edge, then //make sure both edges have a vertex here ... @@ -2683,7 +2670,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if (ePrev && ePrev->Curr.x() == e->Bot.x() && ePrev->Curr.y() == e->Bot.y() && op && ePrev->OutIdx >= 0 && ePrev->Curr.y() > ePrev->Top.y() && - SlopesEqual(*e, *ePrev, m_UseFullRange) && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && (e->WindDelta != 0) && (ePrev->WindDelta != 0)) { OutPt* op2 = AddOutPt(ePrev, e->Bot); @@ -2692,7 +2679,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) else if (eNext && eNext->Curr.x() == e->Bot.x() && eNext->Curr.y() == e->Bot.y() && op && eNext->OutIdx >= 0 && eNext->Curr.y() > eNext->Top.y() && - SlopesEqual(*e, *eNext, m_UseFullRange) && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && (e->WindDelta != 0) && (eNext->WindDelta != 0)) { OutPt* op2 = AddOutPt(eNext, e->Bot); @@ -2792,12 +2779,12 @@ int PointCount(OutPt *Pts) void Clipper::BuildResult(Paths &polys) { polys.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - assert(! outRec->IsOpen); - if (!outRec->Pts) continue; + assert(! outRec.IsOpen); + if (!outRec.Pts) continue; Path pg; - OutPt* p = outRec->Pts->Prev; + OutPt* p = outRec.Pts->Prev; int cnt = PointCount(p); if (cnt < 2) continue; pg.reserve(cnt); @@ -2816,31 +2803,31 @@ void Clipper::BuildResult2(PolyTree& polytree) polytree.Clear(); polytree.AllNodes.reserve(m_PolyOuts.size()); //add each output polygon/contour to polytree ... - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - int cnt = PointCount(outRec->Pts); - if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) + int cnt = PointCount(outRec.Pts); + if ((outRec.IsOpen && cnt < 2) || (!outRec.IsOpen && cnt < 3)) // Ignore an invalid output loop or a polyline. continue; //skip OutRecs that (a) contain outermost polygons or //(b) already have the correct owner/child linkage ... - if (outRec->FirstLeft && - (outRec->IsHole == outRec->FirstLeft->IsHole || ! outRec->FirstLeft->Pts)) { - OutRec* orfl = outRec->FirstLeft; - while (orfl && ((orfl->IsHole == outRec->IsHole) || !orfl->Pts)) + if (outRec.FirstLeft && + (outRec.IsHole == outRec.FirstLeft->IsHole || ! outRec.FirstLeft->Pts)) { + OutRec* orfl = outRec.FirstLeft; + while (orfl && ((orfl->IsHole == outRec.IsHole) || !orfl->Pts)) orfl = orfl->FirstLeft; - outRec->FirstLeft = orfl; + outRec.FirstLeft = orfl; } //nb: polytree takes ownership of all the PolyNodes polytree.AllNodes.emplace_back(PolyNode()); PolyNode* pn = &polytree.AllNodes.back(); - outRec->PolyNd = pn; + outRec.PolyNd = pn; pn->Parent = 0; pn->Index = 0; pn->Contour.reserve(cnt); - OutPt *op = outRec->Pts->Prev; + OutPt *op = outRec.Pts->Prev; for (int j = 0; j < cnt; j++) { pn->Contour.emplace_back(op->Pt); @@ -2850,18 +2837,18 @@ void Clipper::BuildResult2(PolyTree& polytree) //fixup PolyNode links etc ... polytree.Childs.reserve(m_PolyOuts.size()); - for (OutRec* outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->PolyNd) continue; - if (outRec->IsOpen) + if (!outRec.PolyNd) continue; + if (outRec.IsOpen) { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild(*outRec->PolyNd); + outRec.PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec.PolyNd); } - else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) - outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else if (outRec.FirstLeft && outRec.FirstLeft->PolyNd) + outRec.FirstLeft->PolyNd->AddChild(*outRec.PolyNd); else - polytree.AddChild(*outRec->PolyNd); + polytree.AddChild(*outRec.PolyNd); } } //------------------------------------------------------------------------------ @@ -3206,34 +3193,64 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) } //---------------------------------------------------------------------- +inline OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + // This is potentially very expensive! O(n^3)! -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { - CLIPPERLIB_PROFILE_FUNC(); //tests if NewOutRec contains the polygon before reassigning FirstLeft - for (OutRec *outRec : m_PolyOuts) + for (OutRec &outRec : m_PolyOuts) { - if (!outRec->Pts || !outRec->FirstLeft) continue; - OutRec* firstLeft = outRec->FirstLeft; - // Skip empty polygons. - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (firstLeft == OldOutRec && Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) - outRec->FirstLeft = NewOutRec; + OutRec* firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (outRec.Pts && firstLeft == OldOutRec && Poly2ContainsPoly1(outRec.Pts, NewOutRec->Pts)) + outRec.FirstLeft = NewOutRec; } } //---------------------------------------------------------------------- -void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const -{ +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (OutRec &outRec : m_PolyOuts) + { + if (!outRec.Pts || &outRec == OuterOutRec || &outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec.Pts, InnerOutRec->Pts)) + outRec.FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec.Pts, OuterOutRec->Pts)) + outRec.FirstLeft = OuterOutRec; + else if (outRec.FirstLeft == InnerOutRec || outRec.FirstLeft == OuterOutRec) + outRec.FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for (OutRec *outRec : m_PolyOuts) - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + for (OutRec &outRec : m_PolyOuts) + { + OutRec* firstLeft = ParseFirstLeft(outRec.FirstLeft); + if (outRec.Pts && firstLeft == OldOutRec) + outRec.FirstLeft = NewOutRec; + } } //---------------------------------------------------------------------- void Clipper::JoinCommonEdges() { - CLIPPERLIB_PROFILE_FUNC(); for (Join &join : m_Joins) { OutRec *outRec1 = GetOutRec(join.OutPt1->Idx); @@ -3246,8 +3263,8 @@ void Clipper::JoinCommonEdges() //before calling JoinPoints() ... OutRec *holeStateRec; if (outRec1 == outRec2) holeStateRec = outRec1; - else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); if (!JoinPoints(&join, outRec1, outRec2)) continue; @@ -3264,23 +3281,9 @@ void Clipper::JoinCommonEdges() //update all OutRec2.Pts Idx's ... UpdateOutPtIdxs(*outRec2); - //We now need to check every OutRec.FirstLeft pointer. If it points - //to OutRec1 it may need to point to OutRec2 instead ... - if (m_UsingPolyTree) - for (size_t j = 0; j < m_PolyOuts.size() - 1; j++) - { - OutRec* oRec = m_PolyOuts[j]; - OutRec* firstLeft = oRec->FirstLeft; - while (firstLeft && !firstLeft->Pts) firstLeft = firstLeft->FirstLeft; - if (!oRec->Pts || firstLeft != outRec1 || - oRec->IsHole == outRec1->IsHole) continue; - if (Poly2ContainsPoly1(oRec->Pts, join.OutPt2)) - oRec->FirstLeft = outRec2; - } - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) { - //outRec2 is contained by outRec1 ... + //outRec1 contains outRec2 ... outRec2->IsHole = !outRec1->IsHole; outRec2->FirstLeft = outRec1; @@ -3292,7 +3295,7 @@ void Clipper::JoinCommonEdges() } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) { - //outRec1 is contained by outRec2 ... + //outRec2 contains outRec1 ... outRec2->IsHole = outRec1->IsHole; outRec1->IsHole = !outRec2->IsHole; outRec2->FirstLeft = outRec1->FirstLeft; @@ -3330,7 +3333,7 @@ void Clipper::JoinCommonEdges() outRec2->FirstLeft = outRec1; // For each m_PolyOuts, replace FirstLeft from outRec2 to outRec1. - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); } } } @@ -3389,7 +3392,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType break; } newNode->Contour.reserve(highI + 1); - newNode->Contour.push_back(path[0]); + newNode->Contour.emplace_back(path[0]); int j = 0, k = 0; for (int i = 1; i <= highI; i++) { bool same = false; @@ -3402,7 +3405,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType if (same) continue; j++; - newNode->Contour.push_back(path[i]); + newNode->Contour.emplace_back(path[i]); if (path[i].y() > newNode->Contour[k].y() || (path[i].y() == newNode->Contour[k].y() && path[i].x() < newNode->Contour[k].x())) k = j; @@ -3530,7 +3533,7 @@ void ClipperOffset::DoOffset(double delta) { PolyNode& node = *m_polyNodes.Childs[i]; if (node.m_endtype == etClosedPolygon) - m_destPolys.push_back(node.Contour); + m_destPolys.emplace_back(node.Contour); } return; } @@ -3572,9 +3575,9 @@ void ClipperOffset::DoOffset(double delta) double X = 1.0, Y = 0.0; for (cInt j = 1; j <= steps; j++) { - m_destPoly.push_back(IntPoint2d( - Round(m_srcPoly[0].x() + X * delta), - Round(m_srcPoly[0].y() + Y * delta))); + m_destPoly.emplace_back(IntPoint2d( + Round(m_srcPoly[0].x() + X * delta), + Round(m_srcPoly[0].y() + Y * delta))); double X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; @@ -3585,40 +3588,40 @@ void ClipperOffset::DoOffset(double delta) double X = -1.0, Y = -1.0; for (int j = 0; j < 4; ++j) { - m_destPoly.push_back(IntPoint2d( - Round(m_srcPoly[0].x() + X * delta), - Round(m_srcPoly[0].y() + Y * delta))); + m_destPoly.emplace_back(IntPoint2d( + Round(m_srcPoly[0].x() + X * delta), + Round(m_srcPoly[0].y() + Y * delta))); if (X < 0) X = 1; else if (Y < 0) Y = 1; else X = -1; } } - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); continue; } //build m_normals ... m_normals.clear(); m_normals.reserve(len); for (int j = 0; j < len - 1; ++j) - m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + m_normals.emplace_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) - m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + m_normals.emplace_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); else - m_normals.push_back(DoublePoint(m_normals[len - 2])); + m_normals.emplace_back(DoublePoint(m_normals[len - 2])); if (node.m_endtype == etClosedPolygon) { int k = len - 1; for (int j = 0; j < len; ++j) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } else if (node.m_endtype == etClosedLine) { int k = len - 1; for (int j = 0; j < len; ++j) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); m_destPoly.clear(); //re-build m_normals ... DoublePoint n = m_normals[len -1]; @@ -3628,7 +3631,7 @@ void ClipperOffset::DoOffset(double delta) k = 0; for (int j = len - 1; j >= 0; j--) OffsetPoint(j, k, node.m_jointype); - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } else { @@ -3640,10 +3643,10 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { int j = len - 1; - pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); - m_destPoly.push_back(pt1); + pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); + m_destPoly.emplace_back(pt1); + pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); + m_destPoly.emplace_back(pt1); } else { @@ -3667,10 +3670,10 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { - pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); - m_destPoly.push_back(pt1); - pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); - m_destPoly.push_back(pt1); + pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); + m_destPoly.emplace_back(pt1); + pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); + m_destPoly.emplace_back(pt1); } else { @@ -3681,7 +3684,7 @@ void ClipperOffset::DoOffset(double delta) else DoRound(0, 1); } - m_destPolys.push_back(m_destPoly); + m_destPolys.emplace_back(m_destPoly); } } } @@ -3697,8 +3700,8 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() ); if (cosA > 0) // angle => 0 degrees { - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); return; } //else angle => 180 degrees @@ -3708,11 +3711,11 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) if (m_sinA * m_delta < 0) { - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); - m_destPoly.push_back(m_srcPoly[j]); - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); + m_destPoly.emplace_back(m_srcPoly[j]); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } else switch (jointype) @@ -3735,20 +3738,20 @@ void ClipperOffset::DoSquare(int j, int k) { double dx = std::tan(std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4); - m_destPoly.push_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), - Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); - m_destPoly.push_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), - Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); + m_destPoly.emplace_back(IntPoint2d( + Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), + Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); + m_destPoly.emplace_back(IntPoint2d( + Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), + Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); } //------------------------------------------------------------------------------ void ClipperOffset::DoMiter(int j, int k, double r) { double q = m_delta / r; - m_destPoly.push_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), - Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), + Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); } //------------------------------------------------------------------------------ @@ -3756,21 +3759,21 @@ void ClipperOffset::DoRound(int j, int k) { double a = std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()); - auto steps = std::max(Round(m_StepsPerRad * std::fabs(a)), 1); + auto steps = std::max(Round(m_StepsPerRad * std::fabs(a)), 1); double X = m_normals[k].x(), Y = m_normals[k].y(), X2; for (int i = 0; i < steps; ++i) { - m_destPoly.push_back(IntPoint2d( - Round(m_srcPoly[j].x() + X * m_delta), - Round(m_srcPoly[j].y() + Y * m_delta))); + m_destPoly.emplace_back(IntPoint2d( + Round(m_srcPoly[j].x() + X * m_delta), + Round(m_srcPoly[j].y() + Y * m_delta))); X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; } - m_destPoly.push_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d( + Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } //------------------------------------------------------------------------------ @@ -3784,17 +3787,16 @@ void ClipperOffset::DoRound(int j, int k) // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/Clipper/Properties/StrictlySimple.htm void Clipper::DoSimplePolygons() { - CLIPPERLIB_PROFILE_FUNC(); size_t i = 0; while (i < m_PolyOuts.size()) { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - if (!op || outrec->IsOpen) continue; + OutRec &outrec = m_PolyOuts[i++]; + OutPt* op = outrec.Pts; + if (!op || outrec.IsOpen) continue; do //for each Pt in Polygon until duplicate found do ... { OutPt* op2 = op->Next; - while (op2 != outrec->Pts) + while (op2 != outrec.Pts) { if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) { @@ -3806,37 +3808,37 @@ void Clipper::DoSimplePolygons() op2->Prev = op3; op3->Next = op2; - outrec->Pts = op; + outrec.Pts = op; OutRec* outrec2 = CreateOutRec(); outrec2->Pts = op2; UpdateOutPtIdxs(*outrec2); - if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + if (Poly2ContainsPoly1(outrec2->Pts, outrec.Pts)) { //OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; + outrec2->IsHole = !outrec.IsHole; + outrec2->FirstLeft = &outrec; // For each m_PolyOuts, replace FirstLeft from outRec2 to outrec. - if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, &outrec); } else - if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + if (Poly2ContainsPoly1(outrec.Pts, outrec2->Pts)) { //OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; + outrec2->IsHole = outrec.IsHole; + outrec.IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec.FirstLeft; + outrec.FirstLeft = outrec2; // For each m_PolyOuts, replace FirstLeft from outrec to outrec2. - if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + if (m_UsingPolyTree) FixupFirstLefts2(&outrec, outrec2); } else { //the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; + outrec2->IsHole = outrec.IsHole; + outrec2->FirstLeft = outrec.FirstLeft; // For each polygon of m_PolyOuts, replace FirstLeft from outrec to outrec2 if the polygon is inside outRec2. //FIXME This is potentially very expensive! O(n^3)! - if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + if (m_UsingPolyTree) FixupFirstLefts1(&outrec, outrec2); } op2 = op; //ie get ready for the Next iteration } @@ -3844,7 +3846,7 @@ void Clipper::DoSimplePolygons() } op = op->Next; } - while (op != outrec->Pts); + while (op != outrec.Pts); } } //------------------------------------------------------------------------------ @@ -3862,10 +3864,10 @@ void ReversePaths(Paths& p) } //------------------------------------------------------------------------------ -Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType) +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType, bool strictly_simple /* = true */) { Clipper c; - c.StrictlySimple(true); + c.StrictlySimple(strictly_simple); c.AddPath(in_poly, ptSubject, true); Paths out; c.Execute(ctUnion, out, fillType, fillType); @@ -3958,7 +3960,7 @@ void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) return; } - std::vector outPts(size); + OutPts outPts(size); for (size_t i = 0; i < size; ++i) { outPts[i].Pt = in_poly[i]; @@ -4012,6 +4014,7 @@ void CleanPolygon(Path& poly, double distance) void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) { + out_polys.resize(in_polys.size()); for (Paths::size_type i = 0; i < in_polys.size(); ++i) CleanPolygon(in_polys[i], out_polys[i], distance); } @@ -4037,8 +4040,8 @@ void Minkowski(const Path& poly, const Path& path, Path p; p.reserve(polyCnt); for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y())); - pp.push_back(p); + p.emplace_back(IntPoint2d(path[i].x() + poly[j].x(), path[i].y() + poly[j].y())); + pp.emplace_back(p); } else for (size_t i = 0; i < pathCnt; ++i) @@ -4046,8 +4049,8 @@ void Minkowski(const Path& poly, const Path& path, Path p; p.reserve(polyCnt); for (size_t j = 0; j < poly.size(); ++j) - p.push_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y())); - pp.push_back(p); + p.emplace_back(IntPoint2d(path[i].x() - poly[j].x(), path[i].y() - poly[j].y())); + pp.emplace_back(p); } solution.clear(); @@ -4057,12 +4060,12 @@ void Minkowski(const Path& poly, const Path& path, { Path quad; quad.reserve(4); - quad.push_back(pp[i % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); - quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); - quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]); if (!Orientation(quad)) ReversePath(quad); - solution.push_back(quad); + solution.emplace_back(quad); } } //------------------------------------------------------------------------------ @@ -4122,7 +4125,7 @@ void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& path else if (nodetype == ntOpen) return; if (!polynode.Contour.empty() && match) - paths.push_back(polynode.Contour); + paths.emplace_back(polynode.Contour); for (int i = 0; i < polynode.ChildCount(); ++i) AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); } @@ -4172,7 +4175,7 @@ void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) //Open paths are top level only, so ... for (int i = 0; i < polytree.ChildCount(); ++i) if (polytree.Childs[i]->IsOpen()) - paths.push_back(polytree.Childs[i]->Contour); + paths.emplace_back(polytree.Childs[i]->Contour); } //------------------------------------------------------------------------------ diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index 359ce32819..06637effce 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.4.2 * +* Date : 27 February 2017 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * +* Copyright : Angus Johnson 2010-2017 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -39,6 +39,8 @@ #include +#include + #define CLIPPER_VERSION "6.2.6" //CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance. @@ -50,6 +52,7 @@ //use_deprecated: Enables temporary support for the obsolete functions //#define use_deprecated +#include #include #include #include @@ -85,9 +88,11 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; // Point coordinate type #ifdef CLIPPERLIB_INT32 // Coordinates and their differences (vectors of the coordinates) have to fit int32_t. - typedef int32_t cInt; + using cInt = int32_t; + using CrossProductType = int64_t; #else - typedef int64_t cInt; + using cInt = int64_t; + using CrossProductType = double; // Maximum cInt value to allow a cross product calculation using 32bit expressions. static constexpr cInt const loRange = 0x3FFFFFFF; // 0x3FFFFFFF = 1 073 741 823 // Maximum allowed cInt value. @@ -110,8 +115,11 @@ using DoublePoint = Eigen::Matrix; //------------------------------------------------------------------------------ -typedef std::vector Path; -typedef std::vector Paths; +template +using Allocator = tbb::scalable_allocator; +//using Allocator = std::allocator; +using Path = std::vector>; +using Paths = std::vector>; inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} @@ -131,12 +139,12 @@ enum JoinType {jtSquare, jtRound, jtMiter}; enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; class PolyNode; -typedef std::vector< PolyNode* > PolyNodes; +typedef std::vector> PolyNodes; class PolyNode { public: - PolyNode() : Childs(), Parent(0), Index(0), m_IsOpen(false) {} + PolyNode() : Parent(0), Index(0), m_IsOpen(false) {} virtual ~PolyNode(){}; Path Contour; PolyNodes Childs; @@ -184,7 +192,7 @@ public: private: PolyTree(const PolyTree &src) = delete; PolyTree& operator=(const PolyTree &src) = delete; - std::vector AllNodes; + std::vector> AllNodes; friend class Clipper; //to access AllNodes }; @@ -192,7 +200,8 @@ double Area(const Path &poly); inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } int PointInPolygon(const IntPoint &pt, const Path &path); -Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd); +// Union with "strictly simple" fix enabled. +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true); void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); void CleanPolygon(Path& poly, double distance = 1.415); @@ -225,8 +234,6 @@ enum EdgeSide { esLeft = 1, esRight = 2}; IntPoint Curr; // Top point of this edge (with maximum Y). IntPoint Top; - // Vector from Bot to Top. - IntPoint Delta; // Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40). double Dx; PolyType PolyTyp; @@ -275,7 +282,27 @@ enum EdgeSide { esLeft = 1, esRight = 2}; OutPt *Prev; }; - struct OutRec; + using OutPts = std::vector>; + + // Output polygon. + struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + //The 'FirstLeft' field points to another OutRec that contains or is the + //'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is + //parsed left from the current edge (owning OutRec) until the owner OutRec + //is found. This field simplifies sorting the polygons into a tree structure + //which reflects the parent/child relationships of all polygons. + //This field should be renamed Parent, and will be later. + OutRec* FirstLeft; + // Used only by void Clipper::BuildResult2(PolyTree& polytree) + PolyNode* PolyNd; + // Linked list of output points, dynamically allocated. + OutPt* Pts; + OutPt* BottomPt; + }; + struct Join { Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) : OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {} @@ -310,7 +337,7 @@ public: if (num_paths == 1) return AddPath(*paths_provider.begin(), PolyTyp, Closed); - std::vector num_edges(num_paths, 0); + std::vector> num_edges(num_paths, 0); int num_edges_total = 0; size_t i = 0; for (const Path &pg : paths_provider) { @@ -331,7 +358,7 @@ public: return false; // Allocate a new edge array. - std::vector edges(num_edges_total); + std::vector> edges(num_edges_total); // Fill in the edge array. bool result = false; TEdge *p_edge = edges.data(); @@ -367,7 +394,7 @@ protected: void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); // Local minima (Y, left edge, right edge) sorted by ascending Y. - std::vector m_MinimaList; + std::vector> m_MinimaList; #ifdef CLIPPERLIB_INT32 static constexpr const bool m_UseFullRange = false; @@ -378,7 +405,8 @@ protected: #endif // CLIPPERLIB_INT32 // A vector of edges per each input path. - std::vector> m_edges; + using Edges = std::vector>; + std::vector> m_edges; // Don't remove intermediate vertices of a collinear sequence of points. bool m_PreserveCollinear; // Is any of the paths inserted by AddPath() or AddPaths() open? @@ -422,22 +450,23 @@ protected: private: // Output polygons. - std::vector m_PolyOuts; + std::deque> m_PolyOuts; // Output points, allocated by a continuous sets of m_OutPtsChunkSize. - std::vector m_OutPts; + static constexpr const size_t m_OutPtsChunkSize = 32; + std::deque, Allocator>> m_OutPts; // List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk. OutPt *m_OutPtsFree; - size_t m_OutPtsChunkSize; size_t m_OutPtsChunkLast; - std::vector m_Joins; - std::vector m_GhostJoins; - std::vector m_IntersectList; + std::vector> m_Joins; + std::vector> m_GhostJoins; + std::vector> m_IntersectList; ClipType m_ClipType; // A priority queue (a binary heap) of Y coordinates. - std::priority_queue m_Scanbeam; + using cInts = std::vector>; + std::priority_queue m_Scanbeam; // Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal(). - std::vector m_Maxima; + cInts m_Maxima; TEdge *m_ActiveEdges; TEdge *m_SortedEdges; PolyFillType m_ClipFillType; @@ -471,7 +500,7 @@ private: void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2) const; + void AppendPolygon(TEdge *e1, TEdge *e2); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); @@ -487,7 +516,7 @@ private: void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec) const; + void SetHoleState(TEdge *e, OutRec *outrec); bool FixupIntersectionOrder(); void FixupOutPolygon(OutRec &outrec); void FixupOutPolyline(OutRec &outrec); @@ -497,8 +526,9 @@ private: bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft); void JoinCommonEdges(); void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef CLIPPERLIB_USE_XYZ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif @@ -528,7 +558,7 @@ private: Paths m_destPolys; Path m_srcPoly; Path m_destPoly; - std::vector m_normals; + std::vector> m_normals; double m_delta, m_sinA, m_sin, m_cos; double m_miterLim, m_StepsPerRad; // x: index of the lowest contour in m_polyNodes @@ -556,8 +586,9 @@ class clipperException : public std::exception }; //------------------------------------------------------------------------------ +// Union with "strictly simple" fix enabled. template -inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd, bool strictly_simple = true) { +inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) { Clipper c; c.StrictlySimple(strictly_simple); c.AddPaths(std::forward(in_polys), ptSubject, true); diff --git a/src/clipper2/CMakeLists.txt b/src/clipper2/CMakeLists.txt deleted file mode 100644 index 275f9273b1..0000000000 --- a/src/clipper2/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(Clipper2 VERSION 1.0.6 LANGUAGES C CXX) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -set_property(GLOBAL PROPERTY USE_FOLDERS OFF) - -option(BUILD_SHARED_LIBS "Build shared libs" OFF) - -include(GNUInstallDirs) - -set(CLIPPER2_INC - Clipper2Lib/include/clipper2/clipper.h - Clipper2Lib/include/clipper2/clipper.core.h - Clipper2Lib/include/clipper2/clipper.engine.h - Clipper2Lib/include/clipper2/clipper.export.h - Clipper2Lib/include/clipper2/clipper.minkowski.h - Clipper2Lib/include/clipper2/clipper.offset.h - Clipper2Lib/include/clipper2/clipper.rectclip.h -) - -set(CLIPPER2_SRC - Clipper2Lib/src/clipper.engine.cpp - Clipper2Lib/src/clipper.offset.cpp - Clipper2Lib/src/clipper.rectclip.cpp -) - -# 2d version of Clipper2 -add_library(Clipper2 ${CLIPPER2_INC} ${CLIPPER2_SRC}) - -target_include_directories(Clipper2 - PUBLIC Clipper2Lib/include -) - -if (WIN32) - target_compile_options(Clipper2 PRIVATE /W4 /WX) -else() - target_compile_options(Clipper2 PRIVATE -Wall -Wextra -Wpedantic -Werror) - target_link_libraries(Clipper2 PUBLIC -lm) - -endif() - -set_target_properties(Clipper2 PROPERTIES FOLDER Libraries - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - PUBLIC_HEADER "${CLIPPER2_INC}" -) - diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.core.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.core.h deleted file mode 100644 index a02ba63e3d..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.core.h +++ /dev/null @@ -1,653 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 4 November 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : Core Clipper Library structures and functions * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_CORE_H -#define CLIPPER_CORE_H - -#include -#include -#include -#include -#include -#include -#include - -//#define NO_EXCEPTIONS - -namespace Clipper2Lib -{ - -#ifndef NO_EXCEPTIONS - static const char* precision_error = - "Precision exceeds the permitted range"; -#endif - - static double const PI = 3.141592653589793238; - static int64_t const MAX_COORD = LLONG_MAX / 2; - - //By far the most widely used filling rules for polygons are EvenOdd - //and NonZero, sometimes called Alternate and Winding respectively. - //https://en.wikipedia.org/wiki/Nonzero-rule - enum class FillRule { EvenOdd, NonZero, Positive, Negative }; - -// Point ------------------------------------------------------------------------ - -template -struct Point { - T x; - T y; -#ifdef USINGZ - int64_t z; - - template - inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0) - { - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - x = static_cast(std::round(x_)); - y = static_cast(std::round(y_)); - z = z_; - } - else - { - x = static_cast(x_); - y = static_cast(y_); - z = z_; - } - } - - explicit Point() : x(0), y(0), z(0) {}; - - template - Point(const T2 x_, const T2 y_, const int64_t z_ = 0) - { - Init(x_, y_); - z = z_; - } - - template - explicit Point(const Point& p) - { - Init(p.x, p.y, p.z); - } - - Point operator * (const double scale) const - { - return Point(x * scale, y * scale, z); - } - - - friend std::ostream& operator<<(std::ostream& os, const Point& point) - { - os << point.x << "," << point.y << "," << point.z; - return os; - } - -#else - - template - inline void Init(const T2 x_ = 0, const T2 y_ = 0) - { - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - x = static_cast(std::round(x_)); - y = static_cast(std::round(y_)); - } - else - { - x = static_cast(x_); - y = static_cast(y_); - } - } - - explicit Point() : x(0), y(0) {}; - - template - Point(const T2 x_, const T2 y_) { Init(x_, y_); } - - template - explicit Point(const Point& p) { Init(p.x, p.y); } - - Point operator * (const double scale) const - { - return Point(x * scale, y * scale); - } - - friend std::ostream& operator<<(std::ostream& os, const Point& point) - { - os << point.x << "," << point.y; - return os; - } -#endif - - friend bool operator==(const Point &a, const Point &b) - { - return a.x == b.x && a.y == b.y; - } - - friend bool operator!=(const Point& a, const Point& b) - { - return !(a == b); - } - - inline Point operator-() const - { - return Point(-x,-y); - } - - inline Point operator+(const Point &b) const - { - return Point(x+b.x, y+b.y); - } - - inline Point operator-(const Point &b) const - { - return Point(x-b.x, y-b.y); - } - - inline void Negate() { x = -x; y = -y; } - -}; - -//nb: using 'using' here (instead of typedef) as they can be used in templates -using Point64 = Point; -using PointD = Point; - -template -using Path = std::vector>; -template -using Paths = std::vector>; - -using Path64 = Path; -using PathD = Path; -using Paths64 = std::vector< Path64>; -using PathsD = std::vector< PathD>; - -template -std::ostream& operator << (std::ostream& outstream, const Path& path) -{ - if (!path.empty()) - { - auto pt = path.cbegin(), last = path.cend() - 1; - while (pt != last) - outstream << *pt++ << ", "; - outstream << *last << std::endl; - } - return outstream; -} - -template -std::ostream& operator << (std::ostream& outstream, const Paths& paths) -{ - for (auto p : paths) - outstream << p; - return outstream; -} - -template -inline Path ScalePath(const Path& path, double scale) -{ - Path result; - result.reserve(path.size()); -#ifdef USINGZ - for (const Point& pt : path) - result.push_back(Point(pt.x * scale, pt.y * scale, pt.z)); -#else - for (const Point& pt : path) - result.push_back(Point(pt.x * scale, pt.y * scale)); -#endif - return result; -} - -template -inline Paths ScalePaths(const Paths& paths, double scale) -{ - Paths result; - result.reserve(paths.size()); - for (const Path& path : paths) - result.push_back(ScalePath(path, scale)); - return result; -} - -template -inline Path TransformPath(const Path& path) -{ - Path result; - result.reserve(path.size()); - std::transform(path.cbegin(), path.cend(), std::back_inserter(result), - [](const Point& pt) {return Point(pt); }); - return result; -} - -template -inline Paths TransformPaths(const Paths& paths) -{ - Paths result; - std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result), - [](const Path& path) {return TransformPath(path); }); - return result; -} - -inline PathD Path64ToPathD(const Path64& path) -{ - return TransformPath(path); -} - -inline PathsD Paths64ToPathsD(const Paths64& paths) -{ - return TransformPaths(paths); -} - -inline Path64 PathDToPath64(const PathD& path) -{ - return TransformPath(path); -} - -inline Paths64 PathsDToPaths64(const PathsD& paths) -{ - return TransformPaths(paths); -} - -template -inline double Sqr(T val) -{ - return static_cast(val) * static_cast(val); -} - -template -inline bool NearEqual(const Point& p1, - const Point& p2, double max_dist_sqrd) -{ - return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd; -} - -template -inline Path StripNearEqual(const Path& path, - double max_dist_sqrd, bool is_closed_path) -{ - if (path.size() == 0) return Path(); - Path result; - 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); - 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); - } - } - if (!is_closed_path) return result; - while (result.size() > 1 && - NearEqual(result.back(), first_pt, max_dist_sqrd)) result.pop_back(); - return result; -} - -template -inline Paths StripNearEqual(const Paths& paths, - double max_dist_sqrd, bool is_closed_path) -{ - Paths result; - result.reserve(paths.size()); - 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)); - } - return result; -} - -template -inline Path StripDuplicates(const Path& path, bool is_closed_path) -{ - if (path.size() == 0) return Path(); - Path result; - 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); - for (; path_iter != path.cend(); ++path_iter) - { - if (*path_iter != last_pt) - { - last_pt = *path_iter; - result.push_back(last_pt); - } - } - if (!is_closed_path) return result; - while (result.size() > 1 && result.back() == first_pt) result.pop_back(); - return result; -} - -template -inline Paths StripDuplicates(const Paths& paths, bool is_closed_path) -{ - Paths result; - result.reserve(paths.size()); - for (typename Paths::const_iterator paths_citer = paths.cbegin(); - paths_citer != paths.cend(); ++paths_citer) - { - result.push_back(StripDuplicates(*paths_citer, is_closed_path)); - } - return result; -} - -// Rect ------------------------------------------------------------------------ - -template -struct Rect; - -using Rect64 = Rect; -using RectD = Rect; - -template -struct Rect { - T left; - T top; - T right; - T bottom; - - Rect() : - left(0), - top(0), - right(0), - bottom(0) {} - - Rect(T l, T t, T r, T b) : - left(l), - top(t), - right(r), - bottom(b) {} - - - T Width() const { return right - left; } - T Height() const { return bottom - top; } - void Width(T width) { right = left + width; } - void Height(T height) { bottom = top + height; } - - Point MidPoint() const - { - return Point((left + right) / 2, (top + bottom) / 2); - } - - Path AsPath() const - { - 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)); - return result; - } - - bool Contains(const Point& pt) const - { - return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom; - } - - bool Contains(const Rect& rec) const - { - return rec.left >= left && rec.right <= right && - rec.top >= top && rec.bottom <= bottom; - } - - void Scale(double scale) { - left *= scale; - top *= scale; - right *= scale; - bottom *= scale; - } - - bool IsEmpty() const { return bottom <= top || right <= left; }; - - bool Intersects(const Rect& rec) const - { - return (std::max(left, rec.left) < std::min(right, rec.right)) && - (std::max(top, rec.top) < std::min(bottom, rec.bottom)); - }; - - friend std::ostream &operator<<(std::ostream &os, const Rect &rect) { - os << "(" - << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom - << ")"; - return os; - } -}; - -template -inline Rect ScaleRect(const Rect& rect, double scale) -{ - Rect result; - - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - result.left = static_cast(std::round(rect.left * scale)); - result.top = static_cast(std::round(rect.top * scale)); - result.right = static_cast(std::round(rect.right * scale)); - result.bottom = static_cast(std::round(rect.bottom * scale)); - } - else - { - result.left = rect.left * scale; - result.top = rect.top * scale; - result.right = rect.right * scale; - result.bottom = rect.bottom * scale; - } - return result; -} - -// clipper2Exception --------------------------------------------------------- - -#ifndef NO_EXCEPTIONS -class Clipper2Exception : public std::exception { -public: - explicit Clipper2Exception(const char *description) : - m_descr(description) {} - virtual const char *what() const throw() { return m_descr.c_str(); } - -private: - std::string m_descr; -}; -#endif - -// Miscellaneous ------------------------------------------------------------ - -inline void CheckPrecision(int& precision) -{ - if (precision >= -8 && precision <= 8) return; -#ifdef NO_EXCEPTIONS - precision = precision > 8 ? 8 : -8; -#else - throw Clipper2Exception(precision_error); -#endif -} - -template -inline double CrossProduct(const Point& pt1, const Point& pt2, const Point& pt3) { - return (static_cast(pt2.x - pt1.x) * static_cast(pt3.y - - pt2.y) - static_cast(pt2.y - pt1.y) * static_cast(pt3.x - pt2.x)); -} - -template -inline double CrossProduct(const Point& vec1, const Point& vec2) -{ - return static_cast(vec1.y * vec2.x) - static_cast(vec2.y * vec1.x); -} - -template -inline double DotProduct(const Point& pt1, const Point& pt2, const Point& pt3) { - return (static_cast(pt2.x - pt1.x) * static_cast(pt3.x - pt2.x) + - static_cast(pt2.y - pt1.y) * static_cast(pt3.y - pt2.y)); -} - -template -inline double DotProduct(const Point& vec1, const Point& vec2) -{ - return static_cast(vec1.x * vec2.x) + static_cast(vec1.y * vec2.y); -} - -template -inline double DistanceSqr(const Point pt1, const Point pt2) -{ - return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y); -} - -template -inline double DistanceFromLineSqrd(const Point& pt, const Point& ln1, const Point& ln2) -{ - //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - //see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = static_cast(ln1.y - ln2.y); - double B = static_cast(ln2.x - ln1.x); - double C = A * ln1.x + B * ln1.y; - C = A * pt.x + B * pt.y - C; - return (C * C) / (A * A + B * B); -} - -template -inline double Area(const Path& path) -{ - size_t cnt = path.size(); - if (cnt < 3) return 0.0; - double a = 0.0; - typename Path::const_iterator it1, it2 = path.cend() - 1, stop = it2; - if (!(cnt & 1)) ++stop; - for (it1 = path.cbegin(); it1 != stop;) - { - a += static_cast(it2->y + it1->y) * (it2->x - it1->x); - it2 = it1 + 1; - a += static_cast(it1->y + it2->y) * (it1->x - it2->x); - it1 += 2; - } - if (cnt & 1) - a += static_cast(it2->y + it1->y) * (it2->x - it1->x); - return a * 0.5; -} - -template -inline double Area(const Paths& paths) -{ - double a = 0.0; - for (typename Paths::const_iterator paths_iter = paths.cbegin(); - paths_iter != paths.cend(); ++paths_iter) - { - a += Area(*paths_iter); - } - return a; -} - -template -inline bool IsPositive(const Path& poly) -{ - // A curve has positive orientation [and area] if a region 'R' - // is on the left when traveling around the outside of 'R'. - //https://mathworld.wolfram.com/CurveOrientation.html - //nb: This statement is premised on using Cartesian coordinates - return Area(poly) >= 0; -} - -inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b, - const Point64& seg2a, const 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; - return (res1 || res2 || res3 || res4); // ensures not collinear - } - else { - double dx1 = static_cast(seg1a.x - seg1b.x); - double dy1 = static_cast(seg1a.y - seg1b.y); - double dx2 = static_cast(seg2a.x - seg2b.x); - double dy2 = static_cast(seg2a.y - seg2b.y); - return (((dy1 * (seg2a.x - seg1a.x) - dx1 * (seg2a.y - seg1a.y)) * - (dy1 * (seg2b.x - seg1a.x) - dx1 * (seg2b.y - seg1a.y)) < 0) && - ((dy2 * (seg1a.x - seg2a.x) - dx2 * (seg1a.y - seg2a.y)) * - (dy2 * (seg1b.x - seg2a.x) - dx2 * (seg1b.y - seg2a.y)) < 0)); - } -} - - -enum class PointInPolygonResult { IsOn, IsInside, IsOutside }; - -template -inline PointInPolygonResult PointInPolygon(const Point& pt, const Path& polygon) -{ - if (polygon.size() < 3) - return PointInPolygonResult::IsOutside; - - int val = 0; - typename Path::const_iterator start = polygon.cbegin(), cit = start; - typename Path::const_iterator cend = polygon.cend(), pit = cend - 1; - - while (pit->y == pt.y) - { - if (pit == start) return PointInPolygonResult::IsOutside; - --pit; - } - bool is_above = pit->y < pt.y; - - while (cit != cend) - { - if (is_above) - { - while (cit != cend && cit->y < pt.y) ++cit; - if (cit == cend) break; - } - else - { - while (cit != cend && cit->y > pt.y) ++cit; - if (cit == cend) break; - } - - if (cit == start) pit = cend - 1; - else pit = cit - 1; - - if (cit->y == pt.y) - { - if (cit->x == pt.x || (cit->y == pit->y && - ((pt.x < pit->x) != (pt.x < cit->x)))) - return PointInPolygonResult::IsOn; - ++cit; - continue; - } - - if (pt.x < cit->x && pt.x < pit->x) - { - // we're only interested in edges crossing on the left - } - else if (pt.x > pit->x && pt.x > cit->x) - val = 1 - val; // toggle val - else - { - double d = CrossProduct(*pit, *cit, pt); - if (d == 0) return PointInPolygonResult::IsOn; - if ((d < 0) == is_above) val = 1 - val; - } - is_above = !is_above; - ++cit; - } - return (val == 0) ? - PointInPolygonResult::IsOutside : - PointInPolygonResult::IsInside; -} - -} // namespace - -#endif // CLIPPER_CORE_H diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h deleted file mode 100644 index 67383f2130..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h +++ /dev/null @@ -1,588 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 4 November 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : This is the main polygon clipping module * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_ENGINE_H -#define CLIPPER_ENGINE_H - -constexpr auto CLIPPER2_VERSION = "1.0.6"; - -#include -#include -#include -#include -#include -#include -#include -#include "clipper.core.h" - -namespace Clipper2Lib { - - struct Scanline; - struct IntersectNode; - struct Active; - struct Vertex; - struct LocalMinima; - struct OutRec; - struct Joiner; - - //Note: all clipping operations except for Difference are commutative. - enum class ClipType { None, Intersection, Union, Difference, Xor }; - - enum class PathType { Subject, Clip }; - - enum class VertexFlags : uint32_t { - None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 - }; - - constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) - { - return (enum VertexFlags)(uint32_t(a) & uint32_t(b)); - } - - constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b) - { - return (enum VertexFlags)(uint32_t(a) | uint32_t(b)); - } - - struct Vertex { - Point64 pt; - Vertex* next = nullptr; - Vertex* prev = nullptr; - VertexFlags flags = VertexFlags::None; - }; - - struct OutPt { - Point64 pt; - OutPt* next = nullptr; - OutPt* prev = nullptr; - OutRec* outrec; - Joiner* joiner = nullptr; - - OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) { - next = this; - prev = this; - } - }; - - class PolyPath; - class PolyPath64; - class PolyPathD; - using PolyTree64 = PolyPath64; - using PolyTreeD = PolyPathD; - - struct OutRec; - typedef std::vector OutRecList; - - //OutRec: contains a path in the clipping solution. Edges in the AEL will - //have OutRec pointers assigned when they form part of the clipping solution. - struct OutRec { - size_t idx = 0; - OutRec* owner = nullptr; - OutRecList* splits = nullptr; - Active* front_edge = nullptr; - Active* back_edge = nullptr; - OutPt* pts = nullptr; - PolyPath* polypath = nullptr; - Rect64 bounds = {}; - Path64 path; - bool is_open = false; - ~OutRec() { if (splits) delete splits; }; - }; - - /////////////////////////////////////////////////////////////////// - //Important: UP and DOWN here are premised on Y-axis positive down - //displays, which is the orientation used in Clipper's development. - /////////////////////////////////////////////////////////////////// - - struct Active { - Point64 bot; - Point64 top; - int64_t curr_x = 0; //current (updated at every new scanline) - double dx = 0.0; - int wind_dx = 1; //1 or -1 depending on winding direction - int wind_cnt = 0; - int wind_cnt2 = 0; //winding count of the opposite polytype - OutRec* outrec = nullptr; - //AEL: 'active edge list' (Vatti's AET - active edge table) - // a linked list of all edges (from left to right) that are present - // (or 'active') within the current scanbeam (a horizontal 'beam' that - // sweeps from bottom to top over the paths in the clipping operation). - Active* prev_in_ael = nullptr; - Active* next_in_ael = nullptr; - //SEL: 'sorted edge list' (Vatti's ST - sorted table) - // linked list used when sorting edges into their new positions at the - // top of scanbeams, but also (re)used to process horizontals. - Active* prev_in_sel = nullptr; - Active* next_in_sel = nullptr; - Active* jump = nullptr; - Vertex* vertex_top = nullptr; - LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti) - bool is_left_bound = false; - }; - - struct LocalMinima { - Vertex* vertex; - PathType polytype; - bool is_open; - LocalMinima(Vertex* v, PathType pt, bool open) : - vertex(v), polytype(pt), is_open(open){} - }; - - struct IntersectNode { - Point64 pt; - Active* edge1; - Active* edge2; - IntersectNode() : pt(Point64(0, 0)), edge1(NULL), edge2(NULL) {} - IntersectNode(Active* e1, Active* e2, Point64& pt_) : - pt(pt_), edge1(e1), edge2(e2) - { - } - }; - -#ifdef USINGZ - typedef std::function ZCallback64; - - typedef std::function ZCallbackD; -#endif - - // ClipperBase ------------------------------------------------------------- - - class ClipperBase { - private: - ClipType cliptype_ = ClipType::None; - FillRule fillrule_ = FillRule::EvenOdd; - FillRule fillpos = FillRule::Positive; - int64_t bot_y_ = 0; - bool minima_list_sorted_ = false; - bool using_polytree_ = false; - Active* actives_ = nullptr; - Active *sel_ = nullptr; - Joiner *horz_joiners_ = nullptr; - std::vector minima_list_; //pointers in case of memory reallocs - std::vector::iterator current_locmin_iter_; - std::vector vertex_lists_; - std::priority_queue scanline_list_; - std::vector intersect_nodes_; - std::vector joiner_list_; //pointers in case of memory reallocs - void Reset(); - void InsertScanline(int64_t y); - bool PopScanline(int64_t &y); - bool PopLocalMinima(int64_t y, LocalMinima *&local_minima); - void DisposeAllOutRecs(); - void DisposeVerticesAndLocalMinima(); - void DeleteEdges(Active*& e); - void AddLocMin(Vertex &vert, PathType polytype, bool is_open); - bool IsContributingClosed(const Active &e) const; - inline bool IsContributingOpen(const Active &e) const; - void SetWindCountForClosedPathEdge(Active &edge); - void SetWindCountForOpenPathEdge(Active &e); - void InsertLocalMinimaIntoAEL(int64_t bot_y); - void InsertLeftEdge(Active &e); - inline void PushHorz(Active &e); - inline bool PopHorz(Active *&e); - inline OutPt* StartOpenPath(Active &e, const Point64& pt); - inline void UpdateEdgeIntoAEL(Active *e); - OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt); - inline void DeleteFromAEL(Active &e); - inline void AdjustCurrXAndCopyToSEL(const int64_t top_y); - void DoIntersections(const int64_t top_y); - void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y); - bool BuildIntersectList(const int64_t top_y); - void ProcessIntersectList(); - void SwapPositionsInAEL(Active& edge1, Active& edge2); - OutPt* AddOutPt(const Active &e, const Point64& pt); - OutPt* AddLocalMinPoly(Active &e1, Active &e2, - const Point64& pt, bool is_new = false); - OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt); - void DoHorizontal(Active &horz); - bool ResetHorzDirection(const Active &horz, const Active *max_pair, - int64_t &horz_left, int64_t &horz_right); - void DoTopOfScanbeam(const int64_t top_y); - Active *DoMaxima(Active &e); - void JoinOutrecPaths(Active &e1, Active &e2); - void CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec); - bool ValidateClosedPathEx(OutPt*& outrec); - void CleanCollinear(OutRec* outrec); - void FixSelfIntersects(OutRec* outrec); - void DoSplitOp(OutRec* outRec, OutPt* splitOp); - Joiner* GetHorzTrialParent(const OutPt* op); - bool OutPtInTrialHorzList(OutPt* op); - void SafeDisposeOutPts(OutPt*& op); - void SafeDeleteOutPtJoiners(OutPt* op); - void AddTrialHorzJoin(OutPt* op); - void DeleteTrialHorzJoin(OutPt* op); - void ConvertHorzTrialsToJoins(); - void AddJoin(OutPt* op1, OutPt* op2); - void DeleteJoin(Joiner* joiner); - void ProcessJoinerList(); - OutRec* ProcessJoin(Joiner* joiner); - protected: - bool has_open_paths_ = false; - bool succeeded_ = true; - std::vector outrec_list_; //pointers in case list memory reallocated - bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees); - bool DeepCheckOwner(OutRec* outrec, OutRec* owner); -#ifdef USINGZ - ZCallback64 zCallback_ = nullptr; - void SetZ(const Active& e1, const Active& e2, Point64& pt); -#endif - void CleanUp(); // unlike Clear, CleanUp preserves added paths - void AddPath(const Path64& path, PathType polytype, bool is_open); - void AddPaths(const Paths64& paths, PathType polytype, bool is_open); - public: - virtual ~ClipperBase(); - bool PreserveCollinear = true; - bool ReverseSolution = false; - void Clear(); - }; - - // PolyPath / PolyTree -------------------------------------------------------- - - //PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned - //by clipping operations. While this structure is more complex than the - //alternative Paths structure, it does preserve path 'ownership' - ie those - //paths that contain (or own) other paths. This will be useful to some users. - - class PolyPath { - protected: - PolyPath* parent_; - public: - PolyPath(PolyPath* parent = nullptr): parent_(parent){} - virtual ~PolyPath() { Clear(); }; - //https://en.cppreference.com/w/cpp/language/rule_of_three - PolyPath(const PolyPath&) = delete; - PolyPath& operator=(const PolyPath&) = delete; - - unsigned Level() const - { - unsigned result = 0; - const PolyPath* p = parent_; - while (p) { ++result; p = p->parent_; } - return result; - } - - virtual PolyPath* AddChild(const Path64& path) = 0; - - virtual void Clear() {}; - virtual size_t Count() const { return 0; } - - const PolyPath* Parent() const { return parent_; } - - bool IsHole() const - { - const PolyPath* pp = parent_; - bool is_hole = pp; - while (pp) { - is_hole = !is_hole; - pp = pp->parent_; - } - return is_hole; - } - }; - - class PolyPath64 : public PolyPath { - private: - std::vector childs_; - Path64 polygon_; - typedef typename std::vector::const_iterator pp64_itor; - public: - PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {} - PolyPath64* operator [] (size_t index) { return static_cast(childs_[index]); } - pp64_itor begin() const { return childs_.cbegin(); } - pp64_itor end() const { return childs_.cend(); } - - PolyPath64* AddChild(const Path64& path) override - { - PolyPath64* result = new PolyPath64(this); - childs_.push_back(result); - result->polygon_ = path; - return result; - } - - void Clear() override - { - for (const PolyPath64* child : childs_) delete child; - childs_.resize(0); - } - - size_t Count() const override - { - return childs_.size(); - } - - const Path64& Polygon() const { return polygon_; }; - - double Area() const - { - double result = Clipper2Lib::Area(polygon_); - for (const PolyPath64* child : childs_) - result += child->Area(); - return result; - } - - friend std::ostream& operator << (std::ostream& outstream, const PolyPath64& polypath) - { - const size_t level_indent = 4; - const size_t coords_per_line = 4; - const size_t last_on_line = coords_per_line - 1; - unsigned level = polypath.Level(); - if (level > 0) - { - std::string level_padding; - level_padding.insert(0, (level - 1) * level_indent, ' '); - std::string caption = polypath.IsHole() ? "Hole " : "Outer Polygon "; - std::string childs = polypath.Count() == 1 ? " child" : " children"; - outstream << level_padding.c_str() << caption << "with " << polypath.Count() << childs << std::endl; - outstream << level_padding; - size_t i = 0, highI = polypath.Polygon().size() - 1; - for (; i < highI; ++i) - { - outstream << polypath.Polygon()[i] << ' '; - if ((i % coords_per_line) == last_on_line) - outstream << std::endl << level_padding; - } - if (highI > 0) outstream << polypath.Polygon()[i]; - outstream << std::endl; - } - for (auto child : polypath) - outstream << *child; - return outstream; - } - - }; - - class PolyPathD : public PolyPath { - private: - std::vector childs_; - double inv_scale_; - PathD polygon_; - typedef typename std::vector::const_iterator ppD_itor; - public: - PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent) - { - inv_scale_ = parent ? parent->inv_scale_ : 1.0; - } - PolyPathD* operator [] (size_t index) - { - return static_cast(childs_[index]); - } - ppD_itor begin() const { return childs_.cbegin(); } - ppD_itor end() const { return childs_.cend(); } - - void SetInvScale(double value) { inv_scale_ = value; } - double InvScale() { return inv_scale_; } - PolyPathD* AddChild(const Path64& path) override - { - PolyPathD* result = new PolyPathD(this); - childs_.push_back(result); - result->polygon_ = ScalePath(path, inv_scale_); - return result; - } - - void Clear() override - { - for (const PolyPathD* child : childs_) delete child; - childs_.resize(0); - } - - size_t Count() const override - { - return childs_.size(); - } - - const PathD& Polygon() const { return polygon_; }; - - double Area() const - { - double result = Clipper2Lib::Area(polygon_); - for (const PolyPathD* child : childs_) - result += child->Area(); - return result; - } - }; - - class Clipper64 : public ClipperBase - { - private: - void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen); - void BuildTree64(PolyPath64& polytree, Paths64& open_paths); - public: -#ifdef USINGZ - void SetZCallback(ZCallback64 cb) { zCallback_ = cb; } -#endif - - void AddSubject(const Paths64& subjects) - { - AddPaths(subjects, PathType::Subject, false); - } - void AddOpenSubject(const Paths64& open_subjects) - { - AddPaths(open_subjects, PathType::Subject, true); - } - void AddClip(const Paths64& clips) - { - AddPaths(clips, PathType::Clip, false); - } - - bool Execute(ClipType clip_type, - FillRule fill_rule, Paths64& closed_paths) - { - Paths64 dummy; - return Execute(clip_type, fill_rule, closed_paths, dummy); - } - - bool Execute(ClipType clip_type, FillRule fill_rule, - Paths64& closed_paths, Paths64& open_paths) - { - closed_paths.clear(); - open_paths.clear(); - if (ExecuteInternal(clip_type, fill_rule, false)) - BuildPaths64(closed_paths, &open_paths); - CleanUp(); - return succeeded_; - } - - bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree) - { - Paths64 dummy; - return Execute(clip_type, fill_rule, polytree, dummy); - } - - bool Execute(ClipType clip_type, - FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths) - { - if (ExecuteInternal(clip_type, fill_rule, true)) - { - open_paths.clear(); - polytree.Clear(); - BuildTree64(polytree, open_paths); - } - CleanUp(); - return succeeded_; - } - }; - - class ClipperD : public ClipperBase { - private: - double scale_ = 1.0, invScale_ = 1.0; -#ifdef USINGZ - ZCallbackD zCallback_ = nullptr; -#endif - void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen); - void BuildTreeD(PolyPathD& polytree, PathsD& open_paths); - public: - explicit ClipperD(int precision = 2) : ClipperBase() - { - CheckPrecision(precision); - // to optimize scaling / descaling precision - // set the scale to a power of double's radix (2) (#25) - scale_ = std::pow(std::numeric_limits::radix, - std::ilogb(std::pow(10, precision)) + 1); - invScale_ = 1 / scale_; - } - -#ifdef USINGZ - void SetZCallback(ZCallbackD cb) { zCallback_ = cb; }; - - void ZCB(const Point64& e1bot, const Point64& e1top, - const Point64& e2bot, const Point64& e2top, Point64& pt) - { - // de-scale (x & y) - // temporarily convert integers to their initial float values - // this will slow clipping marginally but will make it much easier - // to understand the coordinates passed to the callback function - PointD tmp = PointD(pt) * invScale_; - PointD e1b = PointD(e1bot) * invScale_; - PointD e1t = PointD(e1top) * invScale_; - PointD e2b = PointD(e2bot) * invScale_; - PointD e2t = PointD(e2top) * invScale_; - zCallback_(e1b,e1t, e2b, e2t, tmp); - pt.z = tmp.z; // only update 'z' - }; - - void CheckCallback() - { - if(zCallback_) - // if the user defined float point callback has been assigned - // then assign the proxy callback function - ClipperBase::zCallback_ = - std::bind(&ClipperD::ZCB, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, - std::placeholders::_4, std::placeholders::_5); - else - ClipperBase::zCallback_ = nullptr; - } - -#endif - - void AddSubject(const PathsD& subjects) - { - AddPaths(ScalePaths(subjects, scale_), PathType::Subject, false); - } - - void AddOpenSubject(const PathsD& open_subjects) - { - AddPaths(ScalePaths(open_subjects, scale_), PathType::Subject, true); - } - - void AddClip(const PathsD& clips) - { - AddPaths(ScalePaths(clips, scale_), PathType::Clip, false); - } - - bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths) - { - PathsD dummy; - return Execute(clip_type, fill_rule, closed_paths, dummy); - } - - bool Execute(ClipType clip_type, - FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths) - { -#ifdef USINGZ - CheckCallback(); -#endif - if (ExecuteInternal(clip_type, fill_rule, false)) - { - BuildPathsD(closed_paths, &open_paths); - } - CleanUp(); - return succeeded_; - } - - bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree) - { - PathsD dummy; - return Execute(clip_type, fill_rule, polytree, dummy); - } - - bool Execute(ClipType clip_type, - FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths) - { -#ifdef USINGZ - CheckCallback(); -#endif - if (ExecuteInternal(clip_type, fill_rule, true)) - { - polytree.Clear(); - polytree.SetInvScale(invScale_); - open_paths.clear(); - BuildTreeD(polytree, open_paths); - } - CleanUp(); - return succeeded_; - } - - }; - -} // namespace - -#endif // CLIPPER_ENGINE_H diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.export.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.export.h deleted file mode 100644 index e20ac91332..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.export.h +++ /dev/null @@ -1,830 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 30 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : This module exports the Clipper2 Library (ie DLL/so) * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -// The exported functions below refer to simple structures that -// can be understood across multiple languages. Consequently -// Path64, PathD, Polytree64 etc are converted from C++ classes -// (std::vector<> etc) into the following data structures: -// -// CPath64 (int64_t*) & CPathD (double_t*): -// Path64 and PathD are converted into arrays of x,y coordinates. -// However in these arrays the first x,y coordinate pair is a -// counter with 'x' containing the number of following coordinate -// pairs. ('y' should be 0, with one exception explained below.) -// __________________________________ -// |counter|coord1|coord2|...|coordN| -// |N ,0 |x1, y1|x2, y2|...|xN, yN| -// __________________________________ -// -// CPaths64 (int64_t**) & CPathsD (double_t**): -// These are arrays of pointers to CPath64 and CPathD where -// the first pointer is to a 'counter path'. This 'counter -// path' has a single x,y coord pair with 'y' (not 'x') -// containing the number of paths that follow. ('x' = 0). -// _______________________________ -// |counter|path1|path2|...|pathN| -// |addr0 |addr1|addr2|...|addrN| (*addr0[0]=0; *addr0[1]=N) -// _______________________________ -// -// The structures of CPolytree64 and CPolytreeD are defined -// below and these structures don't need to be explained here. - -#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" - -namespace Clipper2Lib { - -typedef int64_t* CPath64; -typedef int64_t** CPaths64; -typedef double* CPathD; -typedef double** CPathsD; - -typedef struct CPolyPath64 { - CPath64 polygon; - uint32_t is_hole; - uint32_t child_count; - CPolyPath64* childs; -} -CPolyTree64; - -typedef struct CPolyPathD { - CPathD polygon; - uint32_t is_hole; - uint32_t child_count; - CPolyPathD* childs; -} -CPolyTreeD; - -template -struct CRect { - T left; - T top; - T right; - T bottom; -}; - -typedef CRect CRect64; -typedef CRect CRectD; - -template -inline bool CRectIsEmpty(const CRect& rect) -{ - return (rect.right <= rect.left) || (rect.bottom <= rect.top); -} - -template -inline Rect CRectToRect(const CRect& rect) -{ - Rect result; - result.left = rect.left; - result.top = rect.top; - result.right = rect.right; - result.bottom = rect.bottom; - return result; -} - -#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) - -////////////////////////////////////////////////////// -// EXPORTED FUNCTION DEFINITIONS -////////////////////////////////////////////////////// - -EXTERN_DLL_EXPORT const char* Version(); - -// Some of the functions below will return data in the various CPath -// and CPolyTree structures which are pointers to heap allocated -// memory. Eventually this memory will need to be released with one -// of the following 'DisposeExported' functions. (This may be the -// only safe way to release this memory since the executable -// accessing these exported functions may use a memory manager that -// allocates and releases heap memory in a different way. Also, -// CPath structures that have been constructed by the executable -// should not be destroyed using these 'DisposeExported' functions.) -EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p); -EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp); -EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p); -EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp); -EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt); -EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt); - -// Boolean clipping: -// cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4 -// fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3 -EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, - uint8_t fillrule, const CPaths64 subjects, - const CPaths64 subjects_open, const CPaths64 clips, - CPaths64& solution, CPaths64& solution_open, - bool preserve_collinear = true, bool reverse_solution = false); -EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype, - uint8_t fillrule, const CPaths64 subjects, - const CPaths64 subjects_open, const CPaths64 clips, - CPolyTree64*& solution, CPaths64& solution_open, - bool preserve_collinear = true, bool reverse_solution = false); -EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype, - uint8_t fillrule, const CPathsD subjects, - const CPathsD subjects_open, const CPathsD clips, - CPathsD& solution, CPathsD& solution_open, int precision = 2, - bool preserve_collinear = true, bool reverse_solution = false); -EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype, - uint8_t fillrule, const CPathsD subjects, - const CPathsD subjects_open, const CPathsD clips, - CPolyTreeD*& solution, CPathsD& solution_open, int precision = 2, - bool preserve_collinear = true, bool reverse_solution = false); - -// Polygon offsetting (inflate/deflate): -// jointype: Square=0, Round=1, Miter=2 -// endtype: Polygon=0, Joined=1, Butt=2, Square=3, Round=4 -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); - -// RectClip & RectClipLines: -EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, - const CPaths64 paths); -EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, - const CPathsD paths, int precision = 2); -EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, - const CPaths64 paths); -EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, - const CPathsD paths, int precision = 2); - -////////////////////////////////////////////////////// -// INTERNAL FUNCTIONS -////////////////////////////////////////////////////// - -inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2); -inline CPath64 CreateCPath64(const Path64& p); -inline CPaths64 CreateCPaths64(const Paths64& pp); -inline Path64 ConvertCPath64(const CPath64& p); -inline Paths64 ConvertCPaths64(const CPaths64& pp); - -inline CPathD CreateCPathD(size_t cnt1, size_t cnt2); -inline CPathD CreateCPathD(const PathD& p); -inline CPathsD CreateCPathsD(const PathsD& pp); -inline PathD ConvertCPathD(const CPathD& p); -inline PathsD ConvertCPathsD(const CPathsD& pp); - -// the following function avoid multiple conversions -inline CPathD CreateCPathD(const Path64& p, double scale); -inline CPathsD CreateCPathsD(const Paths64& pp, double scale); -inline Path64 ConvertCPathD(const CPathD& p, double scale); -inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale); - -inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt); -inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale); - -EXTERN_DLL_EXPORT const char* Version() -{ - return CLIPPER2_VERSION; -} - -EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p) -{ - if (p) delete[] p; -} - -EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp) -{ - if (!pp) return; - CPaths64 v = pp; - CPath64 cnts = *v; - const size_t cnt = static_cast(cnts[1]); - for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1 - DisposeExportedCPath64(*v++); - delete[] pp; - pp = nullptr; -} - -EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p) -{ - if (p) delete[] p; -} - -EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp) -{ - if (!pp) return; - CPathsD v = pp; - CPathD cnts = *v; - size_t cnt = static_cast(cnts[1]); - for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1 - DisposeExportedCPathD(*v++); - delete[] pp; - pp = nullptr; -} - -EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, - uint8_t fillrule, const CPaths64 subjects, - const CPaths64 subjects_open, const CPaths64 clips, - CPaths64& solution, CPaths64& solution_open, - bool preserve_collinear, bool reverse_solution) -{ - if (cliptype > static_cast(ClipType::Xor)) return -4; - if (fillrule > static_cast(FillRule::Negative)) return -3; - - Paths64 sub, sub_open, clp, sol, sol_open; - sub = ConvertCPaths64(subjects); - sub_open = ConvertCPaths64(subjects_open); - clp = ConvertCPaths64(clips); - - Clipper64 clipper; - clipper.PreserveCollinear = preserve_collinear; - clipper.ReverseSolution = reverse_solution; - 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 = CreateCPaths64(sol); - solution_open = CreateCPaths64(sol_open); - return 0; //success !! -} - -EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype, - uint8_t fillrule, const CPaths64 subjects, - const CPaths64 subjects_open, const CPaths64 clips, - CPolyTree64*& solution, CPaths64& solution_open, - bool preserve_collinear, bool reverse_solution) -{ - if (cliptype > static_cast(ClipType::Xor)) return -4; - if (fillrule > static_cast(FillRule::Negative)) return -3; - Paths64 sub, sub_open, clp, sol_open; - sub = ConvertCPaths64(subjects); - sub_open = ConvertCPaths64(subjects_open); - clp = ConvertCPaths64(clips); - - PolyTree64 pt; - Clipper64 clipper; - clipper.PreserveCollinear = preserve_collinear; - clipper.ReverseSolution = reverse_solution; - 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), pt, sol_open)) - return -1; // clipping bug - should never happen :) - - solution = CreateCPolyTree64(pt); - solution_open = CreateCPaths64(sol_open); - return 0; //success !! -} - -EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype, - uint8_t fillrule, const CPathsD subjects, - const CPathsD subjects_open, const CPathsD clips, - CPathsD& solution, CPathsD& solution_open, int precision, - bool preserve_collinear, bool reverse_solution) -{ - 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); - - Paths64 sub, sub_open, clp, sol, sol_open; - sub = ConvertCPathsD(subjects, scale); - sub_open = ConvertCPathsD(subjects_open, scale); - clp = ConvertCPathsD(clips, scale); - - Clipper64 clipper; - clipper.PreserveCollinear = preserve_collinear; - clipper.ReverseSolution = reverse_solution; - 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; - - if (sol.size() > 0) solution = CreateCPathsD(sol, 1 / scale); - if (sol_open.size() > 0) - solution_open = CreateCPathsD(sol_open, 1 / scale); - return 0; -} - -EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype, - uint8_t fillrule, const CPathsD subjects, - const CPathsD subjects_open, const CPathsD clips, - CPolyTreeD*& solution, CPathsD& solution_open, int precision, - bool preserve_collinear, bool reverse_solution) -{ - 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); - Paths64 sub, sub_open, clp, sol_open; - sub = ConvertCPathsD(subjects, scale); - sub_open = ConvertCPathsD(subjects_open, scale); - clp = ConvertCPathsD(clips, scale); - - PolyTree64 sol; - Clipper64 clipper; - clipper.PreserveCollinear = preserve_collinear; - clipper.ReverseSolution = reverse_solution; - 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 = CreateCPolyTreeD(sol, 1 / scale); - if (sol_open.size() > 0) - solution_open = CreateCPathsD(sol_open, 1 / scale); - return 0; -} - -EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, - double delta, uint8_t jointype, uint8_t endtype, double miter_limit, - double arc_tolerance, bool reverse_solution) -{ - Paths64 pp; - pp = ConvertCPaths64(paths); - - ClipperOffset clip_offset( miter_limit, - arc_tolerance, reverse_solution); - clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); - Paths64 result = clip_offset.Execute(delta); - return CreateCPaths64(result); -} - -EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, - double delta, uint8_t jointype, uint8_t endtype, - int precision, double miter_limit, - double arc_tolerance, bool reverse_solution) -{ - if (precision < -8 || precision > 8 || !paths) return nullptr; - const double scale = std::pow(10, precision); - ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution); - Paths64 pp = ConvertCPathsD(paths, scale); - clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); - Paths64 result = clip_offset.Execute(delta * scale); - return CreateCPathsD(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 RectClip rc(r64); - Paths64 pp = ConvertCPaths64(paths); - Paths64 result; - result.reserve(pp.size()); - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r64.Intersects(pathRec)) continue; - - if (r64.Contains(pathRec)) - result.push_back(p); - else - { - Path64 p2 = rc.Execute(p); - if (!p2.empty()) result.push_back(std::move(p2)); - } - } - return CreateCPaths64(result); -} - -EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, - const CPathsD paths, int precision) -{ - if (CRectIsEmpty(rect) || !paths) return nullptr; - if (precision < -8 || precision > 8) return nullptr; - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(CRectToRect(rect), scale); - Paths64 pp = ConvertCPathsD(paths, scale); - class RectClip rc(r); - Paths64 result; - result.reserve(pp.size()); - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r.Intersects(pathRec)) continue; - - if (r.Contains(pathRec)) - result.push_back(p); - else - { - Path64 p2 = rc.Execute(p); - if (!p2.empty()) result.push_back(std::move(p2)); - } - } - return CreateCPathsD(result, 1/scale); -} - -EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, - const CPaths64 paths) -{ - if (CRectIsEmpty(rect) || !paths) return nullptr; - Rect64 r = CRectToRect(rect); - class RectClipLines rcl (r); - Paths64 pp = ConvertCPaths64(paths); - Paths64 result; - result.reserve(pp.size()); - - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r.Intersects(pathRec)) continue; - - if (r.Contains(pathRec)) - result.push_back(p); - else - { - Paths64 pp2 = rcl.Execute(p); - if (!pp2.empty()) - result.insert(result.end(), pp2.begin(), pp2.end()); - } - } - return CreateCPaths64(result); -} - -EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, - const CPathsD paths, int precision) -{ - Paths64 result; - if (CRectIsEmpty(rect) || !paths) return nullptr; - if (precision < -8 || precision > 8) return nullptr; - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(CRectToRect(rect), scale); - class RectClipLines rcl(r); - Paths64 pp = ConvertCPathsD(paths, scale); - - result.reserve(pp.size()); - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r.Intersects(pathRec)) continue; - - if (r.Contains(pathRec)) - result.push_back(p); - else - { - Paths64 pp2 = rcl.Execute(p); - if (pp2.empty()) continue; - result.insert(result.end(), pp2.begin(), pp2.end()); - } - } - return CreateCPathsD(result, 1/scale); -} - -inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2) -{ - // allocates memory for CPath64, fills in the counter, and - // returns the structure ready to be filled with path data - CPath64 result = new int64_t[2 + cnt1 *2]; - result[0] = cnt1; - result[1] = cnt2; - return result; -} - -inline CPath64 CreateCPath64(const Path64& p) -{ - // allocates memory for CPath64, fills the counter - // and returns the memory filled with path data - size_t cnt = p.size(); - if (!cnt) return nullptr; - CPath64 result = CreateCPath64(cnt, 0); - CPath64 v = result; - v += 2; // skip counters - for (const Point64& pt : p) - { - *v++ = pt.x; - *v++ = pt.y; - } - return result; -} - -inline Path64 ConvertCPath64(const CPath64& p) -{ - Path64 result; - if (p && *p) - { - CPath64 v = p; - const size_t cnt = static_cast(p[0]); - v += 2; // skip counters - result.reserve(cnt); - for (size_t i = 0; i < cnt; ++i) - { - // x,y here avoids right to left function evaluation - // result.push_back(Point64(*v++, *v++)); - int64_t x = *v++; - int64_t y = *v++; - result.push_back(Point64(x, y)); - } - } - return result; -} - -inline CPaths64 CreateCPaths64(const Paths64& pp) -{ - // allocates memory for multiple CPath64 and - // and returns this memory filled with path data - size_t cnt = pp.size(), cnt2 = cnt; - - // don't allocate space for empty paths - for (size_t i = 0; i < cnt; ++i) - if (!pp[i].size()) --cnt2; - if (!cnt2) return nullptr; - - CPaths64 result = new int64_t* [cnt2 + 1]; - CPaths64 v = result; - *v++ = CreateCPath64(0, cnt2); // assign a counter path - for (const Path64& p : pp) - { - *v = CreateCPath64(p); - if (*v) ++v; - } - return result; -} - -inline Paths64 ConvertCPaths64(const CPaths64& pp) -{ - Paths64 result; - if (pp) - { - CPaths64 v = pp; - CPath64 cnts = pp[0]; - const size_t cnt = static_cast(cnts[1]); // nb 2nd cnt - ++v; // skip cnts - result.reserve(cnt); - for (size_t i = 0; i < cnt; ++i) - result.push_back(ConvertCPath64(*v++)); - } - return result; -} - -inline CPathD CreateCPathD(size_t cnt1, size_t cnt2) -{ - // allocates memory for CPathD, fills in the counter, and - // returns the structure ready to be filled with path data - CPathD result = new double[2 + cnt1 * 2]; - result[0] = static_cast(cnt1); - result[1] = static_cast(cnt2); - return result; -} - -inline CPathD CreateCPathD(const PathD& p) -{ - // allocates memory for CPath, fills the counter - // and returns the memory fills with path data - size_t cnt = p.size(); - if (!cnt) return nullptr; - CPathD result = CreateCPathD(cnt, 0); - CPathD v = result; - v += 2; // skip counters - for (const PointD& pt : p) - { - *v++ = pt.x; - *v++ = pt.y; - } - return result; -} - -inline PathD ConvertCPathD(const CPathD& p) -{ - PathD result; - if (p) - { - CPathD v = p; - size_t cnt = static_cast(v[0]); - v += 2; // skip counters - result.reserve(cnt); - for (size_t i = 0; i < cnt; ++i) - { - // x,y here avoids right to left function evaluation - // result.push_back(PointD(*v++, *v++)); - double x = *v++; - double y = *v++; - result.push_back(PointD(x, y)); - } - } - return result; -} - -inline CPathsD CreateCPathsD(const PathsD& pp) -{ - size_t cnt = pp.size(), cnt2 = cnt; - // don't allocate space for empty paths - for (size_t i = 0; i < cnt; ++i) - if (!pp[i].size()) --cnt2; - if (!cnt2) return nullptr; - CPathsD result = new double * [cnt2 + 1]; - CPathsD v = result; - *v++ = CreateCPathD(0, cnt2); // assign counter path - for (const PathD& p : pp) - { - *v = CreateCPathD(p); - if (*v) { ++v; } - } - return result; -} - -inline PathsD ConvertCPathsD(const CPathsD& pp) -{ - PathsD result; - if (pp) - { - CPathsD v = pp; - CPathD cnts = v[0]; - size_t cnt = static_cast(cnts[1]); - ++v; // skip cnts path - result.reserve(cnt); - for (size_t i = 0; i < cnt; ++i) - result.push_back(ConvertCPathD(*v++)); - } - return result; -} - -inline Path64 ConvertCPathD(const CPathD& p, double scale) -{ - Path64 result; - if (p) - { - CPathD v = p; - size_t cnt = static_cast(*v); - v += 2; // skip counters - result.reserve(cnt); - for (size_t i = 0; i < cnt; ++i) - { - // x,y here avoids right to left function evaluation - // result.push_back(PointD(*v++, *v++)); - double x = *v++ * scale; - double y = *v++ * scale; - result.push_back(Point64(x, y)); - } - } - return result; -} - -inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale) -{ - Paths64 result; - if (pp) - { - CPathsD v = pp; - CPathD cnts = v[0]; - size_t cnt = static_cast(cnts[1]); - result.reserve(cnt); - ++v; // skip cnts path - for (size_t i = 0; i < cnt; ++i) - result.push_back(ConvertCPathD(*v++, scale)); - } - return result; -} - -inline CPathD CreateCPathD(const Path64& p, double scale) -{ - // allocates memory for CPathD, fills in the counter, and - // returns the structure filled with *scaled* path data - size_t cnt = p.size(); - if (!cnt) return nullptr; - CPathD result = CreateCPathD(cnt, 0); - CPathD v = result; - v += 2; // skip cnts - for (const Point64& pt : p) - { - *v++ = pt.x * scale; - *v++ = pt.y * scale; - } - return result; -} - -inline CPathsD CreateCPathsD(const Paths64& pp, double scale) -{ - // allocates memory for *multiple* CPathD, and - // returns the structure filled with scaled path data - size_t cnt = pp.size(), cnt2 = cnt; - // don't allocate space for empty paths - for (size_t i = 0; i < cnt; ++i) - if (!pp[i].size()) --cnt2; - if (!cnt2) return nullptr; - CPathsD result = new double* [cnt2 + 1]; - CPathsD v = result; - *v++ = CreateCPathD(0, cnt2); - for (const Path64& p : pp) - { - *v = CreateCPathD(p, scale); - if (*v) ++v; - } - return result; -} - -inline void InitCPolyPath64(CPolyTree64* cpt, - bool is_hole, const PolyPath64* pp) -{ - cpt->polygon = CreateCPath64(pp->Polygon()); - cpt->is_hole = is_hole; - size_t child_cnt = pp->Count(); - cpt->child_count = static_cast(child_cnt); - cpt->childs = nullptr; - if (!child_cnt) return; - cpt->childs = new CPolyPath64[child_cnt]; - CPolyPath64* child = cpt->childs; - for (const PolyPath64* pp_child : *pp) - InitCPolyPath64(child++, !is_hole, pp_child); -} - -inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt) -{ - CPolyTree64* result = new CPolyTree64(); - result->polygon = nullptr; - result->is_hole = false; - size_t child_cnt = pt.Count(); - result->childs = nullptr; - result->child_count = static_cast(child_cnt); - if (!child_cnt) return result; - result->childs = new CPolyPath64[child_cnt]; - CPolyPath64* child = result->childs; - for (const PolyPath64* pp : pt) - InitCPolyPath64(child++, true, pp); - return result; -} - -inline void DisposeCPolyPath64(CPolyPath64* cpp) -{ - if (!cpp->child_count) return; - CPolyPath64* child = cpp->childs; - for (size_t i = 0; i < cpp->child_count; ++i) - DisposeCPolyPath64(child); - delete[] cpp->childs; -} - -EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt) -{ - if (!cpt) return; - DisposeCPolyPath64(cpt); - delete cpt; - cpt = nullptr; -} - -inline void InitCPolyPathD(CPolyTreeD* cpt, - bool is_hole, const PolyPath64* pp, double scale) -{ - cpt->polygon = CreateCPathD(pp->Polygon(), scale); - cpt->is_hole = is_hole; - size_t child_cnt = pp->Count(); - cpt->child_count = static_cast(child_cnt); - cpt->childs = nullptr; - if (!child_cnt) return; - cpt->childs = new CPolyPathD[child_cnt]; - CPolyPathD* child = cpt->childs; - for (const PolyPath64* pp_child : *pp) - InitCPolyPathD(child++, !is_hole, pp_child, scale); -} - -inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale) -{ - CPolyTreeD* result = new CPolyTreeD(); - result->polygon = nullptr; - result->is_hole = false; - size_t child_cnt = pt.Count(); - result->child_count = static_cast(child_cnt); - result->childs = nullptr; - if (!child_cnt) return result; - result->childs = new CPolyPathD[child_cnt]; - CPolyPathD* child = result->childs; - for (const PolyPath64* pp : pt) - InitCPolyPathD(child++, true, pp, scale); - return result; -} - -inline void DisposeCPolyPathD(CPolyPathD* cpp) -{ - if (!cpp->child_count) return; - CPolyPathD* child = cpp->childs; - for (size_t i = 0; i < cpp->child_count; ++i) - DisposeCPolyPathD(child++); - delete[] cpp->childs; -} - -EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt) -{ - if (!cpt) return; - DisposeCPolyPathD(cpt); - delete cpt; - cpt = nullptr; -} - -} // end Clipper2Lib namespace - -#endif // CLIPPER2_EXPORT_H diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.h deleted file mode 100644 index 8bebfcf3cd..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.h +++ /dev/null @@ -1,762 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 29 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : This module provides a simple interface to the Clipper Library * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_H -#define CLIPPER_H - -#include -#include - -#include "clipper.core.h" -#include "clipper.engine.h" -#include "clipper.offset.h" -#include "clipper.minkowski.h" -#include "clipper.rectclip.h" - -namespace Clipper2Lib { - - static const Rect64 MaxInvalidRect64 = Rect64( - (std::numeric_limits::max)(), - (std::numeric_limits::max)(), - (std::numeric_limits::lowest)(), - (std::numeric_limits::lowest)()); - - static const RectD MaxInvalidRectD = RectD( - (std::numeric_limits::max)(), - (std::numeric_limits::max)(), - (std::numeric_limits::lowest)(), - (std::numeric_limits::lowest)()); - - inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule, - const Paths64& subjects, const Paths64& clips) - { - Paths64 result; - Clipper64 clipper; - clipper.AddSubject(subjects); - clipper.AddClip(clips); - clipper.Execute(cliptype, fillrule, result); - return result; - } - - inline void BooleanOp(ClipType cliptype, FillRule fillrule, - const Paths64& subjects, const Paths64& clips, PolyTree64& solution) - { - Paths64 sol_open; - Clipper64 clipper; - clipper.AddSubject(subjects); - clipper.AddClip(clips); - clipper.Execute(cliptype, fillrule, solution, sol_open); - } - - inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule, - const PathsD& subjects, const PathsD& clips, int decimal_prec = 2) - { - CheckPrecision(decimal_prec); - PathsD result; - ClipperD clipper(decimal_prec); - clipper.AddSubject(subjects); - clipper.AddClip(clips); - clipper.Execute(cliptype, fillrule, result); - return result; - } - - inline void BooleanOp(ClipType cliptype, FillRule fillrule, - const PathsD& subjects, const PathsD& clips, - PolyTreeD& polytree, int decimal_prec = 2) - { - CheckPrecision(decimal_prec); - PathsD result; - ClipperD clipper(decimal_prec); - clipper.AddSubject(subjects); - clipper.AddClip(clips); - clipper.Execute(cliptype, fillrule, polytree); - } - - inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule) - { - return BooleanOp(ClipType::Intersection, fillrule, subjects, clips); - } - - inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) - { - return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec); - } - - inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule) - { - return BooleanOp(ClipType::Union, fillrule, subjects, clips); - } - - inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) - { - return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec); - } - - inline Paths64 Union(const Paths64& subjects, FillRule fillrule) - { - Paths64 result; - Clipper64 clipper; - clipper.AddSubject(subjects); - clipper.Execute(ClipType::Union, fillrule, result); - return result; - } - - inline PathsD Union(const PathsD& subjects, FillRule fillrule, int decimal_prec = 2) - { - CheckPrecision(decimal_prec); - PathsD result; - ClipperD clipper(decimal_prec); - clipper.AddSubject(subjects); - clipper.Execute(ClipType::Union, fillrule, result); - return result; - } - - inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule) - { - return BooleanOp(ClipType::Difference, fillrule, subjects, clips); - } - - inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) - { - return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec); - } - - inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule) - { - return BooleanOp(ClipType::Xor, fillrule, subjects, clips); - } - - inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) - { - return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec); - } - - inline Paths64 InflatePaths(const Paths64& paths, double delta, - JoinType jt, EndType et, double miter_limit = 2.0) - { - ClipperOffset clip_offset(miter_limit); - clip_offset.AddPaths(paths, jt, et); - return clip_offset.Execute(delta); - } - - inline PathsD InflatePaths(const PathsD& paths, double delta, - JoinType jt, EndType et, double miter_limit = 2.0, int precision = 2) - { - CheckPrecision(precision); - const double scale = std::pow(10, precision); - ClipperOffset clip_offset(miter_limit); - clip_offset.AddPaths(ScalePaths(paths, scale), jt, et); - Paths64 tmp = clip_offset.Execute(delta * scale); - return ScalePaths(tmp, 1 / scale); - } - - inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy) - { - Path64 result; - result.reserve(path.size()); - for (const Point64& pt : path) - result.push_back(Point64(pt.x + dx, pt.y + dy)); - return result; - } - - inline PathD TranslatePath(const PathD& path, double dx, double dy) - { - PathD result; - result.reserve(path.size()); - for (const PointD& pt : path) - result.push_back(PointD(pt.x + dx, pt.y + dy)); - return result; - } - - inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy) - { - Paths64 result; - result.reserve(paths.size()); - for (const Path64& path : paths) - result.push_back(TranslatePath(path, dx, dy)); - return result; - } - - inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy) - { - PathsD result; - result.reserve(paths.size()); - for (const PathD& path : paths) - result.push_back(TranslatePath(path, dx, dy)); - return result; - } - - inline Rect64 Bounds(const Path64& path) - { - Rect64 rec = MaxInvalidRect64; - for (const Point64& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return Rect64(); - return rec; - } - - inline Rect64 Bounds(const Paths64& paths) - { - Rect64 rec = MaxInvalidRect64; - for (const Path64& path : paths) - for (const Point64& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return Rect64(); - return rec; - } - - inline RectD Bounds(const PathD& path) - { - RectD rec = MaxInvalidRectD; - for (const PointD& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return RectD(); - return rec; - } - - inline RectD Bounds(const PathsD& paths) - { - RectD rec = MaxInvalidRectD; - for (const PathD& path : paths) - for (const PointD& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return RectD(); - return rec; - } - - inline Path64 RectClip(const Rect64& rect, const Path64& path) - { - if (rect.IsEmpty() || path.empty()) return Path64(); - Rect64 pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) return Path64(); - if (rect.Contains(pathRec)) return path; - class RectClip rc(rect); - return rc.Execute(path); - } - - inline Paths64 RectClip(const Rect64& rect, const Paths64& paths) - { - if (rect.IsEmpty() || paths.empty()) return Paths64(); - class RectClip rc(rect); - Paths64 result; - result.reserve(paths.size()); - - for (const Path64& p : paths) - { - Rect64 pathRec = Bounds(p); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(p); - else - { - Path64 p2 = rc.Execute(p); - if (!p2.empty()) result.push_back(std::move(p2)); - } - } - return result; - } - - inline PathD RectClip(const RectD& rect, const PathD& path, int precision = 2) - { - if (rect.IsEmpty() || path.empty() || - !rect.Contains(Bounds(path))) return PathD(); - CheckPrecision(precision); - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(rect, scale); - class RectClip rc(r); - Path64 p = ScalePath(path, scale); - return ScalePath(rc.Execute(p), 1 / scale); - } - - inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2) - { - if (rect.IsEmpty() || paths.empty()) return PathsD(); - CheckPrecision(precision); - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(rect, scale); - class RectClip rc(r); - PathsD result; - result.reserve(paths.size()); - for (const PathD& path : paths) - { - RectD pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(path); - else - { - Path64 p = ScalePath(path, scale); - p = rc.Execute(p); - if (!p.empty()) - result.push_back(ScalePath(p, 1 / scale)); - } - } - return result; - } - - inline Paths64 RectClipLines(const Rect64& rect, const Path64& path) - { - Paths64 result; - if (rect.IsEmpty() || path.empty()) return result; - Rect64 pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) return result; - if (rect.Contains(pathRec)) - { - result.push_back(path); - return result; - } - class RectClipLines rcl(rect); - return rcl.Execute(path); - } - - inline Paths64 RectClipLines(const Rect64& rect, const Paths64& paths) - { - Paths64 result; - if (rect.IsEmpty() || paths.empty()) return result; - class RectClipLines rcl(rect); - for (const Path64& p : paths) - { - Rect64 pathRec = Bounds(p); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(p); - else - { - Paths64 pp = rcl.Execute(p); - if (!pp.empty()) - result.insert(result.end(), pp.begin(), pp.end()); - } - } - return result; - } - - inline PathsD RectClipLines(const RectD& rect, const PathD& path, int precision = 2) - { - if (rect.IsEmpty() || path.empty() || - !rect.Contains(Bounds(path))) return PathsD(); - CheckPrecision(precision); - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(rect, scale); - class RectClipLines rcl(r); - Path64 p = ScalePath(path, scale); - return ScalePaths(rcl.Execute(p), 1 / scale); - } - - inline PathsD RectClipLines(const RectD& rect, const PathsD& paths, int precision = 2) - { - PathsD result; - if (rect.IsEmpty() || paths.empty()) return result; - CheckPrecision(precision); - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(rect, scale); - class RectClipLines rcl(r); - result.reserve(paths.size()); - for (const PathD& path : paths) - { - RectD pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(path); - else - { - Path64 p = ScalePath(path, scale); - Paths64 pp = rcl.Execute(p); - if (pp.empty()) continue; - PathsD ppd = ScalePaths(pp, 1 / scale); - result.insert(result.end(), ppd.begin(), ppd.end()); - } - } - return result; - } - - namespace details - { - - inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths) - { - paths.push_back(polypath.Polygon()); - for (const PolyPath* child : polypath) - PolyPathToPaths64(*(PolyPath64*)(child), paths); - } - - inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths) - { - paths.push_back(polypath.Polygon()); - for (const PolyPath* child : polypath) - PolyPathToPathsD(*(PolyPathD*)(child), paths); - } - - inline bool PolyPath64ContainsChildren(const PolyPath64& pp) - { - for (auto ch : pp) - { - PolyPath64* child = (PolyPath64*)ch; - for (const Point64& pt : child->Polygon()) - if (PointInPolygon(pt, pp.Polygon()) == PointInPolygonResult::IsOutside) - return false; - if (child->Count() > 0 && !PolyPath64ContainsChildren(*child)) - return false; - } - return true; - } - - inline bool GetInt(std::string::const_iterator& iter, const - std::string::const_iterator& end_iter, int64_t& val) - { - val = 0; - bool is_neg = *iter == '-'; - if (is_neg) ++iter; - std::string::const_iterator start_iter = iter; - while (iter != end_iter && - ((*iter >= '0') && (*iter <= '9'))) - { - val = val * 10 + (static_cast(*iter++) - '0'); - } - if (is_neg) val = -val; - return (iter != start_iter); - } - - inline bool GetFloat(std::string::const_iterator& iter, const - std::string::const_iterator& end_iter, double& val) - { - val = 0; - bool is_neg = *iter == '-'; - if (is_neg) ++iter; - int dec_pos = 1; - const std::string::const_iterator start_iter = iter; - while (iter != end_iter && (*iter == '.' || - ((*iter >= '0') && (*iter <= '9')))) - { - if (*iter == '.') - { - if (dec_pos != 1) break; - dec_pos = 0; - ++iter; - continue; - } - if (dec_pos != 1) --dec_pos; - val = val * 10 + ((int64_t)(*iter++) - '0'); - } - if (iter == start_iter || dec_pos == 0) return false; - if (dec_pos < 0) - val *= std::pow(10, dec_pos); - if (is_neg) - val *= -1; - return true; - } - - inline void SkipWhiteSpace(std::string::const_iterator& iter, - const std::string::const_iterator& end_iter) - { - while (iter != end_iter && *iter <= ' ') ++iter; - } - - inline void SkipSpacesWithOptionalComma(std::string::const_iterator& iter, - const std::string::const_iterator& end_iter) - { - bool comma_seen = false; - while (iter != end_iter) - { - if (*iter == ' ') ++iter; - else if (*iter == ',') - { - if (comma_seen) return; // don't skip 2 commas! - comma_seen = true; - ++iter; - } - else return; - } - } - - inline bool has_one_match(const char c, char* chrs) - { - while (*chrs > 0 && c != *chrs) ++chrs; - if (!*chrs) return false; - *chrs = ' '; // only match once per char - return true; - } - - - inline void SkipUserDefinedChars(std::string::const_iterator& iter, - const std::string::const_iterator& end_iter, const std::string& skip_chars) - { - const size_t MAX_CHARS = 16; - char buff[MAX_CHARS] = {0}; - std::copy(skip_chars.cbegin(), skip_chars.cend(), &buff[0]); - while (iter != end_iter && - (*iter <= ' ' || has_one_match(*iter, buff))) ++iter; - return; - } - - } // end details namespace - - inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree) - { - Paths64 result; - for (auto child : polytree) - details::PolyPathToPaths64(*(PolyPath64*)(child), result); - return result; - } - - inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree) - { - PathsD result; - for (auto child : polytree) - details::PolyPathToPathsD(*(PolyPathD*)(child), result); - return result; - } - - inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree) - { - for (auto child : polytree) - if (child->Count() > 0 && - !details::PolyPath64ContainsChildren(*(PolyPath64*)(child))) - return false; - return true; - } - - inline Path64 MakePath(const std::string& s) - { - const std::string skip_chars = " ,(){}[]"; - Path64 result; - std::string::const_iterator s_iter = s.cbegin(); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - while (s_iter != s.cend()) - { - int64_t y = 0, x = 0; - if (!details::GetInt(s_iter, s.cend(), x)) break; - details::SkipSpacesWithOptionalComma(s_iter, s.cend()); - if (!details::GetInt(s_iter, s.cend(), y)) break; - result.push_back(Point64(x, y)); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - } - return result; - } - - inline PathD MakePathD(const std::string& s) - { - const std::string skip_chars = " ,(){}[]"; - PathD result; - std::string::const_iterator s_iter = s.cbegin(); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - while (s_iter != s.cend()) - { - double y = 0, x = 0; - if (!details::GetFloat(s_iter, s.cend(), x)) break; - details::SkipSpacesWithOptionalComma(s_iter, s.cend()); - if (!details::GetFloat(s_iter, s.cend(), y)) break; - result.push_back(PointD(x, y)); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - } - return result; - } - - inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false) - { - size_t len = p.size(); - if (len < 3) - { - if (!is_open_path || len < 2 || p[0] == p[1]) return Path64(); - else return p; - } - - Path64 dst; - dst.reserve(len); - Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1; - - if (!is_open_path) - { - while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1))) - ++srcIt; - while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt)) - --stop; - if (srcIt == stop) return Path64(); - } - - prevIt = srcIt++; - dst.push_back(*prevIt); - for (; srcIt != stop; ++srcIt) - { - if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1))) - { - prevIt = srcIt; - dst.push_back(*prevIt); - } - } - - if (is_open_path) - dst.push_back(*srcIt); - else if (CrossProduct(*prevIt, *stop, dst[0])) - dst.push_back(*stop); - else - { - while (dst.size() > 2 && - !CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0])) - dst.pop_back(); - if (dst.size() < 3) return Path64(); - } - return dst; - } - - inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false) - { - CheckPrecision(precision); - const double scale = std::pow(10, precision); - Path64 p = ScalePath(path, scale); - p = TrimCollinear(p, is_open_path); - return ScalePath(p, 1/scale); - } - - template - inline double Distance(const Point pt1, const Point pt2) - { - return std::sqrt(DistanceSqr(pt1, pt2)); - } - - template - inline double Length(const Path& path, bool is_closed_path = false) - { - double result = 0.0; - if (path.size() < 2) return result; - auto it = path.cbegin(), stop = path.end() - 1; - for (; it != stop; ++it) - result += Distance(*it, *(it + 1)); - if (is_closed_path) - result += Distance(*stop, *path.cbegin()); - return result; - } - - - template - inline bool NearCollinear(const Point& pt1, const Point& pt2, const Point& pt3, double sin_sqrd_min_angle_rads) - { - double cp = std::abs(CrossProduct(pt1, pt2, pt3)); - return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads; - } - - template - inline Path Ellipse(const Rect& rect, int steps = 0) - { - return Ellipse(rect.MidPoint(), - static_cast(rect.Width()) *0.5, - static_cast(rect.Height()) * 0.5, steps); - } - - template - inline Path Ellipse(const Point& center, - double radiusX, double radiusY = 0, int steps = 0) - { - if (radiusX <= 0) return Path(); - if (radiusY <= 0) radiusY = radiusX; - if (steps <= 2) - steps = static_cast(PI * sqrt((radiusX + radiusY) / 2)); - - double si = std::sin(2 * PI / steps); - double co = std::cos(2 * PI / steps); - double dx = co, dy = si; - Path result; - result.reserve(steps); - result.push_back(Point(center.x + radiusX, static_cast(center.y))); - for (int i = 1; i < steps; ++i) - { - result.push_back(Point(center.x + radiusX * dx, center.y + radiusY * dy)); - double x = dx * co - dy * si; - dy = dy * co + dx * si; - dx = x; - } - return result; - } - - template - inline double PerpendicDistFromLineSqrd(const Point& pt, - const Point& line1, const Point& line2) - { - double a = static_cast(pt.x - line1.x); - double b = static_cast(pt.y - line1.y); - double c = static_cast(line2.x - line1.x); - double d = static_cast(line2.y - line1.y); - if (c == 0 && d == 0) return 0; - return Sqr(a * d - c * b) / (c * c + d * d); - } - - template - inline void RDP(const Path path, std::size_t begin, - std::size_t end, double epsSqrd, std::vector& flags) - { - typename Path::size_type idx = 0; - double max_d = 0; - while (end > begin && path[begin] == path[end]) flags[end--] = false; - for (typename Path::size_type 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) RDP(path, idx, end, epsSqrd, flags); - } - - template - inline Path RamerDouglasPeucker(const Path& path, double epsilon) - { - const typename Path::size_type len = path.size(); - if (len < 5) return Path(path); - std::vector flags(len); - flags[0] = true; - flags[len - 1] = true; - RDP(path, 0, len - 1, Sqr(epsilon), flags); - Path result; - result.reserve(len); - for (typename Path::size_type i = 0; i < len; ++i) - if (flags[i]) - result.push_back(path[i]); - return result; - } - - template - inline Paths RamerDouglasPeucker(const Paths& paths, double epsilon) - { - Paths result; - result.reserve(paths.size()); - for (const Path& path : paths) - result.push_back(RamerDouglasPeucker(path, epsilon)); - return result; - } - -} // end Clipper2Lib namespace - -#endif // CLIPPER_H diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.minkowski.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.minkowski.h deleted file mode 100644 index ca0ab6be81..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.minkowski.h +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 15 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : Minkowski Sum and Difference * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_MINKOWSKI_H -#define CLIPPER_MINKOWSKI_H - -#include -#include -#include -#include "clipper.core.h" - -namespace Clipper2Lib -{ - - namespace detail - { - inline Paths64 Minkowski(const Path64& pattern, const Path64& path, bool isSum, bool isClosed) - { - size_t delta = isClosed ? 0 : 1; - size_t patLen = pattern.size(), pathLen = path.size(); - if (patLen == 0 || pathLen == 0) return Paths64(); - Paths64 tmp; - tmp.reserve(pathLen); - - if (isSum) - { - for (const Point64& p : path) - { - Path64 path2(pattern.size()); - std::transform(pattern.cbegin(), pattern.cend(), - path2.begin(), [p](const Point64& pt2) {return p + pt2; }); - tmp.push_back(path2); - } - } - else - { - for (const Point64& p : path) - { - Path64 path2(pattern.size()); - std::transform(pattern.cbegin(), pattern.cend(), - path2.begin(), [p](const Point64& pt2) {return p - pt2; }); - tmp.push_back(path2); - } - } - - Paths64 result; - result.reserve((pathLen - delta) * patLen); - size_t g = isClosed ? pathLen - 1 : 0; - for (size_t h = patLen - 1, i = delta; i < pathLen; ++i) - { - for (size_t j = 0; j < patLen; j++) - { - 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]); - }; - if (!IsPositive(quad)) - std::reverse(quad.begin(), quad.end()); - result.push_back(quad); - h = j; - } - g = i; - } - return result; - } - - inline Paths64 Union(const Paths64& subjects, FillRule fillrule) - { - Paths64 result; - Clipper64 clipper; - clipper.AddSubject(subjects); - clipper.Execute(ClipType::Union, fillrule, result); - return result; - } - - } // namespace internal - - inline Paths64 MinkowskiSum(const Path64& pattern, const Path64& path, bool isClosed) - { - return detail::Union(detail::Minkowski(pattern, path, true, isClosed), FillRule::NonZero); - } - - inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2) - { - double scale = pow(10, decimalPlaces); - Path64 pat64 = ScalePath(pattern, scale); - Path64 path64 = ScalePath(path, scale); - Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero); - return ScalePaths(tmp, 1 / scale); - } - - inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed) - { - return detail::Union(detail::Minkowski(pattern, path, false, isClosed), FillRule::NonZero); - } - - inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2) - { - double scale = pow(10, decimalPlaces); - Path64 pat64 = ScalePath(pattern, scale); - Path64 path64 = ScalePath(path, scale); - Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero); - return ScalePaths(tmp, 1 / scale); - } - -} // Clipper2Lib namespace - -#endif // CLIPPER_MINKOWSKI_H diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h deleted file mode 100644 index 4fd130bf4d..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 15 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : Path Offset (Inflate/Shrink) * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_OFFSET_H_ -#define CLIPPER_OFFSET_H_ - -#include "clipper.core.h" - -namespace Clipper2Lib { - -enum class JoinType { Square, Round, Miter }; - -enum class EndType {Polygon, Joined, Butt, Square, Round}; -//Butt : offsets both sides of a path, with square blunt ends -//Square : offsets both sides of a path, with square extended ends -//Round : offsets both sides of a path, with round extended ends -//Joined : offsets both sides of a path, with joined ends -//Polygon: offsets only one side of a closed path - -class ClipperOffset { -private: - - class Group { - public: - Paths64 paths_in_; - Paths64 paths_out_; - Path64 path_; - bool is_reversed_ = false; - JoinType join_type_; - EndType end_type_; - Group(const Paths64& paths, JoinType join_type, EndType end_type) : - paths_in_(paths), join_type_(join_type), end_type_(end_type) {} - }; - - double group_delta_ = 0.0; - double abs_group_delta_ = 0.0; - double temp_lim_ = 0.0; - double steps_per_rad_ = 0.0; - PathD norms; - Paths64 solution; - std::vector groups_; - JoinType join_type_ = JoinType::Square; - - double miter_limit_ = 0.0; - double arc_tolerance_ = 0.0; - bool merge_groups_ = true; - bool preserve_collinear_ = false; - bool reverse_solution_ = false; - - void DoSquare(Group& group, const Path64& path, size_t j, size_t k); - void DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a); - void DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle); - void BuildNormals(const Path64& path); - void OffsetPolygon(Group& group, Path64& path); - void OffsetOpenJoined(Group& group, Path64& path); - void OffsetOpenPath(Group& group, Path64& path, EndType endType); - void OffsetPoint(Group& group, Path64& path, size_t j, size_t& k); - void DoGroupOffset(Group &group, double delta); -public: - ClipperOffset(double miter_limit = 2.0, - double arc_tolerance = 0.0, - bool preserve_collinear = false, - bool reverse_solution = false) : - miter_limit_(miter_limit), arc_tolerance_(arc_tolerance), - preserve_collinear_(preserve_collinear), - reverse_solution_(reverse_solution) { }; - - ~ClipperOffset() { Clear(); }; - - void AddPath(const Path64& path, JoinType jt_, EndType et_); - void AddPaths(const Paths64& paths, JoinType jt_, EndType et_); - void AddPath(const PathD &p, JoinType jt_, EndType et_); - void AddPaths(const PathsD &p, JoinType jt_, EndType et_); - void Clear() { groups_.clear(); norms.clear(); }; - - Paths64 Execute(double delta); - - double MiterLimit() const { return miter_limit_; } - void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; } - - //ArcTolerance: needed for rounded offsets (See offset_triginometry2.svg) - double ArcTolerance() const { return arc_tolerance_; } - void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; } - - //MergeGroups: A path group is one or more paths added via the AddPath or - //AddPaths methods. By default these path groups will be offset - //independently of other groups and this may cause overlaps (intersections). - //However, when MergeGroups is enabled, any overlapping offsets will be - //merged (via a clipping union operation) to remove overlaps. - bool MergeGroups() const { return merge_groups_; } - void MergeGroups(bool merge_groups) { merge_groups_ = merge_groups; } - - bool PreserveCollinear() const { return preserve_collinear_; } - void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;} - - bool ReverseSolution() const { return reverse_solution_; } - void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;} -}; - -} -#endif /* CLIPPER_OFFSET_H_ */ diff --git a/src/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h b/src/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h deleted file mode 100644 index 98f43e0b2d..0000000000 --- a/src/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 26 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_RECTCLIP_H -#define CLIPPER_RECTCLIP_H - -#include -#include -#include "clipper.h" -#include "clipper.core.h" - -namespace Clipper2Lib -{ - - enum class Location { Left, Top, Right, Bottom, Inside }; - - class RectClip { - protected: - const Rect64 rect_; - const Point64 mp_; - const Path64 rectPath_; - Path64 result_; - std::vector start_locs_; - - void GetNextLocation(const Path64& path, - Location& loc, int& i, int highI); - void AddCorner(Location prev, Location curr); - void AddCorner(Location& loc, bool isClockwise); - public: - RectClip(const Rect64& rect) : - rect_(rect), - mp_(rect.MidPoint()), - rectPath_(rect.AsPath()) {} - Path64 Execute(const Path64& path); - }; - - class RectClipLines : public RectClip { - public: - RectClipLines(const Rect64& rect) : RectClip(rect) {}; - Paths64 Execute(const Path64& path); - }; - -} // Clipper2Lib namespace -#endif // CLIPPER_RECTCLIP_H diff --git a/src/clipper2/Clipper2Lib/src/clipper.engine.cpp b/src/clipper2/Clipper2Lib/src/clipper.engine.cpp deleted file mode 100644 index 069b52392c..0000000000 --- a/src/clipper2/Clipper2Lib/src/clipper.engine.cpp +++ /dev/null @@ -1,3594 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 4 November 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : This is the main polygon clipping module * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "clipper2/clipper.engine.h" - -#include -#include - -namespace Clipper2Lib { - - static const double FloatingPointTolerance = 1.0e-12; - static const Rect64 invalid_rect = Rect64( - std::numeric_limits::max(), - std::numeric_limits::max(), - -std::numeric_limits::max(), - -std::numeric_limits::max() - ); - - //Every closed path (or polygon) is made up of a series of vertices forming - //edges that alternate between going up (relative to the Y-axis) and going - //down. Edges consecutively going up or consecutively going down are called - //'bounds' (or sides if they're simple polygons). 'Local Minima' refer to - //vertices where descending bounds become ascending ones. - - struct Scanline { - int64_t y = 0; - Scanline* next = nullptr; - - explicit Scanline(int64_t y_) : y(y_) {} - }; - - struct Joiner { - int idx; - OutPt* op1; - OutPt* op2; - Joiner* next1; - Joiner* next2; - Joiner* nextH; - - explicit Joiner(OutPt* op1_, OutPt* op2_, Joiner* nexth) : - op1(op1_), op2(op2_), nextH(nexth) - { - idx = -1; - next1 = op1->joiner; - op1->joiner = this; - - if (op2) - { - next2 = op2->joiner; - op2->joiner = this; - } - else - next2 = nullptr; - } - - }; - - struct LocMinSorter { - inline bool operator()(const LocalMinima* locMin1, const LocalMinima* locMin2) - { - if (locMin2->vertex->pt.y != locMin1->vertex->pt.y) - return locMin2->vertex->pt.y < locMin1->vertex->pt.y; - else - return locMin2->vertex->pt.x < locMin1->vertex->pt.x; - } - }; - - inline bool IsOdd(int val) - { - return (val & 1) ? true : false; - } - - - inline bool IsHotEdge(const Active& e) - { - return (e.outrec); - } - - - inline bool IsOpen(const Active& e) - { - return (e.local_min->is_open); - } - - - inline bool IsOpenEnd(const Vertex& v) - { - return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) != - VertexFlags::None; - } - - - inline bool IsOpenEnd(const Active& ae) - { - return IsOpenEnd(*ae.vertex_top); - } - - - inline Active* GetPrevHotEdge(const Active& e) - { - Active* prev = e.prev_in_ael; - while (prev && (IsOpen(*prev) || !IsHotEdge(*prev))) - prev = prev->prev_in_ael; - return prev; - } - - inline bool IsFront(const Active& e) - { - return (&e == e.outrec->front_edge); - } - - inline bool IsInvalidPath(OutPt* op) - { - return (!op || op->next == op); - } - - /******************************************************************************* - * Dx: 0(90deg) * - * | * - * +inf (180deg) <--- o ---> -inf (0deg) * - *******************************************************************************/ - - inline double GetDx(const Point64& pt1, const Point64& pt2) - { - double dy = double(pt2.y - pt1.y); - if (dy != 0) - return double(pt2.x - pt1.x) / dy; - else if (pt2.x > pt1.x) - return -std::numeric_limits::max(); - else - return std::numeric_limits::max(); - } - - - inline int64_t TopX(const Active& ae, const int64_t currentY) - { - if ((currentY == ae.top.y) || (ae.top.x == ae.bot.x)) return ae.top.x; - else if (currentY == ae.bot.y) return ae.bot.x; - else return ae.bot.x + static_cast(std::nearbyint(ae.dx * (currentY - ae.bot.y))); - // nb: std::nearbyint (or std::round) substantially *improves* performance here - // as it greatly improves the likelihood of edge adjacency in ProcessIntersectList(). - } - - - inline bool IsHorizontal(const Active& e) - { - return (e.top.y == e.bot.y); - } - - - inline bool IsHeadingRightHorz(const Active& e) - { - return e.dx == -std::numeric_limits::max(); - } - - - inline bool IsHeadingLeftHorz(const Active& e) - { - return e.dx == std::numeric_limits::max(); - } - - - inline void SwapActives(Active*& e1, Active*& e2) - { - Active* e = e1; - e1 = e2; - e2 = e; - } - - - inline PathType GetPolyType(const Active& e) - { - return e.local_min->polytype; - } - - - inline bool IsSamePolyType(const Active& e1, const Active& e2) - { - return e1.local_min->polytype == e2.local_min->polytype; - } - - inline Point64 GetEndE1ClosestToEndE2( - const Active& e1, const Active& e2) - { - double d[] = { - DistanceSqr(e1.bot, e2.bot), - DistanceSqr(e1.top, e2.top), - DistanceSqr(e1.top, e2.bot), - DistanceSqr(e1.bot, e2.top) - }; - if (d[0] == 0) return e1.bot; - int idx = 0; - for (int i = 1; i < 4; ++i) - { - if (d[i] < d[idx]) idx = i; - if (d[i] == 0) break; - } - switch (idx) - { - case 1: case 2: return e1.top; - default: return e1.bot; - } - } - - Point64 GetIntersectPoint(const Active& e1, const Active& e2) - { - double b1, b2, q = (e1.dx - e2.dx); - if (std::abs(q) < 1e-5) // 1e-5 is a rough empirical limit - return GetEndE1ClosestToEndE2(e1, e2); // ie almost parallel - - if (e1.dx == 0) - { - if (IsHorizontal(e2)) return Point64(e1.bot.x, e2.bot.y); - b2 = e2.bot.y - (e2.bot.x / e2.dx); - return Point64(e1.bot.x, - static_cast(std::round(e1.bot.x / e2.dx + b2))); - } - else if (e2.dx == 0) - { - if (IsHorizontal(e1)) return Point64(e2.bot.x, e1.bot.y); - b1 = e1.bot.y - (e1.bot.x / e1.dx); - return Point64(e2.bot.x, - static_cast(std::round(e2.bot.x / e1.dx + b1))); - } - else - { - b1 = e1.bot.x - e1.bot.y * e1.dx; - b2 = e2.bot.x - e2.bot.y * e2.dx; - - q = (b2 - b1) / q; - return (abs(e1.dx) < abs(e2.dx)) ? - Point64(static_cast(e1.dx * q + b1), - static_cast((q))) : - Point64(static_cast(e2.dx * q + b2), - static_cast((q))); - } - } - - bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b, - const Point64& ln2a, const Point64& ln2b, PointD& ip) - { - ip = PointD(0, 0); - double m1, b1, m2, b2; - if (ln1b.x == ln1a.x) - { - if (ln2b.x == ln2a.x) return false; - m2 = static_cast(ln2b.y - ln2a.y) / - static_cast(ln2b.x - ln2a.x); - b2 = ln2a.y - m2 * ln2a.x; - ip.x = static_cast(ln1a.x); - ip.y = m2 * ln1a.x + b2; - } - else if (ln2b.x == ln2a.x) - { - m1 = static_cast(ln1b.y - ln1a.y) / - static_cast(ln1b.x - ln1a.x); - b1 = ln1a.y - m1 * ln1a.x; - ip.x = static_cast(ln2a.x); - ip.y = m1 * ln2a.x + b1; - } - else - { - m1 = static_cast(ln1b.y - ln1a.y) / - static_cast(ln1b.x - ln1a.x); - b1 = ln1a.y - m1 * ln1a.x; - m2 = static_cast(ln2b.y - ln2a.y) / - static_cast(ln2b.x - ln2a.x); - b2 = ln2a.y - m2 * ln2a.x; - if (std::abs(m1 - m2) > FloatingPointTolerance) - { - ip.x = (b2 - b1) / (m1 - m2); - ip.y = m1 * ip.x + b1; - } - else - { - ip.x = static_cast(ln1a.x + ln1b.x) / 2; - ip.y = static_cast(ln1a.y + ln1b.y) / 2; - } - } - return true; - } - - - inline void SetDx(Active& e) - { - e.dx = GetDx(e.bot, e.top); - } - - - inline Vertex* NextVertex(const Active& e) - { - if (e.wind_dx > 0) - return e.vertex_top->next; - else - return e.vertex_top->prev; - } - - - //PrevPrevVertex: useful to get the (inverted Y-axis) top of the - //alternate edge (ie left or right bound) during edge insertion. - inline Vertex* PrevPrevVertex(const Active& ae) - { - if (ae.wind_dx > 0) - return ae.vertex_top->prev->prev; - else - return ae.vertex_top->next->next; - } - - - inline Active* ExtractFromSEL(Active* ae) - { - Active* res = ae->next_in_sel; - if (res) - res->prev_in_sel = ae->prev_in_sel; - ae->prev_in_sel->next_in_sel = res; - return res; - } - - - inline void Insert1Before2InSEL(Active* ae1, Active* ae2) - { - ae1->prev_in_sel = ae2->prev_in_sel; - if (ae1->prev_in_sel) - ae1->prev_in_sel->next_in_sel = ae1; - ae1->next_in_sel = ae2; - ae2->prev_in_sel = ae1; - } - - inline bool IsMaxima(const Vertex& v) - { - return ((v.flags & VertexFlags::LocalMax) != VertexFlags::None); - } - - - inline bool IsMaxima(const Active& e) - { - return IsMaxima(*e.vertex_top); - } - - Vertex* GetCurrYMaximaVertex(const Active& e) - { - Vertex* result = e.vertex_top; - if (e.wind_dx > 0) - while (result->next->pt.y == result->pt.y) result = result->next; - else - while (result->prev->pt.y == result->pt.y) result = result->prev; - if (!IsMaxima(*result)) result = nullptr; // not a maxima - return result; - } - - Active* GetMaximaPair(const Active& e) - { - Active* e2; - e2 = e.next_in_ael; - while (e2) - { - if (e2->vertex_top == e.vertex_top) return e2; // Found! - e2 = e2->next_in_ael; - } - return nullptr; - } - - Active* GetHorzMaximaPair(const Active& horz, const Vertex* vert_max) - { - //we can't be sure whether the MaximaPair is on the left or right, so ... - Active* result = horz.prev_in_ael; - while (result && result->curr_x >= vert_max->pt.x) - { - if (result->vertex_top == vert_max) return result; // Found! - result = result->prev_in_ael; - } - result = horz.next_in_ael; - while (result && TopX(*result, horz.top.y) <= vert_max->pt.x) - { - if (result->vertex_top == vert_max) return result; // Found! - result = result->next_in_ael; - } - return nullptr; - } - - inline int PointCount(OutPt* op) - { - OutPt* op2 = op; - int cnt = 0; - do - { - op2 = op2->next; - ++cnt; - } while (op2 != op); - return cnt; - } - - - inline OutPt* InsertOp(const Point64& pt, OutPt* insertAfter) - { - OutPt* result = new OutPt(pt, insertAfter->outrec); - result->next = insertAfter->next; - insertAfter->next->prev = result; - insertAfter->next = result; - result->prev = insertAfter; - return result; - } - - - inline OutPt* DisposeOutPt(OutPt* op) - { - OutPt* result = op->next; - op->prev->next = op->next; - op->next->prev = op->prev; - delete op; - return result; - } - - - inline void DisposeOutPts(OutRec& outrec) - { - if (!outrec.pts) return; - OutPt* op2 = outrec.pts->next; - while (op2 != outrec.pts) - { - OutPt* tmp = op2->next; - delete op2; - op2 = tmp; - } - delete outrec.pts; - outrec.pts = nullptr; - } - - - bool IntersectListSort(const IntersectNode& a, const IntersectNode& b) - { - //note different inequality tests ... - return (a.pt.y == b.pt.y) ? (a.pt.x < b.pt.x) : (a.pt.y > b.pt.y); - } - - - inline void SetSides(OutRec& outrec, Active& start_edge, Active& end_edge) - { - outrec.front_edge = &start_edge; - outrec.back_edge = &end_edge; - } - - - void SwapOutrecs(Active& e1, Active& e2) - { - OutRec* or1 = e1.outrec; - OutRec* or2 = e2.outrec; - if (or1 == or2) - { - Active* e = or1->front_edge; - or1->front_edge = or1->back_edge; - or1->back_edge = e; - return; - } - if (or1) - { - if (&e1 == or1->front_edge) - or1->front_edge = &e2; - else - or1->back_edge = &e2; - } - if (or2) - { - if (&e2 == or2->front_edge) - or2->front_edge = &e1; - else - or2->back_edge = &e1; - } - e1.outrec = or2; - e2.outrec = or1; - } - - - double Area(OutPt* op) - { - //https://en.wikipedia.org/wiki/Shoelace_formula - double result = 0.0; - OutPt* op2 = op; - do - { - result += static_cast(op2->prev->pt.y + op2->pt.y) * - static_cast(op2->prev->pt.x - op2->pt.x); - op2 = op2->next; - } while (op2 != op); - return result * 0.5; - } - - inline double AreaTriangle(const Point64& pt1, - const Point64& pt2, const Point64& pt3) - { - return (static_cast(pt3.y + pt1.y) * static_cast(pt3.x - pt1.x) + - static_cast(pt1.y + pt2.y) * static_cast(pt1.x - pt2.x) + - static_cast(pt2.y + pt3.y) * static_cast(pt2.x - pt3.x)); - } - - void ReverseOutPts(OutPt* op) - { - if (!op) return; - - OutPt* op1 = op; - OutPt* op2; - - do - { - op2 = op1->next; - op1->next = op1->prev; - op1->prev = op2; - op1 = op2; - } while (op1 != op); - } - - - inline void SwapSides(OutRec& outrec) - { - Active* e2 = outrec.front_edge; - outrec.front_edge = outrec.back_edge; - outrec.back_edge = e2; - outrec.pts = outrec.pts->next; - } - - - inline OutRec* GetRealOutRec(OutRec* outrec) - { - while (outrec && !outrec->pts) outrec = outrec->owner; - return outrec; - } - - - inline void UncoupleOutRec(Active ae) - { - OutRec* outrec = ae.outrec; - if (!outrec) return; - outrec->front_edge->outrec = nullptr; - outrec->back_edge->outrec = nullptr; - outrec->front_edge = nullptr; - outrec->back_edge = nullptr; - } - - - inline bool PtsReallyClose(const Point64& pt1, const Point64& pt2) - { - return (std::llabs(pt1.x - pt2.x) < 2) && (std::llabs(pt1.y - pt2.y) < 2); - } - - inline bool IsVerySmallTriangle(const OutPt& op) - { - return op.next->next == op.prev && - (PtsReallyClose(op.prev->pt, op.next->pt) || - PtsReallyClose(op.pt, op.next->pt) || - PtsReallyClose(op.pt, op.prev->pt)); - } - - inline bool IsValidClosedPath(const OutPt* op) - { - return op && (op->next != op) && (op->next != op->prev) && - !IsVerySmallTriangle(*op); - } - - inline bool OutrecIsAscending(const Active* hotEdge) - { - return (hotEdge == hotEdge->outrec->front_edge); - } - - inline void SwapFrontBackSides(OutRec& outrec) - { - Active* tmp = outrec.front_edge; - outrec.front_edge = outrec.back_edge; - outrec.back_edge = tmp; - outrec.pts = outrec.pts->next; - } - - inline bool EdgesAdjacentInAEL(const IntersectNode& inode) - { - return (inode.edge1->next_in_ael == inode.edge2) || (inode.edge1->prev_in_ael == inode.edge2); - } - - inline bool TestJoinWithPrev1(const Active& e) - { - //this is marginally quicker than TestJoinWithPrev2 - //but can only be used when e.PrevInAEL.currX is accurate - return IsHotEdge(e) && !IsOpen(e) && - e.prev_in_ael && e.prev_in_ael->curr_x == e.curr_x && - IsHotEdge(*e.prev_in_ael) && !IsOpen(*e.prev_in_ael) && - (CrossProduct(e.prev_in_ael->top, e.bot, e.top) == 0); - } - - inline bool TestJoinWithPrev2(const Active& e, const Point64& curr_pt) - { - return IsHotEdge(e) && !IsOpen(e) && - e.prev_in_ael && !IsOpen(*e.prev_in_ael) && - IsHotEdge(*e.prev_in_ael) && (e.prev_in_ael->top.y < e.bot.y) && - (std::llabs(TopX(*e.prev_in_ael, curr_pt.y) - curr_pt.x) < 2) && - (CrossProduct(e.prev_in_ael->top, curr_pt, e.top) == 0); - } - - inline bool TestJoinWithNext1(const Active& e) - { - //this is marginally quicker than TestJoinWithNext2 - //but can only be used when e.NextInAEL.currX is accurate - return IsHotEdge(e) && !IsOpen(e) && - e.next_in_ael && (e.next_in_ael->curr_x == e.curr_x) && - IsHotEdge(*e.next_in_ael) && !IsOpen(*e.next_in_ael) && - (CrossProduct(e.next_in_ael->top, e.bot, e.top) == 0); - } - - inline bool TestJoinWithNext2(const Active& e, const Point64& curr_pt) - { - return IsHotEdge(e) && !IsOpen(e) && - e.next_in_ael && !IsOpen(*e.next_in_ael) && - IsHotEdge(*e.next_in_ael) && (e.next_in_ael->top.y < e.bot.y) && - (std::llabs(TopX(*e.next_in_ael, curr_pt.y) - curr_pt.x) < 2) && - (CrossProduct(e.next_in_ael->top, curr_pt, e.top) == 0); - } - - //------------------------------------------------------------------------------ - // ClipperBase methods ... - //------------------------------------------------------------------------------ - - ClipperBase::~ClipperBase() - { - Clear(); - } - - void ClipperBase::DeleteEdges(Active*& e) - { - while (e) - { - Active* e2 = e; - e = e->next_in_ael; - delete e2; - } - } - - void ClipperBase::CleanUp() - { - DeleteEdges(actives_); - scanline_list_ = std::priority_queue(); - intersect_nodes_.clear(); - DisposeAllOutRecs(); - } - - - void ClipperBase::Clear() - { - CleanUp(); - DisposeVerticesAndLocalMinima(); - current_locmin_iter_ = minima_list_.begin(); - minima_list_sorted_ = false; - has_open_paths_ = false; - } - - - void ClipperBase::Reset() - { - if (!minima_list_sorted_) - { - std::sort(minima_list_.begin(), minima_list_.end(), LocMinSorter()); - minima_list_sorted_ = true; - } - std::vector::const_reverse_iterator i; - for (i = minima_list_.rbegin(); i != minima_list_.rend(); ++i) - InsertScanline((*i)->vertex->pt.y); - - current_locmin_iter_ = minima_list_.begin(); - actives_ = nullptr; - sel_ = nullptr; - succeeded_ = true; - } - - -#ifdef USINGZ - void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip) - { - if (!zCallback_) return; - // prioritize subject over clip vertices by passing - // subject vertices before clip vertices in the callback - if (GetPolyType(e1) == PathType::Subject) - { - if (ip == e1.bot) ip.z = e1.bot.z; - else if (ip == e1.top) ip.z = e1.top.z; - else if (ip == e2.bot) ip.z = e2.bot.z; - else if (ip == e2.top) ip.z = e2.top.z; - zCallback_(e1.bot, e1.top, e2.bot, e2.top, ip); - } - else - { - if (ip == e2.bot) ip.z = e2.bot.z; - else if (ip == e2.top) ip.z = e2.top.z; - else if (ip == e1.bot) ip.z = e1.bot.z; - else if (ip == e1.top) ip.z = e1.top.z; - zCallback_(e2.bot, e2.top, e1.bot, e1.top, ip); - } - } -#endif - - void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open) - { - Paths64 tmp; - tmp.push_back(path); - AddPaths(tmp, polytype, is_open); - } - - - void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open) - { - if (is_open) has_open_paths_ = true; - minima_list_sorted_ = false; - - Path64::size_type total_vertex_count = 0; - for (const Path64& path : paths) total_vertex_count += path.size(); - if (total_vertex_count == 0) return; - Vertex* vertices = new Vertex[total_vertex_count], *v = vertices; - for (const Path64& path : paths) - { - //for each path create a circular double linked list of vertices - Vertex *v0 = v, *curr_v = v, *prev_v = nullptr; - - v->prev = nullptr; - int cnt = 0; - for (const Point64& pt : path) - { - if (prev_v) - { - if (prev_v->pt == pt) continue; // ie skips duplicates - prev_v->next = curr_v; - } - curr_v->prev = prev_v; - curr_v->pt = pt; - curr_v->flags = VertexFlags::None; - prev_v = curr_v++; - cnt++; - } - if (!prev_v || !prev_v->prev) continue; - if (!is_open && prev_v->pt == v0->pt) - prev_v = prev_v->prev; - prev_v->next = v0; - v0->prev = prev_v; - v = curr_v; // ie get ready for next path - if (cnt < 2 || (cnt == 2 && !is_open)) continue; - - //now find and assign local minima - bool going_up, going_up0; - if (is_open) - { - curr_v = v0->next; - while (curr_v != v0 && curr_v->pt.y == v0->pt.y) - curr_v = curr_v->next; - going_up = curr_v->pt.y <= v0->pt.y; - if (going_up) - { - v0->flags = VertexFlags::OpenStart; - AddLocMin(*v0, polytype, true); - } - else - v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax; - } - else // closed path - { - prev_v = v0->prev; - while (prev_v != v0 && prev_v->pt.y == v0->pt.y) - prev_v = prev_v->prev; - if (prev_v == v0) - continue; // only open paths can be completely flat - going_up = prev_v->pt.y > v0->pt.y; - } - - going_up0 = going_up; - prev_v = v0; - curr_v = v0->next; - while (curr_v != v0) - { - if (curr_v->pt.y > prev_v->pt.y && going_up) - { - prev_v->flags = (prev_v->flags | VertexFlags::LocalMax); - going_up = false; - } - else if (curr_v->pt.y < prev_v->pt.y && !going_up) - { - going_up = true; - AddLocMin(*prev_v, polytype, is_open); - } - prev_v = curr_v; - curr_v = curr_v->next; - } - - if (is_open) - { - prev_v->flags = prev_v->flags | VertexFlags::OpenEnd; - if (going_up) - prev_v->flags = prev_v->flags | VertexFlags::LocalMax; - else - AddLocMin(*prev_v, polytype, is_open); - } - else if (going_up != going_up0) - { - if (going_up0) AddLocMin(*prev_v, polytype, false); - else prev_v->flags = prev_v->flags | VertexFlags::LocalMax; - } - } // end processing current path - - vertex_lists_.emplace_back(vertices); - } // end AddPaths - - - inline void ClipperBase::InsertScanline(int64_t y) - { - scanline_list_.push(y); - } - - - bool ClipperBase::PopScanline(int64_t& y) - { - if (scanline_list_.empty()) return false; - y = scanline_list_.top(); - scanline_list_.pop(); - while (!scanline_list_.empty() && y == scanline_list_.top()) - scanline_list_.pop(); // Pop duplicates. - return true; - } - - - bool ClipperBase::PopLocalMinima(int64_t y, LocalMinima*& local_minima) - { - if (current_locmin_iter_ == minima_list_.end() || (*current_locmin_iter_)->vertex->pt.y != y) return false; - local_minima = (*current_locmin_iter_++); - return true; - } - - - void ClipperBase::DisposeAllOutRecs() - { - for (auto outrec : outrec_list_) - { - if (outrec->pts) DisposeOutPts(*outrec); - delete outrec; - } - outrec_list_.resize(0); - } - - - void ClipperBase::DisposeVerticesAndLocalMinima() - { - for (auto lm : minima_list_) delete lm; - minima_list_.clear(); - for (auto v : vertex_lists_) delete[] v; - vertex_lists_.clear(); - } - - - 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; - - vert.flags = (vert.flags | VertexFlags::LocalMin); - minima_list_.push_back(new LocalMinima(&vert, polytype, is_open)); - } - - bool ClipperBase::IsContributingClosed(const Active & e) const - { - switch (fillrule_) - { - case FillRule::EvenOdd: - break; - case FillRule::NonZero: - if (abs(e.wind_cnt) != 1) return false; - break; - case FillRule::Positive: - if (e.wind_cnt != 1) return false; - break; - case FillRule::Negative: - if (e.wind_cnt != -1) return false; - break; - } - - switch (cliptype_) - { - case ClipType::None: - return false; - case ClipType::Intersection: - switch (fillrule_) - { - case FillRule::Positive: - return (e.wind_cnt2 > 0); - case FillRule::Negative: - return (e.wind_cnt2 < 0); - default: - return (e.wind_cnt2 != 0); - } - break; - - case ClipType::Union: - switch (fillrule_) - { - case FillRule::Positive: - return (e.wind_cnt2 <= 0); - case FillRule::Negative: - return (e.wind_cnt2 >= 0); - default: - return (e.wind_cnt2 == 0); - } - break; - - case ClipType::Difference: - bool result; - switch (fillrule_) - { - case FillRule::Positive: - result = (e.wind_cnt2 <= 0); - break; - case FillRule::Negative: - result = (e.wind_cnt2 >= 0); - break; - default: - result = (e.wind_cnt2 == 0); - } - if (GetPolyType(e) == PathType::Subject) - return result; - else - return !result; - break; - - case ClipType::Xor: return true; break; - } - return false; // we should never get here - } - - - inline bool ClipperBase::IsContributingOpen(const Active& e) const - { - bool is_in_clip, is_in_subj; - switch (fillrule_) - { - case FillRule::Positive: - is_in_clip = e.wind_cnt2 > 0; - is_in_subj = e.wind_cnt > 0; - break; - case FillRule::Negative: - is_in_clip = e.wind_cnt2 < 0; - is_in_subj = e.wind_cnt < 0; - break; - default: - is_in_clip = e.wind_cnt2 != 0; - is_in_subj = e.wind_cnt != 0; - } - - switch (cliptype_) - { - case ClipType::Intersection: return is_in_clip; - case ClipType::Union: return (!is_in_subj && !is_in_clip); - default: return !is_in_clip; - } - } - - - void ClipperBase::SetWindCountForClosedPathEdge(Active& e) - { - //Wind counts refer to polygon regions not edges, so here an edge's WindCnt - //indicates the higher of the wind counts for the two regions touching the - //edge. (NB Adjacent regions can only ever have their wind counts differ by - //one. Also, open paths have no meaningful wind directions or counts.) - - Active* e2 = e.prev_in_ael; - //find the nearest closed path edge of the same PolyType in AEL (heading left) - PathType pt = GetPolyType(e); - while (e2 && (GetPolyType(*e2) != pt || IsOpen(*e2))) e2 = e2->prev_in_ael; - - if (!e2) - { - e.wind_cnt = e.wind_dx; - e2 = actives_; - } - else if (fillrule_ == FillRule::EvenOdd) - { - e.wind_cnt = e.wind_dx; - e.wind_cnt2 = e2->wind_cnt2; - e2 = e2->next_in_ael; - } - else - { - //NonZero, positive, or negative filling here ... - //if e's WindCnt is in the SAME direction as its WindDx, then polygon - //filling will be on the right of 'e'. - //NB neither e2.WindCnt nor e2.WindDx should ever be 0. - if (e2->wind_cnt * e2->wind_dx < 0) - { - //opposite directions so 'e' is outside 'e2' ... - if (abs(e2->wind_cnt) > 1) - { - //outside prev poly but still inside another. - if (e2->wind_dx * e.wind_dx < 0) - //reversing direction so use the same WC - e.wind_cnt = e2->wind_cnt; - else - //otherwise keep 'reducing' the WC by 1 (ie towards 0) ... - e.wind_cnt = e2->wind_cnt + e.wind_dx; - } - else - //now outside all polys of same polytype so set own WC ... - e.wind_cnt = (IsOpen(e) ? 1 : e.wind_dx); - } - else - { - //'e' must be inside 'e2' - if (e2->wind_dx * e.wind_dx < 0) - //reversing direction so use the same WC - e.wind_cnt = e2->wind_cnt; - else - //otherwise keep 'increasing' the WC by 1 (ie away from 0) ... - e.wind_cnt = e2->wind_cnt + e.wind_dx; - } - e.wind_cnt2 = e2->wind_cnt2; - e2 = e2->next_in_ael; // ie get ready to calc WindCnt2 - } - - //update wind_cnt2 ... - if (fillrule_ == FillRule::EvenOdd) - while (e2 != &e) - { - if (GetPolyType(*e2) != pt && !IsOpen(*e2)) - e.wind_cnt2 = (e.wind_cnt2 == 0 ? 1 : 0); - e2 = e2->next_in_ael; - } - else - while (e2 != &e) - { - if (GetPolyType(*e2) != pt && !IsOpen(*e2)) - e.wind_cnt2 += e2->wind_dx; - e2 = e2->next_in_ael; - } - } - - - void ClipperBase::SetWindCountForOpenPathEdge(Active& e) - { - Active* e2 = actives_; - if (fillrule_ == FillRule::EvenOdd) - { - int cnt1 = 0, cnt2 = 0; - while (e2 != &e) - { - if (GetPolyType(*e2) == PathType::Clip) - cnt2++; - else if (!IsOpen(*e2)) - cnt1++; - e2 = e2->next_in_ael; - } - e.wind_cnt = (IsOdd(cnt1) ? 1 : 0); - e.wind_cnt2 = (IsOdd(cnt2) ? 1 : 0); - } - else - { - while (e2 != &e) - { - if (GetPolyType(*e2) == PathType::Clip) - e.wind_cnt2 += e2->wind_dx; - else if (!IsOpen(*e2)) - e.wind_cnt += e2->wind_dx; - e2 = e2->next_in_ael; - } - } - } - - - bool IsValidAelOrder(const Active& resident, const Active& newcomer) - { - if (newcomer.curr_x != resident.curr_x) - return newcomer.curr_x > resident.curr_x; - - //get the turning direction a1.top, a2.bot, a2.top - double d = CrossProduct(resident.top, newcomer.bot, newcomer.top); - if (d != 0) return d < 0; - - //edges must be collinear to get here - //for starting open paths, place them according to - //the direction they're about to turn - if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y)) - { - return CrossProduct(newcomer.bot, - resident.top, NextVertex(resident)->pt) <= 0; - } - else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y)) - { - return CrossProduct(newcomer.bot, - newcomer.top, NextVertex(newcomer)->pt) >= 0; - } - - int64_t y = newcomer.bot.y; - bool newcomerIsLeft = newcomer.is_left_bound; - - if (resident.bot.y != y || resident.local_min->vertex->pt.y != y) - return newcomer.is_left_bound; - //resident must also have just been inserted - else if (resident.is_left_bound != newcomerIsLeft) - return newcomerIsLeft; - else if (CrossProduct(PrevPrevVertex(resident)->pt, - resident.bot, resident.top) == 0) return true; - else - //compare turning direction of the alternate bound - return (CrossProduct(PrevPrevVertex(resident)->pt, - newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft; - } - - - void ClipperBase::InsertLeftEdge(Active& e) - { - Active* e2; - if (!actives_) - { - e.prev_in_ael = nullptr; - e.next_in_ael = nullptr; - actives_ = &e; - } - else if (!IsValidAelOrder(*actives_, e)) - { - e.prev_in_ael = nullptr; - e.next_in_ael = actives_; - actives_->prev_in_ael = &e; - actives_ = &e; - } - else - { - e2 = actives_; - while (e2->next_in_ael && IsValidAelOrder(*e2->next_in_ael, e)) - e2 = e2->next_in_ael; - e.next_in_ael = e2->next_in_ael; - if (e2->next_in_ael) e2->next_in_ael->prev_in_ael = &e; - e.prev_in_ael = e2; - e2->next_in_ael = &e; - } - } - - - void InsertRightEdge(Active& e, Active& e2) - { - e2.next_in_ael = e.next_in_ael; - if (e.next_in_ael) e.next_in_ael->prev_in_ael = &e2; - e2.prev_in_ael = &e; - e.next_in_ael = &e2; - } - - - void ClipperBase::InsertLocalMinimaIntoAEL(int64_t bot_y) - { - LocalMinima* local_minima; - Active* left_bound, * right_bound; - //Add any local minima (if any) at BotY ... - //nb: horizontal local minima edges should contain locMin.vertex.prev - - while (PopLocalMinima(bot_y, local_minima)) - { - if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::None) - { - left_bound = nullptr; - } - else - { - left_bound = new Active(); - left_bound->bot = local_minima->vertex->pt; - left_bound->curr_x = left_bound->bot.x; - left_bound->wind_cnt = 0, - left_bound->wind_cnt2 = 0, - left_bound->wind_dx = -1, - left_bound->vertex_top = local_minima->vertex->prev; // ie descending - left_bound->top = left_bound->vertex_top->pt; - left_bound->outrec = nullptr; - left_bound->local_min = local_minima; - SetDx(*left_bound); - } - - if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::None) - { - right_bound = nullptr; - } - else - { - right_bound = new Active(); - right_bound->bot = local_minima->vertex->pt; - right_bound->curr_x = right_bound->bot.x; - right_bound->wind_cnt = 0, - right_bound->wind_cnt2 = 0, - right_bound->wind_dx = 1, - right_bound->vertex_top = local_minima->vertex->next; // ie ascending - right_bound->top = right_bound->vertex_top->pt; - right_bound->outrec = nullptr; - right_bound->local_min = local_minima; - SetDx(*right_bound); - } - - //Currently LeftB is just the descending bound and RightB is the ascending. - //Now if the LeftB isn't on the left of RightB then we need swap them. - if (left_bound && right_bound) - { - if (IsHorizontal(*left_bound)) - { - if (IsHeadingRightHorz(*left_bound)) SwapActives(left_bound, right_bound); - } - else if (IsHorizontal(*right_bound)) - { - if (IsHeadingLeftHorz(*right_bound)) SwapActives(left_bound, right_bound); - } - else if (left_bound->dx < right_bound->dx) - SwapActives(left_bound, right_bound); - } - else if (!left_bound) - { - left_bound = right_bound; - right_bound = nullptr; - } - - bool contributing; - left_bound->is_left_bound = true; - InsertLeftEdge(*left_bound); - - if (IsOpen(*left_bound)) - { - SetWindCountForOpenPathEdge(*left_bound); - contributing = IsContributingOpen(*left_bound); - } - else - { - SetWindCountForClosedPathEdge(*left_bound); - contributing = IsContributingClosed(*left_bound); - } - - if (right_bound) - { - right_bound->is_left_bound = false; - right_bound->wind_cnt = left_bound->wind_cnt; - right_bound->wind_cnt2 = left_bound->wind_cnt2; - InsertRightEdge(*left_bound, *right_bound); /////// - if (contributing) - { - AddLocalMinPoly(*left_bound, *right_bound, left_bound->bot, true); - if (!IsHorizontal(*left_bound) && TestJoinWithPrev1(*left_bound)) - { - OutPt* op = AddOutPt(*left_bound->prev_in_ael, left_bound->bot); - AddJoin(op, left_bound->outrec->pts); - } - } - - while (right_bound->next_in_ael && - IsValidAelOrder(*right_bound->next_in_ael, *right_bound)) - { - IntersectEdges(*right_bound, *right_bound->next_in_ael, right_bound->bot); - SwapPositionsInAEL(*right_bound, *right_bound->next_in_ael); - } - - if (!IsHorizontal(*right_bound) && - TestJoinWithNext1(*right_bound)) - { - OutPt* op = AddOutPt(*right_bound->next_in_ael, right_bound->bot); - AddJoin(right_bound->outrec->pts, op); - } - - if (IsHorizontal(*right_bound)) - PushHorz(*right_bound); - else - InsertScanline(right_bound->top.y); - } - else if (contributing) - { - StartOpenPath(*left_bound, left_bound->bot); - } - - if (IsHorizontal(*left_bound)) - PushHorz(*left_bound); - else - InsertScanline(left_bound->top.y); - } // while (PopLocalMinima()) - } - - - inline void ClipperBase::PushHorz(Active& e) - { - e.next_in_sel = (sel_ ? sel_ : nullptr); - sel_ = &e; - } - - - inline bool ClipperBase::PopHorz(Active*& e) - { - e = sel_; - if (!e) return false; - sel_ = sel_->next_in_sel; - return true; - } - - - OutPt* ClipperBase::AddLocalMinPoly(Active& e1, Active& e2, - const Point64& pt, bool is_new) - { - OutRec* outrec = new OutRec(); - outrec->idx = (unsigned)outrec_list_.size(); - outrec_list_.push_back(outrec); - outrec->pts = nullptr; - outrec->polypath = nullptr; - e1.outrec = outrec; - e2.outrec = outrec; - - //Setting the owner and inner/outer states (above) is an essential - //precursor to setting edge 'sides' (ie left and right sides of output - //polygons) and hence the orientation of output paths ... - - if (IsOpen(e1)) - { - outrec->owner = nullptr; - outrec->is_open = true; - if (e1.wind_dx > 0) - SetSides(*outrec, e1, e2); - else - SetSides(*outrec, e2, e1); - } - else - { - Active* prevHotEdge = GetPrevHotEdge(e1); - //e.windDx is the winding direction of the **input** paths - //and unrelated to the winding direction of output polygons. - //Output orientation is determined by e.outrec.frontE which is - //the ascending edge (see AddLocalMinPoly). - if (prevHotEdge) - { - outrec->owner = prevHotEdge->outrec; - if (OutrecIsAscending(prevHotEdge) == is_new) - SetSides(*outrec, e2, e1); - else - SetSides(*outrec, e1, e2); - } - else - { - outrec->owner = nullptr; - if (is_new) - SetSides(*outrec, e1, e2); - else - SetSides(*outrec, e2, e1); - } - } - - OutPt* op = new OutPt(pt, outrec); - outrec->pts = op; - return op; - } - - - OutPt* ClipperBase::AddLocalMaxPoly(Active& e1, Active& e2, const Point64& pt) - { - if (IsFront(e1) == IsFront(e2)) - { - if (IsOpenEnd(e1)) - SwapFrontBackSides(*e1.outrec); - else if (IsOpenEnd(e2)) - SwapFrontBackSides(*e2.outrec); - else - { - succeeded_ = false; - return nullptr; - } - } - - OutPt* result = AddOutPt(e1, pt); - if (e1.outrec == e2.outrec) - { - OutRec& outrec = *e1.outrec; - outrec.pts = result; - - UncoupleOutRec(e1); - if (!IsOpen(e1)) CleanCollinear(&outrec); - result = outrec.pts; - if (using_polytree_ && outrec.owner && !outrec.owner->front_edge) - outrec.owner = GetRealOutRec(outrec.owner->owner); - } - //and to preserve the winding orientation of outrec ... - else if (IsOpen(e1)) - { - if (e1.wind_dx < 0) - JoinOutrecPaths(e1, e2); - else - JoinOutrecPaths(e2, e1); - } - else if (e1.outrec->idx < e2.outrec->idx) - JoinOutrecPaths(e1, e2); - else - JoinOutrecPaths(e2, e1); - - return result; - } - - void ClipperBase::JoinOutrecPaths(Active& e1, Active& e2) - { - //join e2 outrec path onto e1 outrec path and then delete e2 outrec path - //pointers. (NB Only very rarely do the joining ends share the same coords.) - OutPt* p1_st = e1.outrec->pts; - OutPt* p2_st = e2.outrec->pts; - OutPt* p1_end = p1_st->next; - OutPt* p2_end = p2_st->next; - if (IsFront(e1)) - { - p2_end->prev = p1_st; - p1_st->next = p2_end; - p2_st->next = p1_end; - p1_end->prev = p2_st; - e1.outrec->pts = p2_st; - e1.outrec->front_edge = e2.outrec->front_edge; - if (e1.outrec->front_edge) - e1.outrec->front_edge->outrec = e1.outrec; - } - else - { - p1_end->prev = p2_st; - p2_st->next = p1_end; - p1_st->next = p2_end; - p2_end->prev = p1_st; - e1.outrec->back_edge = e2.outrec->back_edge; - if (e1.outrec->back_edge) - e1.outrec->back_edge->outrec = e1.outrec; - } - - //an owner must have a lower idx otherwise - //it can't be a valid owner - if (e2.outrec->owner && e2.outrec->owner->idx < e1.outrec->idx) - { - if (!e1.outrec->owner || e2.outrec->owner->idx < e1.outrec->owner->idx) - e1.outrec->owner = e2.outrec->owner; - } - - //after joining, the e2.OutRec must contains no vertices ... - e2.outrec->front_edge = nullptr; - e2.outrec->back_edge = nullptr; - e2.outrec->pts = nullptr; - e2.outrec->owner = e1.outrec; - - if (IsOpenEnd(e1)) - { - e2.outrec->pts = e1.outrec->pts; - e1.outrec->pts = nullptr; - } - - //and e1 and e2 are maxima and are about to be dropped from the Actives list. - e1.outrec = nullptr; - e2.outrec = nullptr; - } - - - OutPt* ClipperBase::AddOutPt(const Active& e, const Point64& pt) - { - OutPt* new_op = nullptr; - - //Outrec.OutPts: a circular doubly-linked-list of POutPt where ... - //op_front[.Prev]* ~~~> op_back & op_back == op_front.Next - OutRec* outrec = e.outrec; - bool to_front = IsFront(e); - OutPt* op_front = outrec->pts; - OutPt* op_back = op_front->next; - - if (to_front && (pt == op_front->pt)) - new_op = op_front; - else if (!to_front && (pt == op_back->pt)) - new_op = op_back; - else - { - new_op = new OutPt(pt, outrec); - op_back->prev = new_op; - new_op->prev = op_front; - new_op->next = op_back; - op_front->next = new_op; - if (to_front) outrec->pts = new_op; - } - return new_op; - } - - - bool ClipperBase::ValidateClosedPathEx(OutPt*& outpt) - { - if (IsValidClosedPath(outpt)) return true; - if (outpt) SafeDisposeOutPts(outpt); - return false; - } - - - void ClipperBase::CleanCollinear(OutRec* outrec) - { - outrec = GetRealOutRec(outrec); - if (!outrec || outrec->is_open || - outrec->front_edge || !ValidateClosedPathEx(outrec->pts)) return; - - OutPt* startOp = outrec->pts, * op2 = startOp; - for (; ; ) - { - if (op2->joiner) return; - - //NB if preserveCollinear == true, then only remove 180 deg. spikes - if ((CrossProduct(op2->prev->pt, op2->pt, op2->next->pt) == 0) && - (op2->pt == op2->prev->pt || - op2->pt == op2->next->pt || !PreserveCollinear || - DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0)) - { - - if (op2 == outrec->pts) outrec->pts = op2->prev; - - op2 = DisposeOutPt(op2); - if (!ValidateClosedPathEx(op2)) - { - outrec->pts = nullptr; - return; - } - startOp = op2; - continue; - } - op2 = op2->next; - if (op2 == startOp) break; - } - FixSelfIntersects(outrec); - } - - void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp) - { - // splitOp.prev -> splitOp && - // splitOp.next -> splitOp.next.next are intersecting - OutPt* prevOp = splitOp->prev; - OutPt* nextNextOp = splitOp->next->next; - outrec->pts = prevOp; - PointD ipD; - GetIntersectPoint(prevOp->pt, - splitOp->pt, splitOp->next->pt, nextNextOp->pt, ipD); - Point64 ip = Point64(ipD); -#ifdef USINGZ - if (zCallback_) - zCallback_(prevOp->pt, splitOp->pt, splitOp->next->pt, nextNextOp->pt, ip); -#endif - double area1 = Area(outrec->pts); - double absArea1 = std::fabs(area1); - if (absArea1 < 2) - { - SafeDisposeOutPts(outrec->pts); - // outrec.pts == nil; :) - return; - } - - // nb: area1 is the path's area *before* splitting, whereas area2 is - // the area of the triangle containing splitOp & splitOp.next. - // 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. - double area2 = AreaTriangle(ip, splitOp->pt, splitOp->next->pt); - double absArea2 = std::fabs(area2); - - // de-link splitOp and splitOp.next from the path - // while inserting the intersection point - if (ip == prevOp->pt || ip == nextNextOp->pt) - { - nextNextOp->prev = prevOp; - prevOp->next = nextNextOp; - } - else - { - OutPt* newOp2 = new OutPt(ip, prevOp->outrec); - newOp2->prev = prevOp; - newOp2->next = nextNextOp; - nextNextOp->prev = newOp2; - prevOp->next = newOp2; - } - - SafeDeleteOutPtJoiners(splitOp->next); - SafeDeleteOutPtJoiners(splitOp); - - if (absArea2 >= 1 && - (absArea2 > absArea1 || (area2 > 0) == (area1 > 0))) - { - OutRec* newOutRec = new OutRec(); - newOutRec->idx = outrec_list_.size(); - outrec_list_.push_back(newOutRec); - newOutRec->owner = prevOp->outrec->owner; - newOutRec->polypath = nullptr; - splitOp->outrec = newOutRec; - splitOp->next->outrec = newOutRec; - - OutPt* newOp = new OutPt(ip, newOutRec); - newOp->prev = splitOp->next; - newOp->next = splitOp; - newOutRec->pts = newOp; - splitOp->prev = newOp; - splitOp->next->next = newOp; - } - else - { - delete splitOp->next; - delete splitOp; - } - } - - void ClipperBase::FixSelfIntersects(OutRec* outrec) - { - OutPt* op2 = outrec->pts; - for (; ; ) - { - // triangles can't self-intersect - if (op2->prev == op2->next->next) break; - if (SegmentsIntersect(op2->prev->pt, - op2->pt, op2->next->pt, op2->next->next->pt)) - { - if (op2 == outrec->pts || op2->next == outrec->pts) - outrec->pts = outrec->pts->prev; - DoSplitOp(outrec, op2); - if (!outrec->pts) break; - op2 = outrec->pts; - continue; - } - else - op2 = op2->next; - - if (op2 == outrec->pts) break; - } - } - - - inline void UpdateOutrecOwner(OutRec* outrec) - { - OutPt* opCurr = outrec->pts; - for (; ; ) - { - opCurr->outrec = outrec; - opCurr = opCurr->next; - if (opCurr == outrec->pts) return; - } - } - - - void ClipperBase::SafeDisposeOutPts(OutPt*& op) - { - OutRec* outrec = GetRealOutRec(op->outrec); - if (outrec->front_edge) - outrec->front_edge->outrec = nullptr; - if (outrec->back_edge) - outrec->back_edge->outrec = nullptr; - - op->prev->next = nullptr; - while (op) - { - SafeDeleteOutPtJoiners(op); - OutPt* tmp = op->next; - delete op; - op = tmp; - } - outrec->pts = nullptr; - } - - - void ClipperBase::CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec) - { - double area1 = Area(op1); - double area2 = Area(op2); - bool signs_change = (area1 > 0) == (area2 < 0); - - if (area1 == 0 || (signs_change && std::abs(area1) < 2)) - { - SafeDisposeOutPts(op1); - outrec.pts = op2; - } - else if (area2 == 0 || (signs_change && std::abs(area2) < 2)) - { - SafeDisposeOutPts(op2); - outrec.pts = op1; - } - else - { - OutRec* newOr = new OutRec(); - newOr->idx = outrec_list_.size(); - outrec_list_.push_back(newOr); - newOr->polypath = nullptr; - - if (using_polytree_) - { - if (!outrec.splits) outrec.splits = new OutRecList(); - outrec.splits->push_back(newOr); - } - - if (std::abs(area1) >= std::abs(area2)) - { - outrec.pts = op1; - newOr->pts = op2; - } - else - { - outrec.pts = op2; - newOr->pts = op1; - } - - if ((area1 > 0) == (area2 > 0)) - newOr->owner = outrec.owner; - else - newOr->owner = &outrec; - - UpdateOutrecOwner(newOr); - CleanCollinear(newOr); - } - } - - - OutPt* ClipperBase::StartOpenPath(Active& e, const Point64& pt) - { - OutRec* outrec = new OutRec(); - outrec->idx = outrec_list_.size(); - outrec_list_.push_back(outrec); - outrec->owner = nullptr; - outrec->is_open = true; - outrec->pts = nullptr; - outrec->polypath = nullptr; - - if (e.wind_dx > 0) - { - outrec->front_edge = &e; - outrec->back_edge = nullptr; - } - else - { - outrec->front_edge = nullptr; - outrec->back_edge =& e; - } - - e.outrec = outrec; - - OutPt* op = new OutPt(pt, outrec); - outrec->pts = op; - return op; - } - - - inline void ClipperBase::UpdateEdgeIntoAEL(Active* e) - { - e->bot = e->top; - e->vertex_top = NextVertex(*e); - e->top = e->vertex_top->pt; - e->curr_x = e->bot.x; - SetDx(*e); - if (IsHorizontal(*e)) return; - InsertScanline(e->top.y); - if (TestJoinWithPrev1(*e)) - { - OutPt* op1 = AddOutPt(*e->prev_in_ael, e->bot); - OutPt* op2 = AddOutPt(*e, e->bot); - AddJoin(op1, op2); - } - } - - - Active* FindEdgeWithMatchingLocMin(Active* e) - { - Active* result = e->next_in_ael; - while (result) - { - if (result->local_min == e->local_min) return result; - else if (!IsHorizontal(*result) && e->bot != result->bot) result = nullptr; - else result = result->next_in_ael; - } - result = e->prev_in_ael; - while (result) - { - if (result->local_min == e->local_min) return result; - else if (!IsHorizontal(*result) && e->bot != result->bot) return nullptr; - else result = result->prev_in_ael; - } - return result; - } - - - OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) - { - //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... - if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2))) - { - if (IsOpen(e1) && IsOpen(e2)) return nullptr; - - Active* edge_o, * edge_c; - if (IsOpen(e1)) - { - edge_o = &e1; - edge_c = &e2; - } - else - { - edge_o = &e2; - edge_c = &e1; - } - - if (abs(edge_c->wind_cnt) != 1) return nullptr; - switch (cliptype_) - { - case ClipType::Union: - if (!IsHotEdge(*edge_c)) return nullptr; - break; - default: - if (edge_c->local_min->polytype == PathType::Subject) - return nullptr; - } - - switch (fillrule_) - { - case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break; - case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break; - default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break; - } - - //toggle contribution ... - if (IsHotEdge(*edge_o)) - { - OutPt* resultOp = AddOutPt(*edge_o, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr; - else edge_o->outrec->back_edge = nullptr; - edge_o->outrec = nullptr; - return resultOp; - } - - //horizontal edges can pass under open paths at a LocMins - else if (pt == edge_o->local_min->vertex->pt && - !IsOpenEnd(*edge_o->local_min->vertex)) - { - //find the other side of the LocMin and - //if it's 'hot' join up with it ... - Active* e3 = FindEdgeWithMatchingLocMin(edge_o); - if (e3 && IsHotEdge(*e3)) - { - edge_o->outrec = e3->outrec; - if (edge_o->wind_dx > 0) - SetSides(*e3->outrec, *edge_o, *e3); - else - SetSides(*e3->outrec, *e3, *edge_o); - return e3->outrec->pts; - } - else - return StartOpenPath(*edge_o, pt); - } - else - return StartOpenPath(*edge_o, pt); - } - - - //MANAGING CLOSED PATHS FROM HERE ON - - //UPDATE WINDING COUNTS... - - int old_e1_windcnt, old_e2_windcnt; - if (e1.local_min->polytype == e2.local_min->polytype) - { - if (fillrule_ == FillRule::EvenOdd) - { - old_e1_windcnt = e1.wind_cnt; - e1.wind_cnt = e2.wind_cnt; - e2.wind_cnt = old_e1_windcnt; - } - else - { - if (e1.wind_cnt + e2.wind_dx == 0) - e1.wind_cnt = -e1.wind_cnt; - else - e1.wind_cnt += e2.wind_dx; - if (e2.wind_cnt - e1.wind_dx == 0) - e2.wind_cnt = -e2.wind_cnt; - else - e2.wind_cnt -= e1.wind_dx; - } - } - else - { - if (fillrule_ != FillRule::EvenOdd) - { - e1.wind_cnt2 += e2.wind_dx; - e2.wind_cnt2 -= e1.wind_dx; - } - else - { - e1.wind_cnt2 = (e1.wind_cnt2 == 0 ? 1 : 0); - e2.wind_cnt2 = (e2.wind_cnt2 == 0 ? 1 : 0); - } - } - - switch (fillrule_) - { - case FillRule::EvenOdd: - case FillRule::NonZero: - old_e1_windcnt = abs(e1.wind_cnt); - old_e2_windcnt = abs(e2.wind_cnt); - break; - default: - if (fillrule_ == fillpos) - { - old_e1_windcnt = e1.wind_cnt; - old_e2_windcnt = e2.wind_cnt; - } - else - { - old_e1_windcnt = -e1.wind_cnt; - old_e2_windcnt = -e2.wind_cnt; - } - break; - } - - const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1; - const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1; - - if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01)) - { - return nullptr; - } - - //NOW PROCESS THE INTERSECTION ... - OutPt* resultOp = nullptr; - //if both edges are 'hot' ... - if (IsHotEdge(e1) && IsHotEdge(e2)) - { - if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) || - (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor)) - { - resultOp = AddLocalMaxPoly(e1, e2, pt); -#ifdef USINGZ - if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); -#endif - } - 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 - //a common vertex (not at common edges). - - resultOp = AddLocalMaxPoly(e1, e2, pt); - OutPt* op2 = AddLocalMinPoly(e1, e2, pt); -#ifdef USINGZ - if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); - if (zCallback_) SetZ(e1, e2, op2->pt); -#endif - if (resultOp && resultOp->pt == op2->pt && - !IsHorizontal(e1) && !IsHorizontal(e2) && - (CrossProduct(e1.bot, resultOp->pt, e2.bot) == 0)) - AddJoin(resultOp, op2); - } - else - { - resultOp = AddOutPt(e1, pt); -#ifdef USINGZ - OutPt* op2 = AddOutPt(e2, pt); - if (zCallback_) - { - SetZ(e1, e2, resultOp->pt); - SetZ(e1, e2, op2->pt); - } -#else - AddOutPt(e2, pt); -#endif - SwapOutrecs(e1, e2); - } - } - else if (IsHotEdge(e1)) - { - resultOp = AddOutPt(e1, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - SwapOutrecs(e1, e2); - } - else if (IsHotEdge(e2)) - { - resultOp = AddOutPt(e2, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - SwapOutrecs(e1, e2); - } - else - { - int64_t e1Wc2, e2Wc2; - switch (fillrule_) - { - case FillRule::EvenOdd: - case FillRule::NonZero: - e1Wc2 = abs(e1.wind_cnt2); - e2Wc2 = abs(e2.wind_cnt2); - break; - default: - if (fillrule_ == fillpos) - { - e1Wc2 = e1.wind_cnt2; - e2Wc2 = e2.wind_cnt2; - } - else - { - e1Wc2 = -e1.wind_cnt2; - e2Wc2 = -e2.wind_cnt2; - } - break; - } - - if (!IsSamePolyType(e1, e2)) - { - resultOp = AddLocalMinPoly(e1, e2, pt, false); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - } - else if (old_e1_windcnt == 1 && old_e2_windcnt == 1) - { - resultOp = nullptr; - switch (cliptype_) - { - case ClipType::Union: - if (e1Wc2 <= 0 && e2Wc2 <= 0) - resultOp = AddLocalMinPoly(e1, e2, pt, false); - break; - case ClipType::Difference: - if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - { - resultOp = AddLocalMinPoly(e1, e2, pt, false); - } - break; - case ClipType::Xor: - resultOp = AddLocalMinPoly(e1, e2, pt, false); - break; - default: - if (e1Wc2 > 0 && e2Wc2 > 0) - resultOp = AddLocalMinPoly(e1, e2, pt, false); - break; - } -#ifdef USINGZ - if (resultOp && zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - } - } - return resultOp; - } - - - inline void ClipperBase::DeleteFromAEL(Active& e) - { - Active* prev = e.prev_in_ael; - Active* next = e.next_in_ael; - if (!prev && !next && (&e != actives_)) return; // already deleted - if (prev) - prev->next_in_ael = next; - else - actives_ = next; - if (next) next->prev_in_ael = prev; - delete& e; - } - - - inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y) - { - Active* e = actives_; - sel_ = e; - while (e) - { - e->prev_in_sel = e->prev_in_ael; - e->next_in_sel = e->next_in_ael; - e->jump = e->next_in_sel; - e->curr_x = TopX(*e, top_y); - e = e->next_in_ael; - } - } - - - bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees) - { - cliptype_ = ct; - fillrule_ = fillrule; - using_polytree_ = use_polytrees; - Reset(); - int64_t y; - if (ct == ClipType::None || !PopScanline(y)) return true; - - while (succeeded_) - { - InsertLocalMinimaIntoAEL(y); - Active* e; - while (PopHorz(e)) DoHorizontal(*e); - if (horz_joiners_) ConvertHorzTrialsToJoins(); - bot_y_ = y; // bot_y_ == bottom of scanbeam - if (!PopScanline(y)) break; // y new top of scanbeam - DoIntersections(y); - DoTopOfScanbeam(y); - while (PopHorz(e)) DoHorizontal(*e); - } - ProcessJoinerList(); - return succeeded_; - } - - void ClipperBase::DoIntersections(const int64_t top_y) - { - if (BuildIntersectList(top_y)) - { - ProcessIntersectList(); - intersect_nodes_.clear(); - } - } - - void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y) - { - Point64 pt = GetIntersectPoint(e1, e2); - - //rounding errors can occasionally place the calculated intersection - //point either below or above the scanbeam, so check and correct ... - if (pt.y > bot_y_) - { - //e.curr.y is still the bottom of scanbeam - pt.y = bot_y_; - //use the more vertical of the 2 edges to derive pt.x ... - if (abs(e1.dx) < abs(e2.dx)) - pt.x = TopX(e1, bot_y_); - else - pt.x = TopX(e2, bot_y_); - } - else if (pt.y < top_y) - { - //top_y is at the top of the scanbeam - pt.y = top_y; - if (e1.top.y == top_y) - pt.x = e1.top.x; - else if (e2.top.y == top_y) - pt.x = e2.top.x; - else if (abs(e1.dx) < abs(e2.dx)) - pt.x = e1.curr_x; - else - pt.x = e2.curr_x; - } - - intersect_nodes_.push_back(IntersectNode(&e1, &e2, pt)); - } - - - bool ClipperBase::BuildIntersectList(const int64_t top_y) - { - if (!actives_ || !actives_->next_in_ael) 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. - AdjustCurrXAndCopyToSEL(top_y); - //Find all edge intersections in the current scanbeam using a stable merge - //sort that ensures only adjacent edges are intersecting. Intersect info is - //stored in FIntersectList ready to be processed in ProcessIntersectList. - //Re merge sorts see https://stackoverflow.com/a/46319131/359538 - - Active* left = sel_, * right, * l_end, * r_end, * curr_base, * tmp; - - while (left && left->jump) - { - Active* prev_base = nullptr; - while (left && left->jump) - { - curr_base = left; - right = left->jump; - l_end = right; - r_end = right->jump; - left->jump = r_end; - while (left != l_end && right != r_end) - { - if (right->curr_x < left->curr_x) - { - tmp = right->prev_in_sel; - for (; ; ) - { - AddNewIntersectNode(*tmp, *right, top_y); - if (tmp == left) break; - tmp = tmp->prev_in_sel; - } - - tmp = right; - right = ExtractFromSEL(tmp); - l_end = right; - Insert1Before2InSEL(tmp, left); - if (left == curr_base) - { - curr_base = tmp; - curr_base->jump = r_end; - if (!prev_base) sel_ = curr_base; - else prev_base->jump = curr_base; - } - } - else left = left->next_in_sel; - } - prev_base = curr_base; - left = r_end; - } - left = sel_; - } - return intersect_nodes_.size() > 0; - } - - void ClipperBase::ProcessIntersectList() - { - //We now have a list of intersections required so that edges will be - //correctly positioned at the top of the scanbeam. However, it's important - //that edge intersections are processed from the bottom up, but it's also - //crucial that intersections only occur between adjacent edges. - - //First we do a quicksort so intersections proceed in a bottom up order ... - std::sort(intersect_nodes_.begin(), intersect_nodes_.end(), IntersectListSort); - //Now as we process these intersections, we must sometimes adjust the order - //to ensure that intersecting edges are always adjacent ... - - std::vector::iterator node_iter, node_iter2; - for (node_iter = intersect_nodes_.begin(); - node_iter != intersect_nodes_.end(); ++node_iter) - { - if (!EdgesAdjacentInAEL(*node_iter)) - { - node_iter2 = node_iter + 1; - while (!EdgesAdjacentInAEL(*node_iter2)) ++node_iter2; - std::swap(*node_iter, *node_iter2); - } - - IntersectNode& node = *node_iter; - IntersectEdges(*node.edge1, *node.edge2, node.pt); - SwapPositionsInAEL(*node.edge1, *node.edge2); - - if (TestJoinWithPrev2(*node.edge2, node.pt)) - { - OutPt* op1 = AddOutPt(*node.edge2->prev_in_ael, node.pt); - OutPt* op2 = AddOutPt(*node.edge2, node.pt); - if (op1 != op2) AddJoin(op1, op2); - } - else if (TestJoinWithNext2(*node.edge1, node.pt)) - { - OutPt* op1 = AddOutPt(*node.edge1, node.pt); - OutPt* op2 = AddOutPt(*node.edge1->next_in_ael, node.pt); - if (op1 != op2) AddJoin(op1, op2); - } - } - } - - - void ClipperBase::SwapPositionsInAEL(Active& e1, Active& e2) - { - //preconditon: e1 must be immediately to the left of e2 - Active* next = e2.next_in_ael; - if (next) next->prev_in_ael = &e1; - Active* prev = e1.prev_in_ael; - if (prev) prev->next_in_ael = &e2; - e2.prev_in_ael = prev; - e2.next_in_ael = &e1; - e1.prev_in_ael = &e2; - e1.next_in_ael = next; - if (!e2.prev_in_ael) actives_ = &e2; - } - - - bool ClipperBase::ResetHorzDirection(const Active& horz, - const Active* max_pair, int64_t& horz_left, int64_t& horz_right) - { - if (horz.bot.x == horz.top.x) - { - //the horizontal edge is going nowhere ... - horz_left = horz.curr_x; - horz_right = horz.curr_x; - Active* e = horz.next_in_ael; - while (e && e != max_pair) e = e->next_in_ael; - return e != nullptr; - } - else if (horz.curr_x < horz.top.x) - { - horz_left = horz.curr_x; - horz_right = horz.top.x; - return true; - } - else - { - horz_left = horz.top.x; - horz_right = horz.curr_x; - return false; // right to left - } - } - - inline bool HorzIsSpike(const Active& horzEdge) - { - Point64 nextPt = NextVertex(horzEdge)->pt; - return (nextPt.y == horzEdge.bot.y) && - (horzEdge.bot.x < horzEdge.top.x) != (horzEdge.top.x < nextPt.x); - } - - inline void TrimHorz(Active& horzEdge, bool preserveCollinear) - { - bool wasTrimmed = false; - Point64 pt = NextVertex(horzEdge)->pt; - while (pt.y == horzEdge.top.y) - { - //always trim 180 deg. spikes (in closed paths) - //but otherwise break if preserveCollinear = true - if (preserveCollinear && - ((pt.x < horzEdge.top.x) != (horzEdge.bot.x < horzEdge.top.x))) - break; - - horzEdge.vertex_top = NextVertex(horzEdge); - horzEdge.top = pt; - wasTrimmed = true; - if (IsMaxima(horzEdge)) break; - pt = NextVertex(horzEdge)->pt; - } - - if (wasTrimmed) SetDx(horzEdge); // +/-infinity - } - - - void ClipperBase::DoHorizontal(Active& horz) - /******************************************************************************* - * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or * - * bottom of a scanbeam) are processed as if layered.The order in which HEs * - * are processed doesn't matter. HEs intersect with the bottom vertices of * - * other HEs[#] and with non-horizontal edges [*]. Once these intersections * - * are completed, intermediate HEs are 'promoted' to the next edge in their * - * bounds, and they in turn may be intersected[%] by other HEs. * - * * - * eg: 3 horizontals at a scanline: / | / / * - * | / | (HE3)o ========%========== o * - * o ======= o(HE2) / | / / * - * o ============#=========*======*========#=========o (HE1) * - * / | / | / * - *******************************************************************************/ - { - Point64 pt; - bool horzIsOpen = IsOpen(horz); - int64_t y = horz.bot.y; - Vertex* vertex_max = nullptr; - Active* max_pair = nullptr; - - if (!horzIsOpen) - { - vertex_max = GetCurrYMaximaVertex(horz); - if (vertex_max) - { - max_pair = GetHorzMaximaPair(horz, vertex_max); - //remove 180 deg.spikes and also simplify - //consecutive horizontals when PreserveCollinear = true - if (vertex_max != horz.vertex_top) - TrimHorz(horz, PreserveCollinear); - } - } - - int64_t horz_left, horz_right; - bool is_left_to_right = - ResetHorzDirection(horz, max_pair, horz_left, horz_right); - - if (IsHotEdge(horz)) - AddOutPt(horz, Point64(horz.curr_x, y)); - - OutPt* op; - while (true) // loop through consec. horizontal edges - { - if (horzIsOpen && IsMaxima(horz) && !IsOpenEnd(horz)) - { - vertex_max = GetCurrYMaximaVertex(horz); - if (vertex_max) - max_pair = GetHorzMaximaPair(horz, vertex_max); - } - - Active* e; - if (is_left_to_right) e = horz.next_in_ael; - else e = horz.prev_in_ael; - - while (e) - { - - if (e == max_pair) - { - if (IsHotEdge(horz)) - { - while (horz.vertex_top != e->vertex_top) - { - AddOutPt(horz, horz.top); - UpdateEdgeIntoAEL(&horz); - } - op = AddLocalMaxPoly(horz, *e, horz.top); - if (op && !IsOpen(horz) && op->pt == horz.top) - AddTrialHorzJoin(op); - } - DeleteFromAEL(*e); - DeleteFromAEL(horz); - return; - } - - //if horzEdge is a maxima, keep going until we reach - //its maxima pair, otherwise check for break conditions - if (vertex_max != horz.vertex_top || IsOpenEnd(horz)) - { - //otherwise stop when 'ae' is beyond the end of the horizontal line - if ((is_left_to_right && e->curr_x > horz_right) || - (!is_left_to_right && e->curr_x < horz_left)) break; - - if (e->curr_x == horz.top.x && !IsHorizontal(*e)) - { - pt = NextVertex(horz)->pt; - if (is_left_to_right) - { - //with open paths we'll only break once past horz's end - if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e)) - { - if (TopX(*e, pt.y) > pt.x) break; - } - //otherwise we'll only break when horz's outslope is greater than e's - else if (TopX(*e, pt.y) >= pt.x) break; - } - else - { - if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e)) - { - if (TopX(*e, pt.y) < pt.x) break; - } - else if (TopX(*e, pt.y) <= pt.x) break; - } - } - } - - pt = Point64(e->curr_x, horz.bot.y); - - if (is_left_to_right) - { - op = IntersectEdges(horz, *e, pt); - SwapPositionsInAEL(horz, *e); - // todo: check if op->pt == pt test is still needed - // expect op != pt only after AddLocalMaxPoly when horz.outrec == nullptr - if (IsHotEdge(horz) && op && !IsOpen(horz) && op->pt == pt) - AddTrialHorzJoin(op); - - if (!IsHorizontal(*e) && TestJoinWithPrev1(*e)) - { - op = AddOutPt(*e->prev_in_ael, pt); - OutPt* op2 = AddOutPt(*e, pt); - AddJoin(op, op2); - } - - horz.curr_x = e->curr_x; - e = horz.next_in_ael; - } - else - { - op = IntersectEdges(*e, horz, pt); - SwapPositionsInAEL(*e, horz); - - if (IsHotEdge(horz) && op && - !IsOpen(horz) && op->pt == pt) - AddTrialHorzJoin(op); - - if (!IsHorizontal(*e) && TestJoinWithNext1(*e)) - { - op = AddOutPt(*e, pt); - OutPt* op2 = AddOutPt(*e->next_in_ael, pt); - AddJoin(op, op2); - } - - horz.curr_x = e->curr_x; - e = horz.prev_in_ael; - } - } - - //check if we've finished with (consecutive) horizontals ... - if (horzIsOpen && IsOpenEnd(horz)) // ie open at top - { - if (IsHotEdge(horz)) - { - AddOutPt(horz, horz.top); - if (IsFront(horz)) - horz.outrec->front_edge = nullptr; - else - horz.outrec->back_edge = nullptr; - horz.outrec = nullptr; - } - DeleteFromAEL(horz); - return; - } - else if (NextVertex(horz)->pt.y != horz.top.y) - break; - - //still more horizontals in bound to process ... - if (IsHotEdge(horz)) - AddOutPt(horz, horz.top); - UpdateEdgeIntoAEL(&horz); - - if (PreserveCollinear && !horzIsOpen && HorzIsSpike(horz)) - TrimHorz(horz, true); - - is_left_to_right = - ResetHorzDirection(horz, max_pair, horz_left, horz_right); - } - - if (IsHotEdge(horz)) - { - op = AddOutPt(horz, horz.top); - if (!IsOpen(horz)) - AddTrialHorzJoin(op); - } - else - op = nullptr; - - if ((horzIsOpen && !IsOpenEnd(horz)) || - (!horzIsOpen && vertex_max != horz.vertex_top)) - { - UpdateEdgeIntoAEL(&horz); // this is the end of an intermediate horiz. - if (IsOpen(horz)) return; - - if (is_left_to_right && TestJoinWithNext1(horz)) - { - OutPt* op2 = AddOutPt(*horz.next_in_ael, horz.bot); - AddJoin(op, op2); - } - else if (!is_left_to_right && TestJoinWithPrev1(horz)) - { - OutPt* op2 = AddOutPt(*horz.prev_in_ael, horz.bot); - AddJoin(op2, op); - } - } - else if (IsHotEdge(horz)) - AddLocalMaxPoly(horz, *max_pair, horz.top); - else - { - DeleteFromAEL(*max_pair); - DeleteFromAEL(horz); - } - } - - - void ClipperBase::DoTopOfScanbeam(const int64_t y) - { - sel_ = nullptr; // sel_ is reused to flag horizontals (see PushHorz below) - Active* e = actives_; - while (e) - { - //nb: 'e' will never be horizontal here - if (e->top.y == y) - { - e->curr_x = e->top.x; - if (IsMaxima(*e)) - { - e = DoMaxima(*e); // TOP OF BOUND (MAXIMA) - continue; - } - else - { - //INTERMEDIATE VERTEX ... - if (IsHotEdge(*e)) AddOutPt(*e, e->top); - UpdateEdgeIntoAEL(e); - if (IsHorizontal(*e)) - PushHorz(*e); // horizontals are processed later - } - } - else // i.e. not the top of the edge - e->curr_x = TopX(*e, y); - - e = e->next_in_ael; - } - } - - - Active* ClipperBase::DoMaxima(Active& e) - { - Active* next_e, * prev_e, * max_pair; - prev_e = e.prev_in_ael; - next_e = e.next_in_ael; - if (IsOpenEnd(e)) - { - if (IsHotEdge(e)) AddOutPt(e, e.top); - if (!IsHorizontal(e)) - { - if (IsHotEdge(e)) - { - if (IsFront(e)) - e.outrec->front_edge = nullptr; - else - e.outrec->back_edge = nullptr; - e.outrec = nullptr; - } - DeleteFromAEL(e); - } - return next_e; - } - else - { - max_pair = GetMaximaPair(e); - if (!max_pair) return next_e; // eMaxPair is horizontal - } - - //only non-horizontal maxima here. - //process any edges between maxima pair ... - while (next_e != max_pair) - { - IntersectEdges(e, *next_e, e.top); - SwapPositionsInAEL(e, *next_e); - next_e = e.next_in_ael; - } - - if (IsOpen(e)) - { - if (IsHotEdge(e)) - AddLocalMaxPoly(e, *max_pair, e.top); - DeleteFromAEL(*max_pair); - DeleteFromAEL(e); - return (prev_e ? prev_e->next_in_ael : actives_); - } - - //here E.next_in_ael == ENext == EMaxPair ... - if (IsHotEdge(e)) - AddLocalMaxPoly(e, *max_pair, e.top); - - DeleteFromAEL(e); - DeleteFromAEL(*max_pair); - return (prev_e ? prev_e->next_in_ael : actives_); - } - - - void ClipperBase::SafeDeleteOutPtJoiners(OutPt* op) - { - Joiner* joiner = op->joiner; - if (!joiner) return; - - while (joiner) - { - if (joiner->idx < 0) - DeleteTrialHorzJoin(op); - else if (horz_joiners_) - { - if (OutPtInTrialHorzList(joiner->op1)) - DeleteTrialHorzJoin(joiner->op1); - if (OutPtInTrialHorzList(joiner->op2)) - DeleteTrialHorzJoin(joiner->op2); - DeleteJoin(joiner); - } - else - DeleteJoin(joiner); - joiner = op->joiner; - } - } - - - Joiner* ClipperBase::GetHorzTrialParent(const OutPt* op) - { - Joiner* joiner = op->joiner; - while (joiner) - { - if (joiner->op1 == op) - { - if (joiner->next1 && joiner->next1->idx < 0) return joiner; - else joiner = joiner->next1; - } - else - { - if (joiner->next2 && joiner->next2->idx < 0) return joiner; - else joiner = joiner->next1; - } - } - return joiner; - } - - - bool ClipperBase::OutPtInTrialHorzList(OutPt* op) - { - return op->joiner && ((op->joiner->idx < 0) || GetHorzTrialParent(op)); - } - - - void ClipperBase::AddTrialHorzJoin(OutPt* op) - { - //make sure 'op' isn't added more than once - if (!op->outrec->is_open && !OutPtInTrialHorzList(op)) - horz_joiners_ = new Joiner(op, nullptr, horz_joiners_); - } - - - Joiner* FindTrialJoinParent(Joiner*& joiner, const OutPt* op) - { - Joiner* parent = joiner; - while (parent) - { - if (op == parent->op1) - { - if (parent->next1 && parent->next1->idx < 0) - { - joiner = parent->next1; - return parent; - } - parent = parent->next1; - } - else - { - if (parent->next2 && parent->next2->idx < 0) - { - joiner = parent->next2; - return parent; - } - parent = parent->next2; - } - } - return nullptr; - } - - - void ClipperBase::DeleteTrialHorzJoin(OutPt* op) - { - if (!horz_joiners_) return; - - Joiner* joiner = op->joiner; - Joiner* parentH, * parentOp = nullptr; - while (joiner) - { - if (joiner->idx < 0) - { - //first remove joiner from FHorzTrials - if (joiner == horz_joiners_) - horz_joiners_ = joiner->nextH; - else - { - parentH = horz_joiners_; - while (parentH->nextH != joiner) - parentH = parentH->nextH; - parentH->nextH = joiner->nextH; - } - - //now remove joiner from op's joiner list - if (!parentOp) - { - //joiner must be first one in list - op->joiner = joiner->next1; - delete joiner; - joiner = op->joiner; - } - else - { - //the trial joiner isn't first - if (op == parentOp->op1) - parentOp->next1 = joiner->next1; - else - parentOp->next2 = joiner->next1; - delete joiner; - joiner = parentOp; - } - } - else - { - //not a trial join so look further along the linked list - parentOp = FindTrialJoinParent(joiner, op); - if (!parentOp) break; - } - //loop in case there's more than one trial join - } - } - - - inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2) - { - OutRec* outrec = GetRealOutRec(op->outrec); - op2 = op; - if (outrec->front_edge) - { - while (op->prev != outrec->pts && - op->prev->pt.y == op->pt.y) op = op->prev; - while (op2 != outrec->pts && - op2->next->pt.y == op2->pt.y) op2 = op2->next; - return op2 != op; - } - else - { - while (op->prev != op2 && op->prev->pt.y == op->pt.y) - op = op->prev; - while (op2->next != op && op2->next->pt.y == op2->pt.y) - op2 = op2->next; - return op2 != op && op2->next != op; - } - } - - - inline bool HorzEdgesOverlap(int64_t x1a, int64_t x1b, int64_t x2a, int64_t x2b) - { - const int64_t minOverlap = 2; - if (x1a > x1b + minOverlap) - { - if (x2a > x2b + minOverlap) - return !((x1a <= x2b) || (x2a <= x1b)); - else - return !((x1a <= x2a) || (x2b <= x1b)); - } - else if (x1b > x1a + minOverlap) - { - if (x2a > x2b + minOverlap) - return !((x1b <= x2b) || (x2a <= x1a)); - else - return !((x1b <= x2a) || (x2b <= x1a)); - } - else - return false; - } - - - inline bool ValueBetween(int64_t val, int64_t end1, int64_t end2) - { - //NB accommodates axis aligned between where end1 == end2 - return ((val != end1) == (val != end2)) && - ((val > end1) == (val < end2)); - } - - - inline bool ValueEqualOrBetween(int64_t val, int64_t end1, int64_t end2) - { - return (val == end1) || (val == end2) || ((val > end1) == (val < end2)); - } - - - inline bool PointBetween(Point64 pt, Point64 corner1, Point64 corner2) - { - //NB points may not be collinear - return ValueBetween(pt.x, corner1.x, corner2.x) && - ValueBetween(pt.y, corner1.y, corner2.y); - } - - inline bool PointEqualOrBetween(Point64 pt, Point64 corner1, Point64 corner2) - { - //NB points may not be collinear - return ValueEqualOrBetween(pt.x, corner1.x, corner2.x) && - ValueEqualOrBetween(pt.y, corner1.y, corner2.y); - } - - - Joiner* FindJoinParent(const Joiner* joiner, OutPt* op) - { - Joiner* result = op->joiner; - for (; ; ) - { - if (op == result->op1) - { - if (result->next1 == joiner) return result; - else result = result->next1; - } - else - { - if (result->next2 == joiner) return result; - else result = result->next2; - } - } - } - - - void ClipperBase::ConvertHorzTrialsToJoins() - { - while (horz_joiners_) - { - Joiner* joiner = horz_joiners_; - horz_joiners_ = horz_joiners_->nextH; - OutPt* op1a = joiner->op1; - if (op1a->joiner == joiner) - { - op1a->joiner = joiner->next1; - } - else - { - Joiner* joinerParent = FindJoinParent(joiner, op1a); - if (joinerParent->op1 == op1a) - joinerParent->next1 = joiner->next1; - else - joinerParent->next2 = joiner->next1; - } - delete joiner; - - OutPt* op1b; - if (!GetHorzExtendedHorzSeg(op1a, op1b)) - { - CleanCollinear(op1a->outrec); - continue; - } - - bool joined = false; - joiner = horz_joiners_; - while (joiner) - { - OutPt* op2a = joiner->op1, * op2b; - if (GetHorzExtendedHorzSeg(op2a, op2b) && - HorzEdgesOverlap(op1a->pt.x, op1b->pt.x, op2a->pt.x, op2b->pt.x)) - { - //overlap found so promote to a 'real' join - joined = true; - if (op1a->pt == op2b->pt) - AddJoin(op1a, op2b); - else if (op1b->pt == op2a->pt) - AddJoin(op1b, op2a); - else if (op1a->pt == op2a->pt) - AddJoin(op1a, op2a); - else if (op1b->pt == op2b->pt) - AddJoin(op1b, op2b); - else if (ValueBetween(op1a->pt.x, op2a->pt.x, op2b->pt.x)) - AddJoin(op1a, InsertOp(op1a->pt, op2a)); - else if (ValueBetween(op1b->pt.x, op2a->pt.x, op2b->pt.x)) - AddJoin(op1b, InsertOp(op1b->pt, op2a)); - else if (ValueBetween(op2a->pt.x, op1a->pt.x, op1b->pt.x)) - AddJoin(op2a, InsertOp(op2a->pt, op1a)); - else if (ValueBetween(op2b->pt.x, op1a->pt.x, op1b->pt.x)) - AddJoin(op2b, InsertOp(op2b->pt, op1a)); - break; - } - joiner = joiner->nextH; - } - if (!joined) - CleanCollinear(op1a->outrec); - } - } - - - void ClipperBase::AddJoin(OutPt* op1, OutPt* op2) - { - if ((op1->outrec == op2->outrec) && ((op1 == op2) || - //unless op1.next or op1.prev crosses the start-end divide - //don't waste time trying to join adjacent vertices - ((op1->next == op2) && (op1 != op1->outrec->pts)) || - ((op2->next == op1) && (op2 != op1->outrec->pts)))) return; - - Joiner* j = new Joiner(op1, op2, nullptr); - j->idx = static_cast(joiner_list_.size()); - joiner_list_.push_back(j); - } - - - void ClipperBase::DeleteJoin(Joiner* joiner) - { - //This method deletes a single join, and it doesn't check for or - //delete trial horz. joins. For that, use the following method. - OutPt* op1 = joiner->op1, * op2 = joiner->op2; - - Joiner* parent_joiner; - if (op1->joiner != joiner) - { - parent_joiner = FindJoinParent(joiner, op1); - if (parent_joiner->op1 == op1) - parent_joiner->next1 = joiner->next1; - else - parent_joiner->next2 = joiner->next1; - } - else - op1->joiner = joiner->next1; - - if (op2->joiner != joiner) - { - parent_joiner = FindJoinParent(joiner, op2); - if (parent_joiner->op1 == op2) - parent_joiner->next1 = joiner->next2; - else - parent_joiner->next2 = joiner->next2; - } - else - op2->joiner = joiner->next2; - - joiner_list_[joiner->idx] = nullptr; - delete joiner; - } - - - void ClipperBase::ProcessJoinerList() - { - for (Joiner* j : joiner_list_) - { - if (!j) continue; - if (succeeded_) - { - OutRec* outrec = ProcessJoin(j); - CleanCollinear(outrec); - } - else - delete j; - } - - joiner_list_.resize(0); - } - - - bool CheckDisposeAdjacent(OutPt*& op, const OutPt* guard, OutRec& outRec) - { - bool result = false; - while (op->prev != op) - { - if (op->pt == op->prev->pt && op != guard && - op->prev->joiner && !op->joiner) - { - if (op == outRec.pts) outRec.pts = op->prev; - op = DisposeOutPt(op); - op = op->prev; - } - else - break; - } - - while (op->next != op) - { - if (op->pt == op->next->pt && op != guard && - op->next->joiner && !op->joiner) - { - if (op == outRec.pts) outRec.pts = op->prev; - op = DisposeOutPt(op); - op = op->prev; - } - else - break; - } - return result; - } - - - inline bool IsValidPath(OutPt* op) - { - return (op && op->next != op); - } - - - bool CollinearSegsOverlap(const Point64& seg1a, const Point64& seg1b, - const Point64& seg2a, const Point64& seg2b) - { - //precondition: seg1 and seg2 are collinear - if (seg1a.x == seg1b.x) - { - if (seg2a.x != seg1a.x || seg2a.x != seg2b.x) return false; - } - else if (seg1a.x < seg1b.x) - { - if (seg2a.x < seg2b.x) - { - if (seg2a.x >= seg1b.x || seg2b.x <= seg1a.x) return false; - } - else - { - if (seg2b.x >= seg1b.x || seg2a.x <= seg1a.x) return false; - } - } - else - { - if (seg2a.x < seg2b.x) - { - if (seg2a.x >= seg1a.x || seg2b.x <= seg1b.x) return false; - } - else - { - if (seg2b.x >= seg1a.x || seg2a.x <= seg1b.x) return false; - } - } - - if (seg1a.y == seg1b.y) - { - if (seg2a.y != seg1a.y || seg2a.y != seg2b.y) return false; - } - else if (seg1a.y < seg1b.y) - { - if (seg2a.y < seg2b.y) - { - if (seg2a.y >= seg1b.y || seg2b.y <= seg1a.y) return false; - } - else - { - if (seg2b.y >= seg1b.y || seg2a.y <= seg1a.y) return false; - } - } - else - { - if (seg2a.y < seg2b.y) - { - if (seg2a.y >= seg1a.y || seg2b.y <= seg1b.y) return false; - } - else - { - if (seg2b.y >= seg1a.y || seg2a.y <= seg1b.y) return false; - } - } - return true; - } - - OutRec* ClipperBase::ProcessJoin(Joiner* joiner) - { - OutPt* op1 = joiner->op1, * op2 = joiner->op2; - OutRec* or1 = GetRealOutRec(op1->outrec); - OutRec* or2 = GetRealOutRec(op2->outrec); - DeleteJoin(joiner); - - if (or2->pts == nullptr) return or1; - else if (!IsValidClosedPath(op2)) - { - SafeDisposeOutPts(op2); - return or1; - } - else if ((or1->pts == nullptr) || !IsValidClosedPath(op1)) - { - SafeDisposeOutPts(op1); - return or2; - } - else if (or1 == or2 && - ((op1 == op2) || (op1->next == op2) || (op1->prev == op2))) return or1; - - CheckDisposeAdjacent(op1, op2, *or1); - CheckDisposeAdjacent(op2, op1, *or2); - if (op1->next == op2 || op2->next == op1) return or1; - OutRec* result = or1; - - for (; ; ) - { - if (!IsValidPath(op1) || !IsValidPath(op2) || - (or1 == or2 && (op1->prev == op2 || op1->next == op2))) return or1; - - if (op1->prev->pt == op2->next->pt || - ((CrossProduct(op1->prev->pt, op1->pt, op2->next->pt) == 0) && - CollinearSegsOverlap(op1->prev->pt, op1->pt, op2->pt, op2->next->pt))) - { - if (or1 == or2) - { - //SPLIT REQUIRED - //make sure op1.prev and op2.next match positions - //by inserting an extra vertex if needed - if (op1->prev->pt != op2->next->pt) - { - if (PointEqualOrBetween(op1->prev->pt, op2->pt, op2->next->pt)) - op2->next = InsertOp(op1->prev->pt, op2); - else - op1->prev = InsertOp(op2->next->pt, op1->prev); - } - - //current to new - //op1.p[opA] >>> op1 ... opA \ / op1 - //op2.n[opB] <<< op2 ... opB / \ op2 - OutPt* opA = op1->prev, * opB = op2->next; - opA->next = opB; - opB->prev = opA; - op1->prev = op2; - op2->next = op1; - CompleteSplit(op1, opA, *or1); - } - else - { - //JOIN, NOT SPLIT - OutPt* opA = op1->prev, * opB = op2->next; - opA->next = opB; - opB->prev = opA; - op1->prev = op2; - op2->next = op1; - - //SafeDeleteOutPtJoiners(op2); - //DisposeOutPt(op2); - - if (or1->idx < or2->idx) - { - or1->pts = op1; - or2->pts = nullptr; - if (or1->owner && (!or2->owner || - or2->owner->idx < or1->owner->idx)) - or1->owner = or2->owner; - or2->owner = or1; - } - else - { - result = or2; - or2->pts = op1; - or1->pts = nullptr; - if (or2->owner && (!or1->owner || - or1->owner->idx < or2->owner->idx)) - or2->owner = or1->owner; - or1->owner = or2; - } - } - break; - } - else if (op1->next->pt == op2->prev->pt || - ((CrossProduct(op1->next->pt, op2->pt, op2->prev->pt) == 0) && - CollinearSegsOverlap(op1->next->pt, op1->pt, op2->pt, op2->prev->pt))) - { - if (or1 == or2) - { - //SPLIT REQUIRED - //make sure op2.prev and op1.next match positions - //by inserting an extra vertex if needed - if (op2->prev->pt != op1->next->pt) - { - if (PointEqualOrBetween(op2->prev->pt, op1->pt, op1->next->pt)) - op1->next = InsertOp(op2->prev->pt, op1); - else - op2->prev = InsertOp(op1->next->pt, op2->prev); - } - - //current to new - //op2.p[opA] >>> op2 ... opA \ / op2 - //op1.n[opB] <<< op1 ... opB / \ op1 - OutPt* opA = op2->prev, * opB = op1->next; - opA->next = opB; - opB->prev = opA; - op2->prev = op1; - op1->next = op2; - CompleteSplit(op1, opA, *or1); - } - else - { - //JOIN, NOT SPLIT - OutPt* opA = op1->next, * opB = op2->prev; - opA->prev = opB; - opB->next = opA; - op1->next = op2; - op2->prev = op1; - - //SafeDeleteOutPtJoiners(op2); - //DisposeOutPt(op2); - - if (or1->idx < or2->idx) - { - or1->pts = op1; - or2->pts = nullptr; - if (or1->owner && (!or2->owner || - or2->owner->idx < or1->owner->idx)) - or1->owner = or2->owner; - or2->owner = or1; - } - else - { - result = or2; - or2->pts = op1; - or1->pts = nullptr; - if (or2->owner && (!or1->owner || - or1->owner->idx < or2->owner->idx)) - or2->owner = or1->owner; - or1->owner = or2; - } - } - break; - } - else if (PointBetween(op1->next->pt, op2->pt, op2->prev->pt) && - DistanceFromLineSqrd(op1->next->pt, op2->pt, op2->prev->pt) < 2.01) - { - InsertOp(op1->next->pt, op2->prev); - continue; - } - else if (PointBetween(op2->next->pt, op1->pt, op1->prev->pt) && - DistanceFromLineSqrd(op2->next->pt, op1->pt, op1->prev->pt) < 2.01) - { - InsertOp(op2->next->pt, op1->prev); - continue; - } - else if (PointBetween(op1->prev->pt, op2->pt, op2->next->pt) && - DistanceFromLineSqrd(op1->prev->pt, op2->pt, op2->next->pt) < 2.01) - { - InsertOp(op1->prev->pt, op2); - continue; - } - else if (PointBetween(op2->prev->pt, op1->pt, op1->next->pt) && - DistanceFromLineSqrd(op2->prev->pt, op1->pt, op1->next->pt) < 2.01) - { - InsertOp(op2->prev->pt, op1); - continue; - } - - //something odd needs tidying up - if (CheckDisposeAdjacent(op1, op2, *or1)) continue; - else if (CheckDisposeAdjacent(op2, op1, *or1)) continue; - else if (op1->prev->pt != op2->next->pt && - (DistanceSqr(op1->prev->pt, op2->next->pt) < 2.01)) - { - op1->prev->pt = op2->next->pt; - continue; - } - else if (op1->next->pt != op2->prev->pt && - (DistanceSqr(op1->next->pt, op2->prev->pt) < 2.01)) - { - op2->prev->pt = op1->next->pt; - continue; - } - else - { - //OK, there doesn't seem to be a way to join after all - //so just tidy up the polygons - or1->pts = op1; - if (or2 != or1) - { - or2->pts = op2; - CleanCollinear(or2); - } - break; - } - } - return result; - - } - - inline bool Path1InsidePath2(const OutRec* or1, const OutRec* or2) - { - PointInPolygonResult result = PointInPolygonResult::IsOn; - OutPt* op = or1->pts; - do - { - result = PointInPolygon(op->pt, or2->path); - if (result != PointInPolygonResult::IsOn) break; - op = op->next; - } while (op != or1->pts); - if (result == PointInPolygonResult::IsOn) - return Area(op) < Area(or2->pts); - else - return result == PointInPolygonResult::IsInside; - } - - inline Rect64 GetBounds(const Path64& path) - { - if (path.empty()) return Rect64(); - Rect64 result = invalid_rect; - for(const Point64& pt : 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; - } - - bool BuildPath64(OutPt* op, bool reverse, bool isOpen, Path64& path) - { - if (op->next == op || (!isOpen && op->next == op->prev)) - return false; - - path.resize(0); - Point64 lastPt; - OutPt* op2; - if (reverse) - { - lastPt = op->pt; - op2 = op->prev; - } - else - { - op = op->next; - lastPt = op->pt; - op2 = op->next; - } - path.push_back(lastPt); - - while (op2 != op) - { - if (op2->pt != lastPt) - { - lastPt = op2->pt; - path.push_back(lastPt); - } - if (reverse) - op2 = op2->prev; - else - op2 = op2->next; - } - - if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false; - else return true; - } - - bool ClipperBase::DeepCheckOwner(OutRec* outrec, OutRec* owner) - { - if (owner->bounds.IsEmpty()) owner->bounds = GetBounds(owner->path); - bool is_inside_owner_bounds = owner->bounds.Contains(outrec->bounds); - - // while looking for the correct owner, check the owner's - // splits **before** checking the owner itself because - // splits can occur internally, and checking the owner - // first would miss the inner split's true ownership - if (owner->splits) - { - for (OutRec* split : *owner->splits) - { - split = GetRealOutRec(split); - if (!split || split->idx <= owner->idx || split == outrec) continue; - - if (split->splits && DeepCheckOwner(outrec, split)) return true; - - if (!split->path.size()) - BuildPath64(split->pts, ReverseSolution, false, split->path); - if (split->bounds.IsEmpty()) split->bounds = GetBounds(split->path); - - if (split->bounds.Contains(outrec->bounds) && - Path1InsidePath2(outrec, split)) - { - outrec->owner = split; - return true; - } - } - } - - // only continue past here when not inside recursion - if (owner != outrec->owner) return false; - - for (;;) - { - if (is_inside_owner_bounds && Path1InsidePath2(outrec, outrec->owner)) - return true; - // otherwise keep trying with owner's owner - outrec->owner = outrec->owner->owner; - if (!outrec->owner) return true; // true or false - is_inside_owner_bounds = outrec->owner->bounds.Contains(outrec->bounds); - } - } - - void Clipper64::BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen) - { - solutionClosed.resize(0); - solutionClosed.reserve(outrec_list_.size()); - if (solutionOpen) - { - solutionOpen->resize(0); - solutionOpen->reserve(outrec_list_.size()); - } - - for (OutRec* outrec : outrec_list_) - { - if (outrec->pts == nullptr) continue; - - Path64 path; - if (solutionOpen && outrec->is_open) - { - if (BuildPath64(outrec->pts, ReverseSolution, true, path)) - solutionOpen->emplace_back(std::move(path)); - } - else - { - //closed paths should always return a Positive orientation - if (BuildPath64(outrec->pts, ReverseSolution, false, path)) - solutionClosed.emplace_back(std::move(path)); - } - } - } - - void Clipper64::BuildTree64(PolyPath64& polytree, Paths64& open_paths) - { - polytree.Clear(); - open_paths.resize(0); - if (has_open_paths_) - open_paths.reserve(outrec_list_.size()); - - for (OutRec* outrec : outrec_list_) - { - if (!outrec || !outrec->pts) continue; - if (outrec->is_open) - { - Path64 path; - if (BuildPath64(outrec->pts, ReverseSolution, true, path)) - open_paths.push_back(path); - continue; - } - - if (!BuildPath64(outrec->pts, ReverseSolution, false, outrec->path)) - continue; - if (outrec->bounds.IsEmpty()) outrec->bounds = GetBounds(outrec->path); - outrec->owner = GetRealOutRec(outrec->owner); - if (outrec->owner) DeepCheckOwner(outrec, outrec->owner); - - // swap the order when a child preceeds its owner - // (because owners must preceed children in polytrees) - if (outrec->owner && outrec->idx < outrec->owner->idx) - { - OutRec* tmp = outrec->owner; - outrec_list_[outrec->owner->idx] = outrec; - outrec_list_[outrec->idx] = tmp; - size_t tmp_idx = outrec->idx; - outrec->idx = tmp->idx; - tmp->idx = tmp_idx; - outrec = tmp; - outrec->owner = GetRealOutRec(outrec->owner); - BuildPath64(outrec->pts, ReverseSolution, false, outrec->path); - if (outrec->bounds.IsEmpty()) outrec->bounds = GetBounds(outrec->path); - if (outrec->owner) DeepCheckOwner(outrec, outrec->owner); - } - - PolyPath* owner_polypath; - if (outrec->owner && outrec->owner->polypath) - owner_polypath = outrec->owner->polypath; - else - owner_polypath = &polytree; - outrec->polypath = owner_polypath->AddChild(outrec->path); - } - } - - bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale) - { - if (op->next == op || (!isOpen && op->next == op->prev)) return false; - path.resize(0); - Point64 lastPt; - OutPt* op2; - if (reverse) - { - lastPt = op->pt; - op2 = op->prev; - } - else - { - op = op->next; - lastPt = op->pt; - op2 = op->next; - } - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); - - while (op2 != op) - { - if (op2->pt != lastPt) - { - lastPt = op2->pt; - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); - } - if (reverse) - op2 = op2->prev; - else - op2 = op2->next; - } - if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false; - return true; - } - - void ClipperD::BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen) - { - solutionClosed.resize(0); - solutionClosed.reserve(outrec_list_.size()); - if (solutionOpen) - { - solutionOpen->resize(0); - solutionOpen->reserve(outrec_list_.size()); - } - - for (OutRec* outrec : outrec_list_) - { - if (outrec->pts == nullptr) continue; - - PathD path; - if (solutionOpen && outrec->is_open) - { - if (BuildPathD(outrec->pts, ReverseSolution, true, path, invScale_)) - solutionOpen->emplace_back(std::move(path)); - } - else - { - //closed paths should always return a Positive orientation - if (BuildPathD(outrec->pts, ReverseSolution, false, path, invScale_)) - solutionClosed.emplace_back(std::move(path)); - } - } - } - - void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths) - { - polytree.Clear(); - open_paths.resize(0); - if (has_open_paths_) - open_paths.reserve(outrec_list_.size()); - - for (OutRec* outrec : outrec_list_) - { - if (!outrec || !outrec->pts) continue; - if (outrec->is_open) - { - PathD path; - if (BuildPathD(outrec->pts, ReverseSolution, true, path, invScale_)) - open_paths.push_back(path); - continue; - } - - if (!BuildPath64(outrec->pts, ReverseSolution, false, outrec->path)) - continue; - if (outrec->bounds.IsEmpty()) outrec->bounds = GetBounds(outrec->path); - outrec->owner = GetRealOutRec(outrec->owner); - if (outrec->owner) DeepCheckOwner(outrec, outrec->owner); - - // swap the order when a child preceeds its owner - // (because owners must preceed children in polytrees) - if (outrec->owner && outrec->idx < outrec->owner->idx) - { - OutRec* tmp = outrec->owner; - outrec_list_[outrec->owner->idx] = outrec; - outrec_list_[outrec->idx] = tmp; - size_t tmp_idx = outrec->idx; - outrec->idx = tmp->idx; - tmp->idx = tmp_idx; - outrec = tmp; - outrec->owner = GetRealOutRec(outrec->owner); - BuildPath64(outrec->pts, ReverseSolution, false, outrec->path); - if (outrec->bounds.IsEmpty()) outrec->bounds = GetBounds(outrec->path); - if (outrec->owner) DeepCheckOwner(outrec, outrec->owner); - } - - PolyPath* owner_polypath; - if (outrec->owner && outrec->owner->polypath) - owner_polypath = outrec->owner->polypath; - else - owner_polypath = &polytree; - outrec->polypath = owner_polypath->AddChild(outrec->path); - } - } - -} // namespace clipper2lib diff --git a/src/clipper2/Clipper2Lib/src/clipper.offset.cpp b/src/clipper2/Clipper2Lib/src/clipper.offset.cpp deleted file mode 100644 index a19a6ff459..0000000000 --- a/src/clipper2/Clipper2Lib/src/clipper.offset.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 15 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : Path Offset (Inflate/Shrink) * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#include -#include "clipper2/clipper.h" -#include "clipper2/clipper.offset.h" - -namespace Clipper2Lib { - -const double default_arc_tolerance = 0.25; -const double floating_point_tolerance = 1e-12; - -//------------------------------------------------------------------------------ -// Miscellaneous methods -//------------------------------------------------------------------------------ - -Paths64::size_type GetLowestPolygonIdx(const Paths64& paths) -{ - Paths64::size_type result = 0; - Point64 lp = Point64(static_cast(0), - std::numeric_limits::min()); - - for (Paths64::size_type i = 0 ; i < paths.size(); ++i) - for (const Point64& p : paths[i]) - { - if (p.y < lp.y || (p.y == lp.y && p.x >= lp.x)) continue; - result = i; - lp = p; - } - return result; -} - -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); - dx *= inverse_hypot; - dy *= inverse_hypot; - return PointD(dy, -dx); -} - -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) -{ - - double h = Hypot(vec.x, vec.y); - if (AlmostZero(h)) return PointD(0,0); - double inverseHypot = 1 / h; - return PointD(vec.x * inverseHypot, vec.y * inverseHypot); -} - -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) -{ - return et == EndType::Polygon || et == EndType::Joined; -} - -inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) -{ - return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta); -} - -inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) -{ - return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta); -} - -//------------------------------------------------------------------------------ -// ClipperOffset methods -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_) -{ - Paths64 paths; - paths.push_back(path); - AddPaths(paths, jt_, et_); -} - -void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_) -{ - if (paths.size() == 0) return; - groups_.push_back(Group(paths, jt_, et_)); -} - -void ClipperOffset::AddPath(const Clipper2Lib::PathD& path, JoinType jt_, EndType et_) -{ - PathsD paths; - paths.push_back(path); - AddPaths(paths, jt_, et_); -} - -void ClipperOffset::AddPaths(const PathsD& paths, JoinType jt_, EndType et_) -{ - if (paths.size() == 0) return; - groups_.push_back(Group(PathsDToPaths64(paths), jt_, et_)); -} - -void ClipperOffset::BuildNormals(const Path64& path) -{ - norms.clear(); - norms.reserve(path.size()); - if (path.size() == 0) return; - Path64::const_iterator path_iter, path_last_iter = --path.cend(); - for (path_iter = path.cbegin(); path_iter != path_last_iter; ++path_iter) - norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1))); - norms.push_back(GetUnitNormal(*path_last_iter, *(path.cbegin()))); -} - -inline PointD TranslatePoint(const PointD& pt, double dx, double dy) -{ - return PointD(pt.x + dx, pt.y + dy); -} - -inline PointD ReflectPoint(const PointD& pt, const PointD& pivot) -{ - return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y)); -} - -PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b, - const PointD& pt2a, const PointD& pt2b) -{ - if (pt1a.x == pt1b.x) //vertical - { - if (pt2a.x == pt2b.x) return PointD(0, 0); - - double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); - double b2 = pt2a.y - m2 * pt2a.x; - return PointD(pt1a.x, m2 * pt1a.x + b2); - } - else if (pt2a.x == pt2b.x) //vertical - { - double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); - double b1 = pt1a.y - m1 * pt1a.x; - return PointD(pt2a.x, m1 * pt2a.x + b1); - } - else - { - double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); - double b1 = pt1a.y - m1 * pt1a.x; - double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); - double b2 = pt2a.y - m2 * pt2a.x; - if (m1 == m2) return PointD(0, 0); - double x = (b2 - b1) / (m1 - m2); - return PointD(x, m1 * x + b1); - } -} - -void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t k) -{ - PointD vec; - if (j == k) - vec = PointD(norms[0].y, -norms[0].x); - else - vec = GetAvgUnitVector( - PointD(-norms[k].y, norms[k].x), - PointD(norms[j].y, -norms[j].x)); - - // now offset the original vertex delta units along unit vector - PointD ptQ = PointD(path[j]); - ptQ = TranslatePoint(ptQ, abs_group_delta_ * vec.x, abs_group_delta_ * vec.y); - // get perpendicular vertices - PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x); - PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x); - // get 2 vertices along one edge offset - PointD pt3 = GetPerpendicD(path[k], norms[k], group_delta_); - if (j == k) - { - PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_); - PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); - //get the second intersect point through reflecion - group.path_.push_back(Point64(ReflectPoint(pt, ptQ))); - group.path_.push_back(Point64(pt)); - } - else - { - PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_); - PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); - group.path_.push_back(Point64(pt)); - //get the second intersect point through reflecion - group.path_.push_back(Point64(ReflectPoint(pt, ptQ))); - } -} - -void ClipperOffset::DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a) -{ - double q = group_delta_ / (cos_a + 1); - group.path_.push_back(Point64( - path[j].x + (norms[k].x + norms[j].x) * q, - path[j].y + (norms[k].y + norms[j].y) * q)); -} - -void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle) -{ - //even though angle may be negative this is a convex join - Point64 pt = path[j]; - int steps = static_cast(std::ceil(steps_per_rad_ * std::abs(angle))); - double step_sin = std::sin(angle / steps); - double step_cos = std::cos(angle / steps); - - PointD pt2 = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_); - if (j == k) pt2.Negate(); - - group.path_.push_back(Point64(pt.x + pt2.x, pt.y + pt2.y)); - for (int i = 0; i < steps; i++) - { - pt2 = PointD(pt2.x * step_cos - step_sin * pt2.y, - pt2.x * step_sin + pt2.y * step_cos); - group.path_.push_back(Point64(pt.x + pt2.x, pt.y + pt2.y)); - } - group.path_.push_back(GetPerpendic(path[j], norms[j], group_delta_)); -} - -void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k) -{ - // Let A = change in angle where edges join - // A == 0: ie no change in angle (flat join) - // A == PI: edges 'spike' - // sin(A) < 0: right turning - // cos(A) < 0: change in angle is more than 90 degree - - if (path[j] == path[k]) { k = j; return; } - - double sin_a = CrossProduct(norms[j], norms[k]); - double cos_a = DotProduct(norms[j], norms[k]); - if (sin_a > 1.0) sin_a = 1.0; - else if (sin_a < -1.0) sin_a = -1.0; - - bool almostNoAngle = AlmostZero(sin_a) && cos_a > 0; - // when there's almost no angle of deviation or it's concave - if (almostNoAngle || (sin_a * group_delta_ < 0)) - { - Point64 p1 = Point64( - path[j].x + norms[k].x * group_delta_, - path[j].y + norms[k].y * group_delta_); - Point64 p2 = Point64( - path[j].x + norms[j].x * group_delta_, - path[j].y + norms[j].y * group_delta_); - group.path_.push_back(p1); - if (p1 != p2) - { - // when concave add an extra vertex to ensure neat clipping - if (!almostNoAngle) group.path_.push_back(path[j]); - group.path_.push_back(p2); - } - } - else // it's convex - { - if (join_type_ == JoinType::Round) - DoRound(group, path, j, k, std::atan2(sin_a, cos_a)); - else if (join_type_ == JoinType::Miter) - { - // miter unless the angle is so acute the miter would exceeds ML - if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a); - else DoSquare(group, path, j, k); - } - // don't bother squaring angles that deviate < ~20 degrees because - // squaring will be indistinguishable from mitering and just be a lot slower - else if (cos_a > 0.9) - DoMiter(group, path, j, k, cos_a); - else - DoSquare(group, path, j, k); - } - k = j; -} - -void ClipperOffset::OffsetPolygon(Group& group, Path64& path) -{ - group.path_.clear(); - for (Path64::size_type i = 0, j = path.size() -1; i < path.size(); j = i, ++i) - OffsetPoint(group, path, i, j); - group.paths_out_.push_back(group.path_); -} - -void ClipperOffset::OffsetOpenJoined(Group& group, Path64& path) -{ - OffsetPolygon(group, path); - std::reverse(path.begin(), path.end()); - BuildNormals(path); - OffsetPolygon(group, path); -} - -void ClipperOffset::OffsetOpenPath(Group& group, Path64& path, EndType end_type) -{ - group.path_.clear(); - - // do the line start cap - switch (end_type) - { - case EndType::Butt: - group.path_.push_back(Point64( - path[0].x - norms[0].x * group_delta_, - path[0].y - norms[0].y * group_delta_)); - group.path_.push_back(GetPerpendic(path[0], norms[0], group_delta_)); - break; - case EndType::Round: - DoRound(group, path, 0,0, PI); - break; - default: - DoSquare(group, path, 0, 0); - break; - } - - size_t highI = path.size() - 1; - - // offset the left side going forward - for (Path64::size_type i = 1, k = 0; i < highI; ++i) - OffsetPoint(group, path, i, k); - - // reverse normals - for (size_t i = highI; i > 0; --i) - norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y); - norms[0] = norms[highI]; - - // do the line end cap - switch (end_type) - { - case EndType::Butt: - group.path_.push_back(Point64( - path[highI].x - norms[highI].x * group_delta_, - path[highI].y - norms[highI].y * group_delta_)); - group.path_.push_back(GetPerpendic(path[highI], norms[highI], group_delta_)); - break; - case EndType::Round: - DoRound(group, path, highI, highI, PI); - break; - default: - DoSquare(group, path, highI, highI); - break; - } - - for (size_t i = highI, k = 0; i > 0; --i) - OffsetPoint(group, path, i, k); - group.paths_out_.push_back(group.path_); -} - -void ClipperOffset::DoGroupOffset(Group& group, double delta) -{ - if (group.end_type_ != EndType::Polygon) delta = std::abs(delta) * 0.5; - bool isClosedPaths = IsClosedPath(group.end_type_); - - if (isClosedPaths) - { - //the lowermost polygon must be an outer polygon. So we can use that as the - //designated orientation for outer polygons (needed for tidy-up clipping) - Paths64::size_type lowestIdx = GetLowestPolygonIdx(group.paths_in_); - // nb: don't use the default orientation here ... - double area = Area(group.paths_in_[lowestIdx]); - if (area == 0) return; - group.is_reversed_ = (area < 0); - if (group.is_reversed_) delta = -delta; - } - else - group.is_reversed_ = false; - - group_delta_ = delta; - abs_group_delta_ = std::abs(group_delta_); - join_type_ = group.join_type_; - - double arcTol = (arc_tolerance_ > floating_point_tolerance ? arc_tolerance_ - : std::log10(2 + abs_group_delta_) * default_arc_tolerance); // empirically derived - -//calculate a sensible number of steps (for 360 deg for the given offset - if (group.join_type_ == JoinType::Round || group.end_type_ == EndType::Round) - { - steps_per_rad_ = PI / std::acos(1 - arcTol / abs_group_delta_) / (PI *2); - } - - bool is_closed_path = IsClosedPath(group.end_type_); - Paths64::const_iterator path_iter; - for(path_iter = group.paths_in_.cbegin(); path_iter != group.paths_in_.cend(); ++path_iter) - { - Path64 path = StripDuplicates(*path_iter, is_closed_path); - Path64::size_type cnt = path.size(); - if (cnt == 0) continue; - - if (cnt == 1) // single point - only valid with open paths - { - group.path_ = Path64(); - //single vertex so build a circle or square ... - if (group.join_type_ == JoinType::Round) - { - double radius = abs_group_delta_; - group.path_ = Ellipse(path[0], radius, radius); - } - else - { - int d = (int)std::ceil(abs_group_delta_); - Rect64 r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d); - group.path_ = r.AsPath(); - } - group.paths_out_.push_back(group.path_); - } - else - { - BuildNormals(path); - if (group.end_type_ == EndType::Polygon) OffsetPolygon(group, path); - else if (group.end_type_ == EndType::Joined) OffsetOpenJoined(group, path); - else OffsetOpenPath(group, path, group.end_type_); - } - } - - if (!merge_groups_) - { - //clean up self-intersections ... - Clipper64 c; - c.PreserveCollinear = false; - //the solution should retain the orientation of the input - c.ReverseSolution = reverse_solution_ != group.is_reversed_; - c.AddSubject(group.paths_out_); - if (group.is_reversed_) - c.Execute(ClipType::Union, FillRule::Negative, group.paths_out_); - else - c.Execute(ClipType::Union, FillRule::Positive, group.paths_out_); - } - - solution.reserve(solution.size() + group.paths_out_.size()); - copy(group.paths_out_.begin(), group.paths_out_.end(), back_inserter(solution)); - group.paths_out_.clear(); -} - -Paths64 ClipperOffset::Execute(double delta) -{ - solution.clear(); - if (std::abs(delta) < default_arc_tolerance) - { - for (const Group& group : groups_) - { - solution.reserve(solution.size() + group.paths_in_.size()); - copy(group.paths_in_.begin(), group.paths_in_.end(), back_inserter(solution)); - } - return solution; - } - - temp_lim_ = (miter_limit_ <= 1) ? - 2.0 : - 2.0 / (miter_limit_ * miter_limit_); - - std::vector::iterator groups_iter; - for (groups_iter = groups_.begin(); - groups_iter != groups_.end(); ++groups_iter) - { - DoGroupOffset(*groups_iter, delta); - } - - if (merge_groups_ && groups_.size() > 0) - { - //clean up self-intersections ... - Clipper64 c; - c.PreserveCollinear = false; - //the solution should retain the orientation of the input - c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed_; - - c.AddSubject(solution); - if (groups_[0].is_reversed_) - c.Execute(ClipType::Union, FillRule::Negative, solution); - else - c.Execute(ClipType::Union, FillRule::Positive, solution); - } - return solution; -} - -} // namespace diff --git a/src/clipper2/Clipper2Lib/src/clipper.rectclip.cpp b/src/clipper2/Clipper2Lib/src/clipper.rectclip.cpp deleted file mode 100644 index 0f6bb9c519..0000000000 --- a/src/clipper2/Clipper2Lib/src/clipper.rectclip.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 26 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#include -#include "clipper2/clipper.h" -#include "clipper2/clipper.rectclip.h" - -namespace Clipper2Lib { - - //------------------------------------------------------------------------------ - // Miscellaneous methods - //------------------------------------------------------------------------------ - - inline PointInPolygonResult Path1ContainsPath2(Path64 path1, Path64 path2) - { - PointInPolygonResult result = PointInPolygonResult::IsOn; - for(const Point64& pt : path2) - { - result = PointInPolygon(pt, path1); - if (result != PointInPolygonResult::IsOn) break; - } - return result; - } - - inline bool GetLocation(const Rect64& rec, - const Point64& pt, Location& loc) - { - if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom) - { - loc = Location::Left; - return false; - } - else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom) - { - loc = Location::Right; - return false; - } - else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right) - { - loc = Location::Top; - return false; - } - else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right) - { - loc = Location::Bottom; - return false; - } - else if (pt.x < rec.left) loc = Location::Left; - else if (pt.x > rec.right) loc = Location::Right; - else if (pt.y < rec.top) loc = Location::Top; - else if (pt.y > rec.bottom) loc = Location::Bottom; - else loc = Location::Inside; - return true; - } - - Point64 GetIntersectPoint64(const Point64& ln1a, const Point64& ln1b, - const Point64& ln2a, const Point64& ln2b) - { - // see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ - if (ln1b.x == ln1a.x) - { - if (ln2b.x == ln2a.x) return Point64(); // parallel lines - double m2 = static_cast(ln2b.y - ln2a.y) / (ln2b.x - ln2a.x); - double b2 = ln2a.y - m2 * ln2a.x; - return Point64(ln1a.x, static_cast(std::round(m2 * ln1a.x + b2))); - } - else if (ln2b.x == ln2a.x) - { - double m1 = static_cast(ln1b.y - ln1a.y) / (ln1b.x - ln1a.x); - double b1 = ln1a.y - m1 * ln1a.x; - return Point64(ln2a.x, static_cast(std::round(m1 * ln2a.x + b1))); - } - else - { - double m1 = static_cast(ln1b.y - ln1a.y) / (ln1b.x - ln1a.x); - double b1 = ln1a.y - m1 * ln1a.x; - double m2 = static_cast(ln2b.y - ln2a.y) / (ln2b.x - ln2a.x); - double b2 = ln2a.y - m2 * ln2a.x; - if (std::fabs(m1 - m2) > 1.0E-15) - { - double x = (b2 - b1) / (m1 - m2); - return Point64(x, m1 * x + b1); - } - else - return Point64((ln1a.x + ln1b.x) * 0.5, (ln1a.y + ln1b.y) * 0.5); - } - } - - inline bool GetIntersection(const Path64& rectPath, - const Point64& p, const Point64& p2, Location& loc, Point64& ip) - { - // gets the intersection closest to 'p' - // when Result = false, loc will remain unchanged - switch (loc) - { - case Location::Left: - if (SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[3]); - else if (p.y < rectPath[0].y && - SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[1]); - loc = Location::Top; - } - else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[2], rectPath[3]); - loc = Location::Bottom; - } - else return false; - break; - - case Location::Top: - if (SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[1]); - else if (p.x < rectPath[0].x && - SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[3]); - loc = Location::Left; - } - else if (p.x > rectPath[1].x && - SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[1], rectPath[2]); - loc = Location::Right; - } - else return false; - break; - - case Location::Right: - if (SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - ip = GetIntersectPoint64(p, p2, rectPath[1], rectPath[2]); - else if (p.y < rectPath[0].y && - SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[1]); - loc = Location::Top; - } - else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[2], rectPath[3]); - loc = Location::Bottom; - } - else return false; - break; - - case Location::Bottom: - if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - ip = GetIntersectPoint64(p, p2, rectPath[2], rectPath[3]); - else if (p.x < rectPath[3].x && - SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[3]); - loc = Location::Left; - } - else if (p.x > rectPath[2].x && - SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[1], rectPath[2]); - loc = Location::Right; - } - else return false; - break; - - default: // loc == rInside - if (SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[3]); - loc = Location::Left; - } - else if (SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[0], rectPath[1]); - loc = Location::Top; - } - else if (SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[1], rectPath[2]); - loc = Location::Right; - } - else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - { - ip = GetIntersectPoint64(p, p2, rectPath[2], rectPath[3]); - loc = Location::Bottom; - } - else return false; - break; - } - - return true; - } - - inline Location GetAdjacentLocation(Location loc, bool isClockwise) - { - int delta = (isClockwise) ? 1 : 3; - return static_cast((static_cast(loc) + delta) % 4); - } - - inline bool HeadingClockwise(Location prev, Location curr) - { - return (static_cast(prev) + 1) % 4 == static_cast(curr); - } - - inline bool AreOpposites(Location prev, Location curr) - { - return abs(static_cast(prev) - static_cast(curr)) == 2; - } - - inline bool IsClockwise(Location prev, Location curr, - Point64 prev_pt, Point64 curr_pt, Point64 rect_mp) - { - if (AreOpposites(prev, curr)) - return CrossProduct(prev_pt, rect_mp, curr_pt) < 0; - else - return HeadingClockwise(prev, curr); - } - - //---------------------------------------------------------------------------- - // RectClip64 - //---------------------------------------------------------------------------- - - void RectClip::AddCorner(Location prev, Location curr) - { - if (HeadingClockwise(prev, curr)) - result_.push_back(rectPath_[static_cast(prev)]); - else - result_.push_back(rectPath_[static_cast(curr)]); - } - - void RectClip::AddCorner(Location& loc, bool isClockwise) - { - if (isClockwise) - { - result_.push_back(rectPath_[static_cast(loc)]); - loc = GetAdjacentLocation(loc, true); - } - else - { - loc = GetAdjacentLocation(loc, false); - result_.push_back(rectPath_[static_cast(loc)]); - } - } - - void RectClip::GetNextLocation(const Path64& path, - Location& loc, int& i, int highI) - { - switch (loc) - { - case Location::Left: - while (i <= highI && path[i].x <= rect_.left) ++i; - if (i > highI) break; - else if (path[i].x >= rect_.right) loc = Location::Right; - else if (path[i].y <= rect_.top) loc = Location::Top; - else if (path[i].y >= rect_.bottom) loc = Location::Bottom; - else loc = Location::Inside; - break; - - case Location::Top: - while (i <= highI && path[i].y <= rect_.top) ++i; - if (i > highI) break; - else if (path[i].y >= rect_.bottom) loc = Location::Bottom; - else if (path[i].x <= rect_.left) loc = Location::Left; - else if (path[i].x >= rect_.right) loc = Location::Right; - else loc = Location::Inside; - break; - - case Location::Right: - while (i <= highI && path[i].x >= rect_.right) ++i; - if (i > highI) break; - else if (path[i].x <= rect_.left) loc = Location::Left; - else if (path[i].y <= rect_.top) loc = Location::Top; - else if (path[i].y >= rect_.bottom) loc = Location::Bottom; - else loc = Location::Inside; - break; - - case Location::Bottom: - while (i <= highI && path[i].y >= rect_.bottom) ++i; - if (i > highI) break; - else if (path[i].y <= rect_.top) loc = Location::Top; - else if (path[i].x <= rect_.left) loc = Location::Left; - else if (path[i].x >= rect_.right) loc = Location::Right; - else loc = Location::Inside; - break; - - case Location::Inside: - while (i <= highI) - { - if (path[i].x < rect_.left) loc = Location::Left; - else if (path[i].x > rect_.right) loc = Location::Right; - else if (path[i].y > rect_.bottom) loc = Location::Bottom; - else if (path[i].y < rect_.top) loc = Location::Top; - else { result_.push_back(path[i]); ++i; continue; } - break; //inner loop - } - break; - } //switch - } - - Path64 RectClip::Execute(const Path64& path) - { - if (rect_.IsEmpty() || path.size() < 3) return Path64(); - - result_.clear(); - start_locs_.clear(); - int i = 0, highI = static_cast(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) return path; - if (prev == Location::Inside) loc = Location::Inside; - i = 0; - } - Location starting_loc = loc; - - /////////////////////////////////////////////////// - while (i <= highI) - { - prev = loc; - Location crossing_prev = crossing_loc; - - GetNextLocation(path, loc, i, highI); - - if (i > highI) break; - Point64 ip, ip2; - Point64 prev_pt = (i) ? path[static_cast(i - 1)] : path[highI]; - - crossing_loc = loc; - if (!GetIntersection(rectPath_, path[i], prev_pt, crossing_loc, ip)) - { - // ie remaining outside - - if (crossing_prev == Location::Inside) - { - bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], mp_); - do { - start_locs_.push_back(prev); - prev = GetAdjacentLocation(prev, isClockw); - } while (prev != loc); - crossing_loc = crossing_prev; // still not crossed - } - else if (prev != Location::Inside && prev != loc) - { - bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], mp_); - do { - AddCorner(prev, isClockw); - } while (prev != loc); - } - ++i; - continue; - } - - //////////////////////////////////////////////////// - // we must be crossing the rect boundary to get here - //////////////////////////////////////////////////// - - if (loc == Location::Inside) // path must be entering rect - { - if (first_cross_ == Location::Inside) - { - first_cross_ = crossing_loc; - start_locs_.push_back(prev); - } - else if (prev != crossing_loc) - { - bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], mp_); - do { - AddCorner(prev, isClockw); - } while (prev != crossing_loc); - } - } - else if (prev != Location::Inside) - { - // passing right through rect. 'ip' here will be the second - // intersect pt but we'll also need the first intersect pt (ip2) - loc = prev; - GetIntersection(rectPath_, prev_pt, path[i], loc, ip2); - if (crossing_prev != Location::Inside) - AddCorner(crossing_prev, loc); - - if (first_cross_ == Location::Inside) - { - first_cross_ = loc; - start_locs_.push_back(prev); - } - - loc = crossing_loc; - result_.push_back(ip2); - if (ip == ip2) - { - // it's very likely that path[i] is on rect - GetLocation(rect_, path[i], loc); - AddCorner(crossing_loc, loc); - crossing_loc = loc; - continue; - } - } - else // path must be exiting rect - { - loc = crossing_loc; - if (first_cross_ == Location::Inside) - first_cross_ = crossing_loc; - } - - result_.push_back(ip); - - } //while i <= highI - /////////////////////////////////////////////////// - - if (first_cross_ == Location::Inside) - { - if (starting_loc == Location::Inside) return path; - Rect64 tmp_rect = Bounds(path); - if (tmp_rect.Contains(rect_) && - Path1ContainsPath2(path, rectPath_) != - PointInPolygonResult::IsOutside) return rectPath_; - else - return Path64(); - } - - if (loc != Location::Inside && - (loc != first_cross_ || start_locs_.size() > 2)) - { - if (start_locs_.size() > 0) - { - prev = loc; - for (auto loc2 : start_locs_) - { - if (prev == loc2) continue; - AddCorner(prev, HeadingClockwise(prev, loc2)); - prev = loc2; - } - loc = prev; - } - if (loc != first_cross_) - AddCorner(loc, HeadingClockwise(loc, first_cross_)); - } - - if (result_.size() < 3) return Path64(); - - // tidy up duplicates and collinear segments - Path64 res; - res.reserve(result_.size()); - size_t k = 0; highI = static_cast(result_.size()) - 1; - Point64 prev_pt = result_[highI]; - res.push_back(result_[0]); - Path64::const_iterator cit; - for (cit = result_.cbegin() + 1; cit != result_.cend(); ++cit) - { - if (CrossProduct(prev_pt, res[k], *cit)) - { - prev_pt = res[k++]; - res.push_back(*cit); - } - else - res[k] = *cit; - } - - if (k < 2) return Path64(); - // and a final check for collinearity - else if (!CrossProduct(res[0], res[k - 1], res[k])) res.pop_back(); - return res; - } - - Paths64 RectClipLines::Execute(const Path64& path) - { - result_.clear(); - Paths64 result; - if (rect_.IsEmpty() || path.size() == 0) return result; - - int i = 1, highI = static_cast(path.size()) - 1; - - Location prev = Location::Inside, loc; - Location crossing_loc = Location::Inside; - if (!GetLocation(rect_, path[0], loc)) - { - while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i; - if (i > highI) { - result.push_back(path); - return result; - } - if (prev == Location::Inside) loc = Location::Inside; - i = 1; - } - if (loc == Location::Inside) result_.push_back(path[0]); - - /////////////////////////////////////////////////// - while (i <= highI) - { - prev = loc; - GetNextLocation(path, loc, i, highI); - if (i > highI) break; - Point64 ip, ip2; - Point64 prev_pt = path[static_cast(i - 1)]; - - crossing_loc = loc; - if (!GetIntersection(rectPath_, path[i], prev_pt, crossing_loc, ip)) - { - // ie remaining outside - ++i; - continue; - } - - //////////////////////////////////////////////////// - // we must be crossing the rect boundary to get here - //////////////////////////////////////////////////// - - if (loc == Location::Inside) // path must be entering rect - { - result_.push_back(ip); - } - else if (prev != Location::Inside) - { - // passing right through rect. 'ip' here will be the second - // intersect pt but we'll also need the first intersect pt (ip2) - crossing_loc = prev; - GetIntersection(rectPath_, prev_pt, path[i], crossing_loc, ip2); - result_.push_back(ip2); - result_.push_back(ip); - result.push_back(result_); - result_.clear(); - } - else // path must be exiting rect - { - result_.push_back(ip); - result.push_back(result_); - result_.clear(); - } - } //while i <= highI - /////////////////////////////////////////////////// - - if (result_.size() > 1) - result.push_back(result_); - return result; - } - -} // namespace diff --git a/src/libslic3r/Algorithm/LineSplit.hpp b/src/libslic3r/Algorithm/LineSplit.hpp index 102ed62bd9..58b9fdc34a 100644 --- a/src/libslic3r/Algorithm/LineSplit.hpp +++ b/src/libslic3r/Algorithm/LineSplit.hpp @@ -1,7 +1,6 @@ #ifndef SRC_LIBSLIC3R_ALGORITHM_LINE_SPLIT_HPP_ #define SRC_LIBSLIC3R_ALGORITHM_LINE_SPLIT_HPP_ -#include "clipper2/clipper.core.h" #include "ClipperZUtils.hpp" namespace Slic3r { diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index b5b3548371..d875b04486 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -253,7 +253,7 @@ std::unique_ptr cre void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) { if (epsilon < 1) { - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); return; } @@ -294,7 +294,7 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss) } } - ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss)); + ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd); } /*! diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp index 15e9145e8f..f2a258797b 100644 --- a/src/libslic3r/BoundingBox.cpp +++ b/src/libslic3r/BoundingBox.cpp @@ -7,23 +7,19 @@ namespace Slic3r { -template BoundingBoxBase::BoundingBoxBase(const std::vector &points); +template BoundingBoxBase::BoundingBoxBase(const Points &points); template BoundingBoxBase::BoundingBoxBase(const std::vector &points); template BoundingBox3Base::BoundingBox3Base(const std::vector &points); void BoundingBox::polygon(Polygon* polygon) const { - polygon->points.clear(); - polygon->points.resize(4); - polygon->points[0](0) = this->min(0); - polygon->points[0](1) = this->min(1); - polygon->points[1](0) = this->max(0); - polygon->points[1](1) = this->min(1); - polygon->points[2](0) = this->max(0); - polygon->points[2](1) = this->max(1); - polygon->points[3](0) = this->min(0); - polygon->points[3](1) = this->max(1); + polygon->points = { + this->min, + { this->max.x(), this->min.y() }, + this->max, + { this->min.x(), this->max.y() } + }; } Polygon BoundingBox::polygon() const @@ -38,8 +34,8 @@ BoundingBox BoundingBox::rotated(double angle) const BoundingBox out; out.merge(this->min.rotated(angle)); out.merge(this->max.rotated(angle)); - out.merge(Point(this->min(0), this->max(1)).rotated(angle)); - out.merge(Point(this->max(0), this->min(1)).rotated(angle)); + out.merge(Point(this->min.x(), this->max.y()).rotated(angle)); + out.merge(Point(this->max.x(), this->min.y()).rotated(angle)); return out; } @@ -48,23 +44,23 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const BoundingBox out; out.merge(this->min.rotated(angle, center)); out.merge(this->max.rotated(angle, center)); - out.merge(Point(this->min(0), this->max(1)).rotated(angle, center)); - out.merge(Point(this->max(0), this->min(1)).rotated(angle, center)); + out.merge(Point(this->min.x(), this->max.y()).rotated(angle, center)); + out.merge(Point(this->max.x(), this->min.y()).rotated(angle, center)); return out; } -template void -BoundingBoxBase::scale(double factor) +template void +BoundingBoxBase::scale(double factor) { this->min *= factor; this->max *= factor; } -template void BoundingBoxBase::scale(double factor); +template void BoundingBoxBase::scale(double factor); template void BoundingBoxBase::scale(double factor); template void BoundingBoxBase::scale(double factor); -template void -BoundingBoxBase::merge(const PointClass &point) +template void +BoundingBoxBase::merge(const PointType &point) { if (this->defined) { this->min = this->min.cwiseMin(point); @@ -75,22 +71,22 @@ BoundingBoxBase::merge(const PointClass &point) this->defined = true; } } -template void BoundingBoxBase::merge(const Point &point); +template void BoundingBoxBase::merge(const Point &point); template void BoundingBoxBase::merge(const Vec2f &point); template void BoundingBoxBase::merge(const Vec2d &point); -template void -BoundingBoxBase::merge(const std::vector &points) +template void +BoundingBoxBase::merge(const PointsType &points) { this->merge(BoundingBoxBase(points)); } -template void BoundingBoxBase::merge(const Points &points); +template void BoundingBoxBase::merge(const Points &points); template void BoundingBoxBase::merge(const Pointfs &points); -template void -BoundingBoxBase::merge(const BoundingBoxBase &bb) +template void +BoundingBoxBase::merge(const BoundingBoxBase &bb) { - assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1)); + assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y()); if (bb.defined) { if (this->defined) { this->min = this->min.cwiseMin(bb.min); @@ -102,13 +98,13 @@ BoundingBoxBase::merge(const BoundingBoxBase &bb) } } } -template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); template void BoundingBoxBase::merge(const BoundingBoxBase &bb); //BBS -template -Polygon BoundingBox3Base::polygon(bool is_scaled) const +template +Polygon BoundingBox3Base::polygon(bool is_scaled) const { Polygon polygon; polygon.points.clear(); @@ -127,8 +123,8 @@ Polygon BoundingBox3Base::polygon(bool is_scaled) const template Polygon BoundingBox3Base::polygon(bool is_scaled) const; template Polygon BoundingBox3Base::polygon(bool is_scaled) const; -template void -BoundingBox3Base::merge(const PointClass &point) +template void +BoundingBox3Base::merge(const PointType &point) { if (this->defined) { this->min = this->min.cwiseMin(point); @@ -142,17 +138,17 @@ BoundingBox3Base::merge(const PointClass &point) template void BoundingBox3Base::merge(const Vec3f &point); template void BoundingBox3Base::merge(const Vec3d &point); -template void -BoundingBox3Base::merge(const std::vector &points) +template void +BoundingBox3Base::merge(const PointsType &points) { this->merge(BoundingBox3Base(points)); } template void BoundingBox3Base::merge(const Pointf3s &points); -template void -BoundingBox3Base::merge(const BoundingBox3Base &bb) +template void +BoundingBox3Base::merge(const BoundingBox3Base &bb) { - assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2)); + assert(bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z()); if (bb.defined) { if (this->defined) { this->min = this->min.cwiseMin(bb.min); @@ -166,83 +162,78 @@ BoundingBox3Base::merge(const BoundingBox3Base &bb) } template void BoundingBox3Base::merge(const BoundingBox3Base &bb); -template PointClass -BoundingBoxBase::size() const +template PointType +BoundingBoxBase::size() const { - return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1)); + return this->max - this->min; } -template Point BoundingBoxBase::size() const; +template Point BoundingBoxBase::size() const; template Vec2f BoundingBoxBase::size() const; template Vec2d BoundingBoxBase::size() const; -template PointClass -BoundingBox3Base::size() const +template PointType +BoundingBox3Base::size() const { - return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2)); + return this->max - this->min; } template Vec3f BoundingBox3Base::size() const; template Vec3d BoundingBox3Base::size() const; -template double BoundingBoxBase::radius() const +template double BoundingBoxBase::radius() const { assert(this->defined); - double x = this->max(0) - this->min(0); - double y = this->max(1) - this->min(1); - return 0.5 * sqrt(x*x+y*y); + return 0.5 * (this->max - this->min).template cast().norm(); } -template double BoundingBoxBase::radius() const; +template double BoundingBoxBase::radius() const; template double BoundingBoxBase::radius() const; -template double BoundingBox3Base::radius() const +template double BoundingBox3Base::radius() const { - double x = this->max(0) - this->min(0); - double y = this->max(1) - this->min(1); - double z = this->max(2) - this->min(2); - return 0.5 * sqrt(x*x+y*y+z*z); + return 0.5 * (this->max - this->min).template cast().norm(); } template double BoundingBox3Base::radius() const; -template void -BoundingBoxBase::offset(coordf_t delta) +template void +BoundingBoxBase::offset(coordf_t delta) { - PointClass v(delta, delta); + PointType v(delta, delta); this->min -= v; this->max += v; } -template void BoundingBoxBase::offset(coordf_t delta); +template void BoundingBoxBase::offset(coordf_t delta); template void BoundingBoxBase::offset(coordf_t delta); -template void -BoundingBox3Base::offset(coordf_t delta) +template void +BoundingBox3Base::offset(coordf_t delta) { - PointClass v(delta, delta, delta); + PointType v(delta, delta, delta); this->min -= v; this->max += v; } template void BoundingBox3Base::offset(coordf_t delta); -template PointClass -BoundingBoxBase::center() const +template PointType +BoundingBoxBase::center() const { return (this->min + this->max) / 2; } -template Point BoundingBoxBase::center() const; +template Point BoundingBoxBase::center() const; template Vec2f BoundingBoxBase::center() const; template Vec2d BoundingBoxBase::center() const; -template PointClass -BoundingBox3Base::center() const +template PointType +BoundingBox3Base::center() const { return (this->min + this->max) / 2; } template Vec3f BoundingBox3Base::center() const; template Vec3d BoundingBox3Base::center() const; -template coordf_t -BoundingBox3Base::max_size() const +template coordf_t +BoundingBox3Base::max_size() const { - PointClass s = size(); - return std::max(s(0), std::max(s(1), s(2))); + PointType s = size(); + return std::max(s.x(), std::max(s.y(), s.z())); } template coordf_t BoundingBox3Base::max_size() const; template coordf_t BoundingBox3Base::max_size() const; @@ -250,8 +241,8 @@ template coordf_t BoundingBox3Base::max_size() const; void BoundingBox::align_to_grid(const coord_t cell_size) { if (this->defined) { - min(0) = Slic3r::align_to_grid(min(0), cell_size); - min(1) = Slic3r::align_to_grid(min(1), cell_size); + min.x() = Slic3r::align_to_grid(min.x(), cell_size); + min.y() = Slic3r::align_to_grid(min.y(), cell_size); } } @@ -260,14 +251,14 @@ BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const typedef Eigen::Matrix Vertices; Vertices src_vertices; - src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2); - src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2); - src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2); - src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2); - src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2); - src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2); - src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2); - src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2); + src_vertices(0, 0) = min.x(); src_vertices(1, 0) = min.y(); src_vertices(2, 0) = min.z(); + src_vertices(0, 1) = max.x(); src_vertices(1, 1) = min.y(); src_vertices(2, 1) = min.z(); + src_vertices(0, 2) = max.x(); src_vertices(1, 2) = max.y(); src_vertices(2, 2) = min.z(); + src_vertices(0, 3) = min.x(); src_vertices(1, 3) = max.y(); src_vertices(2, 3) = min.z(); + src_vertices(0, 4) = min.x(); src_vertices(1, 4) = min.y(); src_vertices(2, 4) = max.z(); + src_vertices(0, 5) = max.x(); src_vertices(1, 5) = min.y(); src_vertices(2, 5) = max.z(); + src_vertices(0, 6) = max.x(); src_vertices(1, 6) = max.y(); src_vertices(2, 6) = max.z(); + src_vertices(0, 7) = min.x(); src_vertices(1, 7) = max.y(); src_vertices(2, 7) = max.z(); Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous(); diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 2218a7b116..9c305bd9ad 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -9,74 +9,75 @@ namespace Slic3r { -template +template > class BoundingBoxBase { public: - PointClass min; - PointClass max; + using PointsType = APointsType; + PointType min; + PointType max; bool defined; - BoundingBoxBase() : min(PointClass::Zero()), max(PointClass::Zero()), defined(false) {} - BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : - min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {} - BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) : + BoundingBoxBase() : min(PointType::Zero()), max(PointType::Zero()), defined(false) {} + BoundingBoxBase(const PointType &pmin, const PointType &pmax) : + min(pmin), max(pmax), defined(pmin.x() < pmax.x() && pmin.y() < pmax.y()) {} + BoundingBoxBase(const PointType &p1, const PointType &p2, const PointType &p3) : min(p1), max(p1), defined(false) { merge(p2); merge(p3); } template> BoundingBoxBase(It from, It to) { construct(*this, from, to); } - BoundingBoxBase(const std::vector &points) + BoundingBoxBase(const PointsType &points) : BoundingBoxBase(points.begin(), points.end()) {} - void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); } - void merge(const PointClass &point); - void merge(const std::vector &points); - void merge(const BoundingBoxBase &bb); + void reset() { this->defined = false; this->min = PointType::Zero(); this->max = PointType::Zero(); } + void merge(const PointType &point); + void merge(const PointsType &points); + void merge(const BoundingBoxBase &bb); void scale(double factor); - PointClass size() const; + PointType size() const; double radius() const; double area() const { return double(this->max(0) - this->min(0)) * (this->max(1) - this->min(1)); } // BBS - void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } - void translate(const Vec2d& v0) { PointClass v(v0.x(), v0.y()); this->min += v; this->max += v; } + void translate(coordf_t x, coordf_t y) { assert(this->defined); PointType v(x, y); this->min += v; this->max += v; } + void translate(const PointType &v) { this->min += v; this->max += v; } void offset(coordf_t delta); - BoundingBoxBase inflated(coordf_t delta) const throw() { BoundingBoxBase out(*this); out.offset(delta); return out; } - PointClass center() const; - bool contains(const PointClass &point) const { - return point(0) >= this->min(0) && point(0) <= this->max(0) - && point(1) >= this->min(1) && point(1) <= this->max(1); + BoundingBoxBase inflated(coordf_t delta) const throw() { BoundingBoxBase out(*this); out.offset(delta); return out; } + PointType center() const; + bool contains(const PointType &point) const { + return point.x() >= this->min.x() && point.x() <= this->max.x() + && point.y() >= this->min.y() && point.y() <= this->max.y(); } - bool contains(const BoundingBoxBase &other) const { + bool contains(const BoundingBoxBase &other) const { return contains(other.min) && contains(other.max); } - bool overlap(const BoundingBoxBase &other) const { - return ! (this->max(0) < other.min(0) || this->min(0) > other.max(0) || - this->max(1) < other.min(1) || this->min(1) > other.max(1)); + bool overlap(const BoundingBoxBase &other) const { + return ! (this->max.x() < other.min.x() || this->min.x() > other.max.x() || + this->max.y() < other.min.y() || this->min.y() > other.max.y()); } - PointClass operator[](size_t idx) const { + PointType operator[](size_t idx) const { switch (idx) { case 0: return min; break; case 1: - return PointClass(max(0), min(1)); + return PointType(max(0), min(1)); break; case 2: return max; break; case 3: - return PointClass(min(0), max(1)); + return PointType(min(0), max(1)); break; default: - return PointClass(); + return PointType(); break; } - return PointClass(); + return PointType(); } - bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } - bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } + bool operator==(const BoundingBoxBase &rhs) { return this->min == rhs.min && this->max == rhs.max; } + bool operator!=(const BoundingBoxBase &rhs) { return ! (*this == rhs); } friend std::ostream &operator<<(std::ostream &os, const BoundingBoxBase &bbox) { os << "[" << bbox.max(0) - bbox.min(0) << " x " << bbox.max(1) - bbox.min(1) << "] from (" << bbox.min(0) << ", " << bbox.min(1) << ")"; @@ -96,10 +97,10 @@ private: { if (from != to) { auto it = from; - out.min = it->template cast(); + out.min = it->template cast(); out.max = out.min; for (++ it; it != to; ++ it) { - auto vec = it->template cast(); + auto vec = it->template cast(); out.min = out.min.cwiseMin(vec); out.max = out.max.cwiseMax(vec); } @@ -108,16 +109,18 @@ private: } }; -template -class BoundingBox3Base : public BoundingBoxBase +template +class BoundingBox3Base : public BoundingBoxBase> { public: - BoundingBox3Base() : BoundingBoxBase() {} - BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : - BoundingBoxBase(pmin, pmax) - { if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; } - BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) : - BoundingBoxBase(p1, p1) { merge(p2); merge(p3); } + using PointsType = std::vector; + + BoundingBox3Base() : BoundingBoxBase() {} + BoundingBox3Base(const PointType &pmin, const PointType &pmax) : + BoundingBoxBase(pmin, pmax) + { if (pmin.z() >= pmax.z()) BoundingBoxBase::defined = false; } + BoundingBox3Base(const PointType &p1, const PointType &p2, const PointType &p3) : + BoundingBoxBase(p1, p1) { merge(p2); merge(p3); } template > BoundingBox3Base(It from, It to) { @@ -125,66 +128,68 @@ public: throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor"); auto it = from; - this->min = it->template cast(); + this->min = it->template cast(); this->max = this->min; for (++ it; it != to; ++ it) { - auto vec = it->template cast(); + auto vec = it->template cast(); this->min = this->min.cwiseMin(vec); this->max = this->max.cwiseMax(vec); } - this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2)); + this->defined = (this->min.x() < this->max.x()) && (this->min.y() < this->max.y()) && (this->min.z() < this->max.z()); } - BoundingBox3Base(const std::vector &points) + BoundingBox3Base(const PointsType &points) : BoundingBox3Base(points.begin(), points.end()) {} Polygon polygon(bool is_scaled = false) const;//BBS: 2D footprint polygon - void merge(const PointClass &point); - void merge(const std::vector &points); - void merge(const BoundingBox3Base &bb); - PointClass size() const; + void merge(const PointType &point); + void merge(const PointsType &points); + void merge(const BoundingBox3Base &bb); + PointType size() const; double radius() const; - void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; } + void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointType v(x, y, z); this->min += v; this->max += v; } void translate(const Vec3d &v) { this->min += v; this->max += v; } void offset(coordf_t delta); - BoundingBox3Base inflated(coordf_t delta) const throw() { BoundingBox3Base out(*this); out.offset(delta); return out; } - PointClass center() const; + BoundingBox3Base inflated(coordf_t delta) const throw() { BoundingBox3Base out(*this); out.offset(delta); return out; } + PointType center() const; coordf_t max_size() const; - bool contains(const PointClass &point) const { - return BoundingBoxBase::contains(point) && point(2) >= this->min(2) && point(2) <= this->max(2); + bool contains(const PointType &point) const { + return BoundingBoxBase::contains(point) && point.z() >= this->min.z() && point.z() <= this->max.z(); } - bool contains(const BoundingBox3Base& other) const { + bool contains(const BoundingBox3Base& other) const { return contains(other.min) && contains(other.max); } - bool intersects(const BoundingBox3Base& other) const { - return (this->min(0) < other.max(0)) && (this->max(0) > other.min(0)) && (this->min(1) < other.max(1)) && (this->max(1) > other.min(1)) && (this->min(2) < other.max(2)) && (this->max(2) > other.min(2)); + // Intersects without boundaries. + bool intersects(const BoundingBox3Base& other) const { + return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() && + this->min.z() < other.max.z() && this->max.z() > other.min.z(); } }; // Will prevent warnings caused by non existing definition of template in hpp -extern template void BoundingBoxBase::scale(double factor); +extern template void BoundingBoxBase::scale(double factor); extern template void BoundingBoxBase::scale(double factor); extern template void BoundingBoxBase::scale(double factor); -extern template void BoundingBoxBase::offset(coordf_t delta); +extern template void BoundingBoxBase::offset(coordf_t delta); extern template void BoundingBoxBase::offset(coordf_t delta); -extern template void BoundingBoxBase::merge(const Point &point); +extern template void BoundingBoxBase::merge(const Point &point); extern template void BoundingBoxBase::merge(const Vec2f &point); extern template void BoundingBoxBase::merge(const Vec2d &point); -extern template void BoundingBoxBase::merge(const Points &points); +extern template void BoundingBoxBase::merge(const Points &points); extern template void BoundingBoxBase::merge(const Pointfs &points); -extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb); -extern template Point BoundingBoxBase::size() const; +extern template Point BoundingBoxBase::size() const; extern template Vec2f BoundingBoxBase::size() const; extern template Vec2d BoundingBoxBase::size() const; -extern template double BoundingBoxBase::radius() const; +extern template double BoundingBoxBase::radius() const; extern template double BoundingBoxBase::radius() const; -extern template Point BoundingBoxBase::center() const; +extern template Point BoundingBoxBase::center() const; extern template Vec2f BoundingBoxBase::center() const; extern template Vec2d BoundingBoxBase::center() const; extern template void BoundingBox3Base::merge(const Vec3f &point); @@ -200,7 +205,7 @@ extern template Vec3d BoundingBox3Base::center() const; extern template coordf_t BoundingBox3Base::max_size() const; extern template coordf_t BoundingBox3Base::max_size() const; -class BoundingBox : public BoundingBoxBase +class BoundingBox : public BoundingBoxBase { public: void polygon(Polygon* polygon) const; @@ -213,9 +218,9 @@ public: // to encompass the original bounding box. void align_to_grid(const coord_t cell_size); - BoundingBox() : BoundingBoxBase() {} - BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {} - BoundingBox(const Points &points) : BoundingBoxBase(points) {} + BoundingBox() : BoundingBoxBase() {} + BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {} + BoundingBox(const Points &points) : BoundingBoxBase(points) {} BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; } @@ -248,22 +253,31 @@ public: BoundingBoxf3 transformed(const Transform3d& matrix) const; }; -template -inline bool empty(const BoundingBoxBase &bb) +template +inline bool empty(const BoundingBoxBase &bb) { - return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1); + return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y(); } -template -inline bool empty(const BoundingBox3Base &bb) +template +inline bool empty(const BoundingBox3Base &bb) { - return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2); + return ! bb.defined || bb.min.x() >= bb.max.x() || bb.min.y() >= bb.max.y() || bb.min.z() >= bb.max.z(); } inline BoundingBox scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; } -inline BoundingBox3 scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), scaled(bb.max)}; } -inline BoundingBoxf unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } -inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } + +template +BoundingBoxBase> scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; } + +template +BoundingBox3Base> scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), scaled(bb.max)}; } + +template +BoundingBoxBase> unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } + +template +BoundingBox3Base> unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } template auto cast(const BoundingBoxBase &b) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e1533b7f05..b267459bc6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -21,6 +21,8 @@ if (TARGET OpenVDB::openvdb) set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp) endif() +option(BUILD_SHARED_LIBS "Build shared libs" OFF) + set(lisbslic3r_sources ArcFitter.cpp ArcFitter.hpp @@ -52,8 +54,6 @@ set(lisbslic3r_sources clipper.hpp ClipperUtils.cpp ClipperUtils.hpp - Clipper2Utils.cpp - Clipper2Utils.hpp ClipperZUtils.hpp Color.cpp Color.hpp @@ -573,7 +573,6 @@ target_link_libraries(libslic3r PNG::PNG ZLIB::ZLIB ${OCCT_LIBS} - Clipper2 mcut JPEG::JPEG qoi diff --git a/src/libslic3r/Clipper2Utils.cpp b/src/libslic3r/Clipper2Utils.cpp deleted file mode 100644 index 5793900a5b..0000000000 --- a/src/libslic3r/Clipper2Utils.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "Clipper2Utils.hpp" - -namespace Slic3r { - -//BBS: FIXME -Slic3r::Polylines Paths64_to_polylines(const Clipper2Lib::Paths64& in) -{ - Slic3r::Polylines out; - out.reserve(in.size()); - for (const Clipper2Lib::Path64& path64 : in) { - Slic3r::Points points; - points.reserve(path64.size()); - for (const Clipper2Lib::Point64& point64 : path64) - points.emplace_back(std::move(Slic3r::Point(point64.x, point64.y))); - out.emplace_back(std::move(Slic3r::Polyline(points))); - } - return out; -} - -//BBS: FIXME -template -Clipper2Lib::Paths64 Slic3rPoints_to_Paths64(const std::vector& in) -{ - Clipper2Lib::Paths64 out; - out.reserve(in.size()); - for (const T& item: in) { - Clipper2Lib::Path64 path; - path.reserve(item.size()); - for (const Slic3r::Point& point : item.points) - path.emplace_back(std::move(Clipper2Lib::Point64(point.x(), point.y()))); - out.emplace_back(std::move(path)); - } - return out; -} - -Polylines _clipper2_pl_open(Clipper2Lib::ClipType clipType, const Slic3r::Polylines& subject, const Slic3r::Polygons& clip) -{ - Clipper2Lib::Clipper64 c; - c.AddOpenSubject(Slic3rPoints_to_Paths64(subject)); - c.AddClip(Slic3rPoints_to_Paths64(clip)); - - Clipper2Lib::ClipType ct = clipType; - Clipper2Lib::FillRule fr = Clipper2Lib::FillRule::NonZero; - Clipper2Lib::Paths64 solution, solution_open; - c.Execute(ct, fr, solution, solution_open); - - Slic3r::Polylines out; - out.reserve(solution.size() + solution_open.size()); - polylines_append(out, std::move(Paths64_to_polylines(solution))); - polylines_append(out, std::move(Paths64_to_polylines(solution_open))); - - return out; -} - -Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip) - { return _clipper2_pl_open(Clipper2Lib::ClipType::Intersection, subject, clip); } -Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip) - { return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); } - -} \ No newline at end of file diff --git a/src/libslic3r/Clipper2Utils.hpp b/src/libslic3r/Clipper2Utils.hpp deleted file mode 100644 index f69421711e..0000000000 --- a/src/libslic3r/Clipper2Utils.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef slic3r_Clipper2Utils_hpp_ -#define slic3r_Clipper2Utils_hpp_ - -#include "libslic3r.h" -#include "clipper2/clipper.h" -#include "Polygon.hpp" -#include "Polyline.hpp" - -namespace Slic3r { - -Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip); -Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip); - -} - -#endif - diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 7a871cea90..2440983f17 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -61,8 +61,10 @@ Points SinglePathProvider::s_end; // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one // with a set of polygons covering the whole layer below. -template inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox, std::vector &out, const bool get_entire_polygons=false) +template inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox, PointsType &out, const bool get_entire_polygons=false) { + using PointType = typename PointsType::value_type; + out.clear(); const size_t cnt = src.size(); if (cnt < 3) return; @@ -112,9 +114,9 @@ template inline void clip_clipper_polygon_with_subject_bbox_ void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out, get_entire_polygons); } void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } -template [[nodiscard]] std::vector clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox) +template [[nodiscard]] PointsType clip_clipper_polygon_with_subject_bbox_templ(const PointsType &src, const BoundingBox &bbox) { - std::vector out; + PointsType out; clip_clipper_polygon_with_subject_bbox(src, bbox, out); return out; } @@ -1001,31 +1003,26 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) return retval; } -Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) +Polygons simplify_polygons(const Polygons &subject) { ClipperLib::Paths output; - if (preserve_collinear) { - ClipperLib::Clipper c; - c.PreserveCollinear(true); - c.StrictlySimple(true); - c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); - c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - } else { - output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero); - } - + ClipperLib::Clipper c; +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? + c.StrictlySimple(true); + c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); + c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + // convert into Slic3r polygons return to_polygons(std::move(output)); } -ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) +ExPolygons simplify_polygons_ex(const Polygons &subject) { - if (! preserve_collinear) - return union_ex(simplify_polygons(subject, false)); - - ClipperLib::PolyTree polytree; + ClipperLib::PolyTree polytree; ClipperLib::Clipper c; - c.PreserveCollinear(true); +// c.PreserveCollinear(true); + //FIXME StrictlySimple is very expensive! Is it needed? c.StrictlySimple(true); c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index ac56c866c4..cfe1f40935 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -124,21 +124,21 @@ namespace ClipperUtils { const std::vector &m_paths; }; - template + template class MultiPointsProvider { public: - MultiPointsProvider(const std::vector &multipoints) : m_multipoints(multipoints) {} + MultiPointsProvider(const MultiPointsType &multipoints) : m_multipoints(multipoints) {} struct iterator : public PathsProviderIteratorBase { public: - explicit iterator(typename std::vector::const_iterator it) : m_it(it) {} + explicit iterator(typename MultiPointsType::const_iterator it) : m_it(it) {} const Points& operator*() const { return m_it->points; } bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; } bool operator!=(const iterator &rhs) const { return !(*this == rhs); } const Points& operator++(int) { return (m_it ++)->points; } iterator& operator++() { ++ m_it; return *this; } private: - typename std::vector::const_iterator m_it; + typename MultiPointsType::const_iterator m_it; }; iterator cbegin() const { return iterator(m_multipoints.begin()); } @@ -148,11 +148,11 @@ namespace ClipperUtils { size_t size() const { return m_multipoints.size(); } private: - const std::vector &m_multipoints; + const MultiPointsType &m_multipoints; }; - using PolygonsProvider = MultiPointsProvider; - using PolylinesProvider = MultiPointsProvider; + using PolygonsProvider = MultiPointsProvider; + using PolylinesProvider = MultiPointsProvider; struct ExPolygonProvider { ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {} @@ -650,8 +650,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) /* OTHER */ -Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); -Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); +Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject); +Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject); Polygons top_level_islands(const Slic3r::Polygons &polygons); diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index 4be2bdd07c..744a23e186 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -17,7 +17,7 @@ public: Contour() = default; Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {} Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {} - Contour(const std::vector &pts, bool open) : Contour(pts.data(), pts.size(), open) {} + Contour(const Points &pts, bool open) : Contour(pts.data(), pts.size(), open) {} const Slic3r::Point *begin() const { return m_begin; } const Slic3r::Point *end() const { return m_end; } diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 8e1dd12ed5..f2dbf7389a 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -440,7 +440,7 @@ bool has_duplicate_points(const ExPolygon &expoly) size_t cnt = expoly.contour.points.size(); for (const Polygon &hole : expoly.holes) cnt += hole.points.size(); - std::vector allpts; + Points allpts; allpts.reserve(cnt); allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); for (const Polygon &hole : expoly.holes) @@ -461,14 +461,8 @@ bool has_duplicate_points(const ExPolygons &expolys) { #if 1 // Check globally. - size_t cnt = 0; - for (const ExPolygon &expoly : expolys) { - cnt += expoly.contour.points.size(); - for (const Polygon &hole : expoly.holes) - cnt += hole.points.size(); - } - std::vector allpts; - allpts.reserve(cnt); + Points allpts; + allpts.reserve(count_points(expolys)); for (const ExPolygon &expoly : expolys) { allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); for (const Polygon &hole : expoly.holes) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index ec4afe9e9d..a3d8b45323 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -69,6 +69,9 @@ struct SurfaceFillParams float lattice_angle_1 = 0.f; float lattice_angle_2 = 0.f; + // Params for 2D honeycomb + float infill_overhang_angle = 60.f; + bool operator<(const SurfaceFillParams &rhs) const { #define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false; #define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false; @@ -97,31 +100,33 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(solid_infill_speed); RETURN_COMPARE_NON_EQUAL(lattice_angle_1); RETURN_COMPARE_NON_EQUAL(lattice_angle_2); + RETURN_COMPARE_NON_EQUAL(infill_overhang_angle); return false; } - bool operator==(const SurfaceFillParams &rhs) const { - return this->extruder == rhs.extruder && - this->pattern == rhs.pattern && - this->spacing == rhs.spacing && - this->overlap == rhs.overlap && - this->angle == rhs.angle && - this->rotate_angle == rhs.rotate_angle && - this->bridge == rhs.bridge && - this->bridge_angle == rhs.bridge_angle && - this->density == rhs.density && -// this->dont_adjust == rhs.dont_adjust && - this->anchor_length == rhs.anchor_length && - this->anchor_length_max == rhs.anchor_length_max && - this->flow == rhs.flow && - this->extrusion_role == rhs.extrusion_role && - this->sparse_infill_speed == rhs.sparse_infill_speed && - this->top_surface_speed == rhs.top_surface_speed && - this->solid_infill_speed == rhs.solid_infill_speed && - this->lattice_angle_1 == rhs.lattice_angle_1 && - this->lattice_angle_2 == rhs.lattice_angle_2; - } + bool operator==(const SurfaceFillParams &rhs) const { + return this->extruder == rhs.extruder && + this->pattern == rhs.pattern && + this->spacing == rhs.spacing && + this->overlap == rhs.overlap && + this->angle == rhs.angle && + this->rotate_angle == rhs.rotate_angle && + this->bridge == rhs.bridge && + this->bridge_angle == rhs.bridge_angle && + this->density == rhs.density && +// this->dont_adjust == rhs.dont_adjust && + this->anchor_length == rhs.anchor_length && + this->anchor_length_max == rhs.anchor_length_max && + this->flow == rhs.flow && + this->extrusion_role == rhs.extrusion_role && + this->sparse_infill_speed == rhs.sparse_infill_speed && + this->top_surface_speed == rhs.top_surface_speed && + this->solid_infill_speed == rhs.solid_infill_speed && + this->lattice_angle_1 == rhs.lattice_angle_1 && + this->lattice_angle_2 == rhs.lattice_angle_2 && + this->infill_overhang_angle == rhs.infill_overhang_angle; + } }; struct SurfaceFill { @@ -622,6 +627,7 @@ std::vector group_fills(const Layer &layer) params.density = float(region_config.sparse_infill_density); params.lattice_angle_1 = region_config.lattice_angle_1; params.lattice_angle_2 = region_config.lattice_angle_2; + params.infill_overhang_angle = region_config.infill_overhang_angle; if (surface.is_solid()) { params.density = 100.f; @@ -966,6 +972,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.layer_height = layerm->layer()->height; params.lattice_angle_1 = surface_fill.params.lattice_angle_1; params.lattice_angle_2 = surface_fill.params.lattice_angle_2; + params.infill_overhang_angle = surface_fill.params.infill_overhang_angle; // BBS params.flow = surface_fill.params.flow; @@ -1046,6 +1053,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc case ipLine: case ipConcentric: case ipHoneycomb: + case ip2DHoneycomb: case ip3DHoneycomb: case ipGyroid: case ipTpmsD: @@ -1097,6 +1105,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc params.layer_height = layerm.layer()->height; params.lattice_angle_1 = surface_fill.params.lattice_angle_1; params.lattice_angle_2 = surface_fill.params.lattice_angle_2; + params.infill_overhang_angle = surface_fill.params.infill_overhang_angle; for (ExPolygon &expoly : surface_fill.expolygons) { // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 619f67036c..3b2f5bcc5c 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -40,6 +40,7 @@ Fill* Fill::new_from_type(const InfillPattern type) switch (type) { case ipConcentric: return new FillConcentric(); case ipHoneycomb: return new FillHoneycomb(); + case ip2DHoneycomb: return new Fill2DHoneycomb(); case ip3DHoneycomb: return new Fill3DHoneycomb(); case ipGyroid: return new FillGyroid(); case ipTpmsD: return new FillTpmsD();//from creality print diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 01aaf38d28..62eeb9000e 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -73,6 +73,9 @@ struct FillParams coordf_t lattice_angle_1 { 0.f }; coordf_t lattice_angle_2 { 0.f }; + // For 2D Honeycomb + float infill_overhang_angle { 60 }; + // BBS Flow flow; ExtrusionRole extrusion_role{ ExtrusionRole(0) }; diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 7fe310e14b..6948cfd799 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -3093,6 +3093,66 @@ Polylines FillQuarterCubic::fill_surface(const Surface* surface, const FillParam return polylines_out; } +Polylines Fill2DHoneycomb::fill_surface(const Surface *surface, const FillParams ¶ms) +{ + // the 2D honeycomb is generated based on a base pattern of an inverted Y with its junction at height zero + // | + // | + // 0 --+-- + // / \ + // why inverted? + // it makes determining some of the properties easier + // and the two angled legs provide additional horizontal stiffness + // the additional horizontal stiffness is not required close to the bed (unless you don't have any kind of bottom or flange) + + using namespace boost::math::float_constants; + + // lets begin calculating some base properties of the honeycomb pattern + const float half_horizontal_period = .5f * (1*(2/3.f) + 2*(1/3.f)) * float(spacing) / params.density; + const float vertical_period = 3 * half_horizontal_period / tanf(degree * float(params.infill_overhang_angle)); + + // we want to align the base pattern with its knot on height 0 + // therefore the double line part is 1/3 below and the single line is 2/3 above 0 + const float vertical_thirds_float = 3 * float(z) / vertical_period; + const int vertical_thirds_int = vertical_thirds_float; // converstion to int does implicit floor wich is desired here + const bool single_line = (vertical_thirds_int + 1) % 3; + + // the base pattern needs to be horizontally shifted by half every odd pattern layer + const bool odd_layer = ((vertical_thirds_int + 1) / 3) % 2; + const float horizontal_offset = odd_layer ? half_horizontal_period : 0; + + Polylines polylines_out; + + if (single_line) + { + FillParams multiline_params = params; + multiline_params.density *= 1 / (1*(2/3.) + 2*(1/3.)); + + if (!fill_surface_by_multilines( + surface, multiline_params, + { { half_pi, horizontal_offset } }, + polylines_out)) + BOOST_LOG_TRIVIAL(error) << "Fill2DHoneycomb::fill_surface() failed to fill a region."; + } else { + FillParams multiline_params = params; + multiline_params.density *= 2 / (1*(2/3.) + 2*(1/3.)); + + const float horizontal_position = (1 - (vertical_thirds_float - vertical_thirds_int)) * half_horizontal_period; + + if (!fill_surface_by_multilines( + surface, multiline_params, + { { half_pi, -horizontal_position + horizontal_offset }, { half_pi, horizontal_position + horizontal_offset } }, + polylines_out)) + BOOST_LOG_TRIVIAL(error) << "Fill2DHoneycomb::fill_surface() failed to fill a region."; + } + + if (this->layer_id % 2 == 1) + for (int i = 0; i < polylines_out.size(); i++) + std::reverse(polylines_out[i].begin(), polylines_out[i].end()); + + return polylines_out; +} + Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams ¶ms) { assert(! params.full_infill()); diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp index 58f9afb1df..2dbcad3e90 100644 --- a/src/libslic3r/Fill/FillRectilinear.hpp +++ b/src/libslic3r/Fill/FillRectilinear.hpp @@ -132,6 +132,14 @@ protected: float _layer_angle(size_t idx) const override { return 0.f; } }; +class Fill2DHoneycomb : public FillAlignedRectilinear +{ +public: + Fill* clone() const override { return new Fill2DHoneycomb(*this); } + ~Fill2DHoneycomb() override = default; + Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; +}; + class FillSupportBase : public FillRectilinear { diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 417ebf9da3..d4a69116bd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6252,7 +6252,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp z_range.first = std::max(0.f, z_range.second - protect_z); std::vector layers_of_objects; std::vector boundingBox_for_objects; - std::vector objects_instances_shift; + VecOfPoints objects_instances_shift; std::vector idx_of_object_sorted = m_curr_print->layers_sorted_for_object(z_range.first, z_range.second, layers_of_objects, boundingBox_for_objects, objects_instances_shift); std::vector is_layers_of_objects_sorted(layers_of_objects.size(), false); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 9560d7aa54..dc4af6362a 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -12,11 +12,6 @@ namespace Slic3r { - namespace ClipperLib { - class PolyNode; - using PolyNodes = std::vector; - } - namespace Geometry { // Generic result of an orientation predicate. diff --git a/src/libslic3r/JumpPointSearch.cpp b/src/libslic3r/JumpPointSearch.cpp index f8ef2ff100..9eb18017fb 100644 --- a/src/libslic3r/JumpPointSearch.cpp +++ b/src/libslic3r/JumpPointSearch.cpp @@ -249,7 +249,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) using QNode = astar::QNode>; std::unordered_map astar_cache{}; - std::vector out_path; + std::vector> out_path; std::vector out_nodes; if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) { @@ -288,7 +288,7 @@ Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1) svg.draw(scaled_point(start), "green", scale_(0.4)); #endif - std::vector tmp_path; + std::vector> tmp_path; tmp_path.reserve(out_path.size()); // Some path found, reverse and remove points that do not change direction std::reverse(out_path.begin(), out_path.end()); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index f6047fbbb1..e419b5497b 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -387,6 +387,7 @@ coordf_t Layer::get_sparse_infill_max_void_area() case ipAlignedRectilinear: case ipOctagramSpiral: case ipHilbertCurve: + case ip2DHoneycomb: case ip3DHoneycomb: case ipArchimedeanChords: max_void_area = std::max(max_void_area, spacing * spacing); diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 3cdae34105..f027e93a07 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -161,9 +161,9 @@ bool MultiPoint::intersections(const Line &line, Points *intersections) const return intersections->size() > intersections_size; } -std::vector MultiPoint::_douglas_peucker(const std::vector& pts, const double tolerance) +Points MultiPoint::_douglas_peucker(const Points &pts, const double tolerance) { - std::vector result_pts; + Points result_pts; double tolerance_sq = tolerance * tolerance; if (! pts.empty()) { const Point *anchor = &pts.front(); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index b6f74e5c88..db51a6a87a 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -134,7 +134,7 @@ public: }; extern BoundingBox get_extents(const MultiPoint &mp); -extern BoundingBox get_extents_rotated(const std::vector &points, double angle); +extern BoundingBox get_extents_rotated(const Points &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); inline double length(const Points &pts) { diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index ffda2b50d8..43e1a91d7c 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -8,7 +8,6 @@ #include "ShortestPath.hpp" #include "VariableWidth.hpp" #include "CurveAnalyzer.hpp" -#include "Clipper2Utils.hpp" #include "Arachne/WallToolPaths.hpp" #include "Geometry/ConvexHull.hpp" #include "ExPolygonCollection.hpp" diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 1c5aa43c09..4e508cdc3a 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -8,7 +8,11 @@ #include #include #include -#include + +#include + + +#include #include "LocalesUtils.hpp" @@ -48,7 +52,9 @@ using Vec2d = Eigen::Matrix; using Vec3d = Eigen::Matrix; using Vec4d = Eigen::Matrix; -using Points = std::vector; +template +using PointsAllocator = tbb::scalable_allocator; +using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; using Points3 = std::vector; @@ -56,7 +62,7 @@ using Pointfs = std::vector; using Vec2ds = std::vector; using Pointf3s = std::vector; -using VecOfPoints = std::vector; +using VecOfPoints = std::vector>; using Matrix2f = Eigen::Matrix; using Matrix2d = Eigen::Matrix; diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 5d3d643349..0d2e2668c7 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -437,11 +437,8 @@ bool has_duplicate_points(const Polygons &polys) { #if 1 // Check globally. - size_t cnt = 0; - for (const Polygon &poly : polys) - cnt += poly.points.size(); - std::vector allpts; - allpts.reserve(cnt); + Points allpts; + allpts.reserve(count_points(polys)); for (const Polygon &poly : polys) allpts.insert(allpts.end(), poly.points.begin(), poly.points.end()); return has_duplicate_points(std::move(allpts)); diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index eacf5a6636..7d996055e5 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -12,9 +12,9 @@ namespace Slic3r { class Polygon; -using Polygons = std::vector; -using PolygonPtrs = std::vector; -using ConstPolygonPtrs = std::vector; +using Polygons = std::vector>; +using PolygonPtrs = std::vector>; +using ConstPolygonPtrs = std::vector>; // Returns true if inside. Returns border_result if on boundary. bool contains(const Polygon& polygon, const Point& p, bool border_result = true); diff --git a/src/libslic3r/PolygonTrimmer.hpp b/src/libslic3r/PolygonTrimmer.hpp index eddffbc7fa..93e94e303f 100644 --- a/src/libslic3r/PolygonTrimmer.hpp +++ b/src/libslic3r/PolygonTrimmer.hpp @@ -17,7 +17,7 @@ namespace EdgeGrid { struct TrimmedLoop { - std::vector points; + Points points; // Number of points per segment. Empty if the loop is std::vector segments; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 2b9119ad5c..5586c4fcff 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -785,7 +785,7 @@ static std::vector s_Preset_print_options { "layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode", "top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness", "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction", - "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "top_surface_pattern", "bottom_surface_pattern", + "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", "infill_direction", "solid_infill_direction", "rotate_solid_infill_direction", "counterbore_hole_bridging", "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target", "ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cae5c63dd4..3d2b9d52d4 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -529,7 +529,7 @@ bool Print::has_brim() const } //BBS -std::vector Print::layers_sorted_for_object(float start, float end, std::vector &layers_of_objects, std::vector &boundingBox_for_objects, std::vector &objects_instances_shift) +std::vector Print::layers_sorted_for_object(float start, float end, std::vector &layers_of_objects, std::vector &boundingBox_for_objects, VecOfPoints &objects_instances_shift) { std::vector idx_of_object_sorted; size_t idx = 0; @@ -2450,9 +2450,9 @@ Polygons Print::first_layer_islands() const return islands; } -std::vector Print::first_layer_wipe_tower_corners(bool check_wipe_tower_existance) const +Points Print::first_layer_wipe_tower_corners(bool check_wipe_tower_existance) const { - std::vector corners; + Points corners; if (check_wipe_tower_existance && (!has_wipe_tower() || m_wipe_tower_data.tool_changes.empty())) return corners; { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 459f390572..ac3e17a4eb 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -433,7 +433,7 @@ public: //BBS BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name); void get_certain_layers(float start, float end, std::vector &out, std::vector &boundingbox_objects); - std::vector get_instances_shift_without_plate_offset(); + Points get_instances_shift_without_plate_offset(); PrintObject* get_shared_object() const { return m_shared_object; } void set_shared_object(PrintObject *object); void clear_shared_object(); @@ -900,7 +900,7 @@ public: // For Perl bindings. PrintObjectPtrs& objects_mutable() { return m_objects; } PrintRegionPtrs& print_regions_mutable() { return m_print_regions; } - std::vector layers_sorted_for_object(float start, float end, std::vector &layers_of_objects, std::vector &boundingBox_for_objects, std::vector& objects_instances_shift); + std::vector layers_sorted_for_object(float start, float end, std::vector &layers_of_objects, std::vector &boundingBox_for_objects, VecOfPoints& objects_instances_shift); const ExtrusionEntityCollection& skirt() const { return m_skirt; } // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower. @@ -954,7 +954,7 @@ public: ConflictResultOpt get_conflict_result() const { return m_conflict_result; } // Return 4 wipe tower corners in the world coordinates (shifted and rotated), including the wipe tower brim. - std::vector first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const; + Points first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const; //SoftFever bool &is_BBL_printer() { return m_isBBLPrinter; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 650018208b..f134902819 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -152,6 +152,7 @@ static t_config_enum_values s_keys_map_InfillPattern { { "monotonic", ipMonotonic }, { "monotonicline", ipMonotonicLine }, { "alignedrectilinear", ipAlignedRectilinear }, + { "2dhoneycomb", ip2DHoneycomb }, { "3dhoneycomb", ip3DHoneycomb }, { "hilbertcurve", ipHilbertCurve }, { "archimedeanchords", ipArchimedeanChords }, @@ -2390,6 +2391,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("honeycomb"); def->enum_values.push_back("adaptivecubic"); def->enum_values.push_back("alignedrectilinear"); + def->enum_values.push_back("2dhoneycomb"); def->enum_values.push_back("3dhoneycomb"); def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); @@ -2411,6 +2413,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Honeycomb")); def->enum_labels.push_back(L("Adaptive Cubic")); def->enum_labels.push_back(L("Aligned Rectilinear")); + def->enum_labels.push_back(L("2D Honeycomb")); def->enum_labels.push_back(L("3D Honeycomb")); def->enum_labels.push_back(L("Hilbert Curve")); def->enum_labels.push_back(L("Archimedean Chords")); @@ -2441,6 +2444,16 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(45)); + def = this->add("infill_overhang_angle", coFloat); + def->label = L("Infill overhang angle"); + def->category = L("Strength"); + def->tooltip = L("The angle of the infill angled lines. 60° will result in a pure honeycomb."); + def->sidetext = L("°"); + def->min = 15; + def->max = 75; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(60)); + auto def_infill_anchor_min = def = this->add("infill_anchor", coFloatOrPercent); def->label = L("Sparse infill anchor length"); def->category = L("Strength"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b0e308986a..226c9f15f8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -58,7 +58,7 @@ enum AuthorizationType { }; enum InfillPattern : int { - ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipTpmsD, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb, + ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipTpmsD, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip2DHoneycomb, ip3DHoneycomb, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal, ipLightning, ipCrossHatch, ipQuarterCubic, ipCount, @@ -960,6 +960,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionEnum, sparse_infill_pattern)) ((ConfigOptionFloat, lattice_angle_1)) ((ConfigOptionFloat, lattice_angle_2)) + ((ConfigOptionFloat, infill_overhang_angle)) ((ConfigOptionEnum, fuzzy_skin)) ((ConfigOptionFloat, fuzzy_skin_thickness)) ((ConfigOptionFloat, fuzzy_skin_point_distance)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 9d12806122..41fcc2819e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1090,7 +1090,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "initial_layer_line_width" || opt_key == "small_area_infill_flow_compensation" || opt_key == "lattice_angle_1" - || opt_key == "lattice_angle_2") { + || opt_key == "lattice_angle_2" + || opt_key == "infill_overhang_angle") { steps.emplace_back(posInfill); } else if (opt_key == "sparse_infill_pattern") { steps.emplace_back(posPrepareInfill); @@ -3357,9 +3358,9 @@ void PrintObject::get_certain_layers(float start, float end, std::vector PrintObject::get_instances_shift_without_plate_offset() +Points PrintObject::get_instances_shift_without_plate_offset() { - std::vector out; + Points out; out.reserve(m_instances.size()); for (const auto& instance : m_instances) out.push_back(instance.shift_without_plate_offset()); @@ -3769,7 +3770,8 @@ void PrintObject::combine_infill() infill_pattern == ipGrid || infill_pattern == ip2DLattice || infill_pattern == ipLine || - infill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + infill_pattern == ipHoneycomb || + infill_pattern == ip2DHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp index 08a2ff676a..f657fce746 100644 --- a/src/libslic3r/SLA/ConcaveHull.cpp +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -43,7 +43,8 @@ Point ConcaveHull::centroid(const Points &pp) Points ConcaveHull::calculate_centroids() const { // We get the centroids of all the islands in the 2D slice - Points centroids = reserve_vector(m_polys.size()); + Points centroids; + centroids.reserve(m_polys.size()); std::transform(m_polys.begin(), m_polys.end(), std::back_inserter(centroids), [](const Polygon &poly) { return centroid(poly); }); diff --git a/src/libslic3r/SLA/Pad.hpp b/src/libslic3r/SLA/Pad.hpp index 0b61495572..da09343c49 100644 --- a/src/libslic3r/SLA/Pad.hpp +++ b/src/libslic3r/SLA/Pad.hpp @@ -6,6 +6,8 @@ #include #include +#include + struct indexed_triangle_set; namespace Slic3r { @@ -13,7 +15,7 @@ namespace Slic3r { class ExPolygon; class Polygon; using ExPolygons = std::vector; -using Polygons = std::vector; +using Polygons = std::vector>; namespace sla { diff --git a/src/libslic3r/SLA/SupportTree.hpp b/src/libslic3r/SLA/SupportTree.hpp index 200b7cbde1..a5e283b0dd 100644 --- a/src/libslic3r/SLA/SupportTree.hpp +++ b/src/libslic3r/SLA/SupportTree.hpp @@ -5,6 +5,9 @@ #include #include +#include +#include + #include #include #include @@ -16,11 +19,6 @@ class TriangleMesh; class Model; class ModelInstance; class ModelObject; -class Polygon; -class ExPolygon; - -using Polygons = std::vector; -using ExPolygons = std::vector; namespace sla { diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index 158608f364..f8956afaa3 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -8,10 +8,13 @@ #include #include -namespace ClipperLib { class PolyNode; } - namespace Slic3r { + namespace ClipperLib { + class PolyNode; + using PolyNodes = std::vector>; + } + std::vector chain_points(const Points &points, Point *start_near = nullptr); std::vector chain_expolygons(const ExPolygons &input_exploy); @@ -39,7 +42,7 @@ template inline void reorder_by_shortest_traverse(std::vector &po for (size_t i:order) polylines_out.emplace_back(std::move(Temp[i])); } -std::vector chain_clipper_polynodes(const Points &points, const std::vector &items); +ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items); // Chain instances of print objects by an approximate shortest path. // Returns pairs of PrintObject idx and instance of that PrintObject. diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 0e8f423d6c..7580976ac9 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -951,7 +951,7 @@ public: } ::fclose(file); - m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false); + m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized); //m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false)); // Create an EdgeGrid, initialize it with projection, initialize signed distance field. diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 1c10cf7912..44a73ee4fd 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -137,8 +137,8 @@ enum Axis { NUM_AXES_WITH_UNKNOWN, }; -template -inline void append(std::vector& dest, const std::vector& src) +template +inline void append(std::vector &dest, const std::vector &src) { if (dest.empty()) dest = src; @@ -146,8 +146,8 @@ inline void append(std::vector& dest, const std::vector& src) dest.insert(dest.end(), src.begin(), src.end()); } -template -inline void append(std::vector& dest, std::vector&& src) +template +inline void append(std::vector &dest, std::vector &&src) { if (dest.empty()) dest = std::move(src); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 048cb065fe..78d8aaeff0 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -804,6 +804,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co bool lattice_options = config->opt_enum("sparse_infill_pattern") == InfillPattern::ip2DLattice; for (auto el : { "lattice_angle_1", "lattice_angle_2"}) toggle_line(el, lattice_options); + + toggle_line("infill_overhang_angle", config->opt_enum("sparse_infill_pattern") == InfillPattern::ip2DHoneycomb); } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index bc43da6115..c1d6c49e61 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -13,7 +13,7 @@ namespace Slic3r { class TriangleMesh; class Polygon; -using Polygons = std::vector; +using Polygons = std::vector>; class BuildVolume; namespace GUI { diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 15c0023cc4..ad32fc6d6c 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -106,7 +106,7 @@ std::map> SettingsFactory::PART_CAT }}, { L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1}, {"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1}, - {"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"lattice_angle_1", "",1},{"lattice_angle_2", "",1},{"infill_anchor", "",1},{"infill_anchor_max", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1}, {"internal_solid_infill_pattern", "",1}, + {"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"lattice_angle_1", "",1},{"lattice_angle_2", "",1},{"infill_overhang_angle", "",1},{"infill_anchor", "",1},{"infill_anchor_max", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1}, {"internal_solid_infill_pattern", "",1}, {"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"rotate_solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"internal_bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1} }}, { L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5}, diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3ba87e3c40..8ee8b60c77 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2168,6 +2168,7 @@ void TabPrint::build() optgroup->append_single_option_line("sparse_infill_pattern", "fill-patterns#infill types and their properties of sparse"); optgroup->append_single_option_line("lattice_angle_1"); optgroup->append_single_option_line("lattice_angle_2"); + optgroup->append_single_option_line("infill_overhang_angle"); optgroup->append_single_option_line("infill_anchor_max"); optgroup->append_single_option_line("infill_anchor"); optgroup->append_single_option_line("internal_solid_infill_pattern");