From e97c2a6864a45ce7d2930721cdcac8ef32c24bf1 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Thu, 1 Jun 2023 11:37:20 -0700 Subject: [PATCH] Update Clipper2 to 9d946d7 Fixed a number of smaller issues as well as overlapping segment cleaning. Fixes https://gitlab.com/kicad/code/kicad/-/issues/14682 (cherry picked from commit fe62a3f98595047136eff2fb6077e7cd4c22879d) --- .../include/clipper2/clipper.core.h | 2 +- .../include/clipper2/clipper.engine.h | 69 ++-- .../include/clipper2/clipper.export.h | 34 +- .../Clipper2Lib/include/clipper2/clipper.h | 126 ++++--- .../include/clipper2/clipper.offset.h | 10 +- .../include/clipper2/clipper.rectclip.h | 16 +- .../Clipper2Lib/src/clipper.engine.cpp | 308 +++++++++++------- .../Clipper2Lib/src/clipper.offset.cpp | 180 ++++++---- .../Clipper2Lib/src/clipper.rectclip.cpp | 39 +-- 9 files changed, 467 insertions(+), 317 deletions(-) diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h index e4442a6817..9fd53d033b 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.core.h @@ -428,7 +428,7 @@ namespace Clipper2Lib } template - inline Path ScalePath(const Path& path, + inline Path ScalePath(const Path& path, double scale, int& error_code) { return ScalePath(path, scale, scale, error_code); diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h index ee664f0a31..9d755f9fd4 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.engine.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 22 April 2023 * +* Date : 15 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : This is the main polygon clipping module * @@ -13,6 +13,7 @@ constexpr auto CLIPPER2_VERSION = "1.2.2"; #include +#include #include #include #include @@ -25,7 +26,6 @@ constexpr auto CLIPPER2_VERSION = "1.2.2"; #ifdef None #undef None #endif - namespace Clipper2Lib { struct Scanline; @@ -38,7 +38,7 @@ namespace Clipper2Lib { //Note: all clipping operations except for Difference are commutative. enum class ClipType { None, Intersection, Union, Difference, Xor }; - + enum class PathType { Subject, Clip }; enum class JoinWith { None, Left, Right }; @@ -46,7 +46,7 @@ namespace Clipper2Lib { None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 }; - constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) + constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) { return (enum VertexFlags)(uint32_t(a) & uint32_t(b)); } @@ -99,7 +99,7 @@ namespace Clipper2Lib { Path64 path; bool is_open = false; bool horz_done = false; - ~OutRec() { + ~OutRec() { if (splits) delete splits; // nb: don't delete the split pointers // as these are owned by ClipperBase's outrec_list_ @@ -110,7 +110,7 @@ namespace Clipper2Lib { //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; @@ -183,6 +183,20 @@ namespace Clipper2Lib { typedef std::vector LocalMinimaList; typedef std::vector IntersectNodeList; + // ReuseableDataContainer64 ------------------------------------------------ + + class ReuseableDataContainer64 { + private: + friend class ClipperBase; + LocalMinimaList minima_list_; + std::vector vertex_lists_; + void AddLocMin(Vertex& vert, PathType polytype, bool is_open); + public: + virtual ~ReuseableDataContainer64(); + void Clear(); + void AddPaths(const Paths64& paths, PathType polytype, bool is_open); + }; + // ClipperBase ------------------------------------------------------------- class ClipperBase { @@ -230,7 +244,7 @@ namespace Clipper2Lib { void SwapPositionsInAEL(Active& edge1, Active& edge2); OutRec* NewOutRec(); OutPt* AddOutPt(const Active &e, const Point64& pt); - OutPt* AddLocalMinPoly(Active &e1, Active &e2, + 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); @@ -242,13 +256,13 @@ namespace Clipper2Lib { void CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec); void FixSelfIntersects(OutRec* outrec); void DoSplitOp(OutRec* outRec, OutPt* splitOp); - + inline void AddTrialHorzJoin(OutPt* op); void ConvertHorzSegsToJoins(); void ProcessHorzJoins(); void Split(Active& e, const Point64& pt); - inline void CheckJoinLeft(Active& e, + inline void CheckJoinLeft(Active& e, const Point64& pt, bool check_curr_x = false); inline void CheckJoinRight(Active& e, const Point64& pt, bool check_curr_x = false); @@ -260,7 +274,7 @@ namespace Clipper2Lib { bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees); void CleanCollinear(OutRec* outrec); bool CheckBounds(OutRec* outrec); - bool CheckSplitOwner(OutRec* outrec); + bool CheckSplitOwner(OutRec* outrec, OutRecList* splits); void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath); #ifdef USINGZ ZCallback64 zCallback_ = nullptr; @@ -275,6 +289,7 @@ namespace Clipper2Lib { bool PreserveCollinear = true; bool ReverseSolution = false; void Clear(); + void AddReuseableData(const ReuseableDataContainer64& reuseable_data); #ifdef USINGZ int64_t DefaultZ = 0; #endif @@ -312,12 +327,12 @@ namespace Clipper2Lib { const PolyPath* Parent() const { return parent_; } - bool IsHole() const + bool IsHole() const { unsigned lvl = Level(); //Even levels except level 0 return lvl && !(lvl & 1); - } + } }; typedef typename std::vector> PolyPath64List; @@ -335,9 +350,9 @@ namespace Clipper2Lib { } const PolyPath64* operator [] (size_t index) const - { - return childs_[index].get(); - } + { + return childs_[index].get(); //std::unique_ptr + } const PolyPath64* Child(size_t index) const { @@ -379,12 +394,12 @@ namespace Clipper2Lib { class PolyPathD : public PolyPath { private: PolyPathDList childs_; - double inv_scale_; + double scale_; PathD polygon_; public: explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent) { - inv_scale_ = parent ? parent->inv_scale_ : 1.0; + scale_ = parent ? parent->scale_ : 1.0; } ~PolyPathD() { @@ -392,7 +407,7 @@ namespace Clipper2Lib { } const PolyPathD* operator [] (size_t index) const - { + { return childs_[index].get(); } @@ -404,14 +419,14 @@ namespace Clipper2Lib { PolyPathDList::const_iterator begin() const { return childs_.cbegin(); } PolyPathDList::const_iterator end() const { return childs_.cend(); } - void SetInvScale(double value) { inv_scale_ = value; } - double InvScale() { return inv_scale_; } + void SetScale(double value) { scale_ = value; } + double Scale() { return scale_; } PolyPathD* AddChild(const Path64& path) override { int error_code = 0; auto p = std::make_unique(this); PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = ScalePath(path, inv_scale_, error_code); + result->polygon_ = ScalePath(path, scale_, error_code); return result; } @@ -465,7 +480,7 @@ namespace Clipper2Lib { return Execute(clip_type, fill_rule, closed_paths, dummy); } - bool Execute(ClipType clip_type, FillRule fill_rule, + bool Execute(ClipType clip_type, FillRule fill_rule, Paths64& closed_paths, Paths64& open_paths) { closed_paths.clear(); @@ -537,12 +552,12 @@ namespace Clipper2Lib { void CheckCallback() { if(zCallbackD_) - // if the user defined float point callback has been assigned + // if the user defined float point callback has been assigned // then assign the proxy callback function - ClipperBase::zCallback_ = + ClipperBase::zCallback_ = std::bind(&ClipperD::ZCB, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, - std::placeholders::_4, std::placeholders::_5); + std::placeholders::_4, std::placeholders::_5); else ClipperBase::zCallback_ = nullptr; } @@ -599,7 +614,7 @@ namespace Clipper2Lib { if (ExecuteInternal(clip_type, fill_rule, true)) { polytree.Clear(); - polytree.SetInvScale(invScale_); + polytree.SetScale(invScale_); open_paths.clear(); BuildTreeD(polytree, open_paths); } @@ -609,6 +624,6 @@ namespace Clipper2Lib { }; -} // namespace +} // namespace #endif // CLIPPER_ENGINE_H diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h index e8d678a41d..5bfab9c893 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.export.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 23 March 2023 * +* Date : 30 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * @@ -157,14 +157,14 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, int precision = 2, double miter_limit = 2.0, double arc_tolerance = 0.0, bool reverse_solution = false); -// ExecuteRectClip & ExecuteRectClipLines: -EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect, +// RectClip & RectClipLines: +EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths, bool convex_only = false); -EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect, +EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision = 2, bool convex_only = false); -EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect, +EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, const CPaths64 paths); -EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect, +EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, const CPathsD paths, int precision = 2); ////////////////////////////////////////////////////// @@ -381,19 +381,17 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, return CreateCPathsD(result, 1/scale); } -EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect, - const CPaths64 paths, bool convex_only) +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); + class RectClip64 rc(r64); Paths64 pp = ConvertCPaths64(paths); - Paths64 result = rc.Execute(pp, convex_only); + Paths64 result = rc.Execute(pp); return CreateCPaths64(result); } -EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect, - const CPathsD paths, int precision, bool convex_only) +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; @@ -402,30 +400,30 @@ EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect, RectD r = CRectToRect(rect); Rect64 rec = ScaleRect(r, scale); Paths64 pp = ConvertCPathsD(paths, scale); - class RectClip rc(rec); - Paths64 result = rc.Execute(pp, convex_only); + class RectClip64 rc(rec); + Paths64 result = rc.Execute(pp); return CreateCPathsD(result, 1/scale); } -EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect, +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); + class RectClipLines64 rcl (r); Paths64 pp = ConvertCPaths64(paths); Paths64 result = rcl.Execute(pp); return CreateCPaths64(result); } -EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect, +EXTERN_DLL_EXPORT CPathsD RectClipLinesD(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); - class RectClipLines rcl(r); + class RectClipLines64 rcl(r); Paths64 pp = ConvertCPathsD(paths, scale); Paths64 result = rcl.Execute(pp); return CreateCPathsD(result, 1/scale); diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.h index f406eaef02..808f49cc7a 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 : 21 April 2023 * +* Date : 26 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : This module provides a simple interface to the Clipper Library * @@ -161,60 +161,61 @@ namespace Clipper2Lib { return ScalePaths(solution, 1 / scale, error_code); } - inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy) + template + inline Path TranslatePath(const Path& path, T dx, T dy) { - Path64 result; + Path result; result.reserve(path.size()); std::transform(path.begin(), path.end(), back_inserter(result), - [dx, dy](const auto& pt) { return Point64(pt.x + dx, pt.y +dy); }); + [dx, dy](const auto& pt) { return Point(pt.x + dx, pt.y +dy); }); return result; } + inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy) + { + return TranslatePath(path, dx, dy); + } + inline PathD TranslatePath(const PathD& path, double dx, double dy) { - PathD result; - result.reserve(path.size()); - std::transform(path.begin(), path.end(), back_inserter(result), - [dx, dy](const auto& pt) { return PointD(pt.x + dx, pt.y + dy); }); + return TranslatePath(path, dx, dy); + } + + template + inline Paths TranslatePaths(const Paths& paths, T dx, T dy) + { + Paths result; + result.reserve(paths.size()); + std::transform(paths.begin(), paths.end(), back_inserter(result), + [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); }); return result; } inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy) { - Paths64 result; - result.reserve(paths.size()); - std::transform(paths.begin(), paths.end(), back_inserter(result), - [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); }); - return result; + return TranslatePaths(paths, dx, dy); } inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy) { - PathsD result; - result.reserve(paths.size()); - std::transform(paths.begin(), paths.end(), back_inserter(result), - [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); }); - return result; + return TranslatePaths(paths, dx, dy); } - inline Paths64 ExecuteRectClip(const Rect64& rect, - const Paths64& paths, bool convex_only) + inline Paths64 RectClip(const Rect64& rect, const Paths64& paths) { if (rect.IsEmpty() || paths.empty()) return Paths64(); - RectClip rc(rect); - return rc.Execute(paths, convex_only); + RectClip64 rc(rect); + return rc.Execute(paths); } - inline Paths64 ExecuteRectClip(const Rect64& rect, - const Path64& path, bool convex_only) + inline Paths64 RectClip(const Rect64& rect, const Path64& path) { if (rect.IsEmpty() || path.empty()) return Paths64(); - RectClip rc(rect); - return rc.Execute(Paths64{ path }, convex_only); + RectClip64 rc(rect); + return rc.Execute(Paths64{ path }); } - inline PathsD ExecuteRectClip(const RectD& rect, - const PathsD& paths, bool convex_only, int precision = 2) + inline PathsD RectClip(const RectD& rect, const PathsD& paths, int precision = 2) { if (rect.IsEmpty() || paths.empty()) return PathsD(); int error_code = 0; @@ -222,32 +223,31 @@ namespace Clipper2Lib { if (error_code) return PathsD(); const double scale = std::pow(10, precision); Rect64 r = ScaleRect(rect, scale); - RectClip rc(r); + RectClip64 rc(r); Paths64 pp = ScalePaths(paths, scale, error_code); if (error_code) return PathsD(); // ie: error_code result is lost return ScalePaths( - rc.Execute(pp, convex_only), 1 / scale, error_code); + rc.Execute(pp), 1 / scale, error_code); } - inline PathsD ExecuteRectClip(const RectD& rect, - const PathD& path, bool convex_only, int precision = 2) + inline PathsD RectClip(const RectD& rect, const PathD& path, int precision = 2) { - return ExecuteRectClip(rect, PathsD{ path }, convex_only, precision); + return RectClip(rect, PathsD{ path }, precision); } - inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Paths64& lines) + inline Paths64 RectClipLines(const Rect64& rect, const Paths64& lines) { if (rect.IsEmpty() || lines.empty()) return Paths64(); - RectClipLines rcl(rect); + RectClipLines64 rcl(rect); return rcl.Execute(lines); } - inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Path64& line) + inline Paths64 RectClipLines(const Rect64& rect, const Path64& line) { - return ExecuteRectClipLines(rect, Paths64{ line }); + return RectClipLines(rect, Paths64{ line }); } - inline PathsD ExecuteRectClipLines(const RectD& rect, const PathsD& lines, int precision = 2) + inline PathsD RectClipLines(const RectD& rect, const PathsD& lines, int precision = 2) { if (rect.IsEmpty() || lines.empty()) return PathsD(); int error_code = 0; @@ -255,16 +255,16 @@ namespace Clipper2Lib { if (error_code) return PathsD(); const double scale = std::pow(10, precision); Rect64 r = ScaleRect(rect, scale); - RectClipLines rcl(r); + RectClipLines64 rcl(r); Paths64 p = ScalePaths(lines, scale, error_code); if (error_code) return PathsD(); p = rcl.Execute(p); return ScalePaths(p, 1 / scale, error_code); } - inline PathsD ExecuteRectClipLines(const RectD& rect, const PathD& line, int precision = 2) + inline PathsD RectClipLines(const RectD& rect, const PathD& line, int precision = 2) { - return ExecuteRectClipLines(rect, PathsD{ line }, precision); + return RectClipLines(rect, PathsD{ line }, precision); } namespace details @@ -290,14 +290,9 @@ namespace Clipper2Lib { { // return false if this child isn't fully contained by its parent - // the following algorithm is a bit too crude, and doesn't account - // for rounding errors. A better algorithm is to return false when - // consecutive vertices are found outside the parent's polygon. - - //const Path64& path = pp.Polygon(); - //if (std::any_of(child->Polygon().cbegin(), child->Polygon().cend(), - // [path](const auto& pt) {return (PointInPolygon(pt, path) == - // PointInPolygonResult::IsOutside); })) return false; + // checking for a single vertex outside is a bit too crude since + // it doesn't account for rounding errors. It's better to check + // for consecutive vertices found outside the parent's polygon. int outsideCnt = 0; for (const Point64& pt : child->Polygon()) @@ -490,6 +485,39 @@ namespace Clipper2Lib { return result; } +#ifdef USINGZ + template + inline Path64 MakePathZ(const T2(&list)[N]) + { + static_assert(N % 3 == 0 && std::numeric_limits::is_integer, + "MakePathZ requires integer values in multiples of 3"); + std::size_t size = N / 3; + Path64 result(size); + for (size_t i = 0; i < size; ++i) + result[i] = Point64(list[i * 3], + list[i * 3 + 1], list[i * 3 + 2]); + return result; + } + + template + inline PathD MakePathZD(const T2(&list)[N]) + { + static_assert(N % 3 == 0, + "MakePathZD requires values in multiples of 3"); + std::size_t size = N / 3; + PathD result(size); + if constexpr (std::numeric_limits::is_integer) + for (size_t i = 0; i < size; ++i) + result[i] = PointD(list[i * 3], + list[i * 3 + 1], list[i * 3 + 2]); + else + for (size_t i = 0; i < size; ++i) + result[i] = PointD(list[i * 3], list[i * 3 + 1], + static_cast(list[i * 3 + 2])); + return result; + } +#endif + inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false) { size_t len = p.size(); diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h index f5d47e07ee..8835fb0f45 100644 --- a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h +++ b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.offset.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 22 March 2023 * +* Date : 15 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : Path Offset (Inflate/Shrink) * @@ -24,6 +24,7 @@ enum class EndType {Polygon, Joined, Butt, Square, Round}; //Joined : offsets both sides of a path, with joined ends //Polygon: offsets only one side of a closed path +typedef std::function DeltaCallback64; class ClipperOffset { private: @@ -43,7 +44,6 @@ private: int error_code_ = 0; double delta_ = 0.0; double group_delta_ = 0.0; - double abs_group_delta_ = 0.0; double temp_lim_ = 0.0; double steps_per_rad_ = 0.0; double step_sin_ = 0.0; @@ -62,6 +62,7 @@ private: #ifdef USINGZ ZCallback64 zCallback64_ = nullptr; #endif + DeltaCallback64 deltaCallback64_ = nullptr; 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); @@ -70,7 +71,7 @@ private: void OffsetPolygon(Group& group, Path64& path); void OffsetOpenJoined(Group& group, Path64& path); void OffsetOpenPath(Group& group, Path64& path); - void OffsetPoint(Group& group, Path64& path, size_t j, size_t& k); + void OffsetPoint(Group& group, Path64& path, size_t j, size_t k); void DoGroupOffset(Group &group); void ExecuteInternal(double delta); public: @@ -91,6 +92,7 @@ public: void Execute(double delta, Paths64& paths); void Execute(double delta, PolyTree64& polytree); + void Execute(DeltaCallback64 delta_cb, Paths64& paths); double MiterLimit() const { return miter_limit_; } void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; } @@ -108,6 +110,8 @@ public: #ifdef USINGZ void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; } #endif + void SetDeltaCallback(DeltaCallback64 cb) { deltaCallback64_ = cb; } + }; } diff --git a/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h b/thirdparty/clipper2/Clipper2Lib/include/clipper2/clipper.rectclip.h index 2a9bb35d08..bcbe7f436c 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 : 9 February 2023 * +* Date : 30 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : FAST rectangular clipping * @@ -34,10 +34,10 @@ namespace Clipper2Lib }; //------------------------------------------------------------------------------ - // RectClip + // RectClip64 //------------------------------------------------------------------------------ - class RectClip { + class RectClip64 { private: void ExecuteInternal(const Path64& path); Path64 GetPath(OutPt2*& op); @@ -58,23 +58,23 @@ namespace Clipper2Lib void AddCorner(Location prev, Location curr); void AddCorner(Location& loc, bool isClockwise); public: - explicit RectClip(const Rect64& rect) : + explicit RectClip64(const Rect64& rect) : rect_(rect), rect_as_path_(rect.AsPath()), rect_mp_(rect.MidPoint()) {} - Paths64 Execute(const Paths64& paths, bool convex_only = false); + Paths64 Execute(const Paths64& paths); }; //------------------------------------------------------------------------------ - // RectClipLines + // RectClipLines64 //------------------------------------------------------------------------------ - class RectClipLines : public RectClip { + class RectClipLines64 : public RectClip64 { private: void ExecuteInternal(const Path64& path); Path64 GetPath(OutPt2*& op); public: - explicit RectClipLines(const Rect64& rect) : RectClip(rect) {}; + explicit RectClipLines64(const Rect64& rect) : RectClip64(rect) {}; Paths64 Execute(const Paths64& paths); }; diff --git a/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp b/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp index 5172bff2d9..b8837669ba 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 : 23 April 2023 * +* Date : 20 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : This is the main polygon clipping module * @@ -419,7 +419,6 @@ namespace Clipper2Lib { return outrec; } - inline void UncoupleOutRec(Active ae) { OutRec* outrec = ae.outrec; @@ -484,6 +483,156 @@ namespace Clipper2Lib { outrec->owner = new_owner; } + //------------------------------------------------------------------------------ + //------------------------------------------------------------------------------ + + void AddLocMin(LocalMinimaList& list, + 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); + list.push_back(std::make_unique (&vert, polytype, is_open)); + } + + void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, + std::vector& vertexLists, LocalMinimaList& locMinList) + { + const auto total_vertex_count = + std::accumulate(paths.begin(), paths.end(), 0, + [](const auto& a, const Path64& path) + {return a + static_cast(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; + + if (path.empty()) + continue; + + 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(locMinList , *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(locMinList, *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(locMinList, *prev_v, polytype, is_open); + } + else if (going_up != going_up0) + { + if (going_up0) AddLocMin(locMinList, *prev_v, polytype, false); + else prev_v->flags = prev_v->flags | VertexFlags::LocalMax; + } + } // end processing current path + + vertexLists.emplace_back(vertices); + } + + + + //------------------------------------------------------------------------------ + // ReuseableDataContainer64 methods ... + //------------------------------------------------------------------------------ + + void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open) + { + //make sure the vertex is added only once ... + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + + vert.flags = (vert.flags | VertexFlags::LocalMin); + minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); + } + + void ReuseableDataContainer64::AddPaths(const Paths64& paths, + PathType polytype, bool is_open) + { + AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_); + } + + ReuseableDataContainer64::~ReuseableDataContainer64() + { + Clear(); + } + + void ReuseableDataContainer64::Clear() + { + minima_list_.clear(); + for (auto v : vertex_lists_) delete[] v; + vertex_lists_.clear(); + } + //------------------------------------------------------------------------------ // ClipperBase methods ... //------------------------------------------------------------------------------ @@ -576,113 +725,26 @@ namespace Clipper2Lib { 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; + AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_); + } - const auto total_vertex_count = - std::accumulate(paths.begin(), paths.end(), 0, - [](const auto& a, const Path64& path) - {return a + static_cast(path.size());}); - if (total_vertex_count == 0) return; - - Vertex* vertices = new Vertex[total_vertex_count], * v = vertices; - for (const Path64& path : paths) + void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data) + { + // nb: reuseable_data will continue to own the vertices + // and remains responsible for their clean up. + succeeded_ = false; + minima_list_sorted_ = false; + LocalMinimaList::const_iterator i; + for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i) { - //for each path create a circular double linked list of vertices - Vertex* v0 = v, * curr_v = v, * prev_v = nullptr; - - if (path.empty()) - continue; - - 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 - + minima_list_.push_back(std::make_unique ((*i)->vertex, (*i)->polytype, (*i)->is_open)); + if ((*i)->is_open) has_open_paths_ = true; + } + } void ClipperBase::InsertScanline(int64_t y) { @@ -1315,6 +1377,7 @@ namespace Clipper2Lib { result->owner = nullptr; result->polypath = nullptr; result->is_open = false; + result->splits = nullptr; return result; } @@ -1596,17 +1659,14 @@ namespace Clipper2Lib { default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break; } + OutPt* resultOp; //toggle contribution ... if (IsHotEdge(*edge_o)) { - OutPt* resultOp = AddOutPt(*edge_o, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif + resultOp = AddOutPt(*edge_o, pt); 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 @@ -1626,11 +1686,16 @@ namespace Clipper2Lib { return e3->outrec->pts; } else - return StartOpenPath(*edge_o, pt); + resultOp = StartOpenPath(*edge_o, pt); } else - return StartOpenPath(*edge_o, pt); - } + resultOp = StartOpenPath(*edge_o, pt); + +#ifdef USINGZ + if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt); +#endif + return resultOp; + } // end of an open path intersection //MANAGING CLOSED PATHS FROM HERE ON @@ -2111,7 +2176,7 @@ namespace Clipper2Lib { if (or1 == or2) { - or2 = new OutRec(); + or2 = NewOutRec(); or2->pts = op1b; FixOutRecPts(or2); if (or1->pts->outrec == or2) @@ -2123,7 +2188,11 @@ namespace Clipper2Lib { if (using_polytree_) { if (Path1InsidePath2(or2->pts, or1->pts)) + { SetOwner(or2, or1); + if (!or1->splits) or1->splits = new OutRecList(); + or1->splits->push_back(or2); //(#520) + } else if (Path1InsidePath2(or1->pts, or2->pts)) SetOwner(or1, or2); else @@ -2135,8 +2204,6 @@ namespace Clipper2Lib { } else or2->owner = or1; - - outrec_list_.push_back(or2); } else { @@ -2763,14 +2830,13 @@ namespace Clipper2Lib { return true; } - bool ClipperBase::CheckSplitOwner(OutRec* outrec) + bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits) { - for (auto s : *outrec->owner->splits) + for (auto split : *splits) { - OutRec* split = GetRealOutRec(s); - if (split && split != outrec && - split != outrec->owner && CheckBounds(split) && - split->bounds.Contains(outrec->bounds) && + if(split == outrec || split == outrec->owner) continue; + else if (split->splits && CheckSplitOwner(outrec, split->splits)) return true; + else if (CheckBounds(split) && split->bounds.Contains(outrec->bounds) && Path1InsidePath2(outrec->pts, split->pts)) { outrec->owner = split; //found in split @@ -2789,7 +2855,7 @@ namespace Clipper2Lib { while (outrec->owner) { - if (outrec->owner->splits && CheckSplitOwner(outrec)) break; + if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break; if (outrec->owner->pts && CheckBounds(outrec->owner) && outrec->owner->bounds.Contains(outrec->bounds) && Path1InsidePath2(outrec->pts, outrec->owner->pts)) break; diff --git a/thirdparty/clipper2/Clipper2Lib/src/clipper.offset.cpp b/thirdparty/clipper2/Clipper2Lib/src/clipper.offset.cpp index a60cf05ba9..2cefb0ed26 100644 --- a/thirdparty/clipper2/Clipper2Lib/src/clipper.offset.cpp +++ b/thirdparty/clipper2/Clipper2Lib/src/clipper.offset.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 8 April 2023 * +* Date : 26 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : Path Offset (Inflate/Shrink) * @@ -205,15 +205,17 @@ void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t { PointD vec; if (j == k) - vec = PointD(norms[0].y, -norms[0].x); + vec = PointD(norms[j].y, -norms[j].x); else vec = GetAvgUnitVector( PointD(-norms[k].y, norms[k].x), PointD(norms[j].y, -norms[j].x)); + double abs_delta = std::abs(group_delta_); + // 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); + ptQ = TranslatePoint(ptQ, abs_delta * vec.x, abs_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); @@ -260,6 +262,20 @@ void ClipperOffset::DoMiter(Group& group, const Path64& path, size_t j, size_t k void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle) { + if (deltaCallback64_) { + // when deltaCallback64_ is assigned, group_delta_ won't be constant, + // so we'll need to do the following calculations for *every* vertex. + double abs_delta = std::fabs(group_delta_); + double arcTol = (arc_tolerance_ > floating_point_tolerance ? + std::min(abs_delta, arc_tolerance_) : + std::log10(2 + abs_delta) * default_arc_tolerance); + double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); + step_sin_ = std::sin(2 * PI / steps_per_360); + step_cos_ = std::cos(2 * PI / steps_per_360); + if (group_delta_ < 0.0) step_sin_ = -step_sin_; + steps_per_rad_ = steps_per_360 / (2 * PI); + } + Point64 pt = path[j]; PointD offsetVec = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_); @@ -287,7 +303,7 @@ void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_)); } -void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k) +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) @@ -302,12 +318,23 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k) if (sin_a > 1.0) sin_a = 1.0; else if (sin_a < -1.0) sin_a = -1.0; - if (cos_a > -0.99 && (sin_a * group_delta_ < 0)) + if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, j, k); + if (std::fabs(group_delta_) <= floating_point_tolerance) + { + group.path.push_back(path[j]); + return; + } + + if (cos_a > 0.999) // almost straight - less than 2.5 degree (#424, #526) + { + DoMiter(group, path, j, k, cos_a); + } + else if (cos_a > -0.99 && (sin_a * group_delta_ < 0)) { // is concave group.path.push_back(GetPerpendic(path[j], norms[k], group_delta_)); // this extra point is the only (simple) way to ensure that - // path reversals are fully cleaned with the trailing clipper + // path reversals are fully cleaned with the trailing clipper group.path.push_back(path[j]); // (#405) group.path.push_back(GetPerpendic(path[j], norms[j], group_delta_)); } @@ -317,22 +344,16 @@ void ClipperOffset::OffsetPoint(Group& group, Path64& path, size_t j, size_t& k) if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a); else DoSquare(group, path, j, k); } - else if (cos_a > 0.9998) - // almost straight - less than 1 degree (#424) - DoMiter(group, path, j, k, cos_a); - else if (cos_a > 0.99 || join_type_ == JoinType::Square) - //angle less than 8 degrees or squared joins + else if (cos_a > 0.99 || join_type_ == JoinType::Square) // 0.99 ~= 8.1 deg. DoSquare(group, path, j, k); else DoRound(group, path, j, k, std::atan2(sin_a, cos_a)); - - k = j; } void ClipperOffset::OffsetPolygon(Group& group, Path64& path) { - for (Path64::size_type i = 0, j = path.size() -1; i < path.size(); j = i, ++i) - OffsetPoint(group, path, i, j); + for (Path64::size_type j = 0, k = path.size() -1; j < path.size(); k = j, ++j) + OffsetPoint(group, path, j, k); group.paths_out.push_back(group.path); } @@ -354,34 +375,40 @@ void ClipperOffset::OffsetOpenJoined(Group& group, Path64& path) void ClipperOffset::OffsetOpenPath(Group& group, Path64& path) { // do the line start cap - switch (end_type_) + if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0); + + if (std::fabs(group_delta_) <= floating_point_tolerance) + group.path.push_back(path[0]); + else { - case EndType::Butt: + switch (end_type_) + { + case EndType::Butt: #ifdef USINGZ - group.path.push_back(Point64( - path[0].x - norms[0].x * group_delta_, - path[0].y - norms[0].y * group_delta_, - path[0].z)); + group.path.push_back(Point64( + path[0].x - norms[0].x * group_delta_, + path[0].y - norms[0].y * group_delta_, + path[0].z)); #else - 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(Point64( + path[0].x - norms[0].x * group_delta_, + path[0].y - norms[0].y * group_delta_)); #endif - 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; + 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); + for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j) + OffsetPoint(group, path, j, k); // reverse normals for (size_t i = highI; i > 0; --i) @@ -389,31 +416,39 @@ void ClipperOffset::OffsetOpenPath(Group& group, Path64& path) norms[0] = norms[highI]; // do the line end cap - switch (end_type_) + if (deltaCallback64_) + group_delta_ = deltaCallback64_(path, norms, highI, highI); + + if (std::fabs(group_delta_) <= floating_point_tolerance) + group.path.push_back(path[highI]); + else { - case EndType::Butt: + switch (end_type_) + { + case EndType::Butt: #ifdef USINGZ - group.path.push_back(Point64( - path[highI].x - norms[highI].x * group_delta_, - path[highI].y - norms[highI].y * group_delta_, - path[highI].z)); + group.path.push_back(Point64( + path[highI].x - norms[highI].x * group_delta_, + path[highI].y - norms[highI].y * group_delta_, + path[highI].z)); #else - 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(Point64( + path[highI].x - norms[highI].x * group_delta_, + path[highI].y - norms[highI].y * group_delta_)); #endif - 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; + 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); + for (size_t j = highI, k = 0; j > 0; k = j, --j) + OffsetPoint(group, path, j, k); group.paths_out.push_back(group.path); } @@ -439,10 +474,10 @@ void ClipperOffset::DoGroupOffset(Group& group) group.is_reversed = false; group_delta_ = std::abs(delta_) * 0.5; } - abs_group_delta_ = std::fabs(group_delta_); + double abs_delta = std::fabs(group_delta_); // do range checking - if (!IsSafeOffset(r, abs_group_delta_)) + if (!IsSafeOffset(r, abs_delta)) { DoError(range_error_i); error_code_ |= range_error_i; @@ -452,24 +487,23 @@ void ClipperOffset::DoGroupOffset(Group& group) join_type_ = group.join_type; end_type_ = group.end_type; - //calculate a sensible number of steps (for 360 deg for the given offset - if (group.join_type == JoinType::Round || group.end_type == EndType::Round) + if (!deltaCallback64_ && + (group.join_type == JoinType::Round || group.end_type == EndType::Round)) { + //calculate a sensible number of steps (for 360 deg for the given offset) // arcTol - when arc_tolerance_ is undefined (0), the amount of // curve imprecision that's allowed is based on the size of the // offset (delta). Obviously very large offsets will almost always // require much less precision. See also offset_triginometry2.svg double arcTol = (arc_tolerance_ > floating_point_tolerance ? - std::min(abs_group_delta_, arc_tolerance_) : - std::log10(2 + abs_group_delta_) * default_arc_tolerance); - double steps_per_360 = PI / std::acos(1 - arcTol / abs_group_delta_); - if (steps_per_360 > abs_group_delta_ * PI) - steps_per_360 = abs_group_delta_ * PI; //ie avoids excessive precision + std::min(abs_delta, arc_tolerance_) : + std::log10(2 + abs_delta) * default_arc_tolerance); + double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); step_sin_ = std::sin(2 * PI / steps_per_360); step_cos_ = std::cos(2 * PI / steps_per_360); - if (group_delta_ < 0.0) step_sin_ = -step_sin_; - steps_per_rad_ = steps_per_360 / (2 *PI); + if (group_delta_ < 0.0) step_sin_ = -step_sin_; + steps_per_rad_ = steps_per_360 / (2 * PI); } bool is_joined = @@ -478,7 +512,7 @@ void ClipperOffset::DoGroupOffset(Group& group) Paths64::iterator path_iter; for(path_iter = group.paths_in.begin(); path_iter != group.paths_in.end(); ++path_iter) { - auto path = *path_iter; + Path64 &path = *path_iter; StripDuplicates(path, is_joined); Path64::size_type cnt = path.size(); if (cnt == 0 || ((cnt < 3) && group.end_type == EndType::Polygon)) @@ -491,7 +525,7 @@ void ClipperOffset::DoGroupOffset(Group& group) //single vertex so build a circle or square ... if (group.join_type == JoinType::Round) { - double radius = abs_group_delta_; + double radius = abs_delta; group.path = Ellipse(path[0], radius, radius); #ifdef USINGZ for (auto& p : group.path) p.z = path[0].z; @@ -499,7 +533,7 @@ void ClipperOffset::DoGroupOffset(Group& group) } else { - int d = (int)std::ceil(abs_group_delta_); + int d = (int)std::ceil(abs_delta); r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d); group.path = r.AsPath(); #ifdef USINGZ @@ -568,6 +602,7 @@ void ClipperOffset::Execute(double delta, Paths64& paths) if (!solution.size()) return; paths = solution; + /**/ //clean up self-intersections ... Clipper64 c; c.PreserveCollinear = false; @@ -583,6 +618,7 @@ void ClipperOffset::Execute(double delta, Paths64& paths) c.Execute(ClipType::Union, FillRule::Negative, paths); else c.Execute(ClipType::Union, FillRule::Positive, paths); + /**/ } @@ -610,4 +646,10 @@ void ClipperOffset::Execute(double delta, PolyTree64& polytree) c.Execute(ClipType::Union, FillRule::Positive, polytree); } +void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths) +{ + deltaCallback64_ = delta_cb; + Execute(1.0, paths); +} + } // namespace diff --git a/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp b/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp index 54e750f815..49cb21ec0c 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 : 22 April 2023 * +* Date : 30 May 2023 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2023 * * Purpose : FAST rectangular clipping * @@ -281,7 +281,7 @@ namespace Clipper2Lib { // RectClip64 //---------------------------------------------------------------------------- - OutPt2* RectClip::Add(Point64 pt, bool start_new) + OutPt2* RectClip64::Add(Point64 pt, bool start_new) { // this method is only called by InternalExecute. // Later splitting & rejoining won't create additional op's, @@ -312,7 +312,7 @@ namespace Clipper2Lib { return result; } - void RectClip::AddCorner(Location prev, Location curr) + void RectClip64::AddCorner(Location prev, Location curr) { if (HeadingClockwise(prev, curr)) Add(rect_as_path_[static_cast(prev)]); @@ -320,7 +320,7 @@ namespace Clipper2Lib { Add(rect_as_path_[static_cast(curr)]); } - void RectClip::AddCorner(Location& loc, bool isClockwise) + void RectClip64::AddCorner(Location& loc, bool isClockwise) { if (isClockwise) { @@ -334,7 +334,7 @@ namespace Clipper2Lib { } } - void RectClip::GetNextLocation(const Path64& path, + void RectClip64::GetNextLocation(const Path64& path, Location& loc, int& i, int highI) { switch (loc) @@ -389,7 +389,7 @@ namespace Clipper2Lib { } //switch } - void RectClip::ExecuteInternal(const Path64& path) + void RectClip64::ExecuteInternal(const Path64& path) { int i = 0, highI = static_cast(path.size()) - 1; Location prev = Location::Inside, loc; @@ -546,7 +546,7 @@ namespace Clipper2Lib { } } - void RectClip::CheckEdges() + void RectClip64::CheckEdges() { for (size_t i = 0; i < results_.size(); ++i) { @@ -606,7 +606,7 @@ namespace Clipper2Lib { } } - void RectClip::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw) + void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw) { if (ccw.empty()) return; bool isHorz = ((idx == 1) || (idx == 3)); @@ -784,7 +784,7 @@ namespace Clipper2Lib { } } - Path64 RectClip::GetPath(OutPt2*& op) + Path64 RectClip64::GetPath(OutPt2*& op) { if (!op || op->next == op->prev) return Path64(); @@ -814,7 +814,7 @@ namespace Clipper2Lib { return result; } - Paths64 RectClip::Execute(const Paths64& paths, bool convex_only) + Paths64 RectClip64::Execute(const Paths64& paths) { Paths64 result; if (rect_.IsEmpty()) return result; @@ -833,13 +833,10 @@ namespace Clipper2Lib { } ExecuteInternal(path); - if (!convex_only) - { - CheckEdges(); - for (int i = 0; i < 4; ++i) - TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]); - } - + CheckEdges(); + for (int i = 0; i < 4; ++i) + TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]); + for (OutPt2*& op : results_) { Path64 tmp = GetPath(op); @@ -857,10 +854,10 @@ namespace Clipper2Lib { } //------------------------------------------------------------------------------ - // RectClipLines + // RectClipLines64 //------------------------------------------------------------------------------ - Paths64 RectClipLines::Execute(const Paths64& paths) + Paths64 RectClipLines64::Execute(const Paths64& paths) { Paths64 result; if (rect_.IsEmpty()) return result; @@ -886,7 +883,7 @@ namespace Clipper2Lib { return result; } - void RectClipLines::ExecuteInternal(const Path64& path) + void RectClipLines64::ExecuteInternal(const Path64& path) { if (rect_.IsEmpty() || path.size() < 2) return; @@ -956,7 +953,7 @@ namespace Clipper2Lib { /////////////////////////////////////////////////// } - Path64 RectClipLines::GetPath(OutPt2*& op) + Path64 RectClipLines64::GetPath(OutPt2*& op) { Path64 result; if (!op || op == op->next) return result;