From 209d292934dd56456c1abc9d7f0b5bf32c2e7051 Mon Sep 17 00:00:00 2001 From: aismann Date: Sat, 25 Jan 2025 19:15:50 +0100 Subject: [PATCH 1/2] Clipper2 1.5.0 --- 3rdparty/README.md | 2 +- .../clipper2/include/clipper2/clipper.core.h | 35 +- .../include/clipper2/clipper.engine.h | 18 +- .../include/clipper2/clipper.export.h | 477 +++++++++++++----- .../include/clipper2/clipper.offset.h | 6 +- .../include/clipper2/clipper.version.h | 2 +- 3rdparty/clipper2/src/clipper.engine.cpp | 42 +- 3rdparty/clipper2/src/clipper.offset.cpp | 86 ++-- 8 files changed, 471 insertions(+), 197 deletions(-) diff --git a/3rdparty/README.md b/3rdparty/README.md index 335a584537ea..4e04a60f11be 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -32,7 +32,7 @@ ## Clipper2 - [![Upstream](https://img.shields.io/github/v/tag/AngusJohnson/Clipper2?label=Upstream)](https://github.com/AngusJohnson/Clipper2) -- Version: 1.4.0 +- Version: 1.5.0 - License: BSL-1.0 ## ConcurrentQueue diff --git a/3rdparty/clipper2/include/clipper2/clipper.core.h b/3rdparty/clipper2/include/clipper2/clipper.core.h index 925c04685e81..624de9032625 100644 --- a/3rdparty/clipper2/include/clipper2/clipper.core.h +++ b/3rdparty/clipper2/include/clipper2/clipper.core.h @@ -31,7 +31,7 @@ namespace Clipper2Lib public: explicit Clipper2Exception(const char* description) : m_descr(description) {} - virtual const char* what() const throw() override { return m_descr.c_str(); } + virtual const char* what() const noexcept override { return m_descr.c_str(); } private: std::string m_descr; }; @@ -88,6 +88,9 @@ namespace Clipper2Lib throw Clipper2Exception(undefined_error); case range_error_i: throw Clipper2Exception(range_error); + // Should never happen, but adding this to stop a compiler warning + default: + throw Clipper2Exception("Unknown error"); } #else ++error_code; // only to stop compiler warning @@ -107,6 +110,10 @@ namespace Clipper2Lib //https://en.wikipedia.org/wiki/Nonzero-rule enum class FillRule { EvenOdd, NonZero, Positive, Negative }; +#ifdef USINGZ + using z_type = int64_t; +#endif + // Point ------------------------------------------------------------------------ template @@ -114,10 +121,10 @@ namespace Clipper2Lib T x; T y; #ifdef USINGZ - int64_t z; + z_type z; template - inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0) + inline void Init(const T2 x_ = 0, const T2 y_ = 0, const z_type z_ = 0) { if constexpr (std::is_integral_v && is_round_invocable::value && !std::is_integral_v) @@ -137,7 +144,7 @@ namespace Clipper2Lib explicit Point() : x(0), y(0), z(0) {}; template - Point(const T2 x_, const T2 y_, const int64_t z_ = 0) + Point(const T2 x_, const T2 y_, const z_type z_ = 0) { Init(x_, y_); z = z_; @@ -150,7 +157,7 @@ namespace Clipper2Lib } template - explicit Point(const Point& p, int64_t z_) + explicit Point(const Point& p, z_type z_) { Init(p.x, p.y, z_); } @@ -160,7 +167,7 @@ namespace Clipper2Lib return Point(x * scale, y * scale, z); } - void SetZ(const int64_t z_value) { z = z_value; } + void SetZ(const z_type z_value) { z = z_value; } friend std::ostream& operator<<(std::ostream& os, const Point& point) { @@ -362,6 +369,22 @@ namespace Clipper2Lib top == other.top && bottom == other.bottom; } + Rect& operator+=(const Rect& other) + { + left = (std::min)(left, other.left); + top = (std::min)(top, other.top); + right = (std::max)(right, other.right); + bottom = (std::max)(bottom, other.bottom); + return *this; + } + + Rect operator+(const Rect& other) const + { + Rect result = *this; + result += other; + return result; + } + friend std::ostream& operator<<(std::ostream& os, const Rect& rect) { os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") "; return os; diff --git a/3rdparty/clipper2/include/clipper2/clipper.engine.h b/3rdparty/clipper2/include/clipper2/clipper.engine.h index f6108832cd5b..8129beb01e76 100644 --- a/3rdparty/clipper2/include/clipper2/clipper.engine.h +++ b/3rdparty/clipper2/include/clipper2/clipper.engine.h @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 5 July 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -32,13 +32,13 @@ namespace Clipper2Lib { struct HorzSegment; //Note: all clipping operations except for Difference are commutative. - enum class ClipType { None, Intersection, Union, Difference, Xor }; + enum class ClipType { NoClip, Intersection, Union, Difference, Xor }; enum class PathType { Subject, Clip }; - enum class JoinWith { None, Left, Right }; + enum class JoinWith { NoJoin, Left, Right }; enum class VertexFlags : uint32_t { - None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 + Empty = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 }; constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) @@ -55,7 +55,7 @@ namespace Clipper2Lib { Point64 pt; Vertex* next = nullptr; Vertex* prev = nullptr; - VertexFlags flags = VertexFlags::None; + VertexFlags flags = VertexFlags::Empty; }; struct OutPt { @@ -131,7 +131,7 @@ namespace Clipper2Lib { Vertex* vertex_top = nullptr; LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti) bool is_left_bound = false; - JoinWith join_with = JoinWith::None; + JoinWith join_with = JoinWith::NoJoin; }; struct LocalMinima { @@ -167,7 +167,7 @@ namespace Clipper2Lib { }; #ifdef USINGZ - typedef std::function ZCallback64; typedef std::function vertex_lists_; std::priority_queue scanline_list_; IntersectNodeList intersect_nodes_; - HorzSegmentList horz_seg_list_; + HorzSegmentList horz_seg_list_; std::vector horz_join_list_; void Reset(); inline void InsertScanline(int64_t y); diff --git a/3rdparty/clipper2/include/clipper2/clipper.export.h b/3rdparty/clipper2/include/clipper2/clipper.export.h index 53a445368e41..1642d1f50857 100644 --- a/3rdparty/clipper2/include/clipper2/clipper.export.h +++ b/3rdparty/clipper2/include/clipper2/clipper.export.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 14 May 2024 * +* Date : 24 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -10,7 +10,7 @@ /* Boolean clipping: - cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4 + cliptype: NoClip=0, Intersection=1, Union=2, Difference=3, Xor=4 fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3 Polygon offsetting (inflate/deflate): @@ -19,69 +19,101 @@ The path structures used extensively in other parts of this library are all based on std::vector classes. Since C++ classes can't be accessed by other -languages, these paths are converted into very simple array data structures -(of either int64_t for CPath64 or double for CPathD) that can be parsed by -just about any programming language. +languages, these paths are exported here as very simple array structures +(either of int64_t or double) that can be parsed by just about any +programming language. + +These 2D paths are defined by series of x and y coordinates together with an +optional user-defined 'z' value (see Z-values below). Hence, a vertex refers +to a single x and y coordinate (+/- a user-defined value). Data structures +have names with suffixes that indicate the array type (either int64_t or +double). For example, the data structure CPath64 contains an array of int64_t +values, whereas the data structure CPathD contains an array of double. +Where documentation omits the type suffix (eg CPath), it is referring to an +array whose data type could be either int64_t or double. + +For conciseness, the following letters are used in the diagrams below: +N: Number of vertices in a given path +C: Count (ie number) of paths (or PolyPaths) in the structure +A: Number of elements in an array + CPath64 and CPathD: -These are arrays of consecutive x and y path coordinates preceeded by -a pair of values containing the path's length (N) and a 0 value. -__________________________________ -|counter|coord1|coord2|...|coordN| -|N, 0 |x1, y1|x2, y2|...|xN, yN| -__________________________________ +These are arrays of either int64_t or double values. Apart from +the first two elements, these arrays are a series of vertices +that together define a path. The very first element contains the +number of vertices (N) in the path, while second element should +contain a 0 value. +_______________________________________________________________ +| counters | vertex1 | vertex2 | ... | vertexN | +| N, 0 | x1, y1, (z1) | x2, y2, (z2) | ... | xN, yN, (zN) | +--------------------------------------------------------------- + CPaths64 and CPathsD: -These are also arrays containing any number of consecutive CPath64 or -CPathD structures. But preceeding these consecutive paths, there is pair of -values that contain the total length of the array structure (A) and the -number of CPath64 or CPathD it contains (C). The space these structures will -occupy in memory = A * sizeof(int64_t) or A * sizeof(double) respectively. -_______________________________ -|counter|path1|path2|...|pathC| -|A , C | | -_______________________________ +These are also arrays of either int64_t or double values that +contain any number of consecutive CPath structures. However, +preceeding the first path is a pair of values. The first value +contains the length of the entire array structure (A), and the +second contains the number (ie count) of contained paths (C). + Memory allocation for CPaths64 = A * sizeof(int64_t) + Memory allocation for CPathsD = A * sizeof(double) +__________________________________________ +| counters | path1 | path2 | ... | pathC | +| A, C | | | ... | | +------------------------------------------ + CPolytree64 and CPolytreeD: -These are also arrays consisting of CPolyPath structures that represent -individual paths in a tree structure. However, the very first (ie top) -CPolyPath is just the tree container that doesn't have a path. And because -of that, its structure will be very slightly different from the remaining -CPolyPath. This difference will be discussed below. +The entire polytree structure is an array of int64_t or double. The +first element in the array indicates the array's total length (A). +The second element indicates the number (C) of CPolyPath structures +that are the TOP LEVEL CPolyPath in the polytree, and these top +level CPolyPath immediately follow these first two array elements. +These top level CPolyPath structures may, in turn, contain nested +CPolyPath children, and these collectively make a tree structure. +_________________________________________________________ +| counters | CPolyPath1 | CPolyPath2 | ... | CPolyPathC | +| A, C | | | ... | | +--------------------------------------------------------- -CPolyPath64 and CPolyPathD: -These are simple arrays consisting of a series of path coordinates followed -by any number of child (ie nested) CPolyPath. Preceeding these are two values -indicating the length of the path (N) and the number of child CPolyPath (C). -____________________________________________________________ -|counter|coord1|coord2|...|coordN| child1|child2|...|childC| -|N , C |x1, y1|x2, y2|...|xN, yN| | -____________________________________________________________ - -As mentioned above, the very first CPolyPath structure is just a container -that owns (both directly and indirectly) every other CPolyPath in the tree. -Since this first CPolyPath has no path, instead of a path length, its very -first value will contain the total length of the CPolytree array (not its -total bytes length). - -Again, all theses exported structures (CPaths64, CPathsD, CPolyTree64 & -CPolyTreeD) are arrays of either type int64_t or double, and the first -value in these arrays will always be the length of that array. - -These array structures are allocated in heap memory which will eventually -need to be released. However, since applications dynamically linking to -these functions may use different memory managers, the only safe way to -free up this memory is to use the exported DisposeArray64 and -DisposeArrayD functions (see below). -*/ +CPolyPath64 and CPolyPathD: +These array structures consist of a pair of counter values followed by a +series of polygon vertices and a series of nested CPolyPath children. +The first counter values indicates the number of vertices in the +polygon (N), and the second counter indicates the CPolyPath child count (C). +_____________________________________________________________________________ +|cntrs |vertex1 |vertex2 |...|vertexN |child1|child2|...|childC| +|N, C |x1, y1, (z1)| x2, y2, (z2)|...|xN, yN, (zN)| | |...| | +----------------------------------------------------------------------------- + + +DisposeArray64 & DisposeArrayD: +All array structures are allocated in heap memory which will eventually +need to be released. However, since applications linking to these DLL +functions may use different memory managers, the only safe way to release +this memory is to use the exported DisposeArray functions. + + +(Optional) Z-Values: +Structures will only contain user-defined z-values when the USINGZ +pre-processor identifier is used. The library does not assign z-values +because this field is intended for users to assign custom values to vertices. +Z-values in input paths (subject and clip) will be copied to solution paths. +New vertices at path intersections will generate a callback event that allows +users to assign z-values at these new vertices. The user's callback function +must conform with the DLLZCallback definition and be registered with the +DLL via SetZCallback. To assist the user in assigning z-values, the library +passes in the callback function the new intersection point together with +the four vertices that define the two segments that are intersecting. +*/ #ifndef CLIPPER2_EXPORT_H #define CLIPPER2_EXPORT_H #include #include - #include "clipper2/clipper.core.h" #include "clipper2/clipper.engine.h" #include "clipper2/clipper.offset.h" @@ -127,6 +159,12 @@ inline Rect CRectToRect(const CRect& rect) return result; } +template +inline T1 Reinterpret(T2 value) { + return *reinterpret_cast(&value); +} + + #ifdef _WIN32 #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) #else @@ -178,11 +216,22 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, double delta, uint8_t jointype, uint8_t endtype, double miter_limit = 2.0, double arc_tolerance = 0.0, bool reverse_solution = false); + EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, double delta, uint8_t jointype, uint8_t endtype, int precision = 2, double miter_limit = 2.0, double arc_tolerance = 0.0, bool reverse_solution = false); +EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path, + double delta, uint8_t jointype, uint8_t endtype, + double miter_limit = 2.0, double arc_tolerance = 0.0, + bool reverse_solution = false); + +EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path, + double delta, uint8_t jointype, uint8_t endtype, + int precision = 2, double miter_limit = 2.0, + double arc_tolerance = 0.0, bool reverse_solution = false); + // RectClip & RectClipLines: EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths); @@ -197,6 +246,15 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, // INTERNAL FUNCTIONS ////////////////////////////////////////////////////// +#ifdef USINGZ +ZCallback64 dllCallback64 = nullptr; +ZCallbackD dllCallbackD = nullptr; + +constexpr int EXPORT_VERTEX_DIMENSIONALITY = 3; +#else +constexpr int EXPORT_VERTEX_DIMENSIONALITY = 2; +#endif + template static void GetPathCountAndCPathsArrayLen(const Paths& paths, size_t& cnt, size_t& array_len) @@ -206,30 +264,47 @@ static void GetPathCountAndCPathsArrayLen(const Paths& paths, for (const Path& path : paths) if (path.size()) { - array_len += path.size() * 2 + 2; + array_len += path.size() * EXPORT_VERTEX_DIMENSIONALITY + 2; ++cnt; } } -static size_t GetPolyPath64ArrayLen(const PolyPath64& pp) +static size_t GetPolyPathArrayLen64(const PolyPath64& pp) +{ + size_t result = 2; // poly_length + child_count + result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY; + //plus nested children :) + for (size_t i = 0; i < pp.Count(); ++i) + result += GetPolyPathArrayLen64(*pp[i]); + return result; +} + +static size_t GetPolyPathArrayLenD(const PolyPathD& pp) { size_t result = 2; // poly_length + child_count - result += pp.Polygon().size() * 2; + result += pp.Polygon().size() * EXPORT_VERTEX_DIMENSIONALITY; //plus nested children :) for (size_t i = 0; i < pp.Count(); ++i) - result += GetPolyPath64ArrayLen(*pp[i]); + result += GetPolyPathArrayLenD(*pp[i]); return result; } -static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree, +static void GetPolytreeCountAndCStorageSize64(const PolyTree64& tree, size_t& cnt, size_t& array_len) { cnt = tree.Count(); // nb: top level count only - array_len = GetPolyPath64ArrayLen(tree); + array_len = GetPolyPathArrayLen64(tree); +} + +static void GetPolytreeCountAndCStorageSizeD(const PolyTreeD& tree, + size_t& cnt, size_t& array_len) +{ + cnt = tree.Count(); // nb: top level count only + array_len = GetPolyPathArrayLenD(tree); } template -static T* CreateCPaths(const Paths& paths) +static T* CreateCPathsFromPathsT(const Paths& paths) { size_t cnt = 0, array_len = 0; GetPathCountAndCPathsArrayLen(paths, cnt, array_len); @@ -245,11 +320,38 @@ static T* CreateCPaths(const Paths& paths) { *v++ = pt.x; *v++ = pt.y; +#ifdef USINGZ + *v++ = Reinterpret(pt.z); +#endif } } return result; } +CPathsD CreateCPathsDFromPathsD(const PathsD& paths) +{ + if (!paths.size()) return nullptr; + size_t cnt, array_len; + GetPathCountAndCPathsArrayLen(paths, cnt, array_len); + CPathsD result = new double[array_len], v = result; + *v++ = (double)array_len; + *v++ = (double)cnt; + for (const PathD& path : paths) + { + if (!path.size()) continue; + *v = (double)path.size(); + ++v; *v++ = 0; + for (const PointD& pt : path) + { + *v++ = pt.x; + *v++ = pt.y; +#ifdef USINGZ + * v++ = Reinterpret(pt.z); +#endif + } + } + return result; +} CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale) { @@ -268,13 +370,16 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale) { *v++ = pt.x * scale; *v++ = pt.y * scale; +#ifdef USINGZ + *v++ = Reinterpret(pt.z); +#endif } } return result; } template -static Path ConvertCPath(T* path) +static Path ConvertCPathToPathT(T* path) { Path result; if (!path) return result; @@ -284,14 +389,19 @@ static Path ConvertCPath(T* path) result.reserve(cnt); for (size_t j = 0; j < cnt; ++j) { - T x = *v++, y = *v++; - result.push_back(Point(x, y)); + T x = *v++, y = *v++; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + result.push_back(Point(x, y, z)); +#else + result.push_back(Point(x, y)); +#endif } return result; } template -static Paths ConvertCPaths(T* paths) +static Paths ConvertCPathsToPathsT(T* paths) { Paths result; if (!paths) return result; @@ -301,19 +411,45 @@ static Paths ConvertCPaths(T* paths) for (size_t i = 0; i < cnt; ++i) { size_t cnt2 = static_cast(*v); - v += 2; + v += 2; Path path; path.reserve(cnt2); for (size_t j = 0; j < cnt2; ++j) { T x = *v++, y = *v++; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + path.push_back(Point(x, y, z)); +#else path.push_back(Point(x, y)); +#endif } result.push_back(path); } return result; } +static Path64 ConvertCPathDToPath64WithScale(const CPathD path, double scale) +{ + Path64 result; + if (!path) return result; + double* v = path; + size_t cnt = static_cast(*v); + v += 2; // skip 0 value + result.reserve(cnt); + for (size_t j = 0; j < cnt; ++j) + { + double x = *v++ * scale; + double y = *v++ * scale; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + result.push_back(Point64(x, y, z)); +#else + result.push_back(Point64(x, y)); +#endif + } + return result; +} static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) { @@ -333,42 +469,78 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) { double x = *v++ * scale; double y = *v++ * scale; +#ifdef USINGZ + z_type z = Reinterpret(*v++); + path.push_back(Point64(x, y, z)); +#else path.push_back(Point64(x, y)); +#endif } result.push_back(path); } return result; } -template -static void CreateCPolyPath(const PolyPath64* pp, T*& v, T scale) +static void CreateCPolyPath64(const PolyPath64* pp, int64_t*& v) { - *v++ = static_cast(pp->Polygon().size()); - *v++ = static_cast(pp->Count()); + *v++ = static_cast(pp->Polygon().size()); + *v++ = static_cast(pp->Count()); for (const Point64& pt : pp->Polygon()) { - *v++ = static_cast(pt.x * scale); - *v++ = static_cast(pt.y * scale); + *v++ = pt.x; + *v++ = pt.y; +#ifdef USINGZ + * v++ = Reinterpret(pt.z); // raw memory copy +#endif } for (size_t i = 0; i < pp->Count(); ++i) - CreateCPolyPath(pp->Child(i), v, scale); + CreateCPolyPath64(pp->Child(i), v); } -template -static T* CreateCPolyTree(const PolyTree64& tree, T scale) +static void CreateCPolyPathD(const PolyPathD* pp, double*& v) +{ + *v++ = static_cast(pp->Polygon().size()); + *v++ = static_cast(pp->Count()); + for (const PointD& pt : pp->Polygon()) + { + *v++ = pt.x; + *v++ = pt.y; +#ifdef USINGZ + * v++ = Reinterpret(pt.z); // raw memory copy +#endif + } + for (size_t i = 0; i < pp->Count(); ++i) + CreateCPolyPathD(pp->Child(i), v); +} + +static int64_t* CreateCPolyTree64(const PolyTree64& tree) { - if (scale == 0) scale = 1; size_t cnt, array_len; - GetPolytreeCountAndCStorageSize(tree, cnt, array_len); + GetPolytreeCountAndCStorageSize64(tree, cnt, array_len); if (!cnt) return nullptr; // allocate storage - T* result = new T[array_len]; - T* v = result; + int64_t* result = new int64_t[array_len]; + int64_t* v = result; + *v++ = static_cast(array_len); + *v++ = static_cast(tree.Count()); + for (size_t i = 0; i < tree.Count(); ++i) + CreateCPolyPath64(tree.Child(i), v); + return result; +} - *v++ = static_cast(array_len); - *v++ = static_cast(tree.Count()); +static double* CreateCPolyTreeD(const PolyTreeD& tree) +{ + double scale = std::log10(tree.Scale()); + size_t cnt, array_len; + GetPolytreeCountAndCStorageSizeD(tree, cnt, array_len); + if (!cnt) return nullptr; + // allocate storage + double* result = new double[array_len]; + double* v = result; + *v++ = static_cast(array_len); + *v++ = static_cast(tree.Count()); for (size_t i = 0; i < tree.Count(); ++i) - CreateCPolyPath(tree.Child(i), v, scale); + CreateCPolyPathD(tree.Child(i), v); return result; } @@ -391,20 +563,24 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, if (fillrule > static_cast(FillRule::Negative)) return -3; Paths64 sub, sub_open, clp, sol, sol_open; - sub = ConvertCPaths(subjects); - sub_open = ConvertCPaths(subjects_open); - clp = ConvertCPaths(clips); + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); Clipper64 clipper; clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallback64) + clipper.SetZCallback(dllCallback64); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) return -1; // clipping bug - should never happen :) - solution = CreateCPaths(sol); - solution_open = CreateCPaths(sol_open); + solution = CreateCPathsFromPathsT(sol); + solution_open = CreateCPathsFromPathsT(sol_open); return 0; //success !! } @@ -417,22 +593,26 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype, if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; Paths64 sub, sub_open, clp, sol_open; - sub = ConvertCPaths(subjects); - sub_open = ConvertCPaths(subjects_open); - clp = ConvertCPaths(clips); + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); PolyTree64 tree; Clipper64 clipper; clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallback64) + clipper.SetZCallback(dllCallback64); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open)) return -1; // clipping bug - should never happen :) - sol_tree = CreateCPolyTree(tree, (int64_t)1); - solution_open = CreateCPaths(sol_open); + sol_tree = CreateCPolyTree64(tree); + solution_open = CreateCPathsFromPathsT(sol_open); return 0; //success !! } @@ -445,23 +625,27 @@ EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype, if (precision < -8 || precision > 8) return -5; if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; - const double scale = std::pow(10, precision); + //const double scale = std::pow(10, precision); - Paths64 sub, sub_open, clp, sol, sol_open; - sub = ConvertCPathsDToPaths64(subjects, scale); - sub_open = ConvertCPathsDToPaths64(subjects_open, scale); - clp = ConvertCPathsDToPaths64(clips, scale); + PathsD sub, sub_open, clp, sol, sol_open; + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); - Clipper64 clipper; + ClipperD clipper(precision); clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallbackD) + clipper.SetZCallback(dllCallbackD); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) return -1; - solution = CreateCPathsDFromPaths64(sol, 1 / scale); - solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale); + solution = CreateCPathsDFromPathsD(sol); + solution_open = CreateCPathsDFromPathsD(sol_open); return 0; } @@ -474,27 +658,30 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype, if (precision < -8 || precision > 8) return -5; if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; - - double scale = std::pow(10, precision); + //double scale = std::pow(10, precision); int err = 0; - Paths64 sub, sub_open, clp, sol_open; - sub = ConvertCPathsDToPaths64(subjects, scale); - sub_open = ConvertCPathsDToPaths64(subjects_open, scale); - clp = ConvertCPathsDToPaths64(clips, scale); + PathsD sub, sub_open, clp, sol_open; + sub = ConvertCPathsToPathsT(subjects); + sub_open = ConvertCPathsToPathsT(subjects_open); + clp = ConvertCPathsToPathsT(clips); - PolyTree64 tree; - Clipper64 clipper; + PolyTreeD tree; + ClipperD clipper(precision); clipper.PreserveCollinear(preserve_collinear); clipper.ReverseSolution(reverse_solution); +#ifdef USINGZ + if (dllCallbackD) + clipper.SetZCallback(dllCallbackD); +#endif if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), tree, sol_open)) return -1; // clipping bug - should never happen :) - solution = CreateCPolyTree(tree, 1/scale); - solution_open = CreateCPathsDFromPaths64(sol_open, 1 / scale); + solution = CreateCPolyTreeD(tree); + solution_open = CreateCPathsDFromPathsD(sol_open); return 0; //success !! } @@ -503,13 +690,13 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, double arc_tolerance, bool reverse_solution) { Paths64 pp; - pp = ConvertCPaths(paths); + pp = ConvertCPathsToPathsT(paths); ClipperOffset clip_offset( miter_limit, arc_tolerance, reverse_solution); clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); Paths64 result; clip_offset.Execute(delta, result); - return CreateCPaths(result); + return CreateCPathsFromPathsT(result); } EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, @@ -525,18 +712,49 @@ EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); Paths64 result; clip_offset.Execute(delta * scale, result); - return CreateCPathsDFromPaths64(result, 1 / scale); } + +EXTERN_DLL_EXPORT CPaths64 InflatePath64(const CPath64 path, + double delta, uint8_t jointype, uint8_t endtype, double miter_limit, + double arc_tolerance, bool reverse_solution) +{ + Path64 pp; + pp = ConvertCPathToPathT(path); + ClipperOffset clip_offset(miter_limit, + arc_tolerance, reverse_solution); + clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype)); + Paths64 result; + clip_offset.Execute(delta, result); + return CreateCPathsFromPathsT(result); +} + +EXTERN_DLL_EXPORT CPathsD InflatePathD(const CPathD path, + double delta, uint8_t jointype, uint8_t endtype, + int precision, double miter_limit, + double arc_tolerance, bool reverse_solution) +{ + if (precision < -8 || precision > 8 || !path) return nullptr; + + const double scale = std::pow(10, precision); + ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution); + Path64 pp = ConvertCPathDToPath64WithScale(path, scale); + clip_offset.AddPath(pp, JoinType(jointype), EndType(endtype)); + Paths64 result; + clip_offset.Execute(delta * scale, result); + + return CreateCPathsDFromPaths64(result, 1 / scale); +} + EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, const CPaths64 paths) { if (CRectIsEmpty(rect) || !paths) return nullptr; Rect64 r64 = CRectToRect(rect); class RectClip64 rc(r64); - Paths64 pp = ConvertCPaths(paths); + Paths64 pp = ConvertCPathsToPathsT(paths); Paths64 result = rc.Execute(pp); - return CreateCPaths(result); + return CreateCPathsFromPathsT(result); } EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, const CPathsD paths, int precision) @@ -560,9 +778,9 @@ EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, if (CRectIsEmpty(rect) || !paths) return nullptr; Rect64 r = CRectToRect(rect); class RectClipLines64 rcl (r); - Paths64 pp = ConvertCPaths(paths); + Paths64 pp = ConvertCPathsToPathsT(paths); Paths64 result = rcl.Execute(pp); - return CreateCPaths(result); + return CreateCPathsFromPathsT(result); } EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, @@ -581,20 +799,35 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) { - Path64 path = ConvertCPath(cpath); - Path64 pattern = ConvertCPath(cpattern); + Path64 path = ConvertCPathToPathT(cpath); + Path64 pattern = ConvertCPathToPathT(cpattern); Paths64 solution = MinkowskiSum(pattern, path, is_closed); - return CreateCPaths(solution); + return CreateCPathsFromPathsT(solution); } EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) { - Path64 path = ConvertCPath(cpath); - Path64 pattern = ConvertCPath(cpattern); + Path64 path = ConvertCPathToPathT(cpath); + Path64 pattern = ConvertCPathToPathT(cpattern); Paths64 solution = MinkowskiDiff(pattern, path, is_closed); - return CreateCPaths(solution); + return CreateCPathsFromPathsT(solution); } -} // end Clipper2Lib namespace +#ifdef USINGZ +typedef void (*DLLZCallback64)(const Point64& e1bot, const Point64& e1top, const Point64& e2bot, const Point64& e2top, Point64& pt); +typedef void (*DLLZCallbackD)(const PointD& e1bot, const PointD& e1top, const PointD& e2bot, const PointD& e2top, PointD& pt); + +EXTERN_DLL_EXPORT void SetZCallback64(DLLZCallback64 callback) +{ + dllCallback64 = callback; +} +EXTERN_DLL_EXPORT void SetZCallbackD(DLLZCallbackD callback) +{ + dllCallbackD = callback; +} + +#endif + +} #endif // CLIPPER2_EXPORT_H diff --git a/3rdparty/clipper2/include/clipper2/clipper.offset.h b/3rdparty/clipper2/include/clipper2/clipper.offset.h index bb075a6d4920..c0ec99ddd05c 100644 --- a/3rdparty/clipper2/include/clipper2/clipper.offset.h +++ b/3rdparty/clipper2/include/clipper2/clipper.offset.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 March 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -96,7 +96,7 @@ class ClipperOffset { void AddPaths(const Paths64& paths, JoinType jt_, EndType et_); void Clear() { groups_.clear(); norms.clear(); }; - void Execute(double delta, Paths64& paths); + void Execute(double delta, Paths64& sols_64); void Execute(double delta, PolyTree64& polytree); void Execute(DeltaCallback64 delta_cb, Paths64& paths); diff --git a/3rdparty/clipper2/include/clipper2/clipper.version.h b/3rdparty/clipper2/include/clipper2/clipper.version.h index 61464095f6b8..130b4c97b26c 100644 --- a/3rdparty/clipper2/include/clipper2/clipper.version.h +++ b/3rdparty/clipper2/include/clipper2/clipper.version.h @@ -1,6 +1,6 @@ #ifndef CLIPPER_VERSION_H #define CLIPPER_VERSION_H -constexpr auto CLIPPER2_VERSION = "1.4.0"; +constexpr auto CLIPPER2_VERSION = "1.5.0"; #endif // CLIPPER_VERSION_H diff --git a/3rdparty/clipper2/src/clipper.engine.cpp b/3rdparty/clipper2/src/clipper.engine.cpp index 8f120267c3cc..97717322c5ff 100644 --- a/3rdparty/clipper2/src/clipper.engine.cpp +++ b/3rdparty/clipper2/src/clipper.engine.cpp @@ -1,6 +1,6 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 27 April 2024 * +* Date : 17 September 2024 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * @@ -85,7 +85,7 @@ namespace Clipper2Lib { inline bool IsOpenEnd(const Vertex& v) { return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) != - VertexFlags::None; + VertexFlags::Empty; } @@ -220,7 +220,7 @@ namespace Clipper2Lib { inline bool IsMaxima(const Vertex& v) { - return ((v.flags & VertexFlags::LocalMax) != VertexFlags::None); + return ((v.flags & VertexFlags::LocalMax) != VertexFlags::Empty); } @@ -235,12 +235,12 @@ namespace Clipper2Lib { if (e.wind_dx > 0) while ((result->next->pt.y == result->pt.y) && ((result->flags & (VertexFlags::OpenEnd | - VertexFlags::LocalMax)) == VertexFlags::None)) + VertexFlags::LocalMax)) == VertexFlags::Empty)) result = result->next; else while (result->prev->pt.y == result->pt.y && ((result->flags & (VertexFlags::OpenEnd | - VertexFlags::LocalMax)) == VertexFlags::None)) + VertexFlags::LocalMax)) == VertexFlags::Empty)) result = result->prev; if (!IsMaxima(*result)) result = nullptr; // not a maxima return result; @@ -478,7 +478,7 @@ namespace Clipper2Lib { inline bool IsJoined(const Active& e) { - return e.join_with != JoinWith::None; + return e.join_with != JoinWith::NoJoin; } inline void SetOwner(OutRec* outrec, OutRec* new_owner) @@ -608,7 +608,7 @@ namespace Clipper2Lib { Vertex& vert, PathType polytype, bool is_open) { //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); list.push_back(std::make_unique (&vert, polytype, is_open)); @@ -643,7 +643,7 @@ namespace Clipper2Lib { } curr_v->prev = prev_v; curr_v->pt = pt; - curr_v->flags = VertexFlags::None; + curr_v->flags = VertexFlags::Empty; prev_v = curr_v++; cnt++; } @@ -725,7 +725,7 @@ namespace Clipper2Lib { void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open) { //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); @@ -907,7 +907,7 @@ namespace Clipper2Lib { void ClipperBase::AddLocMin(Vertex& vert, PathType polytype, bool is_open) { //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; + if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::Empty) return; vert.flags = (vert.flags | VertexFlags::LocalMin); minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); @@ -928,11 +928,14 @@ namespace Clipper2Lib { case FillRule::Negative: if (e.wind_cnt != -1) return false; break; + // Should never happen, but adding this to stop a compiler warning + default: + break; } switch (cliptype_) { - case ClipType::None: + case ClipType::NoClip: return false; case ClipType::Intersection: switch (fillrule_) @@ -978,6 +981,9 @@ namespace Clipper2Lib { break; case ClipType::Xor: return true; break; + // Should never happen, but adding this to stop a compiler warning + default: + break; } return false; // we should never get here } @@ -1208,7 +1214,7 @@ namespace Clipper2Lib { while (PopLocalMinima(bot_y, local_minima)) { - if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::None) + if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::Empty) { left_bound = nullptr; } @@ -1224,7 +1230,7 @@ namespace Clipper2Lib { SetDx(*left_bound); } - if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::None) + if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::Empty) { right_bound = nullptr; } @@ -2125,7 +2131,7 @@ namespace Clipper2Lib { using_polytree_ = use_polytrees; Reset(); int64_t y; - if (ct == ClipType::None || !PopScanline(y)) return true; + if (ct == ClipType::NoClip || !PopScanline(y)) return true; while (succeeded_) { @@ -2789,14 +2795,14 @@ namespace Clipper2Lib { { if (e.join_with == JoinWith::Right) { - e.join_with = JoinWith::None; - e.next_in_ael->join_with = JoinWith::None; + e.join_with = JoinWith::NoJoin; + e.next_in_ael->join_with = JoinWith::NoJoin; AddLocalMinPoly(e, *e.next_in_ael, pt, true); } else { - e.join_with = JoinWith::None; - e.prev_in_ael->join_with = JoinWith::None; + e.join_with = JoinWith::NoJoin; + e.prev_in_ael->join_with = JoinWith::NoJoin; AddLocalMinPoly(*e.prev_in_ael, e, pt, true); } } diff --git a/3rdparty/clipper2/src/clipper.offset.cpp b/3rdparty/clipper2/src/clipper.offset.cpp index 508a7f0831d1..7f54f28a25ae 100644 --- a/3rdparty/clipper2/src/clipper.offset.cpp +++ b/3rdparty/clipper2/src/clipper.offset.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 17 April 2024 * +* Date : 22 January 2025 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2024 * +* Copyright : Angus Johnson 2010-2025 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -13,9 +13,23 @@ namespace Clipper2Lib { -const double default_arc_tolerance = 0.25; const double floating_point_tolerance = 1e-12; +// Clipper2 approximates arcs by using series of relatively short straight +//line segments. And logically, shorter line segments will produce better arc +// approximations. But very short segments can degrade performance, usually +// with little or no discernable improvement in curve quality. Very short +// segments can even detract from curve quality, due to the effects of integer +// rounding. Since there isn't an optimal number of line segments for any given +// arc radius (that perfectly balances curve approximation with performance), +// arc tolerance is user defined. Nevertheless, when the user doesn't define +// an arc tolerance (ie leaves alone the 0 default value), the calculated +// default arc tolerance (offset_radius / 500) generally produces good (smooth) +// arc approximations without producing excessively small segment lengths. +// See also: https://www.angusj.com/clipper2/Docs/Trigonometry.htm +const double arc_const = 0.002; // <-- 1/500 + + //------------------------------------------------------------------------------ // Miscellaneous methods //------------------------------------------------------------------------------ @@ -38,13 +52,22 @@ std::optional GetLowestClosedPathIdx(const Paths64& paths) return result; } -PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) +inline double Hypot(double x, double y) +{ + // given that this is an internal function, and given the x and y parameters + // will always be coordinate values (or the difference between coordinate values), + // x and y should always be within INT64_MIN to INT64_MAX. Consequently, + // there should be no risk that the following computation will overflow + // see https://stackoverflow.com/a/32436148/359538 + return std::sqrt(x * x + y * y); +} + +static PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) { - double dx, dy, inverse_hypot; if (pt1 == pt2) return PointD(0.0, 0.0); - dx = static_cast(pt2.x - pt1.x); - dy = static_cast(pt2.y - pt1.y); - inverse_hypot = 1.0 / hypot(dx, dy); + double dx = static_cast(pt2.x - pt1.x); + double dy = static_cast(pt2.y - pt1.y); + double inverse_hypot = 1.0 / Hypot(dx, dy); dx *= inverse_hypot; dy *= inverse_hypot; return PointD(dy, -dx); @@ -55,12 +78,6 @@ inline bool AlmostZero(double value, double epsilon = 0.001) return std::fabs(value) < epsilon; } -inline double Hypot(double x, double y) -{ - //see https://stackoverflow.com/a/32436148/359538 - return std::sqrt(x * x + y * y); -} - inline PointD NormalizeVector(const PointD& vec) { double h = Hypot(vec.x, vec.y); @@ -79,7 +96,7 @@ inline bool IsClosedPath(EndType et) return et == EndType::Polygon || et == EndType::Joined; } -inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) +static inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) { #ifdef USINGZ return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta, pt.z); @@ -129,11 +146,11 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType // the lowermost path must be an outer path, so if its orientation is negative, // then flag the whole group is 'reversed' (will negate delta etc.) // as this is much more efficient than reversing every path. - is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; + is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; } else { - lowest_path_idx = std::nullopt; + lowest_path_idx = std::nullopt; is_reversed = false; } } @@ -256,8 +273,7 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle // so we'll need to do the following calculations for *every* vertex. double abs_delta = std::fabs(group_delta_); double arcTol = (arc_tolerance_ > floating_point_tolerance ? - std::min(abs_delta, arc_tolerance_) : - std::log10(2 + abs_delta) * default_arc_tolerance); + std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const); double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); step_sin_ = std::sin(2 * PI / steps_per_360); step_cos_ = std::cos(2 * PI / steps_per_360); @@ -315,21 +331,18 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593) { - // is concave (so insert 3 points that will create a negative region) + // is concave + // by far the simplest way to construct concave joins, especially those joining very + // short segments, is to insert 3 points that produce negative regions. These regions + // will be removed later by the finishing union operation. This is also the best way + // to ensure that path reversals (ie over-shrunk paths) are removed. #ifdef USINGZ path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); -#else - path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); -#endif - - // this extra point is the only simple way to ensure that path reversals - // (ie over-shrunk paths) are fully cleaned out with the trailing union op. - // However it's probably safe to skip this whenever an angle is almost flat. - if (cos_a < 0.99) path_out.push_back(path[j]); // (#405) - -#ifdef USINGZ + path_out.push_back(path[j]); // (#405, #873, #916) path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); #else + path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); + path_out.push_back(path[j]); // (#405, #873, #916) path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); #endif } @@ -458,9 +471,8 @@ void ClipperOffset::DoGroupOffset(Group& group) // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision // will be relative to the size of the offset (delta). Obviously very //large offsets will almost always require much less precision. - double arcTol = (arc_tolerance_ > floating_point_tolerance ? - std::min(abs_delta, arc_tolerance_) : - std::log10(2 + abs_delta) * default_arc_tolerance); + double arcTol = (arc_tolerance_ > floating_point_tolerance) ? + std::min(abs_delta, arc_tolerance_) : abs_delta * arc_const; double steps_per_360 = std::min(PI / std::acos(1 - arcTol / abs_delta), abs_delta * PI); step_sin_ = std::sin(2 * PI / steps_per_360); @@ -588,7 +600,7 @@ void ClipperOffset::ExecuteInternal(double delta) if (!solution->size()) return; - bool paths_reversed = CheckReverseOrientation(); + bool paths_reversed = CheckReverseOrientation(); //clean up self-intersections ... Clipper64 c; c.PreserveCollinear(false); @@ -617,10 +629,10 @@ void ClipperOffset::ExecuteInternal(double delta) } } -void ClipperOffset::Execute(double delta, Paths64& paths) +void ClipperOffset::Execute(double delta, Paths64& paths64) { - paths.clear(); - solution = &paths; + paths64.clear(); + solution = &paths64; solution_tree = nullptr; ExecuteInternal(delta); } From 0e07823175084e5b1789d09184ecda46ef932f20 Mon Sep 17 00:00:00 2001 From: aismann Date: Sat, 25 Jan 2025 19:28:10 +0100 Subject: [PATCH 2/2] Update README.md (found a typo) --- 3rdparty/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/README.md b/3rdparty/README.md index 4e04a60f11be..ed7e9cc5f2ca 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -157,7 +157,7 @@ - Version: 1.0.1 - License: Apache-2.0 -## oboe (Adnroid only) +## oboe (Android only) - [![Upstream](https://img.shields.io/github/v/tag/google/oboe?label=Upstream)](https://github.com/google/oboe) - Version: 1.9.3 - License: Apache-2.0