diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h index 0f41529622..eaa9fcc779 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 26 October 2022 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * * Purpose : Core Clipper Library structures and functions * @@ -16,6 +16,7 @@ #include #include #include +#include namespace Clipper2Lib { diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h index ca4c51aa40..306afa1233 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h @@ -1,16 +1,16 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 26 October 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 +#ifndef CLIPPER_ENGINE_H +#define CLIPPER_ENGINE_H -#define CLIPPER2_VERSION "1.0.5" +#define CLIPPER2_VERSION "1.0.6" #include #include @@ -68,16 +68,11 @@ namespace Clipper2Lib { } }; - template class PolyPath; - - using PolyPath64 = PolyPath; - using PolyPathD = PolyPath; - - template - using PolyTree = PolyPath; - using PolyTree64 = PolyTree; - using PolyTreeD = PolyTree; + class PolyPath64; + class PolyPathD; + using PolyTree64 = PolyPath64; + using PolyTreeD = PolyPathD; struct OutRec; typedef std::vector OutRecList; @@ -91,7 +86,7 @@ namespace Clipper2Lib { Active* front_edge = nullptr; Active* back_edge = nullptr; OutPt* pts = nullptr; - PolyPath64* polypath = nullptr; + PolyPath* polypath = nullptr; Rect64 bounds = {}; Path64 path; bool is_open = false; @@ -164,10 +159,8 @@ namespace Clipper2Lib { FillRule fillrule_ = FillRule::EvenOdd; FillRule fillpos = FillRule::Positive; int64_t bot_y_ = 0; - bool has_open_paths_ = false; bool minima_list_sorted_ = false; bool using_polytree_ = false; - bool succeeded_ = true; Active* actives_ = nullptr; Active *sel_ = nullptr; Joiner *horz_joiners_ = nullptr; @@ -176,7 +169,6 @@ namespace Clipper2Lib { std::vector vertex_lists_; std::priority_queue scanline_list_; std::vector intersect_nodes_; - std::vector outrec_list_; //pointers in case of memory reallocs std::vector joiner_list_; //pointers in case of memory reallocs void Reset(); void InsertScanline(int64_t y); @@ -230,11 +222,12 @@ namespace Clipper2Lib { 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); - void BuildPaths(Paths64& solutionClosed, Paths64* solutionOpen); - void BuildTree(PolyPath64& polytree, Paths64& open_paths); - protected: #ifdef USINGZ ZCallback64 zCallback_ = nullptr; void SetZ(const Active& e1, const Active& e2, Point64& pt); @@ -242,14 +235,6 @@ namespace Clipper2Lib { 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); - - bool Execute(ClipType clip_type, - FillRule fill_rule, Paths64& solution_closed); - bool Execute(ClipType clip_type, - FillRule fill_rule, Paths64& solution_closed, Paths64& solution_open); - bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree); - bool Execute(ClipType clip_type, - FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths); public: virtual ~ClipperBase(); bool PreserveCollinear = true; @@ -264,63 +249,30 @@ namespace Clipper2Lib { //alternative Paths structure, it does preserve path 'ownership' - ie those //paths that contain (or own) other paths. This will be useful to some users. - template - class PolyPath final { - private: - double scale_; - Path polygon_; - std::vector childs_; + class PolyPath { protected: - const PolyPath* parent_; - PolyPath(const PolyPath* parent, - const Path& path) : - scale_(parent->scale_), polygon_(path), parent_(parent){} + PolyPath* parent_; public: - - explicit PolyPath(int precision = 0) // NB only for root node - { - scale_ = std::pow(10, precision); - parent_ = nullptr; - } - - ~PolyPath() { Clear(); }; - + 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; - PolyPath* operator [] (size_t index) { return childs_[index]; } - - typename std::vector::const_iterator begin() const { return childs_.cbegin(); } - typename std::vector::const_iterator end() const { return childs_.cend(); } - - void Clear() { - for (PolyPath* child : childs_) delete child; - childs_.resize(0); - } - unsigned Level() const { unsigned result = 0; - const PolyPath* p = parent_; + const PolyPath* p = parent_; while (p) { ++result; p = p->parent_; } return result; } - void reserve(size_t size) - { - if (size > childs_.size()) childs_.reserve(size); - } + virtual PolyPath* AddChild(const Path64& path) = 0; - PolyPath* AddChild(const Path& path) - { - childs_.push_back(new PolyPath(this, path)); - return childs_.back(); - } + virtual void Clear() {}; + virtual size_t Count() const { return 0; } - size_t Count() const { return childs_.size(); } - - const PolyPath* parent() const { return parent_; } + const PolyPath* Parent() const { return parent_; } bool IsHole() const { @@ -332,41 +284,70 @@ namespace Clipper2Lib { } return is_hole; } + }; - const Path& Polygon() const { return polygon_; } + 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 PolyPath* child : childs_) + double result = Clipper2Lib::Area(polygon_); + for (const PolyPath64* child : childs_) result += child->Area(); return result; } - friend std::ostream& operator << (std::ostream& outstream, const PolyPath& polypath) + friend std::ostream& operator << (std::ostream& outstream, const PolyPath64& polypath) { - const unsigned level_indent = 4; - const unsigned coords_per_line = 4; - + 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, ' '); + 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; - int last_on_line = coords_per_line - 1; outstream << level_padding; - int i = 0, highI = polypath.Polygon().size() - 1; + 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; + outstream << std::endl << level_padding; } - if (highI >= 0) - outstream << polypath.Polygon()[i]; + if (highI > 0) outstream << polypath.Polygon()[i]; outstream << std::endl; } for (auto child : polypath) @@ -376,10 +357,61 @@ namespace Clipper2Lib { }; - void Polytree64ToPolytreeD(const PolyPath64& polytree, PolyPathD& result); + 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; } @@ -401,23 +433,38 @@ namespace Clipper2Lib { bool Execute(ClipType clip_type, FillRule fill_rule, Paths64& closed_paths) { - return ClipperBase::Execute(clip_type, fill_rule, 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) + bool Execute(ClipType clip_type, FillRule fill_rule, + Paths64& closed_paths, Paths64& open_paths) { - return ClipperBase::Execute(clip_type, fill_rule, closed_paths, 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) { - return ClipperBase::Execute(clip_type, fill_rule, polytree); + Paths64 dummy; + return Execute(clip_type, fill_rule, polytree, dummy); } + bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths) { - return ClipperBase::Execute(clip_type, fill_rule, polytree, open_paths); + if (ExecuteInternal(clip_type, fill_rule, true)) + { + open_paths.clear(); + polytree.Clear(); + BuildTree64(polytree, open_paths); + } + CleanUp(); + return succeeded_; } }; @@ -427,6 +474,8 @@ namespace Clipper2Lib { #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() { @@ -485,13 +534,8 @@ namespace Clipper2Lib { bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths) { -#ifdef USINGZ - CheckCallback(); -#endif - Paths64 closed_paths64; - if (!ClipperBase::Execute(clip_type, fill_rule, closed_paths64)) return false; - closed_paths = ScalePaths(closed_paths64, invScale_); - return true; + PathsD dummy; + return Execute(clip_type, fill_rule, closed_paths, dummy); } bool Execute(ClipType clip_type, @@ -500,40 +544,39 @@ namespace Clipper2Lib { #ifdef USINGZ CheckCallback(); #endif - Paths64 closed_paths64; - Paths64 open_paths64; - if (!ClipperBase::Execute(clip_type, - fill_rule, closed_paths64, open_paths64)) return false; - closed_paths = ScalePaths(closed_paths64, invScale_); - open_paths = ScalePaths(open_paths64, invScale_); - return true; + 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) { -#ifdef USINGZ - CheckCallback(); -#endif - PolyTree64 tree_result; - if (!ClipperBase::Execute(clip_type, fill_rule, tree_result)) return false;; - Polytree64ToPolytreeD(tree_result, polytree); - return true; + PathsD dummy; + return Execute(clip_type, fill_rule, polytree, dummy); } bool Execute(ClipType clip_type, - FillRule fill_rule, PolyTreeD& polytree, Paths64& open_paths) + FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths) { #ifdef USINGZ CheckCallback(); #endif - PolyTree64 tree_result; - if (!ClipperBase::Execute(clip_type, fill_rule, tree_result, open_paths)) return false;; - Polytree64ToPolytreeD(tree_result, polytree); - return true; + 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 +#endif // CLIPPER_ENGINE_H diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h new file mode 100644 index 0000000000..9988df5ea2 --- /dev/null +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h @@ -0,0 +1,753 @@ +/******************************************************************************* +* Author : Angus Johnson * +* Date : 28 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 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' must always be 0.) +//__________________________________ +// |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 where 'y' contains +// the number of paths that follow (and with 'x' always 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 they don't need to be repeated or explained here. +// +// Finally, the pointer structures created and exported through +// these functions can't safely be destroyed externally, so +// a number of 'dispose functions are also exported. + +#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; +} + +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 Path64 ConvertCPathD(const CPathD& p, double scale); +inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale); +inline CPathD CreateCPathD(const Path64& p, double scale); +inline CPathsD CreateCPathsD(const Paths64& pp, double scale); + +inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt); +inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale); + +#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) + + +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 = true, bool reverse_solution = false) +{ + 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 = true, bool reverse_solution = false) +{ + 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 = 2, + bool preserve_collinear = true, bool reverse_solution = false) +{ + 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 = 2, + bool preserve_collinear = true, bool reverse_solution = false) +{ + 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 jt, uint8_t et, double miter_limit = 2.0, + double arc_tolerance = 0.0, bool reverse_solution = false) +{ + Paths64 pp; + pp = ConvertCPaths64(paths); + + ClipperOffset clip_offset( miter_limit, + arc_tolerance, reverse_solution); + clip_offset.AddPaths(pp, JoinType(jt), EndType(et)); + Paths64 result = clip_offset.Execute(delta); + return CreateCPaths64(result); +} + +EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, + double delta, uint8_t jt, uint8_t et, + double precision = 2, double miter_limit = 2.0, + double arc_tolerance = 0.0, bool reverse_solution = false) +{ + 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(jt), EndType(et)); + Paths64 result = clip_offset.Execute(delta * scale); + return CreateCPathsD(result, 1/scale); +} + +EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, + const CPaths64 paths) +{ + log(rect.left); + log(rect.right); + 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 = 2) +{ + 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 = 2) +{ + 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) +{ + // create a dummy counter path + CPath64 result = new int64_t[2 + cnt1 *2]; + result[0] = cnt1; + result[1] = cnt2; + return result; +} + +inline CPath64 CreateCPath64(const Path64& p) +{ + 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) +{ + 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) +{ + // create a dummy path counter + 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) +{ + 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) +{ + 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) +{ + 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 = 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 = 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 = 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/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.h index 4905e37527..13929cdcaf 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 26 October 2022 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * * Purpose : This module provides a simple interface to the Clipper Library * @@ -13,11 +13,6 @@ #include #include -// Remove this define (comes in from X.h on older Linuxes) -#ifdef None -#undef None -#endif - #include "clipper.core.h" #include "clipper.engine.h" #include "clipper.offset.h" @@ -75,6 +70,19 @@ namespace Clipper2Lib { return result; } + inline void BooleanOp(ClipType cliptype, FillRule fillrule, + const PathsD& subjects, const PathsD& clips, + PolyTreeD& polytree, int decimal_prec = 2) + { + if (decimal_prec > 8 || decimal_prec < -8) + throw Clipper2Exception(precision_error); + 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); @@ -135,11 +143,6 @@ namespace Clipper2Lib { return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec); } - inline bool IsFullOpenEndType(EndType et) - { - return (et != EndType::Polygon) && (et != EndType::Joined); - } - inline Paths64 InflatePaths(const Paths64& paths, double delta, JoinType jt, EndType et, double miter_limit = 2.0) { @@ -152,7 +155,7 @@ namespace Clipper2Lib { JoinType jt, EndType et, double miter_limit = 2.0, double precision = 2) { if (precision < -8 || precision > 8) - throw new Clipper2Exception(precision_error); + throw Clipper2Exception(precision_error); const double scale = std::pow(10, precision); ClipperOffset clip_offset(miter_limit); clip_offset.AddPaths(ScalePaths(paths, scale), jt, et); @@ -260,14 +263,14 @@ namespace Clipper2Lib { Rect64 pathRec = Bounds(path); if (!rect.Intersects(pathRec)) return Path64(); if (rect.Contains(pathRec)) return path; - RectClip64 rc(rect); + class RectClip rc(rect); return rc.Execute(path); } inline Paths64 RectClip(const Rect64& rect, const Paths64& paths) { if (rect.IsEmpty() || paths.empty()) return Paths64(); - RectClip64 rc(rect); + class RectClip rc(rect); Paths64 result; result.reserve(paths.size()); @@ -292,10 +295,10 @@ namespace Clipper2Lib { if (rect.IsEmpty() || path.empty() || !rect.Contains(Bounds(path))) return PathD(); if (precision < -8 || precision > 8) - throw new Clipper2Exception(precision_error); + throw Clipper2Exception(precision_error); const double scale = std::pow(10, precision); Rect64 r = ScaleRect(rect, scale); - RectClip64 rc(r); + class RectClip rc(r); Path64 p = ScalePath(path, scale); return ScalePath(rc.Execute(p), 1 / scale); } @@ -304,10 +307,10 @@ namespace Clipper2Lib { { if (rect.IsEmpty() || paths.empty()) return PathsD(); if (precision < -8 || precision > 8) - throw new Clipper2Exception(precision_error); + throw Clipper2Exception(precision_error); const double scale = std::pow(10, precision); Rect64 r = ScaleRect(rect, scale); - RectClip64 rc(r); + class RectClip rc(r); PathsD result; result.reserve(paths.size()); for (const PathD& path : paths) @@ -328,25 +331,111 @@ namespace Clipper2Lib { 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(); + if (precision < -8 || precision > 8) + throw Clipper2Exception(precision_error); + 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; + if (precision < -8 || precision > 8) + throw Clipper2Exception(precision_error); + 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 { - template - inline void InternalPolyNodeToPaths(const PolyPath& polypath, Paths& paths) + inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths) { paths.push_back(polypath.Polygon()); - for (auto child : polypath) - InternalPolyNodeToPaths(*child, paths); + for (const PolyPath* child : polypath) + PolyPathToPaths64(*(PolyPath64*)(child), paths); } - inline bool InternalPolyPathContainsChildren(const PolyPath64& pp) + inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths) { - for (auto child : pp) + 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 && !InternalPolyPathContainsChildren(*child)) + if (child->Count() > 0 && !PolyPath64ContainsChildren(*child)) return false; } return true; @@ -442,20 +531,28 @@ namespace Clipper2Lib { } // end details namespace - template - inline Paths PolyTreeToPaths(const PolyTree& polytree) + inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree) { - Paths result; + Paths64 result; for (auto child : polytree) - details::InternalPolyNodeToPaths(*child, result); + 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::InternalPolyPathContainsChildren(*child)) - return false; + if (child->Count() > 0 && + !details::PolyPath64ContainsChildren(*(PolyPath64*)(child))) + return false; return true; } @@ -545,7 +642,7 @@ namespace Clipper2Lib { inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false) { if (precision > 8 || precision < -8) - throw new Clipper2Exception(precision_error); + throw Clipper2Exception(precision_error); const double scale = std::pow(10, precision); Path64 p = ScalePath(path, scale); p = TrimCollinear(p, is_open_path); diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h index a610b7bbbe..98f43e0b2d 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 26 October 2022 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * * Purpose : FAST rectangular clipping * @@ -20,27 +20,31 @@ namespace Clipper2Lib enum class Location { Left, Top, Right, Bottom, Inside }; - class RectClip64 { - private: + class RectClip { + protected: const Rect64 rect_; const Point64 mp_; const Path64 rectPath_; Path64 result_; std::vector start_locs_; - void Reset(); void GetNextLocation(const Path64& path, Location& loc, int& i, int highI); void AddCorner(Location prev, Location curr); void AddCorner(Location& loc, bool isClockwise); - public: - RectClip64(const Rect64& rect) : + 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/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp b/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp index 28bc806b73..57ad7cad6a 100644 --- a/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp +++ b/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 26 October 2022 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * * Purpose : This is the main polygon clipping module * @@ -145,8 +145,9 @@ namespace Clipper2Lib { { 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::round(ae.dx * (currentY - ae.bot.y))); - //nb: std::round above substantially *improves* performance + 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(). } @@ -2050,52 +2051,6 @@ namespace Clipper2Lib { return succeeded_; } - - bool ClipperBase::Execute(ClipType clip_type, - FillRule fill_rule, Paths64& solution_closed) - { - solution_closed.clear(); - if (ExecuteInternal(clip_type, fill_rule, false)) - BuildPaths(solution_closed, nullptr); - CleanUp(); - return succeeded_; - } - - - bool ClipperBase::Execute(ClipType clip_type, FillRule fill_rule, - Paths64& solution_closed, Paths64& solution_open) - { - solution_closed.clear(); - solution_open.clear(); - if (ExecuteInternal(clip_type, fill_rule, false)) - BuildPaths(solution_closed, &solution_open); - CleanUp(); - return succeeded_; - } - - - bool ClipperBase::Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree) - { - Paths64 dummy; - polytree.Clear(); - if (ExecuteInternal(clip_type, fill_rule, true)) - BuildTree(polytree, dummy); - CleanUp(); - return succeeded_; - } - - bool ClipperBase::Execute(ClipType clip_type, - FillRule fill_rule, PolyTree64& polytree, Paths64& solution_open) - { - polytree.Clear(); - solution_open.clear(); - if (ExecuteInternal(clip_type, fill_rule, true)) - BuildTree(polytree, solution_open); - CleanUp(); - return succeeded_; - } - - void ClipperBase::DoIntersections(const int64_t top_y) { if (BuildIntersectList(top_y)) @@ -3284,72 +3239,6 @@ namespace Clipper2Lib { } - - bool BuildPath(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; - } - return true; - } - - - void ClipperBase::BuildPaths(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 (BuildPath(outrec->pts, ReverseSolution, true, path)) - solutionOpen->emplace_back(std::move(path)); - } - else - { - //closed paths should always return a Positive orientation - if (BuildPath(outrec->pts, ReverseSolution, false, path)) - solutionClosed.emplace_back(std::move(path)); - } - } - } - - inline bool Path1InsidePath2(const OutRec* or1, const OutRec* or2) { PointInPolygonResult result = PointInPolygonResult::IsOn; @@ -3380,7 +3269,40 @@ namespace Clipper2Lib { 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; + } + return true; + } + bool ClipperBase::DeepCheckOwner(OutRec* outrec, OutRec* owner) { if (owner->bounds.IsEmpty()) owner->bounds = GetBounds(owner->path); @@ -3400,7 +3322,7 @@ namespace Clipper2Lib { if (split->splits && DeepCheckOwner(outrec, split)) return true; if (!split->path.size()) - BuildPath(split->pts, ReverseSolution, false, split->path); + BuildPath64(split->pts, ReverseSolution, false, split->path); if (split->bounds.IsEmpty()) split->bounds = GetBounds(split->path); if (split->bounds.Contains(outrec->bounds) && @@ -3426,8 +3348,36 @@ namespace Clipper2Lib { } } + 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()); + } - void ClipperBase::BuildTree(PolyPath64& polytree, Paths64& open_paths) + 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); @@ -3440,13 +3390,13 @@ namespace Clipper2Lib { if (outrec->is_open) { Path64 path; - if (BuildPath(outrec->pts, ReverseSolution, true, path)) + if (BuildPath64(outrec->pts, ReverseSolution, true, path)) open_paths.push_back(path); continue; } - if (!BuildPath(outrec->pts, ReverseSolution, false, outrec->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); @@ -3463,12 +3413,12 @@ namespace Clipper2Lib { tmp->idx = tmp_idx; outrec = tmp; outrec->owner = GetRealOutRec(outrec->owner); - BuildPath(outrec->pts, ReverseSolution, false, outrec->path); + BuildPath64(outrec->pts, ReverseSolution, false, outrec->path); if (outrec->bounds.IsEmpty()) outrec->bounds = GetBounds(outrec->path); if (outrec->owner) DeepCheckOwner(outrec, outrec->owner); } - PolyPath64* owner_polypath; + PolyPath* owner_polypath; if (outrec->owner && outrec->owner->polypath) owner_polypath = outrec->owner->polypath; else @@ -3477,20 +3427,117 @@ namespace Clipper2Lib { } } - static void PolyPath64ToPolyPathD(const PolyPath64& polypath, PolyPathD& result) + bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale) { - for (auto child : polypath) + if (op->next == op || (!isOpen && op->next == op->prev)) return false; + path.resize(0); + Point64 lastPt; + OutPt* op2; + if (reverse) { - PolyPathD* res_child = result.AddChild( - Path64ToPathD(child->Polygon())); - PolyPath64ToPolyPathD(*child, *res_child); + 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; + } + 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)); + } } } - inline void Polytree64ToPolytreeD(const PolyPath64& polytree, PolyPathD& result) + void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths) { - result.Clear(); - PolyPath64ToPolyPathD(polytree, result); + 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/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp b/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp index e58dbecce6..0f6bb9c519 100644 --- a/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp +++ b/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 15 October 2022 * +* Date : 26 October 2022 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2022 * * Purpose : FAST rectangular clipping * @@ -226,13 +226,7 @@ namespace Clipper2Lib { // RectClip64 //---------------------------------------------------------------------------- - inline void RectClip64::Reset() - { - result_.clear(); - start_locs_.clear(); - } - - void RectClip64::AddCorner(Location prev, Location curr) + void RectClip::AddCorner(Location prev, Location curr) { if (HeadingClockwise(prev, curr)) result_.push_back(rectPath_[static_cast(prev)]); @@ -240,7 +234,7 @@ namespace Clipper2Lib { result_.push_back(rectPath_[static_cast(curr)]); } - void RectClip64::AddCorner(Location& loc, bool isClockwise) + void RectClip::AddCorner(Location& loc, bool isClockwise) { if (isClockwise) { @@ -254,7 +248,7 @@ namespace Clipper2Lib { } } - void RectClip64::GetNextLocation(const Path64& path, + void RectClip::GetNextLocation(const Path64& path, Location& loc, int& i, int highI) { switch (loc) @@ -309,11 +303,12 @@ namespace Clipper2Lib { } //switch } - Path64 RectClip64::Execute(const Path64& path) + Path64 RectClip::Execute(const Path64& path) { if (rect_.IsEmpty() || path.size() < 3) return Path64(); - Reset(); + result_.clear(); + start_locs_.clear(); int i = 0, highI = static_cast(path.size()) - 1; Location prev = Location::Inside, loc; Location crossing_loc = Location::Inside; @@ -427,13 +422,13 @@ namespace Clipper2Lib { 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 + Path1ContainsPath2(path, rectPath_) != + PointInPolygonResult::IsOutside) return rectPath_; + else return Path64(); } - if (loc != Location::Inside && + if (loc != Location::Inside && (loc != first_cross_ || start_locs_.size() > 2)) { if (start_locs_.size() > 0) @@ -477,4 +472,76 @@ namespace Clipper2Lib { 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