Browse Source

clipper2: Update to 1.5.2

Jakub Marcowski 5 months ago
parent
commit
100225c081

+ 1 - 1
COPYRIGHT.txt

@@ -200,7 +200,7 @@ License: MPL-2.0
 
 Files: thirdparty/clipper2/*
 Comment: Clipper2
-Copyright: 2010-2024, Angus Johnson
+Copyright: 2010-2025, Angus Johnson
 License: BSL-1.0
 
 Files: thirdparty/cvtt/*

+ 2 - 2
thirdparty/README.md

@@ -107,7 +107,7 @@ Files extracted from upstream source:
 ## clipper2
 
 - Upstream: https://github.com/AngusJohnson/Clipper2
-- Version: 1.4.0 (736ddb0b53d97fd5f65dd3d9bbf8a0993eaf387c, 2024)
+- Version: 1.5.2 (6901921c4be75126d1de60bfd24bd86a61319fd0, 2025)
 - License: BSL 1.0
 
 Files extracted from upstream source:
@@ -118,7 +118,7 @@ Files extracted from upstream source:
 Patches:
 
 - `0001-disable-exceptions.patch` (GH-80796)
-- `0002-llvm-disable-int1280-math.patch` (GH-95964)
+- `0002-llvm-disable-int128-math.patch` (GH-95964)
 
 
 ## cvtt

+ 46 - 26
thirdparty/clipper2/include/clipper2/clipper.core.h

@@ -1,26 +1,23 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Date      :  12 May 2024                                                     *
-* Website   :  http://www.angusj.com                                           *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  Core Clipper Library structures and functions                   *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 #ifndef CLIPPER_CORE_H
 #define CLIPPER_CORE_H
 
+#include "clipper2/clipper.version.h"
 #include <cstdint>
-#include <cstdlib>
-#include <cmath>
 #include <vector>
 #include <string>
 #include <iostream>
 #include <algorithm>
-#include <climits>
 #include <numeric>
-#include <optional>
-#include "clipper2/clipper.version.h"
+#include <cmath>
 
 #define CLIPPER2_THROW(exception) std::abort()
 
@@ -33,7 +30,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;
   };
@@ -90,6 +87,9 @@ namespace Clipper2Lib
       CLIPPER2_THROW(Clipper2Exception(undefined_error));
     case range_error_i:
       CLIPPER2_THROW(Clipper2Exception(range_error));
+    // Should never happen, but adding this to stop a compiler warning
+    default:
+      CLIPPER2_THROW(Clipper2Exception("Unknown error"));
     }
 #else
     if(error_code) {}; // only to stop compiler 'parameter not used' warning
@@ -109,6 +109,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 <typename T>
@@ -116,10 +120,10 @@ namespace Clipper2Lib
     T x;
     T y;
 #ifdef USINGZ
-    int64_t z;
+     z_type z;
 
     template <typename T2>
-    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<T> &&
         is_round_invocable<T2>::value && !std::is_integral_v<T2>)
@@ -139,7 +143,7 @@ namespace Clipper2Lib
     explicit Point() : x(0), y(0), z(0) {};
 
     template <typename T2>
-    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_;
@@ -152,7 +156,7 @@ namespace Clipper2Lib
     }
 
     template <typename T2>
-    explicit Point(const Point<T2>& p, int64_t z_)
+    explicit Point(const Point<T2>& p, z_type z_)
     {
       Init(p.x, p.y, z_);
     }
@@ -162,7 +166,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)
     {
@@ -326,10 +330,10 @@ namespace Clipper2Lib
     {
       Path<T> result;
       result.reserve(4);
-      result.push_back(Point<T>(left, top));
-      result.push_back(Point<T>(right, top));
-      result.push_back(Point<T>(right, bottom));
-      result.push_back(Point<T>(left, bottom));
+      result.emplace_back(left, top);
+      result.emplace_back(right, top);
+      result.emplace_back(right, bottom);
+      result.emplace_back(left, bottom);
       return result;
     }
 
@@ -364,6 +368,22 @@ namespace Clipper2Lib
         top == other.top && bottom == other.bottom;
     }
 
+    Rect<T>& operator+=(const Rect<T>& 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<T> operator+(const Rect<T>& other) const
+    {
+      Rect<T> result = *this;
+      result += other;
+      return result;
+    }
+
     friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
       os << "(" << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom << ") ";
       return os;
@@ -597,13 +617,13 @@ namespace Clipper2Lib
     result.reserve(path.size());
     typename Path<T>::const_iterator path_iter = path.cbegin();
     Point<T> first_pt = *path_iter++, last_pt = first_pt;
-    result.push_back(first_pt);
+    result.emplace_back(first_pt);
     for (; path_iter != path.cend(); ++path_iter)
     {
       if (!NearEqual(*path_iter, last_pt, max_dist_sqrd))
       {
         last_pt = *path_iter;
-        result.push_back(last_pt);
+        result.emplace_back(last_pt);
       }
     }
     if (!is_closed_path) return result;
@@ -621,7 +641,7 @@ namespace Clipper2Lib
     for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
       paths_citer != paths.cend(); ++paths_citer)
     {
-      result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path));
+      result.emplace_back(std::move(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path)));
     }
     return result;
   }
@@ -697,11 +717,11 @@ namespace Clipper2Lib
   {
 // Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
 // Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
-//#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
-//    const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
-//    const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
-//    return ab == cd;
-//#else
+// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
+//     const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
+//     const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
+//     return ab == cd;
+// #else
     // nb: unsigned values needed for calculating overflow carry
     const auto abs_a = static_cast<uint64_t>(std::abs(a));
     const auto abs_b = static_cast<uint64_t>(std::abs(b));
@@ -768,7 +788,7 @@ namespace Clipper2Lib
     const Point<T>& line1, const Point<T>& line2)
   {
     //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
-    //see http://en.wikipedia.org/wiki/Perpendicular_distance
+    //see https://en.wikipedia.org/wiki/Perpendicular_distance
     double a = static_cast<double>(pt.x - line1.x);
     double b = static_cast<double>(pt.y - line1.y);
     double c = static_cast<double>(line2.x - line1.x);

+ 12 - 18
thirdparty/clipper2/include/clipper2/clipper.engine.h

@@ -1,26 +1,20 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
-* Date      :  5 July 2024                                                     *
-* Website   :  http://www.angusj.com                                           *
+* Date      :  17 September 2024                                               *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This is the main polygon clipping module                        *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 #ifndef CLIPPER_ENGINE_H
 #define CLIPPER_ENGINE_H
 
-#include <cstdlib>
-#include <stdint.h> //#541
-#include <iostream>
+#include "clipper2/clipper.core.h"
 #include <queue>
-#include <vector>
 #include <functional>
-#include <numeric>
 #include <memory>
 
-#include "clipper2/clipper.core.h"
-
 namespace Clipper2Lib {
 
 	struct Scanline;
@@ -32,13 +26,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 +49,7 @@ namespace Clipper2Lib {
 		Point64 pt;
 		Vertex* next = nullptr;
 		Vertex* prev = nullptr;
-		VertexFlags flags = VertexFlags::None;
+		VertexFlags flags = VertexFlags::Empty;
 	};
 
 	struct OutPt {
@@ -131,7 +125,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 +161,7 @@ namespace Clipper2Lib {
 	};
 
 #ifdef USINGZ
-		typedef std::function<void(const Point64& e1bot, const Point64& e1top,
+	typedef std::function<void(const Point64& e1bot, const Point64& e1top,
 		const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
 
 	typedef std::function<void(const PointD& e1bot, const PointD& e1top,
@@ -197,7 +191,7 @@ namespace Clipper2Lib {
 
 	class ClipperBase {
 	private:
-		ClipType cliptype_ = ClipType::None;
+		ClipType cliptype_ = ClipType::NoClip;
 		FillRule fillrule_ = FillRule::EvenOdd;
 		FillRule fillpos = FillRule::Positive;
 		int64_t bot_y_ = 0;
@@ -210,7 +204,7 @@ namespace Clipper2Lib {
 		std::vector<Vertex*> vertex_lists_;
 		std::priority_queue<int64_t> scanline_list_;
 		IntersectNodeList intersect_nodes_;
-    HorzSegmentList horz_seg_list_;
+        HorzSegmentList horz_seg_list_;
 		std::vector<HorzJoin> horz_join_list_;
 		void Reset();
 		inline void InsertScanline(int64_t y);

+ 362 - 130
thirdparty/clipper2/include/clipper2/clipper.export.h

@@ -1,16 +1,16 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
-* Date      :  14 May 2024                                                     *
-* Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2024                                         *
+* Date      :  24 January 2025                                                 *
+* Website   :  https://www.angusj.com                                          *
+* Copyright :  Angus Johnson 2010-2025                                         *
 * Purpose   :  This module exports the Clipper2 Library (ie DLL/so)            *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 
 /*
  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,73 +19,104 @@
 
 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, 
+preceding the first path is a pair of values. The first value
+contains the length of the entire array structure (A), and the 
+second contains the number (ie count) of contained paths (C).
+  Memory allocation for CPaths64 = A * sizeof(int64_t)
+  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 <cstdlib>
-#include <vector>
-
 #include "clipper2/clipper.core.h"
 #include "clipper2/clipper.engine.h"
 #include "clipper2/clipper.offset.h"
 #include "clipper2/clipper.rectclip.h"
+#include <cstdlib>
 
 namespace Clipper2Lib {
 
@@ -127,6 +158,12 @@ inline Rect<T> CRectToRect(const CRect<T>& rect)
   return result;
 }
 
+template <typename T1, typename T2>
+inline T1 Reinterpret(T2 value) {
+  return *reinterpret_cast<T1*>(&value);
+}
+
+
 #ifdef _WIN32
   #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
 #else
@@ -178,11 +215,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 +245,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 <typename T>
 static void GetPathCountAndCPathsArrayLen(const Paths<T>& paths,
   size_t& cnt, size_t& array_len)
@@ -206,30 +263,47 @@ static void GetPathCountAndCPathsArrayLen(const Paths<T>& paths,
   for (const Path<T>& 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 = 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 = GetPolyPath64ArrayLen(tree);
+  array_len = GetPolyPathArrayLenD(tree);
 }
 
 template <typename T>
-static T* CreateCPaths(const Paths<T>& paths)
+static T* CreateCPathsFromPathsT(const Paths<T>& paths)
 {
   size_t cnt = 0, array_len = 0;
   GetPathCountAndCPathsArrayLen(paths, cnt, array_len);
@@ -245,11 +319,38 @@ static T* CreateCPaths(const Paths<T>& paths)
     {
       *v++ = pt.x;
       *v++ = pt.y;
+#ifdef USINGZ
+      *v++ = Reinterpret<T>(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<double>(pt.z);
+#endif
+    }
+  }
+  return result;
+}
 
 CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
 {
@@ -268,13 +369,16 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
     {
       *v++ = pt.x * scale;
       *v++ = pt.y * scale;
+#ifdef USINGZ
+      *v++ = Reinterpret<double>(pt.z);
+#endif
     }
   }
   return result;
 }
 
 template <typename T>
-static Path<T> ConvertCPath(T* path)
+static Path<T> ConvertCPathToPathT(T* path)
 {
   Path<T> result;
   if (!path) return result;
@@ -284,14 +388,19 @@ static Path<T> ConvertCPath(T* path)
   result.reserve(cnt);
   for (size_t j = 0; j < cnt; ++j)
   {
-    T x = *v++, y = *v++;
-    result.push_back(Point<T>(x, y));
+      T x = *v++, y = *v++;
+#ifdef USINGZ
+      z_type z = Reinterpret<z_type>(*v++);
+      result.emplace_back(x, y, z);
+#else  
+      result.emplace_back(x, y);
+#endif
   }
   return result;
 }
 
 template <typename T>
-static Paths<T> ConvertCPaths(T* paths)
+static Paths<T> ConvertCPathsToPathsT(T* paths)
 {
   Paths<T> result;
   if (!paths) return result;
@@ -301,19 +410,45 @@ static Paths<T> ConvertCPaths(T* paths)
   for (size_t i = 0; i < cnt; ++i)
   {
     size_t cnt2 = static_cast<size_t>(*v);
-    v += 2;
+    v += 2; 
     Path<T> path;
     path.reserve(cnt2);
     for (size_t j = 0; j < cnt2; ++j)
     {
       T x = *v++, y = *v++;
-      path.push_back(Point<T>(x, y));
+#ifdef USINGZ
+      z_type z = Reinterpret<z_type>(*v++);
+      path.emplace_back(x, y, z);
+#else
+      path.emplace_back(x, y);
+#endif
     }
-    result.push_back(path);
+    result.emplace_back(std::move(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<size_t>(*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<z_type>(*v++);
+        result.emplace_back(x, y, z);
+#else  
+        result.emplace_back(x, y);
+#endif
+    }
+    return result;
+}
 
 static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
 {
@@ -333,42 +468,78 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
     {
       double x = *v++ * scale;
       double y = *v++ * scale;
-      path.push_back(Point64(x, y));
+#ifdef USINGZ
+      z_type z = Reinterpret<z_type>(*v++);
+      path.emplace_back(x, y, z);
+#else
+      path.emplace_back(x, y);
+#endif
     }
-    result.push_back(path);
+    result.emplace_back(std::move(path));
   }
   return result;
 }
 
-template <typename T>
-static void CreateCPolyPath(const PolyPath64* pp, T*& v, T scale)
+static void CreateCPolyPath64(const PolyPath64* pp, int64_t*& v)
 {
-  *v++ = static_cast<T>(pp->Polygon().size());
-  *v++ = static_cast<T>(pp->Count());
+  *v++ = static_cast<int64_t>(pp->Polygon().size());
+  *v++ = static_cast<int64_t>(pp->Count());
   for (const Point64& pt : pp->Polygon())
   {
-    *v++ = static_cast<T>(pt.x * scale);
-    *v++ = static_cast<T>(pt.y * scale);
+    *v++ = pt.x;
+    *v++ = pt.y;
+#ifdef USINGZ   
+    * v++ = Reinterpret<int64_t>(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 <typename T>
-static T* CreateCPolyTree(const PolyTree64& tree, T scale)
+static void CreateCPolyPathD(const PolyPathD* pp, double*& v)
+{
+  *v++ = static_cast<double>(pp->Polygon().size());
+  *v++ = static_cast<double>(pp->Count());
+  for (const PointD& pt : pp->Polygon())
+  {
+    *v++ = pt.x;
+    *v++ = pt.y;
+#ifdef USINGZ   
+    * v++ = Reinterpret<double>(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<int64_t>(array_len);
+  *v++ = static_cast<int64_t>(tree.Count());
+  for (size_t i = 0; i < tree.Count(); ++i)
+    CreateCPolyPath64(tree.Child(i), v);
+  return result;
+}
 
-  *v++ = static_cast<T>(array_len);
-  *v++ = static_cast<T>(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<double>(array_len);
+  *v++ = static_cast<double>(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 +562,24 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
   if (fillrule > static_cast<uint8_t>(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 +592,26 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTree64(uint8_t cliptype,
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (fillrule > static_cast<uint8_t>(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 +624,27 @@ EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
   if (precision < -8 || precision > 8) return -5;
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (fillrule > static_cast<uint8_t>(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 +657,30 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
   if (precision < -8 || precision > 8) return -5;
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (fillrule > static_cast<uint8_t>(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 +689,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 +711,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 +777,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 +798,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);
+}
+
+#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;
 }
 
-}  // end Clipper2Lib namespace
+EXTERN_DLL_EXPORT void SetZCallbackD(DLLZCallbackD callback)
+{
+  dllCallbackD = callback;
+}
 
+#endif
+
+}
 #endif  // CLIPPER2_EXPORT_H

+ 16 - 19
thirdparty/clipper2/include/clipper2/clipper.h

@@ -1,24 +1,21 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Date      :  27 April 2024                                                   *
-* Website   :  http://www.angusj.com                                           *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This module provides a simple interface to the Clipper Library  *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 #ifndef CLIPPER_H
 #define CLIPPER_H
 
-#include <cstdlib>
-#include <type_traits>
-#include <vector>
-
 #include "clipper2/clipper.core.h"
 #include "clipper2/clipper.engine.h"
 #include "clipper2/clipper.offset.h"
 #include "clipper2/clipper.minkowski.h"
 #include "clipper2/clipper.rectclip.h"
+#include <type_traits>
 
 namespace Clipper2Lib {
 
@@ -272,14 +269,14 @@ namespace Clipper2Lib {
 
     inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths)
     {
-      paths.push_back(polypath.Polygon());
+      paths.emplace_back(polypath.Polygon());
       for (const auto& child : polypath)
         PolyPathToPaths64(*child, paths);
     }
 
     inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths)
     {
-      paths.push_back(polypath.Polygon());
+      paths.emplace_back(polypath.Polygon());
       for (const auto& child : polypath)
         PolyPathToPathsD(*child, paths);
     }
@@ -348,9 +345,9 @@ namespace Clipper2Lib {
       result.reserve(array_size / 2);
       for (size_t i = 0; i < array_size; i +=2)
 #ifdef USINGZ
-        result.push_back( U{ an_array[i], an_array[i + 1], 0} );
+        result.emplace_back( an_array[i], an_array[i + 1], 0 );
 #else
-        result.push_back( U{ an_array[i], an_array[i + 1]} );
+        result.emplace_back( an_array[i], an_array[i + 1] );
 #endif
     }
 
@@ -518,20 +515,20 @@ namespace Clipper2Lib {
     }
 
     prevIt = srcIt++;
-    dst.push_back(*prevIt);
+    dst.emplace_back(*prevIt);
     for (; srcIt != stop; ++srcIt)
     {
       if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1)))
       {
         prevIt = srcIt;
-        dst.push_back(*prevIt);
+        dst.emplace_back(*prevIt);
       }
     }
 
     if (is_open_path)
-      dst.push_back(*srcIt);
+      dst.emplace_back(*srcIt);
     else if (!IsCollinear(*prevIt, *stop, dst[0]))
-      dst.push_back(*stop);
+      dst.emplace_back(*stop);
     else
     {
       while (dst.size() > 2 &&
@@ -603,10 +600,10 @@ namespace Clipper2Lib {
     double dx = co, dy = si;
     Path<T> result;
     result.reserve(steps);
-    result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
+    result.emplace_back(center.x + radiusX, static_cast<double>(center.y));
     for (size_t i = 1; i < steps; ++i)
     {
-      result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
+      result.emplace_back(center.x + radiusX * dx, center.y + radiusY * dy);
       double x = dx * co - dy * si;
       dy = dy * co + dx * si;
       dx = x;
@@ -700,7 +697,7 @@ namespace Clipper2Lib {
     Path<T> result;
     result.reserve(len);
     for (typename Path<T>::size_type i = 0; i < len; ++i)
-      if (!flags[i]) result.push_back(path[i]);
+      if (!flags[i]) result.emplace_back(path[i]);
     return result;
   }
 
@@ -711,7 +708,7 @@ namespace Clipper2Lib {
     Paths<T> result;
     result.reserve(paths.size());
     for (const auto& path : paths)
-      result.push_back(SimplifyPath(path, epsilon, isClosedPath));
+      result.emplace_back(std::move(SimplifyPath(path, epsilon, isClosedPath)));
     return result;
   }
 
@@ -749,7 +746,7 @@ namespace Clipper2Lib {
     result.reserve(len);
     for (typename Path<T>::size_type i = 0; i < len; ++i)
       if (flags[i])
-        result.push_back(path[i]);
+        result.emplace_back(path[i]);
     return result;
   }
 

+ 9 - 12
thirdparty/clipper2/include/clipper2/clipper.minkowski.h

@@ -1,18 +1,15 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Date      :  1 November 2023                                                 *
-* Website   :  http://www.angusj.com                                           *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2023                                         *
 * Purpose   :  Minkowski Sum and Difference                                    *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 #ifndef CLIPPER_MINKOWSKI_H
 #define CLIPPER_MINKOWSKI_H
 
-#include <cstdlib>
-#include <vector>
-#include <string>
 #include "clipper2/clipper.core.h"
 
 namespace Clipper2Lib
@@ -35,7 +32,7 @@ namespace Clipper2Lib
           Path64 path2(pattern.size());
           std::transform(pattern.cbegin(), pattern.cend(),
             path2.begin(), [p](const Point64& pt2) {return p + pt2; });
-          tmp.push_back(path2);
+          tmp.emplace_back(std::move(path2));
         }
       }
       else
@@ -45,7 +42,7 @@ namespace Clipper2Lib
           Path64 path2(pattern.size());
           std::transform(pattern.cbegin(), pattern.cend(),
             path2.begin(), [p](const Point64& pt2) {return p - pt2; });
-          tmp.push_back(path2);
+          tmp.emplace_back(std::move(path2));
         }
       }
 
@@ -59,14 +56,14 @@ namespace Clipper2Lib
           Path64 quad;
           quad.reserve(4);
           {
-            quad.push_back(tmp[g][h]);
-            quad.push_back(tmp[i][h]);
-            quad.push_back(tmp[i][j]);
-            quad.push_back(tmp[g][j]);
+            quad.emplace_back(tmp[g][h]);
+            quad.emplace_back(tmp[i][h]);
+            quad.emplace_back(tmp[i][j]);
+            quad.emplace_back(tmp[g][j]);
           };
           if (!IsPositive(quad))
             std::reverse(quad.begin(), quad.end());
-          result.push_back(quad);
+          result.emplace_back(std::move(quad));
           h = j;
         }
         g = i;

+ 6 - 5
thirdparty/clipper2/include/clipper2/clipper.offset.h

@@ -1,10 +1,10 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
-* Date      :  24 March 2024                                                   *
-* Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2024                                         *
+* Date      :  22 January 2025                                                 *
+* Website   :  https://www.angusj.com                                          *
+* Copyright :  Angus Johnson 2010-2025                                         *
 * Purpose   :  Path Offset (Inflate/Shrink)                                    *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 #ifndef CLIPPER_OFFSET_H_
@@ -12,6 +12,7 @@
 
 #include "clipper.core.h"
 #include "clipper.engine.h"
+#include <optional>
 
 namespace Clipper2Lib {
 
@@ -96,7 +97,7 @@ public:
 	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);
 

+ 3 - 5
thirdparty/clipper2/include/clipper2/clipper.rectclip.h

@@ -1,19 +1,17 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Date      :  5 July 2024                                                     *
-* Website   :  http://www.angusj.com                                           *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  FAST rectangular clipping                                       *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
 #ifndef CLIPPER_RECTCLIP_H
 #define CLIPPER_RECTCLIP_H
 
-#include <cstdlib>
-#include <vector>
-#include <queue>
 #include "clipper2/clipper.core.h"
+#include <queue>
 
 namespace Clipper2Lib
 {

+ 1 - 1
thirdparty/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.2";
 
 #endif  // CLIPPER_VERSION_H

+ 9 - 5
thirdparty/clipper2/patches/0001-disable-exceptions.patch

@@ -1,17 +1,17 @@
 diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
-index 925c04685e..67dd731af6 100644
+index ab71aeb072..110bee4c10 100644
 --- a/thirdparty/clipper2/include/clipper2/clipper.core.h
 +++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
-@@ -22,6 +22,8 @@
- #include <optional>
- #include "clipper2/clipper.version.h"
+@@ -19,6 +19,8 @@
+ #include <numeric>
+ #include <cmath>
  
 +#define CLIPPER2_THROW(exception) std::abort()
 +
  namespace Clipper2Lib
  {
  
-@@ -79,18 +81,18 @@ namespace Clipper2Lib
+@@ -76,21 +78,21 @@ namespace Clipper2Lib
      switch (error_code)
      {
      case precision_error_i:
@@ -29,6 +29,10 @@ index 925c04685e..67dd731af6 100644
      case range_error_i:
 -      throw Clipper2Exception(range_error);
 +      CLIPPER2_THROW(Clipper2Exception(range_error));
+     // Should never happen, but adding this to stop a compiler warning
+     default:
+-      throw Clipper2Exception("Unknown error");
++      CLIPPER2_THROW(Clipper2Exception("Unknown error"));
      }
  #else
 -    ++error_code; // only to stop compiler warning

+ 8 - 8
thirdparty/clipper2/patches/0002-llvm-disable-int1280-math.patch → thirdparty/clipper2/patches/0002-llvm-disable-int128-math.patch

@@ -1,8 +1,8 @@
 diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
-index 67dd731af6..dd1b873d5d 100644
+index 110bee4c10..aa003bf032 100644
 --- a/thirdparty/clipper2/include/clipper2/clipper.core.h
 +++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
-@@ -695,11 +695,13 @@ namespace Clipper2Lib
+@@ -715,11 +715,13 @@ namespace Clipper2Lib
    // returns true if (and only if) a * b == c * d
    inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d)
    {
@@ -13,15 +13,15 @@ index 67dd731af6..dd1b873d5d 100644
 -#else
 +// Work around LLVM issue: https://github.com/llvm/llvm-project/issues/16778
 +// Details: https://github.com/godotengine/godot/pull/95964#issuecomment-2306581804
-+//#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
-+//    const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
-+//    const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
-+//    return ab == cd;
-+//#else
++// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
++//     const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
++//     const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
++//     return ab == cd;
++// #else
      // nb: unsigned values needed for calculating overflow carry
      const auto abs_a = static_cast<uint64_t>(std::abs(a));
      const auto abs_b = static_cast<uint64_t>(std::abs(b));
-@@ -714,7 +716,7 @@ namespace Clipper2Lib
+@@ -734,7 +736,7 @@ namespace Clipper2Lib
      const auto sign_cd = TriSign(c) * TriSign(d);
  
      return abs_ab == abs_cd && sign_ab == sign_cd;

+ 54 - 56
thirdparty/clipper2/src/clipper.engine.cpp

@@ -1,21 +1,15 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
-* Date      :  27 April 2024                                                   *
-* Website   :  http://www.angusj.com                                           *
+* Date      :  17 September 2024                                               *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This is the main polygon clipping module                        *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
-#include <cstdlib>
-#include <cmath>
-#include <stdexcept>
-#include <vector>
-#include <numeric>
-#include <algorithm>
-
 #include "clipper2/clipper.engine.h"
 #include "clipper2/clipper.h"
+#include <stdexcept>
 
 // https://github.com/AngusJohnson/Clipper2/discussions/334
 // #discussioncomment-4248602
@@ -85,7 +79,7 @@ namespace Clipper2Lib {
   inline bool IsOpenEnd(const Vertex& v)
   {
     return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) !=
-      VertexFlags::None;
+      VertexFlags::Empty;
   }
 
 
@@ -220,7 +214,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 +229,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 +472,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)
@@ -517,7 +511,7 @@ namespace Clipper2Lib {
         while (op2 != op && op2->pt.y > pt.y) op2 = op2->next;
       if (op2 == op) break;
 
-      // must have touched or crossed the pt.Y horizonal
+      // must have touched or crossed the pt.Y horizontal
       // and this must happen an even number of times
 
       if (op2->pt.y == pt.y) // touching the horizontal
@@ -564,7 +558,7 @@ namespace Clipper2Lib {
     while (op2->next != op &&
       ((op2->pt.x == op2->next->pt.x && op2->pt.x == op2->prev->pt.x) ||
         (op2->pt.y == op2->next->pt.y && op2->pt.y == op2->prev->pt.y))) op2 = op2->next;
-    result.push_back(op2->pt);
+    result.emplace_back(op2->pt);
     OutPt* prevOp = op2;
     op2 = op2->next;
     while (op2 != op)
@@ -572,7 +566,7 @@ namespace Clipper2Lib {
       if ((op2->pt.x != op2->next->pt.x || op2->pt.x != prevOp->pt.x) &&
         (op2->pt.y != op2->next->pt.y || op2->pt.y != prevOp->pt.y))
       {
-        result.push_back(op2->pt);
+        result.emplace_back(op2->pt);
         prevOp = op2;
       }
       op2 = op2->next;
@@ -608,10 +602,10 @@ 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 <LocalMinima>(&vert, polytype, is_open));
+    list.emplace_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
   }
 
   void AddPaths_(const Paths64& paths, PathType polytype, bool is_open,
@@ -643,7 +637,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,10 +719,10 @@ 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 <LocalMinima>(&vert, polytype, is_open));
+    minima_list_.emplace_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
   }
 
   void ReuseableDataContainer64::AddPaths(const Paths64& paths,
@@ -836,9 +830,7 @@ namespace Clipper2Lib {
 
   void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open)
   {
-    Paths64 tmp;
-    tmp.push_back(path);
-    AddPaths(tmp, polytype, is_open);
+    AddPaths(Paths64(1, path), polytype, is_open);
   }
 
   void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open)
@@ -857,7 +849,7 @@ namespace Clipper2Lib {
     LocalMinimaList::const_iterator i;
     for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i)
     {
-      minima_list_.push_back(std::make_unique <LocalMinima>((*i)->vertex, (*i)->polytype, (*i)->is_open));
+      minima_list_.emplace_back(std::make_unique <LocalMinima>((*i)->vertex, (*i)->polytype, (*i)->is_open));
       if ((*i)->is_open) has_open_paths_ = true;
     }
   }
@@ -907,10 +899,10 @@ 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 <LocalMinima>(&vert, polytype, is_open));
+    minima_list_.emplace_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
   }
 
   bool ClipperBase::IsContributingClosed(const Active& e) const
@@ -928,11 +920,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 +973,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 +1206,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 +1222,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;
       }
@@ -1488,7 +1486,7 @@ namespace Clipper2Lib {
   {
     OutRec* result = new OutRec();
     result->idx = outrec_list_.size();
-    outrec_list_.push_back(result);
+    outrec_list_.emplace_back(result);
     result->pts = nullptr;
     result->owner = nullptr;
     result->polypath = nullptr;
@@ -1631,12 +1629,12 @@ namespace Clipper2Lib {
         if (Path1InsidePath2(prevOp, newOp))
         {
           newOr->splits = new OutRecList();
-          newOr->splits->push_back(outrec);
+          newOr->splits->emplace_back(outrec);
         }
         else
         {
           if (!outrec->splits) outrec->splits = new OutRecList();
-          outrec->splits->push_back(newOr);
+          outrec->splits->emplace_back(newOr);
         }
       }
     }
@@ -1955,7 +1953,7 @@ namespace Clipper2Lib {
       else if (IsFront(e1) || (e1.outrec == e2.outrec))
       {
         //this 'else if' condition isn't strictly needed but
-        //it's sensible to split polygons that ony touch at
+        //it's sensible to split polygons that only touch at
         //a common vertex (not at common edges).
 
 #ifdef USINGZ
@@ -2125,7 +2123,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_)
     {
@@ -2239,7 +2237,7 @@ namespace Clipper2Lib {
           HorzJoin join = HorzJoin(
             DuplicateOp(hs1->left_op, true),
             DuplicateOp(hs2->left_op, false));
-          horz_join_list_.push_back(join);
+          horz_join_list_.emplace_back(join);
         }
         else
         {
@@ -2252,7 +2250,7 @@ namespace Clipper2Lib {
           HorzJoin join = HorzJoin(
             DuplicateOp(hs2->left_op, true),
             DuplicateOp(hs1->left_op, false));
-          horz_join_list_.push_back(join);
+          horz_join_list_.emplace_back(join);
         }
       }
     }
@@ -2264,7 +2262,7 @@ namespace Clipper2Lib {
     if (!toOr->splits) toOr->splits = new OutRecList();
     OutRecList::iterator orIter = fromOr->splits->begin();
     for (; orIter != fromOr->splits->end(); ++orIter)
-      toOr->splits->push_back(*orIter);
+      toOr->splits->emplace_back(*orIter);
     fromOr->splits->clear();
   }
 
@@ -2317,7 +2315,7 @@ namespace Clipper2Lib {
             or2->owner = or1->owner;
 
           if (!or1->splits) or1->splits = new OutRecList();
-          or1->splits->push_back(or2);
+          or1->splits->emplace_back(or2);
         }
         else
           or2->owner = or1;
@@ -2376,7 +2374,7 @@ namespace Clipper2Lib {
         else ip.x = TopX(e2, ip.y);
       }
     }
-    intersect_nodes_.push_back(IntersectNode(&e1, &e2, ip));
+    intersect_nodes_.emplace_back(&e1, &e2, ip);
   }
 
   bool ClipperBase::BuildIntersectList(const int64_t top_y)
@@ -2497,7 +2495,7 @@ namespace Clipper2Lib {
   void ClipperBase::AddTrialHorzJoin(OutPt* op)
   {
     if (op->outrec->is_open) return;
-    horz_seg_list_.push_back(HorzSegment(op));
+    horz_seg_list_.emplace_back(op);
   }
 
   bool ClipperBase::ResetHorzDirection(const Active& horz,
@@ -2655,7 +2653,7 @@ namespace Clipper2Lib {
 
         if (horz.outrec)
         {
-          //nb: The outrec containining the op returned by IntersectEdges
+          //nb: The outrec containing the op returned by IntersectEdges
           //above may no longer be associated with horzEdge.
           AddTrialHorzJoin(GetLastOp(horz));
         }
@@ -2789,14 +2787,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);
     }
   }
@@ -2899,14 +2897,14 @@ namespace Clipper2Lib {
       lastPt = op->pt;
       op2 = op->next;
     }
-    path.push_back(lastPt);
+    path.emplace_back(lastPt);
 
     while (op2 != op)
     {
       if (op2->pt != lastPt)
       {
         lastPt = op2->pt;
-        path.push_back(lastPt);
+        path.emplace_back(lastPt);
       }
       if (reverse)
         op2 = op2->prev;
@@ -3031,7 +3029,7 @@ namespace Clipper2Lib {
       {
         Path64 path;
         if (BuildPath64(outrec->pts, reverse_solution_, true, path))
-          open_paths.push_back(path);
+          open_paths.emplace_back(std::move(path));
         continue;
       }
 
@@ -3060,9 +3058,9 @@ namespace Clipper2Lib {
       op2 = op->next;
     }
 #ifdef USINGZ
-    path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z));
+    path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z);
 #else
-    path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
+    path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale);
 #endif
 
     while (op2 != op)
@@ -3071,9 +3069,9 @@ namespace Clipper2Lib {
       {
         lastPt = op2->pt;
 #ifdef USINGZ
-        path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z));
+        path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z);
 #else
-        path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
+        path.emplace_back(lastPt.x * inv_scale, lastPt.y * inv_scale);
 #endif
 
       }
@@ -3137,7 +3135,7 @@ namespace Clipper2Lib {
       {
         PathD path;
         if (BuildPathD(outrec->pts, reverse_solution_, true, path, invScale_))
-          open_paths.push_back(path);
+          open_paths.emplace_back(std::move(path));
         continue;
       }
 

+ 81 - 72
thirdparty/clipper2/src/clipper.offset.cpp

@@ -1,21 +1,34 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
-* Date      :  17 April 2024                                                   *
-* Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2024                                         *
+* Date      :  22 January 2025                                                 *
+* Website   :  https://www.angusj.com                                          *
+* Copyright :  Angus Johnson 2010-2025                                         *
 * Purpose   :  Path Offset (Inflate/Shrink)                                    *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
-#include <cmath>
 #include "clipper2/clipper.h"
 #include "clipper2/clipper.offset.h"
 
 namespace Clipper2Lib {
 
-const double default_arc_tolerance = 0.25;
 const double floating_point_tolerance = 1e-12;
 
+// 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 +51,22 @@ std::optional<size_t> 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<double>(pt2.x - pt1.x);
-	dy = static_cast<double>(pt2.y - pt1.y);
-	inverse_hypot = 1.0 / hypot(dx, dy);
+	double dx = static_cast<double>(pt2.x - pt1.x);
+	double dy = static_cast<double>(pt2.y - pt1.y);
+	double inverse_hypot = 1.0 / Hypot(dx, dy);
 	dx *= inverse_hypot;
 	dy *= inverse_hypot;
 	return PointD(dy, -dx);
@@ -55,12 +77,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 +95,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 +145,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;
 	}
 }
@@ -144,15 +160,13 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType
 
 void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_)
 {
-	Paths64 paths;
-	paths.push_back(path);
-	AddPaths(paths, jt_, et_);
+    groups_.emplace_back(Paths64(1, path), jt_, et_);
 }
 
 void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_)
 {
 	if (paths.size() == 0) return;
-	groups_.push_back(Group(paths, jt_, et_));
+    groups_.emplace_back(paths, jt_, et_);
 }
 
 void ClipperOffset::BuildNormals(const Path64& path)
@@ -162,8 +176,8 @@ void ClipperOffset::BuildNormals(const Path64& path)
 	if (path.size() == 0) return;
 	Path64::const_iterator path_iter, path_stop_iter = --path.cend();
 	for (path_iter = path.cbegin(); path_iter != path_stop_iter; ++path_iter)
-		norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1)));
-	norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
+        norms.emplace_back(GetUnitNormal(*path_iter,*(path_iter +1)));
+    norms.emplace_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
 }
 
 void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
@@ -190,8 +204,8 @@ void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
 		pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y);
 #endif
 	}
-	path_out.push_back(Point64(pt1));
-	path_out.push_back(Point64(pt2));
+    path_out.emplace_back(pt1);
+    path_out.emplace_back(pt2);
 }
 
 void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
@@ -220,17 +234,17 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
 		PointD pt = ptQ;
 		GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
 		//get the second intersect point through reflecion
-		path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
-		path_out.push_back(Point64(pt));
+        path_out.emplace_back(ReflectPoint(pt, ptQ));
+        path_out.emplace_back(pt);
 	}
 	else
 	{
 		PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
 		PointD pt = ptQ;
 		GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
-		path_out.push_back(Point64(pt));
+        path_out.emplace_back(pt);
 		//get the second intersect point through reflecion
-		path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
+        path_out.emplace_back(ReflectPoint(pt, ptQ));
 	}
 }
 
@@ -238,14 +252,14 @@ void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a
 {
 	double q = group_delta_ / (cos_a + 1);
 #ifdef USINGZ
-	path_out.push_back(Point64(
+    path_out.emplace_back(
 		path[j].x + (norms[k].x + norms[j].x) * q,
 		path[j].y + (norms[k].y + norms[j].y) * q,
-		path[j].z));
+        path[j].z);
 #else
-	path_out.push_back(Point64(
+    path_out.emplace_back(
 		path[j].x + (norms[k].x + norms[j].x) * q,
-		path[j].y + (norms[k].y + norms[j].y) * q));
+        path[j].y + (norms[k].y + norms[j].y) * q);
 #endif
 }
 
@@ -256,8 +270,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);
@@ -270,9 +283,9 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle
 
 	if (j == k) offsetVec.Negate();
 #ifdef USINGZ
-	path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z));
+    path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z);
 #else
-	path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y));
+    path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
 #endif
 	int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456
 	for (int i = 1; i < steps; ++i) // ie 1 less than steps
@@ -280,12 +293,12 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle
 		offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y,
 			offsetVec.x * step_sin_ + offsetVec.y * step_cos_);
 #ifdef USINGZ
-		path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z));
+        path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y, pt.z);
 #else
-		path_out.push_back(Point64(pt.x + offsetVec.x, pt.y + offsetVec.y));
+        path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
 #endif
 	}
-	path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
+    path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_));
 }
 
 void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size_t k)
@@ -309,28 +322,25 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
 	}
 	if (std::fabs(group_delta_) <= floating_point_tolerance)
 	{
-		path_out.push_back(path[j]);
+        path_out.emplace_back(path[j]);
 		return;
 	}
 
 	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)
-#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)
-
+		// 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[j], group_delta_), path[j].z));
+        path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_), path[j].z);
+        path_out.emplace_back(path[j]); // (#405, #873, #916)
+        path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_), path[j].z);
 #else
-		path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
+        path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_));
+        path_out.emplace_back(path[j]); // (#405, #873, #916)
+        path_out.emplace_back(GetPerpendic(path[j], norms[j], group_delta_));
 #endif
 	}
 	else if (cos_a > 0.999 && join_type_ != JoinType::Round)
@@ -357,7 +367,7 @@ void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
 	path_out.clear();
 	for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j)
 		OffsetPoint(group, path, j, k);	
-	solution->push_back(path_out);
+    solution->emplace_back(path_out);
 }
 
 void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
@@ -368,7 +378,7 @@ void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
 
 	//rebuild normals 
 	std::reverse(norms.begin(), norms.end());
-	norms.push_back(norms[0]);
+    norms.emplace_back(norms[0]);
 	norms.erase(norms.begin());
 	NegatePath(norms);
 
@@ -381,7 +391,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
 	if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
 
 	if (std::fabs(group_delta_) <= floating_point_tolerance)
-		path_out.push_back(path[0]);
+        path_out.emplace_back(path[0]);
 	else
 	{
 		switch (end_type_)
@@ -413,7 +423,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
 		group_delta_ = deltaCallback64_(path, norms, highI, highI);
 
 	if (std::fabs(group_delta_) <= floating_point_tolerance)
-		path_out.push_back(path[highI]);
+        path_out.emplace_back(path[highI]);
 	else
 	{
 		switch (end_type_)
@@ -432,7 +442,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
 
 	for (size_t j = highI -1, k = highI; j > 0; k = j, --j)
 		OffsetPoint(group, path, j, k);
-	solution->push_back(path_out);
+    solution->emplace_back(path_out);
 }
 
 void ClipperOffset::DoGroupOffset(Group& group)
@@ -454,13 +464,12 @@ void ClipperOffset::DoGroupOffset(Group& group)
 	if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
 	{
 		// calculate the number of steps required to approximate a circle
-		// (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm)
+		// (see https://www.angusj.com/clipper2/Docs/Trigonometry.htm)
 		// arcTol - when arc_tolerance_ is undefined (0) then curve imprecision
 		// will be relative to the size of the offset (delta). Obviously very
 		//large offsets will almost always require much less precision.
-		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);
@@ -507,7 +516,7 @@ void ClipperOffset::DoGroupOffset(Group& group)
 #endif
 			}
 
-			solution->push_back(path_out);
+            solution->emplace_back(path_out);
 			continue;
 		} // end of offsetting a single point
 
@@ -588,7 +597,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 +626,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);
 }

+ 15 - 16
thirdparty/clipper2/src/clipper.rectclip.cpp

@@ -1,13 +1,12 @@
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Date      :  5 July 2024                                                     *
-* Website   :  http://www.angusj.com                                           *
+* Website   :  https://www.angusj.com                                          *
 * Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  FAST rectangular clipping                                       *
-* License   :  http://www.boost.org/LICENSE_1_0.txt                            *
+* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
 *******************************************************************************/
 
-#include <cmath>
 #include "clipper2/clipper.h"
 #include "clipper2/clipper.rectclip.h"
 
@@ -282,7 +281,7 @@ namespace Clipper2Lib {
   {
     if (op->edge) return;
     op->edge = &edge;
-    edge.push_back(op);
+    edge.emplace_back(op);
   }
 
   inline void UncoupleEdge(OutPt2* op)
@@ -328,7 +327,7 @@ namespace Clipper2Lib {
       result->pt = pt;
       result->next = result;
       result->prev = result;
-      results_.push_back(result);
+      results_.emplace_back(result);
     }
     else
     {
@@ -489,7 +488,7 @@ namespace Clipper2Lib {
         {
           bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
           do {
-            start_locs_.push_back(prev);
+            start_locs_.emplace_back(prev);
             prev = GetAdjacentLocation(prev, isClockw);
           } while (prev != loc);
           crossing_loc = crossing_prev; // still not crossed
@@ -514,7 +513,7 @@ namespace Clipper2Lib {
         if (first_cross_ == Location::Inside)
         {
           first_cross_ = crossing_loc;
-          start_locs_.push_back(prev);
+          start_locs_.emplace_back(prev);
         }
         else if (prev != crossing_loc)
         {
@@ -536,7 +535,7 @@ namespace Clipper2Lib {
         if (first_cross_ == Location::Inside)
         {
           first_cross_ = loc;
-          start_locs_.push_back(prev);
+          start_locs_.emplace_back(prev);
         }
 
         loc = crossing_loc;
@@ -750,7 +749,7 @@ namespace Clipper2Lib {
       if (!isRejoining)
       {
         size_t new_idx = results_.size();
-        results_.push_back(p1a);
+        results_.emplace_back(p1a);
         SetNewOwner(p1a, new_idx);
       }
 
@@ -861,11 +860,11 @@ namespace Clipper2Lib {
     if (!op2) return Path64();
 
     Path64 result;
-    result.push_back(op->pt);
+    result.emplace_back(op->pt);
     op2 = op->next;
     while (op2 != op)
     {
-      result.push_back(op2->pt);
+      result.emplace_back(op2->pt);
       op2 = op2->next;
     }
     return result;
@@ -885,7 +884,7 @@ namespace Clipper2Lib {
       else if (rect_.Contains(path_bounds_))
       {
         // the path must be completely inside rect_
-        result.push_back(path);
+        result.emplace_back(path);
         continue;
       }
 
@@ -898,7 +897,7 @@ namespace Clipper2Lib {
       {
         Path64 tmp = GetPath(op);
         if (!tmp.empty())
-          result.emplace_back(tmp);
+          result.emplace_back(std::move(tmp));
       }
 
       //clean up after every loop
@@ -930,7 +929,7 @@ namespace Clipper2Lib {
       {
         Path64 tmp = GetPath(op);
         if (!tmp.empty())
-          result.emplace_back(tmp);
+          result.emplace_back(std::move(tmp));
       }
       results_.clear();
 
@@ -1015,11 +1014,11 @@ namespace Clipper2Lib {
     Path64 result;
     if (!op || op == op->next) return result;
     op = op->next; // starting at path beginning
-    result.push_back(op->pt);
+    result.emplace_back(op->pt);
     OutPt2 *op2 = op->next;
     while (op2 != op)
     {
-      result.push_back(op2->pt);
+      result.emplace_back(op2->pt);
       op2 = op2->next;
     }
     return result;