Explorar o código

clipper2: Update to 1.4.0

Jakub Marcowski hai 1 ano
pai
achega
1bd52fed76

+ 1 - 1
COPYRIGHT.txt

@@ -186,7 +186,7 @@ License: MPL-2.0
 
 
 Files: ./thirdparty/clipper2/
 Files: ./thirdparty/clipper2/
 Comment: Clipper2
 Comment: Clipper2
-Copyright: 2010-2023, Angus Johnson
+Copyright: 2010-2024, Angus Johnson
 License: BSL-1.0
 License: BSL-1.0
 
 
 Files: ./thirdparty/cvtt/
 Files: ./thirdparty/cvtt/

+ 1 - 1
thirdparty/README.md

@@ -111,7 +111,7 @@ Files extracted from upstream source:
 ## clipper2
 ## clipper2
 
 
 - Upstream: https://github.com/AngusJohnson/Clipper2
 - Upstream: https://github.com/AngusJohnson/Clipper2
-- Version: 1.3.0 (98db5662e8dd1808a5a7b50c5605a2289bb390e8, 2023)
+- Version: 1.4.0 (736ddb0b53d97fd5f65dd3d9bbf8a0993eaf387c, 2024)
 - License: BSL 1.0
 - License: BSL 1.0
 
 
 Files extracted from upstream source:
 Files extracted from upstream source:

+ 305 - 86
thirdparty/clipper2/include/clipper2/clipper.core.h

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  24 November 2023                                                *
+* Date      :  12 May 2024                                                     *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  Core Clipper Library structures and functions                   *
 * Purpose   :  Core Clipper Library structures and functions                   *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <algorithm>
 #include <climits>
 #include <climits>
 #include <numeric>
 #include <numeric>
+#include <optional>
 #include "clipper2/clipper.version.h"
 #include "clipper2/clipper.version.h"
 
 
 #define CLIPPER2_THROW(exception) std::abort()
 #define CLIPPER2_THROW(exception) std::abort()
@@ -51,19 +52,19 @@ namespace Clipper2Lib
 
 
   // error codes (2^n)
   // error codes (2^n)
   const int precision_error_i   = 1;  // non-fatal
   const int precision_error_i   = 1;  // non-fatal
-  const int scale_error_i       = 2;  // non-fatal 
-  const int non_pair_error_i    = 4;  // non-fatal 
-  const int undefined_error_i   = 32; // fatal 
+  const int scale_error_i       = 2;  // non-fatal
+  const int non_pair_error_i    = 4;  // non-fatal
+  const int undefined_error_i   = 32; // fatal
   const int range_error_i       = 64;
   const int range_error_i       = 64;
 
 
 #ifndef PI
 #ifndef PI
   static const double PI = 3.141592653589793238;
   static const double PI = 3.141592653589793238;
 #endif
 #endif
 
 
-#ifdef CLIPPER2_MAX_PRECISION
-  const int MAX_DECIMAL_PRECISION = CLIPPER2_MAX_PRECISION;
+#ifdef CLIPPER2_MAX_DECIMAL_PRECISION
+  const int CLIPPER2_MAX_DEC_PRECISION = CLIPPER2_MAX_DECIMAL_PRECISION;
 #else
 #else
-  const int MAX_DECIMAL_PRECISION = 8; // see Discussions #564
+  const int CLIPPER2_MAX_DEC_PRECISION = 8; // see Discussions #564
 #endif
 #endif
 
 
   static const int64_t MAX_COORD = INT64_MAX >> 2;
   static const int64_t MAX_COORD = INT64_MAX >> 2;
@@ -74,7 +75,7 @@ namespace Clipper2Lib
 
 
   static const double MAX_DBL = (std::numeric_limits<double>::max)();
   static const double MAX_DBL = (std::numeric_limits<double>::max)();
 
 
-  static void DoError(int error_code)
+  static void DoError([[maybe_unused]] int error_code)
   {
   {
 #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
 #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
     switch (error_code)
     switch (error_code)
@@ -95,6 +96,13 @@ namespace Clipper2Lib
 #endif
 #endif
   }
   }
 
 
+  // can we call std::round on T? (default false) (#824)
+  template <typename T, typename = void>
+  struct is_round_invocable : std::false_type {};
+
+  template <typename T>
+  struct is_round_invocable<T, std::void_t<decltype(std::round(std::declval<T>()))>> : std::true_type {};
+
 
 
   //By far the most widely used filling rules for polygons are EvenOdd
   //By far the most widely used filling rules for polygons are EvenOdd
   //and NonZero, sometimes called Alternate and Winding respectively.
   //and NonZero, sometimes called Alternate and Winding respectively.
@@ -113,8 +121,8 @@ namespace Clipper2Lib
     template <typename T2>
     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 int64_t z_ = 0)
     {
     {
-      if constexpr (std::numeric_limits<T>::is_integer &&
-        !std::numeric_limits<T2>::is_integer)
+      if constexpr (std::is_integral_v<T> &&
+        is_round_invocable<T2>::value && !std::is_integral_v<T2>)
       {
       {
         x = static_cast<T>(std::round(x_));
         x = static_cast<T>(std::round(x_));
         y = static_cast<T>(std::round(y_));
         y = static_cast<T>(std::round(y_));
@@ -143,6 +151,12 @@ namespace Clipper2Lib
       Init(p.x, p.y, p.z);
       Init(p.x, p.y, p.z);
     }
     }
 
 
+    template <typename T2>
+    explicit Point(const Point<T2>& p, int64_t z_)
+    {
+      Init(p.x, p.y, z_);
+    }
+
     Point operator * (const double scale) const
     Point operator * (const double scale) const
     {
     {
       return Point(x * scale, y * scale, z);
       return Point(x * scale, y * scale, z);
@@ -161,8 +175,8 @@ namespace Clipper2Lib
     template <typename T2>
     template <typename T2>
     inline void Init(const T2 x_ = 0, const T2 y_ = 0)
     inline void Init(const T2 x_ = 0, const T2 y_ = 0)
     {
     {
-      if constexpr (std::numeric_limits<T>::is_integer &&
-        !std::numeric_limits<T2>::is_integer)
+      if constexpr (std::is_integral_v<T> &&
+        is_round_invocable<T2>::value && !std::is_integral_v<T2>)
       {
       {
         x = static_cast<T>(std::round(x_));
         x = static_cast<T>(std::round(x_));
         y = static_cast<T>(std::round(y_));
         y = static_cast<T>(std::round(y_));
@@ -244,6 +258,14 @@ namespace Clipper2Lib
     (std::numeric_limits<double>::max)(),
     (std::numeric_limits<double>::max)(),
     (std::numeric_limits<double>::max)());
     (std::numeric_limits<double>::max)());
 
 
+  template<typename T>
+  static inline Point<T> MidPoint(const Point<T>& p1, const Point<T>& p2)
+  {
+    Point<T> result;
+    result.x = (p1.x + p2.x) / 2;
+    result.y = (p1.y + p2.y) / 2;
+    return result;
+  }
 
 
   // Rect ------------------------------------------------------------------------
   // Rect ------------------------------------------------------------------------
 
 
@@ -275,10 +297,19 @@ namespace Clipper2Lib
       else
       else
       {
       {
         left = top = (std::numeric_limits<T>::max)();
         left = top = (std::numeric_limits<T>::max)();
-        right = bottom = (std::numeric_limits<T>::lowest)();
+        right = bottom = std::numeric_limits<T>::lowest();
       }
       }
     }
     }
 
 
+    static Rect<T> InvalidRect()
+    {
+      return {
+        (std::numeric_limits<T>::max)(),
+        (std::numeric_limits<T>::max)(),
+        std::numeric_limits<T>::lowest(),
+        std::numeric_limits<T>::lowest() };
+    }
+
     bool IsValid() const { return left != (std::numeric_limits<T>::max)(); }
     bool IsValid() const { return left != (std::numeric_limits<T>::max)(); }
 
 
     T Width() const { return right - left; }
     T Width() const { return right - left; }
@@ -329,7 +360,7 @@ namespace Clipper2Lib
     };
     };
 
 
     bool operator==(const Rect<T>& other) const {
     bool operator==(const Rect<T>& other) const {
-      return left == other.left && right == other.right && 
+      return left == other.left && right == other.right &&
         top == other.top && bottom == other.bottom;
         top == other.top && bottom == other.bottom;
     }
     }
 
 
@@ -344,8 +375,8 @@ namespace Clipper2Lib
   {
   {
     Rect<T1> result;
     Rect<T1> result;
 
 
-    if constexpr (std::numeric_limits<T1>::is_integer &&
-      !std::numeric_limits<T2>::is_integer)
+    if constexpr (std::is_integral_v<T1> &&
+      is_round_invocable<T2>::value && !std::is_integral_v<T2>)
     {
     {
       result.left = static_cast<T1>(std::round(rect.left * scale));
       result.left = static_cast<T1>(std::round(rect.left * scale));
       result.top = static_cast<T1>(std::round(rect.top * scale));
       result.top = static_cast<T1>(std::round(rect.top * scale));
@@ -354,32 +385,24 @@ namespace Clipper2Lib
     }
     }
     else
     else
     {
     {
-      result.left = rect.left * scale;
-      result.top = rect.top * scale;
-      result.right = rect.right * scale;
-      result.bottom = rect.bottom * scale;
+      result.left = static_cast<T1>(rect.left * scale);
+      result.top = static_cast<T1>(rect.top * scale);
+      result.right = static_cast<T1>(rect.right * scale);
+      result.bottom = static_cast<T1>(rect.bottom * scale);
     }
     }
     return result;
     return result;
   }
   }
 
 
-  static const Rect64 InvalidRect64 = Rect64(
-    (std::numeric_limits<int64_t>::max)(), 
-    (std::numeric_limits<int64_t>::max)(), 
-    (std::numeric_limits<int64_t>::lowest)(),
-    (std::numeric_limits<int64_t>::lowest)());
-  static const RectD InvalidRectD = RectD(
-    (std::numeric_limits<double>::max)(),
-    (std::numeric_limits<double>::max)(),
-    (std::numeric_limits<double>::lowest)(),
-    (std::numeric_limits<double>::lowest)());
+  static const Rect64 InvalidRect64 = Rect64::InvalidRect();
+  static const RectD InvalidRectD = RectD::InvalidRect();
 
 
   template <typename T>
   template <typename T>
   Rect<T> GetBounds(const Path<T>& path)
   Rect<T> GetBounds(const Path<T>& path)
   {
   {
-    auto xmin = (std::numeric_limits<T>::max)();
-    auto ymin = (std::numeric_limits<T>::max)();
-    auto xmax = std::numeric_limits<T>::lowest();
-    auto ymax = std::numeric_limits<T>::lowest();
+    T xmin = (std::numeric_limits<T>::max)();
+    T ymin = (std::numeric_limits<T>::max)();
+    T xmax = std::numeric_limits<T>::lowest();
+    T ymax = std::numeric_limits<T>::lowest();
     for (const auto& p : path)
     for (const auto& p : path)
     {
     {
       if (p.x < xmin) xmin = p.x;
       if (p.x < xmin) xmin = p.x;
@@ -393,17 +416,52 @@ namespace Clipper2Lib
   template <typename T>
   template <typename T>
   Rect<T> GetBounds(const Paths<T>& paths)
   Rect<T> GetBounds(const Paths<T>& paths)
   {
   {
-    auto xmin = (std::numeric_limits<T>::max)();
-    auto ymin = (std::numeric_limits<T>::max)();
-    auto xmax = std::numeric_limits<T>::lowest();
-    auto ymax = std::numeric_limits<T>::lowest();
+    T xmin = (std::numeric_limits<T>::max)();
+    T ymin = (std::numeric_limits<T>::max)();
+    T xmax = std::numeric_limits<T>::lowest();
+    T ymax = std::numeric_limits<T>::lowest();
     for (const Path<T>& path : paths)
     for (const Path<T>& path : paths)
       for (const Point<T>& p : path)
       for (const Point<T>& p : path)
       {
       {
-      if (p.x < xmin) xmin = p.x;
-      if (p.x > xmax) xmax = p.x;
-      if (p.y < ymin) ymin = p.y;
-      if (p.y > ymax) ymax = p.y;
+        if (p.x < xmin) xmin = p.x;
+        if (p.x > xmax) xmax = p.x;
+        if (p.y < ymin) ymin = p.y;
+        if (p.y > ymax) ymax = p.y;
+      }
+    return Rect<T>(xmin, ymin, xmax, ymax);
+  }
+
+  template <typename T, typename T2>
+  Rect<T> GetBounds(const Path<T2>& path)
+  {
+    T xmin = (std::numeric_limits<T>::max)();
+    T ymin = (std::numeric_limits<T>::max)();
+    T xmax = std::numeric_limits<T>::lowest();
+    T ymax = std::numeric_limits<T>::lowest();
+    for (const auto& p : path)
+    {
+      if (p.x < xmin) xmin = static_cast<T>(p.x);
+      if (p.x > xmax) xmax = static_cast<T>(p.x);
+      if (p.y < ymin) ymin = static_cast<T>(p.y);
+      if (p.y > ymax) ymax = static_cast<T>(p.y);
+    }
+    return Rect<T>(xmin, ymin, xmax, ymax);
+  }
+
+  template <typename T, typename T2>
+  Rect<T> GetBounds(const Paths<T2>& paths)
+  {
+    T xmin = (std::numeric_limits<T>::max)();
+    T ymin = (std::numeric_limits<T>::max)();
+    T xmax = std::numeric_limits<T>::lowest();
+    T ymax = std::numeric_limits<T>::lowest();
+    for (const Path<T2>& path : paths)
+      for (const Point<T2>& p : path)
+      {
+        if (p.x < xmin) xmin = static_cast<T>(p.x);
+        if (p.x > xmax) xmax = static_cast<T>(p.x);
+        if (p.y < ymin) ymin = static_cast<T>(p.y);
+        if (p.y > ymax) ymax = static_cast<T>(p.y);
       }
       }
     return Rect<T>(xmin, ymin, xmax, ymax);
     return Rect<T>(xmin, ymin, xmax, ymax);
   }
   }
@@ -431,7 +489,7 @@ namespace Clipper2Lib
 
 
 
 
   template <typename T1, typename T2>
   template <typename T1, typename T2>
-  inline Path<T1> ScalePath(const Path<T2>& path, 
+  inline Path<T1> ScalePath(const Path<T2>& path,
     double scale_x, double scale_y, int& error_code)
     double scale_x, double scale_y, int& error_code)
   {
   {
     Path<T1> result;
     Path<T1> result;
@@ -447,11 +505,11 @@ namespace Clipper2Lib
     result.reserve(path.size());
     result.reserve(path.size());
 #ifdef USINGZ
 #ifdef USINGZ
     std::transform(path.begin(), path.end(), back_inserter(result),
     std::transform(path.begin(), path.end(), back_inserter(result),
-      [scale_x, scale_y](const auto& pt) 
+      [scale_x, scale_y](const auto& pt)
       { return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
       { return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
 #else
 #else
     std::transform(path.begin(), path.end(), back_inserter(result),
     std::transform(path.begin(), path.end(), back_inserter(result),
-      [scale_x, scale_y](const auto& pt) 
+      [scale_x, scale_y](const auto& pt)
       { return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
       { return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
 #endif
 #endif
     return result;
     return result;
@@ -465,20 +523,19 @@ namespace Clipper2Lib
   }
   }
 
 
   template <typename T1, typename T2>
   template <typename T1, typename T2>
-  inline Paths<T1> ScalePaths(const Paths<T2>& paths, 
+  inline Paths<T1> ScalePaths(const Paths<T2>& paths,
     double scale_x, double scale_y, int& error_code)
     double scale_x, double scale_y, int& error_code)
   {
   {
     Paths<T1> result;
     Paths<T1> result;
 
 
-    if constexpr (std::numeric_limits<T1>::is_integer &&
-      !std::numeric_limits<T2>::is_integer)
+    if constexpr (std::is_integral_v<T1>)
     {
     {
-      RectD r = GetBounds(paths);
+      RectD r = GetBounds<double, T2>(paths);
       if ((r.left * scale_x) < min_coord ||
       if ((r.left * scale_x) < min_coord ||
         (r.right * scale_x) > max_coord ||
         (r.right * scale_x) > max_coord ||
         (r.top * scale_y) < min_coord ||
         (r.top * scale_y) < min_coord ||
         (r.bottom * scale_y) > max_coord)
         (r.bottom * scale_y) > max_coord)
-      { 
+      {
         error_code |= range_error_i;
         error_code |= range_error_i;
         DoError(range_error_i);
         DoError(range_error_i);
         return result; // empty path
         return result; // empty path
@@ -493,7 +550,7 @@ namespace Clipper2Lib
   }
   }
 
 
   template <typename T1, typename T2>
   template <typename T1, typename T2>
-  inline Paths<T1> ScalePaths(const Paths<T2>& paths, 
+  inline Paths<T1> ScalePaths(const Paths<T2>& paths,
     double scale, int& error_code)
     double scale, int& error_code)
   {
   {
     return ScalePaths<T1, T2>(paths, scale, scale, error_code);
     return ScalePaths<T1, T2>(paths, scale, scale, error_code);
@@ -590,20 +647,94 @@ namespace Clipper2Lib
 
 
   // Miscellaneous ------------------------------------------------------------
   // Miscellaneous ------------------------------------------------------------
 
 
-  inline void CheckPrecision(int& precision, int& error_code)
+  inline void CheckPrecisionRange(int& precision, int& error_code)
   {
   {
-    if (precision >= -MAX_DECIMAL_PRECISION && precision <= MAX_DECIMAL_PRECISION) return;
+    if (precision >= -CLIPPER2_MAX_DEC_PRECISION &&
+      precision <= CLIPPER2_MAX_DEC_PRECISION) return;
     error_code |= precision_error_i; // non-fatal error
     error_code |= precision_error_i; // non-fatal error
-    DoError(precision_error_i);      // does nothing unless exceptions enabled
-    precision = precision > 0 ? MAX_DECIMAL_PRECISION : -MAX_DECIMAL_PRECISION;
+    DoError(precision_error_i);      // does nothing when exceptions are disabled
+    precision = precision > 0 ? CLIPPER2_MAX_DEC_PRECISION : -CLIPPER2_MAX_DEC_PRECISION;
   }
   }
 
 
-  inline void CheckPrecision(int& precision)
+  inline void CheckPrecisionRange(int& precision)
   {
   {
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
+  }
+
+  inline int TriSign(int64_t x) // returns 0, 1 or -1
+  {
+    return (x > 0) - (x < 0); 
+  }
+
+  struct MultiplyUInt64Result
+  {
+    const uint64_t result = 0;
+    const uint64_t carry = 0;
+
+    bool operator==(const MultiplyUInt64Result& other) const
+    {
+      return result == other.result && carry == other.carry;
+    };
+  };
+
+  inline MultiplyUInt64Result Multiply(uint64_t a, uint64_t b) // #834, #835
+  {
+    const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
+    const auto hi = [](uint64_t x) { return x >> 32; };
+
+    const uint64_t x1 = lo(a) * lo(b);
+    const uint64_t x2 = hi(a) * lo(b) + hi(x1);
+    const uint64_t x3 = lo(a) * hi(b) + lo(x2);
+    const uint64_t result = lo(x3) << 32 | lo(x1);
+    const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3);
+
+    return { result, carry };
+  }
+
+  // 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)
+  {
+// -- GODOT start --
+// #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
+// -- GODOT end --
+    // 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));
+    const auto abs_c = static_cast<uint64_t>(std::abs(c));
+    const auto abs_d = static_cast<uint64_t>(std::abs(d));
+
+    const auto abs_ab = Multiply(abs_a, abs_b);
+    const auto abs_cd = Multiply(abs_c, abs_d);
+
+    // nb: it's important to differentiate 0 values here from other values
+    const auto sign_ab = TriSign(a) * TriSign(b);
+    const auto sign_cd = TriSign(c) * TriSign(d);
+
+    return abs_ab == abs_cd && sign_ab == sign_cd;
+// -- GODOT start --
+// #endif
+// -- GODOT end --
+  }
+
+  template <typename T>
+  inline bool IsCollinear(const Point<T>& pt1,
+    const Point<T>& sharedPt, const Point<T>& pt2) // #777
+  {
+    const auto a = sharedPt.x - pt1.x;
+    const auto b = pt2.y - sharedPt.y;
+    const auto c = sharedPt.y - pt1.y;
+    const auto d = pt2.x - sharedPt.x;
+    // When checking for collinearity with very large coordinate values
+    // then ProductsAreEqual is more accurate than using CrossProduct.
+    return ProductsAreEqual(a, b, c, d);
   }
   }
 
 
+
   template <typename T>
   template <typename T>
   inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
   inline double CrossProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
     return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
     return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.y -
@@ -635,15 +766,17 @@ namespace Clipper2Lib
   }
   }
 
 
   template <typename T>
   template <typename T>
-  inline double DistanceFromLineSqrd(const Point<T>& pt, const Point<T>& ln1, const Point<T>& ln2)
+  inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
+    const Point<T>& line1, const Point<T>& line2)
   {
   {
     //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
     //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
     //see http://en.wikipedia.org/wiki/Perpendicular_distance
     //see http://en.wikipedia.org/wiki/Perpendicular_distance
-    double A = static_cast<double>(ln1.y - ln2.y);
-    double B = static_cast<double>(ln2.x - ln1.x);
-    double C = A * ln1.x + B * ln1.y;
-    C = A * pt.x + B * pt.y - C;
-    return (C * C) / (A * A + B * B);
+    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);
+    double d = static_cast<double>(line2.y - line1.y);
+    if (c == 0 && d == 0) return 0;
+    return Sqr(a * d - c * b) / (c * c + d * d);
   }
   }
 
 
   template <typename T>
   template <typename T>
@@ -663,7 +796,7 @@ namespace Clipper2Lib
     }
     }
     if (cnt & 1)
     if (cnt & 1)
       a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
       a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
-    return a * 0.5;
+    return (a * 0.5);
   }
   }
 
 
   template <typename T>
   template <typename T>
@@ -681,16 +814,73 @@ namespace Clipper2Lib
   template <typename T>
   template <typename T>
   inline bool IsPositive(const Path<T>& poly)
   inline bool IsPositive(const Path<T>& poly)
   {
   {
-    // A curve has positive orientation [and area] if a region 'R' 
+    // A curve has positive orientation [and area] if a region 'R'
     // is on the left when traveling around the outside of 'R'.
     // is on the left when traveling around the outside of 'R'.
     //https://mathworld.wolfram.com/CurveOrientation.html
     //https://mathworld.wolfram.com/CurveOrientation.html
     //nb: This statement is premised on using Cartesian coordinates
     //nb: This statement is premised on using Cartesian coordinates
     return Area<T>(poly) >= 0;
     return Area<T>(poly) >= 0;
   }
   }
-  
-  inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b,
-    const Point64& ln2a, const Point64& ln2b, Point64& ip)
-  {  
+
+#if CLIPPER2_HI_PRECISION
+  // caution: this will compromise performance
+  // https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253
+  // See also CPP/BenchMark/GetIntersectPtBenchmark.cpp
+  #define CC_MIN(x,y) ((x)>(y)?(y):(x))
+  #define CC_MAX(x,y) ((x)<(y)?(y):(x))
+  template<typename T>
+  inline bool GetSegmentIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
+    const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
+  {
+    double ln1dy = static_cast<double>(ln1b.y - ln1a.y);
+    double ln1dx = static_cast<double>(ln1a.x - ln1b.x);
+    double ln2dy = static_cast<double>(ln2b.y - ln2a.y);
+    double ln2dx = static_cast<double>(ln2a.x - ln2b.x);
+    double det = (ln2dy * ln1dx) - (ln1dy * ln2dx);
+    if (det == 0.0) return false;
+    T bb0minx = CC_MIN(ln1a.x, ln1b.x);
+    T bb0miny = CC_MIN(ln1a.y, ln1b.y);
+    T bb0maxx = CC_MAX(ln1a.x, ln1b.x);
+    T bb0maxy = CC_MAX(ln1a.y, ln1b.y);
+    T bb1minx = CC_MIN(ln2a.x, ln2b.x);
+    T bb1miny = CC_MIN(ln2a.y, ln2b.y);
+    T bb1maxx = CC_MAX(ln2a.x, ln2b.x);
+    T bb1maxy = CC_MAX(ln2a.y, ln2b.y);
+
+    if constexpr (std::is_integral_v<T>)
+    {
+      int64_t originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) >> 1;
+      int64_t originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) >> 1;
+      double ln0c = (ln1dy * static_cast<double>(ln1a.x - originx)) +
+        (ln1dx * static_cast<double>(ln1a.y - originy));
+      double ln1c = (ln2dy * static_cast<double>(ln2a.x - originx)) +
+        (ln2dx * static_cast<double>(ln2a.y - originy));
+      double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det;
+      double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det;
+
+      ip.x = originx + (T)nearbyint(hitx);
+      ip.y = originy + (T)nearbyint(hity);
+    }
+    else
+    {
+      double originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) / 2.0;
+      double originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) / 2.0;
+      double ln0c = (ln1dy * static_cast<double>(ln1a.x - originx)) +
+        (ln1dx * static_cast<double>(ln1a.y - originy));
+      double ln1c = (ln2dy * static_cast<double>(ln2a.x - originx)) +
+        (ln2dx * static_cast<double>(ln2a.y - originy));
+      double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det;
+      double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det;
+
+      ip.x = originx + static_cast<T>(hitx);
+      ip.y = originy + static_cast<T>(hity);
+    }
+    return true;
+}
+#else
+  template<typename T>
+  inline bool GetSegmentIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
+    const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
+  {
     // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
     // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
     double dx1 = static_cast<double>(ln1b.x - ln1a.x);
     double dx1 = static_cast<double>(ln1b.x - ln1a.x);
     double dy1 = static_cast<double>(ln1b.y - ln1a.y);
     double dy1 = static_cast<double>(ln1b.y - ln1a.y);
@@ -700,15 +890,44 @@ namespace Clipper2Lib
     double det = dy1 * dx2 - dy2 * dx1;
     double det = dy1 * dx2 - dy2 * dx1;
     if (det == 0.0) return false;
     if (det == 0.0) return false;
     double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
     double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
-    if (t <= 0.0) ip = ln1a;        // ?? check further (see also #568)
-    else if (t >= 1.0) ip = ln1b;   // ?? check further
+    if (t <= 0.0) ip = ln1a;
+    else if (t >= 1.0) ip = ln1b;
     else
     else
     {
     {
-      ip.x = static_cast<int64_t>(ln1a.x + t * dx1);
-      ip.y = static_cast<int64_t>(ln1a.y + t * dy1);
-    }
+      ip.x = static_cast<T>(ln1a.x + t * dx1);
+      ip.y = static_cast<T>(ln1a.y + t * dy1);
+  }
     return true;
     return true;
   }
   }
+#endif
+
+  template<typename T>
+  inline Point<T> TranslatePoint(const Point<T>& pt, double dx, double dy)
+  {
+#ifdef USINGZ
+    return Point<T>(pt.x + dx, pt.y + dy, pt.z);
+#else
+    return Point<T>(pt.x + dx, pt.y + dy);
+#endif
+  }
+
+
+  template<typename T>
+  inline Point<T> ReflectPoint(const Point<T>& pt, const Point<T>& pivot)
+  {
+#ifdef USINGZ
+    return Point<T>(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z);
+#else
+    return Point<T>(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
+#endif
+  }
+
+  template<typename T>
+  inline int GetSign(const T& val) 
+  { 
+    if (!val) return 0; 
+    return (val > 0) ? 1 : -1;
+  }
 
 
   inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
   inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
     const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
     const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
@@ -724,10 +943,10 @@ namespace Clipper2Lib
       return (res1 || res2 || res3 || res4); // ensures not collinear
       return (res1 || res2 || res3 || res4); // ensures not collinear
     }
     }
     else {
     else {
-      return (CrossProduct(seg1a, seg2a, seg2b) *
-        CrossProduct(seg1b, seg2a, seg2b) < 0) &&
-        (CrossProduct(seg2a, seg1a, seg1b) *
-          CrossProduct(seg2b, seg1a, seg1b) < 0);
+      return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) *
+        GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) &&
+        (GetSign(CrossProduct(seg2a, seg1a, seg1b)) *
+          GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0);
     }
     }
   }
   }
 
 
@@ -743,7 +962,7 @@ namespace Clipper2Lib
         static_cast<double>(offPt.y - seg1.y) * dy) /
         static_cast<double>(offPt.y - seg1.y) * dy) /
       (Sqr(dx) + Sqr(dy));
       (Sqr(dx) + Sqr(dy));
     if (q < 0) q = 0; else if (q > 1) q = 1;
     if (q < 0) q = 0; else if (q > 1) q = 1;
-    if constexpr (std::numeric_limits<T>::is_integer)
+    if constexpr (std::is_integral_v<T>)
       return Point<T>(
       return Point<T>(
         seg1.x + static_cast<T>(nearbyint(q * dx)),
         seg1.x + static_cast<T>(nearbyint(q * dx)),
         seg1.y + static_cast<T>(nearbyint(q * dy)));
         seg1.y + static_cast<T>(nearbyint(q * dy)));
@@ -770,7 +989,7 @@ namespace Clipper2Lib
       return PointInPolygonResult::IsOutside;
       return PointInPolygonResult::IsOutside;
 
 
     bool is_above = first->y < pt.y, starting_above = is_above;
     bool is_above = first->y < pt.y, starting_above = is_above;
-    curr = first +1; 
+    curr = first +1;
     while (true)
     while (true)
     {
     {
       if (curr == cend)
       if (curr == cend)
@@ -779,7 +998,7 @@ namespace Clipper2Lib
         cend = first;
         cend = first;
         curr = cbegin;
         curr = cbegin;
       }
       }
-      
+
       if (is_above)
       if (is_above)
       {
       {
         while (curr != cend && curr->y < pt.y) ++curr;
         while (curr != cend && curr->y < pt.y) ++curr;
@@ -791,14 +1010,14 @@ namespace Clipper2Lib
         if (curr == cend) continue;
         if (curr == cend) continue;
       }
       }
 
 
-      if (curr == cbegin) 
+      if (curr == cbegin)
         prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
         prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
-      else  
+      else
         prev = curr - 1;
         prev = curr - 1;
 
 
       if (curr->y == pt.y)
       if (curr->y == pt.y)
       {
       {
-        if (curr->x == pt.x || 
+        if (curr->x == pt.x ||
           (curr->y == prev->y &&
           (curr->y == prev->y &&
             ((pt.x < prev->x) != (pt.x < curr->x))))
             ((pt.x < prev->x) != (pt.x < curr->x))))
               return PointInPolygonResult::IsOn;
               return PointInPolygonResult::IsOn;
@@ -822,7 +1041,7 @@ namespace Clipper2Lib
       is_above = !is_above;
       is_above = !is_above;
       ++curr;
       ++curr;
     }
     }
-    
+
     if (is_above != starting_above)
     if (is_above != starting_above)
     {
     {
       cend = polygon.cend();
       cend = polygon.cend();

+ 39 - 35
thirdparty/clipper2/include/clipper2/clipper.engine.h

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  22 November 2023                                                *
+* Date      :  5 July 2024                                                     *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This is the main polygon clipping module                        *
 * Purpose   :  This is the main polygon clipping module                        *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -33,7 +33,7 @@ namespace Clipper2Lib {
 
 
 	//Note: all clipping operations except for Difference are commutative.
 	//Note: all clipping operations except for Difference are commutative.
 	enum class ClipType { None, Intersection, Union, Difference, Xor };
 	enum class ClipType { None, Intersection, Union, Difference, Xor };
-	
+
 	enum class PathType { Subject, Clip };
 	enum class PathType { Subject, Clip };
 	enum class JoinWith { None, Left, Right };
 	enum class JoinWith { None, Left, Right };
 
 
@@ -41,7 +41,7 @@ namespace Clipper2Lib {
 		None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
 		None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
 	};
 	};
 
 
-	constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) 
+	constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
 	{
 	{
 		return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
 		return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
 	}
 	}
@@ -95,7 +95,7 @@ namespace Clipper2Lib {
 		Path64 path;
 		Path64 path;
 		bool is_open = false;
 		bool is_open = false;
 
 
-		~OutRec() { 
+		~OutRec() {
 			if (splits) delete splits;
 			if (splits) delete splits;
 			// nb: don't delete the split pointers
 			// nb: don't delete the split pointers
 			// as these are owned by ClipperBase's outrec_list_
 			// as these are owned by ClipperBase's outrec_list_
@@ -106,7 +106,7 @@ namespace Clipper2Lib {
 	//Important: UP and DOWN here are premised on Y-axis positive down
 	//Important: UP and DOWN here are premised on Y-axis positive down
 	//displays, which is the orientation used in Clipper's development.
 	//displays, which is the orientation used in Clipper's development.
 	///////////////////////////////////////////////////////////////////
 	///////////////////////////////////////////////////////////////////
-	
+
 	struct Active {
 	struct Active {
 		Point64 bot;
 		Point64 bot;
 		Point64 top;
 		Point64 top;
@@ -230,7 +230,7 @@ namespace Clipper2Lib {
 		inline bool PopHorz(Active *&e);
 		inline bool PopHorz(Active *&e);
 		inline OutPt* StartOpenPath(Active &e, const Point64& pt);
 		inline OutPt* StartOpenPath(Active &e, const Point64& pt);
 		inline void UpdateEdgeIntoAEL(Active *e);
 		inline void UpdateEdgeIntoAEL(Active *e);
-		OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt);
+		void IntersectEdges(Active &e1, Active &e2, const Point64& pt);
 		inline void DeleteFromAEL(Active &e);
 		inline void DeleteFromAEL(Active &e);
 		inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
 		inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
 		void DoIntersections(const int64_t top_y);
 		void DoIntersections(const int64_t top_y);
@@ -240,7 +240,7 @@ namespace Clipper2Lib {
 		void SwapPositionsInAEL(Active& edge1, Active& edge2);
 		void SwapPositionsInAEL(Active& edge1, Active& edge2);
 		OutRec* NewOutRec();
 		OutRec* NewOutRec();
 		OutPt* AddOutPt(const Active &e, const Point64& pt);
 		OutPt* AddOutPt(const Active &e, const Point64& pt);
-		OutPt* AddLocalMinPoly(Active &e1, Active &e2, 
+		OutPt* AddLocalMinPoly(Active &e1, Active &e2,
 			const Point64& pt, bool is_new = false);
 			const Point64& pt, bool is_new = false);
 		OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
 		OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
 		void DoHorizontal(Active &horz);
 		void DoHorizontal(Active &horz);
@@ -251,13 +251,13 @@ namespace Clipper2Lib {
 		void JoinOutrecPaths(Active &e1, Active &e2);
 		void JoinOutrecPaths(Active &e1, Active &e2);
 		void FixSelfIntersects(OutRec* outrec);
 		void FixSelfIntersects(OutRec* outrec);
 		void DoSplitOp(OutRec* outRec, OutPt* splitOp);
 		void DoSplitOp(OutRec* outRec, OutPt* splitOp);
-		
+
 		inline void AddTrialHorzJoin(OutPt* op);
 		inline void AddTrialHorzJoin(OutPt* op);
 		void ConvertHorzSegsToJoins();
 		void ConvertHorzSegsToJoins();
 		void ProcessHorzJoins();
 		void ProcessHorzJoins();
 
 
 		void Split(Active& e, const Point64& pt);
 		void Split(Active& e, const Point64& pt);
-		inline void CheckJoinLeft(Active& e, 
+		inline void CheckJoinLeft(Active& e,
 			const Point64& pt, bool check_curr_x = false);
 			const Point64& pt, bool check_curr_x = false);
 		inline void CheckJoinRight(Active& e,
 		inline void CheckJoinRight(Active& e,
 			const Point64& pt, bool check_curr_x = false);
 			const Point64& pt, bool check_curr_x = false);
@@ -326,12 +326,12 @@ namespace Clipper2Lib {
 
 
 		const PolyPath* Parent() const { return parent_; }
 		const PolyPath* Parent() const { return parent_; }
 
 
-		bool IsHole() const 
+		bool IsHole() const
 		{
 		{
 			unsigned lvl = Level();
 			unsigned lvl = Level();
 			//Even levels except level 0
 			//Even levels except level 0
 			return lvl && !(lvl & 1);
 			return lvl && !(lvl & 1);
-		}		
+		}
 	};
 	};
 
 
 	typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
 	typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
@@ -343,15 +343,16 @@ namespace Clipper2Lib {
 		Path64 polygon_;
 		Path64 polygon_;
 	public:
 	public:
 		explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
 		explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
+		explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; }
 
 
 		~PolyPath64() {
 		~PolyPath64() {
 			childs_.resize(0);
 			childs_.resize(0);
 		}
 		}
 
 
 		PolyPath64* operator [] (size_t index) const
 		PolyPath64* operator [] (size_t index) const
-		{ 
+		{
 			return childs_[index].get(); //std::unique_ptr
 			return childs_[index].get(); //std::unique_ptr
-		} 
+		}
 
 
 		PolyPath64* Child(size_t index) const
 		PolyPath64* Child(size_t index) const
 		{
 		{
@@ -363,10 +364,7 @@ namespace Clipper2Lib {
 
 
 		PolyPath64* AddChild(const Path64& path) override
 		PolyPath64* AddChild(const Path64& path) override
 		{
 		{
-			auto p = std::make_unique<PolyPath64>(this);
-			auto* result = childs_.emplace_back(std::move(p)).get();
-			result->polygon_ = path;
-			return result;
+			return childs_.emplace_back(std::make_unique<PolyPath64>(this, path)).get();
 		}
 		}
 
 
 		void Clear() override
 		void Clear() override
@@ -401,12 +399,25 @@ namespace Clipper2Lib {
 			scale_ = parent ? parent->scale_ : 1.0;
 			scale_ = parent ? parent->scale_ : 1.0;
 		}
 		}
 
 
+		explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent)
+		{
+			scale_ = parent ? parent->scale_ : 1.0;
+			int error_code = 0;
+			polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
+		}
+
+		explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent)
+		{
+			scale_ = parent ? parent->scale_ : 1.0;
+			polygon_ = path;
+		}
+
 		~PolyPathD() {
 		~PolyPathD() {
 			childs_.resize(0);
 			childs_.resize(0);
 		}
 		}
 
 
 		PolyPathD* operator [] (size_t index) const
 		PolyPathD* operator [] (size_t index) const
-		{ 
+		{
 			return childs_[index].get();
 			return childs_[index].get();
 		}
 		}
 
 
@@ -420,22 +431,15 @@ namespace Clipper2Lib {
 
 
 		void SetScale(double value) { scale_ = value; }
 		void SetScale(double value) { scale_ = value; }
 		double Scale() const { return scale_; }
 		double Scale() const { return scale_; }
-		
+
 		PolyPathD* AddChild(const Path64& path) override
 		PolyPathD* AddChild(const Path64& path) override
 		{
 		{
-			int error_code = 0;
-			auto p = std::make_unique<PolyPathD>(this);
-			PolyPathD* result = childs_.emplace_back(std::move(p)).get();
-			result->polygon_ = ScalePath<double, int64_t>(path, scale_, error_code);
-			return result;
+			return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
 		}
 		}
 
 
 		PolyPathD* AddChild(const PathD& path)
 		PolyPathD* AddChild(const PathD& path)
 		{
 		{
-			auto p = std::make_unique<PolyPathD>(this);
-			PolyPathD* result = childs_.emplace_back(std::move(p)).get();
-			result->polygon_ = path;
-			return result;
+			return childs_.emplace_back(std::make_unique<PolyPathD>(this, path)).get();
 		}
 		}
 
 
 		void Clear() override
 		void Clear() override
@@ -488,7 +492,7 @@ namespace Clipper2Lib {
 			return Execute(clip_type, fill_rule, closed_paths, dummy);
 			return Execute(clip_type, fill_rule, closed_paths, dummy);
 		}
 		}
 
 
-		bool Execute(ClipType clip_type, FillRule fill_rule, 
+		bool Execute(ClipType clip_type, FillRule fill_rule,
 			Paths64& closed_paths, Paths64& open_paths)
 			Paths64& closed_paths, Paths64& open_paths)
 		{
 		{
 			closed_paths.clear();
 			closed_paths.clear();
@@ -530,7 +534,7 @@ namespace Clipper2Lib {
 	public:
 	public:
 		explicit ClipperD(int precision = 2) : ClipperBase()
 		explicit ClipperD(int precision = 2) : ClipperBase()
 		{
 		{
-			CheckPrecision(precision, error_code_);
+			CheckPrecisionRange(precision, error_code_);
 			// to optimize scaling / descaling precision
 			// to optimize scaling / descaling precision
 			// set the scale to a power of double's radix (2) (#25)
 			// set the scale to a power of double's radix (2) (#25)
 			scale_ = std::pow(std::numeric_limits<double>::radix,
 			scale_ = std::pow(std::numeric_limits<double>::radix,
@@ -560,12 +564,12 @@ namespace Clipper2Lib {
 		void CheckCallback()
 		void CheckCallback()
 		{
 		{
 			if(zCallbackD_)
 			if(zCallbackD_)
-				// if the user defined float point callback has been assigned 
+				// if the user defined float point callback has been assigned
 				// then assign the proxy callback function
 				// then assign the proxy callback function
-				ClipperBase::zCallback_ = 
+				ClipperBase::zCallback_ =
 					std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
 					std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
 					std::placeholders::_2, std::placeholders::_3,
 					std::placeholders::_2, std::placeholders::_3,
-					std::placeholders::_4, std::placeholders::_5); 
+					std::placeholders::_4, std::placeholders::_5);
 			else
 			else
 				ClipperBase::zCallback_ = nullptr;
 				ClipperBase::zCallback_ = nullptr;
 		}
 		}
@@ -632,6 +636,6 @@ namespace Clipper2Lib {
 
 
 	};
 	};
 
 
-}  // namespace 
+}  // namespace
 
 
 #endif  // CLIPPER_ENGINE_H
 #endif  // CLIPPER_ENGINE_H

+ 79 - 44
thirdparty/clipper2/include/clipper2/clipper.export.h

@@ -1,14 +1,14 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  26 November 2023                                                *
+* Date      :  14 May 2024                                                     *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This module exports the Clipper2 Library (ie DLL/so)            *
 * Purpose   :  This module exports the Clipper2 Library (ie DLL/so)            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
 
 
 
 
-/* 
+/*
  Boolean clipping:
  Boolean clipping:
  cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4
  cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4
  fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
  fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
@@ -19,12 +19,12 @@
 
 
 The path structures used extensively in other parts of this library are all
 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
 based on std::vector classes. Since C++ classes can't be accessed by other
-languages, these paths must be converted into simple C data structures that
-can be understood by just about any programming language. And these C style
-path structures are simple arrays of int64_t (CPath64) and double (CPathD).
+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.
 
 
 CPath64 and CPathD:
 CPath64 and CPathD:
-These are arrays of consecutive x and y path coordinates preceeded by  
+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.
 a pair of values containing the path's length (N) and a 0 value.
 __________________________________
 __________________________________
 |counter|coord1|coord2|...|coordN|
 |counter|coord1|coord2|...|coordN|
@@ -34,23 +34,24 @@ __________________________________
 CPaths64 and CPathsD:
 CPaths64 and CPathsD:
 These are also arrays containing any number of consecutive CPath64 or
 These are also arrays containing any number of consecutive CPath64 or
 CPathD  structures. But preceeding these consecutive paths, there is pair of
 CPathD  structures. But preceeding these consecutive paths, there is pair of
-values that contain the total length of the array (A) structure and 
-the number (C) of CPath64 or CPathD it contains.
+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|
 |counter|path1|path2|...|pathC|
 |A  , C |                     |
 |A  , C |                     |
 _______________________________
 _______________________________
 
 
 CPolytree64 and CPolytreeD:
 CPolytree64 and CPolytreeD:
-These are also arrays consisting of CPolyPath structures that represent 
+These are also arrays consisting of CPolyPath structures that represent
 individual paths in a tree structure. However, the very first (ie top)
 individual paths in a tree structure. However, the very first (ie top)
-CPolyPath is just the tree container that won't have a path. And because
+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
 of that, its structure will be very slightly different from the remaining
 CPolyPath. This difference will be discussed below.
 CPolyPath. This difference will be discussed below.
 
 
 CPolyPath64 and CPolyPathD:
 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 
+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).
 indicating the length of the path (N) and the number of child CPolyPath (C).
 ____________________________________________________________
 ____________________________________________________________
 |counter|coord1|coord2|...|coordN| child1|child2|...|childC|
 |counter|coord1|coord2|...|coordN| child1|child2|...|childC|
@@ -58,19 +59,20 @@ ____________________________________________________________
 ____________________________________________________________
 ____________________________________________________________
 
 
 As mentioned above, the very first CPolyPath structure is just a container
 As mentioned above, the very first CPolyPath structure is just a container
-that owns (both directly and indirectly) every other CPolyPath in the tree. 
+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
 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 structure.
-
-All theses exported structures (CPaths64, CPathsD, CPolyTree64 & CPolyTreeD)
-are arrays of type int64_t or double. And the first value in these arrays 
-will always contain the length of that array.
-
-These array structures are allocated in heap memory which will eventually 
-need to be released. But 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 below.
+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).
 */
 */
 
 
 
 
@@ -128,7 +130,7 @@ inline Rect<T> CRectToRect(const CRect<T>& rect)
 #ifdef _WIN32
 #ifdef _WIN32
   #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
   #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
 #else
 #else
-  #define EXTERN_DLL_EXPORT extern "C" 
+  #define EXTERN_DLL_EXPORT extern "C"
 #endif
 #endif
 
 
 
 
@@ -173,8 +175,8 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
   bool preserve_collinear = true, bool reverse_solution = false);
   bool preserve_collinear = true, bool reverse_solution = false);
 
 
 EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
 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, 
+  double delta, uint8_t jointype, uint8_t endtype,
+  double miter_limit = 2.0, double arc_tolerance = 0.0,
   bool reverse_solution = false);
   bool reverse_solution = false);
 EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
 EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
   double delta, uint8_t jointype, uint8_t endtype,
   double delta, uint8_t jointype, uint8_t endtype,
@@ -219,10 +221,10 @@ static size_t GetPolyPath64ArrayLen(const PolyPath64& pp)
   return result;
   return result;
 }
 }
 
 
-static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree, 
+static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree,
   size_t& cnt, size_t& array_len)
   size_t& cnt, size_t& array_len)
 {
 {
-  cnt = tree.Count(); // nb: top level count only 
+  cnt = tree.Count(); // nb: top level count only
   array_len = GetPolyPath64ArrayLen(tree);
   array_len = GetPolyPath64ArrayLen(tree);
 }
 }
 
 
@@ -271,17 +273,34 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale)
   return result;
   return result;
 }
 }
 
 
+template <typename T>
+static Path<T> ConvertCPath(T* path)
+{
+  Path<T> result;
+  if (!path) return result;
+  T* 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)
+  {
+    T x = *v++, y = *v++;
+    result.push_back(Point<T>(x, y));
+  }
+  return result;
+}
+
 template <typename T>
 template <typename T>
 static Paths<T> ConvertCPaths(T* paths)
 static Paths<T> ConvertCPaths(T* paths)
 {
 {
   Paths<T> result;
   Paths<T> result;
   if (!paths) return result;
   if (!paths) return result;
   T* v = paths; ++v;
   T* v = paths; ++v;
-  size_t cnt = *v++;
+  size_t cnt = static_cast<size_t>(*v++);
   result.reserve(cnt);
   result.reserve(cnt);
   for (size_t i = 0; i < cnt; ++i)
   for (size_t i = 0; i < cnt; ++i)
   {
   {
-    size_t cnt2 = *v;
+    size_t cnt2 = static_cast<size_t>(*v);
     v += 2;
     v += 2;
     Path<T> path;
     Path<T> path;
     path.reserve(cnt2);
     path.reserve(cnt2);
@@ -300,17 +319,17 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale)
 {
 {
   Paths64 result;
   Paths64 result;
   if (!paths) return result;
   if (!paths) return result;
-  double* v = paths; 
+  double* v = paths;
   ++v; // skip the first value (0)
   ++v; // skip the first value (0)
-  int64_t cnt = (int64_t)*v++;
+  size_t cnt = static_cast<size_t>(*v++);
   result.reserve(cnt);
   result.reserve(cnt);
-  for (int i = 0; i < cnt; ++i)
+  for (size_t i = 0; i < cnt; ++i)
   {
   {
-    int64_t cnt2 = (int64_t)*v;
+    size_t cnt2 = static_cast<size_t>(*v);
     v += 2;
     v += 2;
     Path64 path;
     Path64 path;
     path.reserve(cnt2);
     path.reserve(cnt2);
-    for (int j = 0; j < cnt2; ++j)
+    for (size_t j = 0; j < cnt2; ++j)
     {
     {
       double x = *v++ * scale;
       double x = *v++ * scale;
       double y = *v++ * scale;
       double y = *v++ * scale;
@@ -362,7 +381,7 @@ EXTERN_DLL_EXPORT const char* Version()
   return CLIPPER2_VERSION;
   return CLIPPER2_VERSION;
 }
 }
 
 
-EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, 
+EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
   uint8_t fillrule, const CPaths64 subjects,
   uint8_t fillrule, const CPaths64 subjects,
   const CPaths64 subjects_open, const CPaths64 clips,
   const CPaths64 subjects_open, const CPaths64 clips,
   CPaths64& solution, CPaths64& solution_open,
   CPaths64& solution, CPaths64& solution_open,
@@ -370,7 +389,7 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
 {
 {
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
   if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
-  
+
   Paths64 sub, sub_open, clp, sol, sol_open;
   Paths64 sub, sub_open, clp, sol, sol_open;
   sub       = ConvertCPaths(subjects);
   sub       = ConvertCPaths(subjects);
   sub_open  = ConvertCPaths(subjects_open);
   sub_open  = ConvertCPaths(subjects_open);
@@ -382,7 +401,7 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
   if (sub.size() > 0) clipper.AddSubject(sub);
   if (sub.size() > 0) clipper.AddSubject(sub);
   if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
   if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
   if (clp.size() > 0) clipper.AddClip(clp);
   if (clp.size() > 0) clipper.AddClip(clp);
-  if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) 
+  if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
     return -1; // clipping bug - should never happen :)
     return -1; // clipping bug - should never happen :)
   solution = CreateCPaths(sol);
   solution = CreateCPaths(sol);
   solution_open = CreateCPaths(sol_open);
   solution_open = CreateCPaths(sol_open);
@@ -455,7 +474,7 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype,
   if (precision < -8 || precision > 8) return -5;
   if (precision < -8 || precision > 8) return -5;
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
   if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
   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;
   int err = 0;
@@ -485,10 +504,10 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
 {
 {
   Paths64 pp;
   Paths64 pp;
   pp = ConvertCPaths(paths);
   pp = ConvertCPaths(paths);
-  ClipperOffset clip_offset( miter_limit, 
+  ClipperOffset clip_offset( miter_limit,
     arc_tolerance, reverse_solution);
     arc_tolerance, reverse_solution);
   clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
   clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
-  Paths64 result; 
+  Paths64 result;
   clip_offset.Execute(delta, result);
   clip_offset.Execute(delta, result);
   return CreateCPaths(result);
   return CreateCPaths(result);
 }
 }
@@ -560,6 +579,22 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect,
   return CreateCPathsDFromPaths64(result, 1 / scale);
   return CreateCPathsDFromPaths64(result, 1 / scale);
 }
 }
 
 
+EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
+{
+  Path64 path = ConvertCPath(cpath);
+  Path64 pattern = ConvertCPath(cpattern);
+  Paths64 solution = MinkowskiSum(pattern, path, is_closed);
+  return CreateCPaths(solution);
+}
+
+EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed)
+{
+  Path64 path = ConvertCPath(cpath);
+  Path64 pattern = ConvertCPath(cpattern);
+  Paths64 solution = MinkowskiDiff(pattern, path, is_closed);
+  return CreateCPaths(solution);
+}
+
 }  // end Clipper2Lib namespace
 }  // end Clipper2Lib namespace
-  
+
 #endif  // CLIPPER2_EXPORT_H
 #endif  // CLIPPER2_EXPORT_H

+ 42 - 54
thirdparty/clipper2/include/clipper2/clipper.h

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  18 November 2023                                                *
+* Date      :  27 April 2024                                                   *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This module provides a simple interface to the Clipper Library  *
 * Purpose   :  This module provides a simple interface to the Clipper Library  *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -24,7 +24,7 @@ namespace Clipper2Lib {
 
 
   inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
   inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
     const Paths64& subjects, const Paths64& clips)
     const Paths64& subjects, const Paths64& clips)
-  {    
+  {
     Paths64 result;
     Paths64 result;
     Clipper64 clipper;
     Clipper64 clipper;
     clipper.AddSubject(subjects);
     clipper.AddSubject(subjects);
@@ -47,7 +47,7 @@ namespace Clipper2Lib {
     const PathsD& subjects, const PathsD& clips, int precision = 2)
     const PathsD& subjects, const PathsD& clips, int precision = 2)
   {
   {
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     PathsD result;
     PathsD result;
     if (error_code) return result;
     if (error_code) return result;
     ClipperD clipper(precision);
     ClipperD clipper(precision);
@@ -58,12 +58,12 @@ namespace Clipper2Lib {
   }
   }
 
 
   inline void BooleanOp(ClipType cliptype, FillRule fillrule,
   inline void BooleanOp(ClipType cliptype, FillRule fillrule,
-    const PathsD& subjects, const PathsD& clips, 
+    const PathsD& subjects, const PathsD& clips,
     PolyTreeD& polytree, int precision = 2)
     PolyTreeD& polytree, int precision = 2)
   {
   {
     polytree.Clear();
     polytree.Clear();
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     if (error_code) return;
     if (error_code) return;
     ClipperD clipper(precision);
     ClipperD clipper(precision);
     clipper.AddSubject(subjects);
     clipper.AddSubject(subjects);
@@ -75,7 +75,7 @@ namespace Clipper2Lib {
   {
   {
     return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
     return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
   }
   }
-  
+
   inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
   inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
   {
   {
     return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
     return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
@@ -104,7 +104,7 @@ namespace Clipper2Lib {
   {
   {
     PathsD result;
     PathsD result;
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     if (error_code) return result;
     if (error_code) return result;
     ClipperD clipper(precision);
     ClipperD clipper(precision);
     clipper.AddSubject(subjects);
     clipper.AddSubject(subjects);
@@ -145,11 +145,11 @@ namespace Clipper2Lib {
   }
   }
 
 
   inline PathsD InflatePaths(const PathsD& paths, double delta,
   inline PathsD InflatePaths(const PathsD& paths, double delta,
-    JoinType jt, EndType et, double miter_limit = 2.0, 
+    JoinType jt, EndType et, double miter_limit = 2.0,
     int precision = 2, double arc_tolerance = 0.0)
     int precision = 2, double arc_tolerance = 0.0)
   {
   {
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     if (!delta) return paths;
     if (!delta) return paths;
     if (error_code) return PathsD();
     if (error_code) return PathsD();
     const double scale = std::pow(10, precision);
     const double scale = std::pow(10, precision);
@@ -219,13 +219,13 @@ namespace Clipper2Lib {
   {
   {
     if (rect.IsEmpty() || paths.empty()) return PathsD();
     if (rect.IsEmpty() || paths.empty()) return PathsD();
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     if (error_code) return PathsD();
     if (error_code) return PathsD();
     const double scale = std::pow(10, precision);
     const double scale = std::pow(10, precision);
     Rect64 r = ScaleRect<int64_t, double>(rect, scale);
     Rect64 r = ScaleRect<int64_t, double>(rect, scale);
     RectClip64 rc(r);
     RectClip64 rc(r);
     Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
     Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
-    if (error_code) return PathsD(); // ie: error_code result is lost 
+    if (error_code) return PathsD(); // ie: error_code result is lost
     return ScalePaths<double, int64_t>(
     return ScalePaths<double, int64_t>(
       rc.Execute(pp), 1 / scale, error_code);
       rc.Execute(pp), 1 / scale, error_code);
   }
   }
@@ -251,7 +251,7 @@ namespace Clipper2Lib {
   {
   {
     if (rect.IsEmpty() || lines.empty()) return PathsD();
     if (rect.IsEmpty() || lines.empty()) return PathsD();
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     if (error_code) return PathsD();
     if (error_code) return PathsD();
     const double scale = std::pow(10, precision);
     const double scale = std::pow(10, precision);
     Rect64 r = ScaleRect<int64_t, double>(rect, scale);
     Rect64 r = ScaleRect<int64_t, double>(rect, scale);
@@ -290,8 +290,8 @@ namespace Clipper2Lib {
       {
       {
         // return false if this child isn't fully contained by its parent
         // return false if this child isn't fully contained by its parent
 
 
-        // checking for a single vertex outside is a bit too crude since 
-        // it doesn't account for rounding errors. It's better to check 
+        // checking for a single vertex outside is a bit too crude since
+        // it doesn't account for rounding errors. It's better to check
         // for consecutive vertices found outside the parent's polygon.
         // for consecutive vertices found outside the parent's polygon.
 
 
         int outsideCnt = 0;
         int outsideCnt = 0;
@@ -311,7 +311,7 @@ namespace Clipper2Lib {
       return true;
       return true;
     }
     }
 
 
-    static void OutlinePolyPath(std::ostream& os, 
+    static void OutlinePolyPath(std::ostream& os,
       size_t idx, bool isHole, size_t count, const std::string& preamble)
       size_t idx, bool isHole, size_t count, const std::string& preamble)
     {
     {
       std::string plural = (count == 1) ? "." : "s.";
       std::string plural = (count == 1) ? "." : "s.";
@@ -342,19 +342,19 @@ namespace Clipper2Lib {
     }
     }
 
 
     template<typename T, typename U>
     template<typename T, typename U>
-    inline constexpr void MakePathGeneric(const T an_array, 
+    inline constexpr void MakePathGeneric(const T an_array,
       size_t array_size, std::vector<U>& result)
       size_t array_size, std::vector<U>& result)
     {
     {
       result.reserve(array_size / 2);
       result.reserve(array_size / 2);
       for (size_t i = 0; i < array_size; i +=2)
       for (size_t i = 0; i < array_size; i +=2)
 #ifdef USINGZ
 #ifdef USINGZ
-        result.push_back( U{ an_array[i], an_array[i +1], 0} );
+        result.push_back( U{ an_array[i], an_array[i + 1], 0} );
 #else
 #else
         result.push_back( U{ an_array[i], an_array[i + 1]} );
         result.push_back( U{ an_array[i], an_array[i + 1]} );
 #endif
 #endif
     }
     }
 
 
-  } // end details namespace 
+  } // end details namespace
 
 
   inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
   inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
   {
   {
@@ -398,7 +398,7 @@ namespace Clipper2Lib {
   inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
   inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
   {
   {
     for (const auto& child : polytree)
     for (const auto& child : polytree)
-      if (child->Count() > 0 && 
+      if (child->Count() > 0 &&
         !details::PolyPath64ContainsChildren(*child))
         !details::PolyPath64ContainsChildren(*child))
           return false;
           return false;
     return true;
     return true;
@@ -471,7 +471,7 @@ namespace Clipper2Lib {
     std::size_t size = N / 3;
     std::size_t size = N / 3;
     Path64 result(size);
     Path64 result(size);
     for (size_t i = 0; i < size; ++i)
     for (size_t i = 0; i < size; ++i)
-      result[i] = Point64(list[i * 3], 
+      result[i] = Point64(list[i * 3],
         list[i * 3 + 1], list[i * 3 + 2]);
         list[i * 3 + 1], list[i * 3 + 2]);
     return result;
     return result;
   }
   }
@@ -489,7 +489,7 @@ namespace Clipper2Lib {
           list[i * 3 + 1], list[i * 3 + 2]);
           list[i * 3 + 1], list[i * 3 + 2]);
     else
     else
       for (size_t i = 0; i < size; ++i)
       for (size_t i = 0; i < size; ++i)
-        result[i] = PointD(list[i * 3], list[i * 3 + 1], 
+        result[i] = PointD(list[i * 3], list[i * 3 + 1],
           static_cast<int64_t>(list[i * 3 + 2]));
           static_cast<int64_t>(list[i * 3 + 2]));
     return result;
     return result;
   }
   }
@@ -510,9 +510,9 @@ namespace Clipper2Lib {
 
 
     if (!is_open_path)
     if (!is_open_path)
     {
     {
-      while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1)))
+      while (srcIt != stop && IsCollinear(*stop, *srcIt, *(srcIt + 1)))
         ++srcIt;
         ++srcIt;
-      while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt))
+      while (srcIt != stop && IsCollinear(*(stop - 1), *stop, *srcIt))
         --stop;
         --stop;
       if (srcIt == stop) return Path64();
       if (srcIt == stop) return Path64();
     }
     }
@@ -521,7 +521,7 @@ namespace Clipper2Lib {
     dst.push_back(*prevIt);
     dst.push_back(*prevIt);
     for (; srcIt != stop; ++srcIt)
     for (; srcIt != stop; ++srcIt)
     {
     {
-      if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1)))
+      if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1)))
       {
       {
         prevIt = srcIt;
         prevIt = srcIt;
         dst.push_back(*prevIt);
         dst.push_back(*prevIt);
@@ -530,12 +530,12 @@ namespace Clipper2Lib {
 
 
     if (is_open_path)
     if (is_open_path)
       dst.push_back(*srcIt);
       dst.push_back(*srcIt);
-    else if (CrossProduct(*prevIt, *stop, dst[0]))
+    else if (!IsCollinear(*prevIt, *stop, dst[0]))
       dst.push_back(*stop);
       dst.push_back(*stop);
     else
     else
     {
     {
       while (dst.size() > 2 &&
       while (dst.size() > 2 &&
-        !CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
+        IsCollinear(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
           dst.pop_back();
           dst.pop_back();
       if (dst.size() < 3) return Path64();
       if (dst.size() < 3) return Path64();
     }
     }
@@ -545,7 +545,7 @@ namespace Clipper2Lib {
   inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
   inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
   {
   {
     int error_code = 0;
     int error_code = 0;
-    CheckPrecision(precision, error_code);
+    CheckPrecisionRange(precision, error_code);
     if (error_code) return PathD();
     if (error_code) return PathD();
     const double scale = std::pow(10, precision);
     const double scale = std::pow(10, precision);
     Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
     Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
@@ -580,23 +580,23 @@ namespace Clipper2Lib {
     double cp = std::abs(CrossProduct(pt1, pt2, pt3));
     double cp = std::abs(CrossProduct(pt1, pt2, pt3));
     return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
     return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
   }
   }
-  
+
   template <typename T>
   template <typename T>
-  inline Path<T> Ellipse(const Rect<T>& rect, int steps = 0)
+  inline Path<T> Ellipse(const Rect<T>& rect, size_t steps = 0)
   {
   {
-    return Ellipse(rect.MidPoint(), 
-      static_cast<double>(rect.Width()) *0.5, 
+    return Ellipse(rect.MidPoint(),
+      static_cast<double>(rect.Width()) *0.5,
       static_cast<double>(rect.Height()) * 0.5, steps);
       static_cast<double>(rect.Height()) * 0.5, steps);
   }
   }
 
 
   template <typename T>
   template <typename T>
   inline Path<T> Ellipse(const Point<T>& center,
   inline Path<T> Ellipse(const Point<T>& center,
-    double radiusX, double radiusY = 0, int steps = 0)
+    double radiusX, double radiusY = 0, size_t steps = 0)
   {
   {
     if (radiusX <= 0) return Path<T>();
     if (radiusX <= 0) return Path<T>();
     if (radiusY <= 0) radiusY = radiusX;
     if (radiusY <= 0) radiusY = radiusX;
     if (steps <= 2)
     if (steps <= 2)
-      steps = static_cast<int>(PI * sqrt((radiusX + radiusY) / 2));
+      steps = static_cast<size_t>(PI * sqrt((radiusX + radiusY) / 2));
 
 
     double si = std::sin(2 * PI / steps);
     double si = std::sin(2 * PI / steps);
     double co = std::cos(2 * PI / steps);
     double co = std::cos(2 * PI / steps);
@@ -604,7 +604,7 @@ namespace Clipper2Lib {
     Path<T> result;
     Path<T> result;
     result.reserve(steps);
     result.reserve(steps);
     result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
     result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
-    for (int i = 1; i < steps; ++i)
+    for (size_t i = 1; i < steps; ++i)
     {
     {
       result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
       result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
       double x = dx * co - dy * si;
       double x = dx * co - dy * si;
@@ -614,19 +614,7 @@ namespace Clipper2Lib {
     return result;
     return result;
   }
   }
 
 
-  template <typename T>
-  inline double PerpendicDistFromLineSqrd(const Point<T>& pt,
-    const Point<T>& line1, const Point<T>& line2)
-  {
-    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);
-    double d = static_cast<double>(line2.y - line1.y);
-    if (c == 0 && d == 0) return 0;
-    return Sqr(a * d - c * b) / (c * c + d * d);
-  }
-
-  inline size_t GetNext(size_t current, size_t high, 
+  inline size_t GetNext(size_t current, size_t high,
     const std::vector<bool>& flags)
     const std::vector<bool>& flags)
   {
   {
     ++current;
     ++current;
@@ -637,7 +625,7 @@ namespace Clipper2Lib {
     return current;
     return current;
   }
   }
 
 
-  inline size_t GetPrior(size_t current, size_t high, 
+  inline size_t GetPrior(size_t current, size_t high,
     const std::vector<bool>& flags)
     const std::vector<bool>& flags)
   {
   {
     if (current == 0) current = high;
     if (current == 0) current = high;
@@ -650,7 +638,7 @@ namespace Clipper2Lib {
   }
   }
 
 
   template <typename T>
   template <typename T>
-  inline Path<T> SimplifyPath(const Path<T> &path, 
+  inline Path<T> SimplifyPath(const Path<T> &path,
     double epsilon, bool isClosedPath = true)
     double epsilon, bool isClosedPath = true)
   {
   {
     const size_t len = path.size(), high = len -1;
     const size_t len = path.size(), high = len -1;
@@ -665,7 +653,7 @@ namespace Clipper2Lib {
       distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
       distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
       distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
       distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
     }
     }
-    else 
+    else
     {
     {
       distSqr[0] = MAX_DBL;
       distSqr[0] = MAX_DBL;
       distSqr[high] = MAX_DBL;
       distSqr[high] = MAX_DBL;
@@ -684,7 +672,7 @@ namespace Clipper2Lib {
         } while (curr != start && distSqr[curr] > epsSqr);
         } while (curr != start && distSqr[curr] > epsSqr);
         if (curr == start) break;
         if (curr == start) break;
       }
       }
-      
+
       prior = GetPrior(curr, high, flags);
       prior = GetPrior(curr, high, flags);
       next = GetNext(curr, high, flags);
       next = GetNext(curr, high, flags);
       if (next == prior) break;
       if (next == prior) break;
@@ -699,7 +687,7 @@ namespace Clipper2Lib {
       }
       }
       else
       else
         prior2 = GetPrior(prior, high, flags);
         prior2 = GetPrior(prior, high, flags);
-        
+
       flags[curr] = true;
       flags[curr] = true;
       curr = next;
       curr = next;
       next = GetNext(next, high, flags);
       next = GetNext(next, high, flags);
@@ -717,7 +705,7 @@ namespace Clipper2Lib {
   }
   }
 
 
   template <typename T>
   template <typename T>
-  inline Paths<T> SimplifyPaths(const Paths<T> &paths, 
+  inline Paths<T> SimplifyPaths(const Paths<T> &paths,
     double epsilon, bool isClosedPath = true)
     double epsilon, bool isClosedPath = true)
   {
   {
     Paths<T> result;
     Paths<T> result;

+ 1 - 1
thirdparty/clipper2/include/clipper2/clipper.minkowski.h

@@ -15,7 +15,7 @@
 #include <string>
 #include <string>
 #include "clipper2/clipper.core.h"
 #include "clipper2/clipper.core.h"
 
 
-namespace Clipper2Lib 
+namespace Clipper2Lib
 {
 {
 
 
   namespace detail
   namespace detail

+ 9 - 9
thirdparty/clipper2/include/clipper2/clipper.offset.h

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  19 November 2023                                                *
+* Date      :  24 March 2024                                                   *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  Path Offset (Inflate/Shrink)                                    *
 * Purpose   :  Path Offset (Inflate/Shrink)                                    *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -34,9 +34,7 @@ private:
 	class Group {
 	class Group {
 	public:
 	public:
 		Paths64 paths_in;
 		Paths64 paths_in;
-		std::vector<bool> is_hole_list;
-		std::vector<Rect64> bounds_list;
-		int lowest_path_idx = -1;
+        std::optional<size_t> lowest_path_idx{};
 		bool is_reversed = false;
 		bool is_reversed = false;
 		JoinType join_type;
 		JoinType join_type;
 		EndType end_type;
 		EndType end_type;
@@ -52,7 +50,8 @@ private:
 	double step_cos_ = 0.0;
 	double step_cos_ = 0.0;
 	PathD norms;
 	PathD norms;
 	Path64 path_out;
 	Path64 path_out;
-	Paths64 solution;
+	Paths64* solution = nullptr;
+	PolyTree64* solution_tree = nullptr;
 	std::vector<Group> groups_;
 	std::vector<Group> groups_;
 	JoinType join_type_ = JoinType::Bevel;
 	JoinType join_type_ = JoinType::Bevel;
 	EndType end_type_ = EndType::Polygon;
 	EndType end_type_ = EndType::Polygon;
@@ -64,9 +63,10 @@ private:
 
 
 #ifdef USINGZ
 #ifdef USINGZ
 	ZCallback64 zCallback64_ = nullptr;
 	ZCallback64 zCallback64_ = nullptr;
+	void ZCB(const Point64& bot1, const Point64& top1,
+		const Point64& bot2, const Point64& top2, Point64& ip);
 #endif
 #endif
 	DeltaCallback64 deltaCallback64_ = nullptr;
 	DeltaCallback64 deltaCallback64_ = nullptr;
-
 	size_t CalcSolutionCapacity();
 	size_t CalcSolutionCapacity();
 	bool CheckReverseOrientation();
 	bool CheckReverseOrientation();
 	void DoBevel(const Path64& path, size_t j, size_t k);
 	void DoBevel(const Path64& path, size_t j, size_t k);
@@ -83,7 +83,7 @@ private:
 public:
 public:
 	explicit ClipperOffset(double miter_limit = 2.0,
 	explicit ClipperOffset(double miter_limit = 2.0,
 		double arc_tolerance = 0.0,
 		double arc_tolerance = 0.0,
-		bool preserve_collinear = false, 
+		bool preserve_collinear = false,
 		bool reverse_solution = false) :
 		bool reverse_solution = false) :
 		miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
 		miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
 		preserve_collinear_(preserve_collinear),
 		preserve_collinear_(preserve_collinear),
@@ -91,7 +91,7 @@ public:
 
 
 	~ClipperOffset() { Clear(); };
 	~ClipperOffset() { Clear(); };
 
 
-	int ErrorCode() { return error_code_; };
+	int ErrorCode() const { return error_code_; };
 	void AddPath(const Path64& path, JoinType jt_, EndType et_);
 	void AddPath(const Path64& path, JoinType jt_, EndType et_);
 	void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
 	void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
 	void Clear() { groups_.clear(); norms.clear(); };
 	void Clear() { groups_.clear(); norms.clear(); };

+ 9 - 8
thirdparty/clipper2/include/clipper2/clipper.rectclip.h

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  1 November 2023                                                 *
+* Date      :  5 July 2024                                                     *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  FAST rectangular clipping                                       *
 * Purpose   :  FAST rectangular clipping                                       *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -18,6 +18,7 @@
 namespace Clipper2Lib
 namespace Clipper2Lib
 {
 {
 
 
+  // Location: the order is important here, see StartLocsIsClockwise()
   enum class Location { Left, Top, Right, Bottom, Inside };
   enum class Location { Left, Top, Right, Bottom, Inside };
 
 
   class OutPt2;
   class OutPt2;
@@ -26,10 +27,10 @@ namespace Clipper2Lib
   class OutPt2 {
   class OutPt2 {
   public:
   public:
     Point64 pt;
     Point64 pt;
-    size_t owner_idx;
-    OutPt2List* edge;
-    OutPt2* next;
-    OutPt2* prev;
+    size_t owner_idx = 0;
+    OutPt2List* edge = nullptr;
+    OutPt2* next = nullptr;
+    OutPt2* prev = nullptr;
   };
   };
 
 
   //------------------------------------------------------------------------------
   //------------------------------------------------------------------------------
@@ -50,9 +51,9 @@ namespace Clipper2Lib
     OutPt2List edges_[8]; // clockwise and counter-clockwise
     OutPt2List edges_[8]; // clockwise and counter-clockwise
     std::vector<Location> start_locs_;
     std::vector<Location> start_locs_;
     void CheckEdges();
     void CheckEdges();
-    void TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw);
+    void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw);
     void GetNextLocation(const Path64& path,
     void GetNextLocation(const Path64& path,
-      Location& loc, int& i, int highI);
+      Location& loc, size_t& i, size_t highI);
     OutPt2* Add(Point64 pt, bool start_new = false);
     OutPt2* Add(Point64 pt, bool start_new = false);
     void AddCorner(Location prev, Location curr);
     void AddCorner(Location prev, Location curr);
     void AddCorner(Location& loc, bool isClockwise);
     void AddCorner(Location& loc, bool isClockwise);

+ 1 - 1
thirdparty/clipper2/include/clipper2/clipper.version.h

@@ -1,6 +1,6 @@
 #ifndef CLIPPER_VERSION_H
 #ifndef CLIPPER_VERSION_H
 #define CLIPPER_VERSION_H
 #define CLIPPER_VERSION_H
 
 
-constexpr auto CLIPPER2_VERSION = "1.3.0";
+constexpr auto CLIPPER2_VERSION = "1.4.0";
 
 
 #endif  // CLIPPER_VERSION_H
 #endif  // CLIPPER_VERSION_H

+ 4 - 4
thirdparty/clipper2/patches/clipper2-exceptions.patch

@@ -1,9 +1,9 @@
 diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
 diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
-index b3dddeeaa2..a77cdad5f4 100644
+index 925c04685e..d0d159b949 100644
 --- a/thirdparty/clipper2/include/clipper2/clipper.core.h
 --- a/thirdparty/clipper2/include/clipper2/clipper.core.h
 +++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
 +++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
-@@ -21,6 +21,8 @@
- #include <numeric>
+@@ -22,6 +22,8 @@
+ #include <optional>
  #include "clipper2/clipper.version.h"
  #include "clipper2/clipper.version.h"
  
  
 +#define CLIPPER2_THROW(exception) std::abort()
 +#define CLIPPER2_THROW(exception) std::abort()
@@ -11,7 +11,7 @@ index b3dddeeaa2..a77cdad5f4 100644
  namespace Clipper2Lib
  namespace Clipper2Lib
  {
  {
  
  
-@@ -78,18 +80,18 @@ namespace Clipper2Lib
+@@ -79,18 +81,18 @@ namespace Clipper2Lib
      switch (error_code)
      switch (error_code)
      {
      {
      case precision_error_i:
      case precision_error_i:

+ 0 - 22
thirdparty/clipper2/patches/gcc14-warning.patch

@@ -1,22 +0,0 @@
-diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
-index a77cdad5f4..0de7c3720e 100644
---- a/thirdparty/clipper2/include/clipper2/clipper.core.h
-+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
-@@ -138,7 +138,7 @@ namespace Clipper2Lib
-     }
- 
-     template <typename T2>
--    explicit Point<T>(const Point<T2>& p)
-+    explicit Point(const Point<T2>& p)
-     {
-       Init(p.x, p.y, p.z);
-     }
-@@ -180,7 +180,7 @@ namespace Clipper2Lib
-     Point(const T2 x_, const T2 y_) { Init(x_, y_); }
- 
-     template <typename T2>
--    explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
-+    explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
- 
-     Point operator * (const double scale) const
-     {

+ 34 - 0
thirdparty/clipper2/patches/llvm-error.patch

@@ -0,0 +1,34 @@
+diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
+index 67dd731af6..0f69bf2d9f 100644
+--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
++++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
+@@ -695,11 +695,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)
+   {
+-#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
++// -- GODOT start --
++// #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
++// -- GODOT end --
+     // 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,9 @@ namespace Clipper2Lib
+     const auto sign_cd = TriSign(c) * TriSign(d);
+ 
+     return abs_ab == abs_cd && sign_ab == sign_cd;
+-#endif
++// -- GODOT start --
++// #endif
++// -- GODOT end --
+   }
+ 
+   template <typename T>

+ 138 - 87
thirdparty/clipper2/src/clipper.engine.cpp

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  22 November 2023                                                *
+* Date      :  27 April 2024                                                   *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  This is the main polygon clipping module                        *
 * Purpose   :  This is the main polygon clipping module                        *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -31,11 +31,11 @@ namespace Clipper2Lib {
 
 
   static const Rect64 invalid_rect = Rect64(false);
   static const Rect64 invalid_rect = Rect64(false);
 
 
-  // Every closed path (or polygon) is made up of a series of vertices forming
-  // edges that alternate between going up (relative to the Y-axis) and going
-  // down. Edges consecutively going up or consecutively going down are called
-  // 'bounds' (ie sides if they're simple polygons). 'Local Minima' refer to
-  // vertices where descending bounds become ascending ones.
+  // Every closed path (ie polygon) is made up of a series of vertices forming edge 
+  // 'bounds' that alternate between ascending bounds (containing edges going up 
+  // relative to the Y-axis) and descending bounds. 'Local Minima' refers to
+  // vertices where ascending and descending bounds join at the bottom, and
+  // 'Local Maxima' are where ascending and descending bounds join at the top.
 
 
   struct Scanline {
   struct Scanline {
     int64_t y = 0;
     int64_t y = 0;
@@ -63,6 +63,7 @@ namespace Clipper2Lib {
     }
     }
   };
   };
 
 
+
   inline bool IsOdd(int val)
   inline bool IsOdd(int val)
   {
   {
     return (val & 1) ? true : false;
     return (val & 1) ? true : false;
@@ -188,7 +189,7 @@ namespace Clipper2Lib {
   }
   }
 
 
   //PrevPrevVertex: useful to get the (inverted Y-axis) top of the
   //PrevPrevVertex: useful to get the (inverted Y-axis) top of the
-  //alternate edge (ie left or right bound) during edge insertion.  
+  //alternate edge (ie left or right bound) during edge insertion.
   inline Vertex* PrevPrevVertex(const Active& ae)
   inline Vertex* PrevPrevVertex(const Active& ae)
   {
   {
     if (ae.wind_dx > 0)
     if (ae.wind_dx > 0)
@@ -233,15 +234,15 @@ namespace Clipper2Lib {
     Vertex* result = e.vertex_top;
     Vertex* result = e.vertex_top;
     if (e.wind_dx > 0)
     if (e.wind_dx > 0)
       while ((result->next->pt.y == result->pt.y) &&
       while ((result->next->pt.y == result->pt.y) &&
-        ((result->flags & (VertexFlags::OpenEnd | 
+        ((result->flags & (VertexFlags::OpenEnd |
           VertexFlags::LocalMax)) == VertexFlags::None))
           VertexFlags::LocalMax)) == VertexFlags::None))
             result = result->next;
             result = result->next;
     else
     else
       while (result->prev->pt.y == result->pt.y &&
       while (result->prev->pt.y == result->pt.y &&
-        ((result->flags & (VertexFlags::OpenEnd | 
+        ((result->flags & (VertexFlags::OpenEnd |
           VertexFlags::LocalMax)) == VertexFlags::None))
           VertexFlags::LocalMax)) == VertexFlags::None))
           result = result->prev;
           result = result->prev;
-    if (!IsMaxima(*result)) result = nullptr; // not a maxima   
+    if (!IsMaxima(*result)) result = nullptr; // not a maxima
     return result;
     return result;
   }
   }
 
 
@@ -252,7 +253,7 @@ namespace Clipper2Lib {
       while (result->next->pt.y == result->pt.y) result = result->next;
       while (result->next->pt.y == result->pt.y) result = result->next;
     else
     else
       while (result->prev->pt.y == result->pt.y) result = result->prev;
       while (result->prev->pt.y == result->pt.y) result = result->prev;
-    if (!IsMaxima(*result)) result = nullptr; // not a maxima   
+    if (!IsMaxima(*result)) result = nullptr; // not a maxima
     return result;
     return result;
   }
   }
 
 
@@ -613,13 +614,13 @@ namespace Clipper2Lib {
     list.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
     list.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
   }
   }
 
 
-  void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, 
+  void AddPaths_(const Paths64& paths, PathType polytype, bool is_open,
     std::vector<Vertex*>& vertexLists, LocalMinimaList& locMinList)
     std::vector<Vertex*>& vertexLists, LocalMinimaList& locMinList)
   {
   {
     const auto total_vertex_count =
     const auto total_vertex_count =
-      std::accumulate(paths.begin(), paths.end(), 0,
+      std::accumulate(paths.begin(), paths.end(), size_t(0),
         [](const auto& a, const Path64& path)
         [](const auto& a, const Path64& path)
-        {return a + static_cast<unsigned>(path.size()); });
+        {return a + path.size(); });
     if (total_vertex_count == 0) return;
     if (total_vertex_count == 0) return;
 
 
     Vertex* vertices = new Vertex[total_vertex_count], * v = vertices;
     Vertex* vertices = new Vertex[total_vertex_count], * v = vertices;
@@ -810,7 +811,7 @@ namespace Clipper2Lib {
   void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip)
   void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip)
   {
   {
     if (!zCallback_) return;
     if (!zCallback_) return;
-    // prioritize subject over clip vertices by passing 
+    // prioritize subject over clip vertices by passing
     // subject vertices before clip vertices in the callback
     // subject vertices before clip vertices in the callback
     if (GetPolyType(e1) == PathType::Subject)
     if (GetPolyType(e1) == PathType::Subject)
     {
     {
@@ -845,11 +846,11 @@ namespace Clipper2Lib {
     if (is_open) has_open_paths_ = true;
     if (is_open) has_open_paths_ = true;
     minima_list_sorted_ = false;
     minima_list_sorted_ = false;
     AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
     AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
-  } 
+  }
 
 
-  void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data) 
+  void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data)
   {
   {
-    // nb: reuseable_data will continue to own the vertices 
+    // nb: reuseable_data will continue to own the vertices
     // and remains responsible for their clean up.
     // and remains responsible for their clean up.
     succeeded_ = false;
     succeeded_ = false;
     minima_list_sorted_ = false;
     minima_list_sorted_ = false;
@@ -1117,7 +1118,6 @@ namespace Clipper2Lib {
     }
     }
   }
   }
 
 
-
   bool IsValidAelOrder(const Active& resident, const Active& newcomer)
   bool IsValidAelOrder(const Active& resident, const Active& newcomer)
   {
   {
     if (newcomer.curr_x != resident.curr_x)
     if (newcomer.curr_x != resident.curr_x)
@@ -1149,8 +1149,8 @@ namespace Clipper2Lib {
     //resident must also have just been inserted
     //resident must also have just been inserted
     else if (resident.is_left_bound != newcomerIsLeft)
     else if (resident.is_left_bound != newcomerIsLeft)
       return newcomerIsLeft;
       return newcomerIsLeft;
-    else if (CrossProduct(PrevPrevVertex(resident)->pt,
-      resident.bot, resident.top) == 0) return true;
+    else if (IsCollinear(PrevPrevVertex(resident)->pt,
+      resident.bot, resident.top)) return true;
     else
     else
       //compare turning direction of the alternate bound
       //compare turning direction of the alternate bound
       return (CrossProduct(PrevPrevVertex(resident)->pt,
       return (CrossProduct(PrevPrevVertex(resident)->pt,
@@ -1385,7 +1385,7 @@ namespace Clipper2Lib {
   {
   {
     if (IsJoined(e1)) Split(e1, pt);
     if (IsJoined(e1)) Split(e1, pt);
     if (IsJoined(e2)) Split(e2, pt);
     if (IsJoined(e2)) Split(e2, pt);
-    
+
     if (IsFront(e1) == IsFront(e2))
     if (IsFront(e1) == IsFront(e2))
     {
     {
       if (IsOpenEnd(e1))
       if (IsOpenEnd(e1))
@@ -1409,7 +1409,7 @@ namespace Clipper2Lib {
       {
       {
         Active* e = GetPrevHotEdge(e1);
         Active* e = GetPrevHotEdge(e1);
         if (!e)
         if (!e)
-          outrec.owner = nullptr; 
+          outrec.owner = nullptr;
         else
         else
           SetOwner(&outrec, e->outrec);
           SetOwner(&outrec, e->outrec);
         // nb: outRec.owner here is likely NOT the real
         // nb: outRec.owner here is likely NOT the real
@@ -1476,7 +1476,7 @@ namespace Clipper2Lib {
       e2.outrec->pts = e1.outrec->pts;
       e2.outrec->pts = e1.outrec->pts;
       e1.outrec->pts = nullptr;
       e1.outrec->pts = nullptr;
     }
     }
-    else 
+    else
       SetOwner(e2.outrec, e1.outrec);
       SetOwner(e2.outrec, e1.outrec);
 
 
     //and e1 and e2 are maxima and are about to be dropped from the Actives list.
     //and e1 and e2 are maxima and are about to be dropped from the Actives list.
@@ -1526,7 +1526,6 @@ namespace Clipper2Lib {
     return new_op;
     return new_op;
   }
   }
 
 
-
   void ClipperBase::CleanCollinear(OutRec* outrec)
   void ClipperBase::CleanCollinear(OutRec* outrec)
   {
   {
     outrec = GetRealOutRec(outrec);
     outrec = GetRealOutRec(outrec);
@@ -1541,7 +1540,7 @@ namespace Clipper2Lib {
     for (; ; )
     for (; ; )
     {
     {
       //NB if preserveCollinear == true, then only remove 180 deg. spikes
       //NB if preserveCollinear == true, then only remove 180 deg. spikes
-      if ((CrossProduct(op2->prev->pt, op2->pt, op2->next->pt) == 0) &&
+      if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt) &&
         (op2->pt == op2->prev->pt ||
         (op2->pt == op2->prev->pt ||
           op2->pt == op2->next->pt || !preserve_collinear_ ||
           op2->pt == op2->next->pt || !preserve_collinear_ ||
           DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0))
           DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0))
@@ -1566,14 +1565,14 @@ namespace Clipper2Lib {
 
 
   void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
   void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
   {
   {
-    // splitOp.prev -> splitOp && 
+    // splitOp.prev -> splitOp &&
     // splitOp.next -> splitOp.next.next are intersecting
     // splitOp.next -> splitOp.next.next are intersecting
     OutPt* prevOp = splitOp->prev;
     OutPt* prevOp = splitOp->prev;
     OutPt* nextNextOp = splitOp->next->next;
     OutPt* nextNextOp = splitOp->next->next;
     outrec->pts = prevOp;
     outrec->pts = prevOp;
 
 
     Point64 ip;
     Point64 ip;
-    GetIntersectPoint(prevOp->pt, splitOp->pt,
+    GetSegmentIntersectPt(prevOp->pt, splitOp->pt,
       splitOp->next->pt, nextNextOp->pt, ip);
       splitOp->next->pt, nextNextOp->pt, ip);
 
 
 #ifdef USINGZ
 #ifdef USINGZ
@@ -1617,7 +1616,7 @@ namespace Clipper2Lib {
     {
     {
       OutRec* newOr = NewOutRec();
       OutRec* newOr = NewOutRec();
       newOr->owner = outrec->owner;
       newOr->owner = outrec->owner;
-      
+
       splitOp->outrec = newOr;
       splitOp->outrec = newOr;
       splitOp->next->outrec = newOr;
       splitOp->next->outrec = newOr;
       OutPt* newOp = new OutPt(ip, newOr);
       OutPt* newOp = new OutPt(ip, newOr);
@@ -1772,12 +1771,12 @@ namespace Clipper2Lib {
   }
   }
 
 
 
 
-  OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
+  void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
   {
   {
     //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
     //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
     if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2)))
     if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2)))
     {
     {
-      if (IsOpen(e1) && IsOpen(e2)) return nullptr;
+      if (IsOpen(e1) && IsOpen(e2)) return;
       Active* edge_o, * edge_c;
       Active* edge_o, * edge_c;
       if (IsOpen(e1))
       if (IsOpen(e1))
       {
       {
@@ -1791,29 +1790,40 @@ namespace Clipper2Lib {
       }
       }
       if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety
       if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety
 
 
-      if (abs(edge_c->wind_cnt) != 1) return nullptr;
+      if (abs(edge_c->wind_cnt) != 1) return;
       switch (cliptype_)
       switch (cliptype_)
       {
       {
       case ClipType::Union:
       case ClipType::Union:
-        if (!IsHotEdge(*edge_c)) return nullptr;
+        if (!IsHotEdge(*edge_c)) return;
         break;
         break;
       default:
       default:
         if (edge_c->local_min->polytype == PathType::Subject)
         if (edge_c->local_min->polytype == PathType::Subject)
-          return nullptr;
+          return;
       }
       }
 
 
       switch (fillrule_)
       switch (fillrule_)
       {
       {
-      case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break;
-      case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break;
-      default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break;
+      case FillRule::Positive: 
+        if (edge_c->wind_cnt != 1) return; 
+        break;
+      case FillRule::Negative: 
+        if (edge_c->wind_cnt != -1) return; 
+        break;
+      default: 
+        if (std::abs(edge_c->wind_cnt) != 1) return; 
       }
       }
 
 
+#ifdef USINGZ
       OutPt* resultOp;
       OutPt* resultOp;
+#endif
       //toggle contribution ...
       //toggle contribution ...
       if (IsHotEdge(*edge_o))
       if (IsHotEdge(*edge_o))
       {
       {
+#ifdef USINGZ
         resultOp = AddOutPt(*edge_o, pt);
         resultOp = AddOutPt(*edge_o, pt);
+#else
+        AddOutPt(*edge_o, pt);
+#endif
         if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
         if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
         else edge_o->outrec->back_edge = nullptr;
         else edge_o->outrec->back_edge = nullptr;
         edge_o->outrec = nullptr;
         edge_o->outrec = nullptr;
@@ -1833,18 +1843,26 @@ namespace Clipper2Lib {
             SetSides(*e3->outrec, *edge_o, *e3);
             SetSides(*e3->outrec, *edge_o, *e3);
           else
           else
             SetSides(*e3->outrec, *e3, *edge_o);
             SetSides(*e3->outrec, *e3, *edge_o);
-          return e3->outrec->pts;
+          return;
         }
         }
         else
         else
+#ifdef USINGZ
           resultOp = StartOpenPath(*edge_o, pt);
           resultOp = StartOpenPath(*edge_o, pt);
+#else
+          StartOpenPath(*edge_o, pt);
+#endif
       }
       }
       else
       else
+#ifdef USINGZ
         resultOp = StartOpenPath(*edge_o, pt);
         resultOp = StartOpenPath(*edge_o, pt);
+#else
+        StartOpenPath(*edge_o, pt);
+#endif
 
 
 #ifdef USINGZ
 #ifdef USINGZ
       if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
       if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
 #endif
 #endif
-      return resultOp;
+      return;
     } // end of an open path intersection
     } // end of an open path intersection
 
 
     //MANAGING CLOSED PATHS FROM HERE ON
     //MANAGING CLOSED PATHS FROM HERE ON
@@ -1913,22 +1931,25 @@ namespace Clipper2Lib {
     const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
     const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
     const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
     const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
 
 
-    if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01))
-    {
-      return nullptr;
-    }
+    if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || 
+      (!IsHotEdge(e2) && !e2_windcnt_in_01))
+        return;
 
 
     //NOW PROCESS THE INTERSECTION ...
     //NOW PROCESS THE INTERSECTION ...
+#ifdef USINGZ
     OutPt* resultOp = nullptr;
     OutPt* resultOp = nullptr;
+#endif
     //if both edges are 'hot' ...
     //if both edges are 'hot' ...
     if (IsHotEdge(e1) && IsHotEdge(e2))
     if (IsHotEdge(e1) && IsHotEdge(e2))
     {
     {
       if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) ||
       if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) ||
         (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor))
         (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor))
       {
       {
-        resultOp = AddLocalMaxPoly(e1, e2, pt);
 #ifdef USINGZ
 #ifdef USINGZ
+        resultOp = AddLocalMaxPoly(e1, e2, pt);
         if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
         if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
+#else
+        AddLocalMaxPoly(e1, e2, pt);
 #endif
 #endif
       }
       }
       else if (IsFront(e1) || (e1.outrec == e2.outrec))
       else if (IsFront(e1) || (e1.outrec == e2.outrec))
@@ -1937,19 +1958,20 @@ namespace Clipper2Lib {
         //it's sensible to split polygons that ony touch at
         //it's sensible to split polygons that ony touch at
         //a common vertex (not at common edges).
         //a common vertex (not at common edges).
 
 
-        resultOp = AddLocalMaxPoly(e1, e2, pt);
 #ifdef USINGZ
 #ifdef USINGZ
+        resultOp = AddLocalMaxPoly(e1, e2, pt);
         OutPt* op2 = AddLocalMinPoly(e1, e2, pt);
         OutPt* op2 = AddLocalMinPoly(e1, e2, pt);
         if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
         if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
         if (zCallback_) SetZ(e1, e2, op2->pt);
         if (zCallback_) SetZ(e1, e2, op2->pt);
 #else
 #else
+        AddLocalMaxPoly(e1, e2, pt);
         AddLocalMinPoly(e1, e2, pt);
         AddLocalMinPoly(e1, e2, pt);
 #endif
 #endif
       }
       }
       else
       else
       {
       {
-        resultOp = AddOutPt(e1, pt);
 #ifdef USINGZ
 #ifdef USINGZ
+        resultOp = AddOutPt(e1, pt);
         OutPt* op2 = AddOutPt(e2, pt);
         OutPt* op2 = AddOutPt(e2, pt);
         if (zCallback_)
         if (zCallback_)
         {
         {
@@ -1957,6 +1979,7 @@ namespace Clipper2Lib {
           SetZ(e1, e2, op2->pt);
           SetZ(e1, e2, op2->pt);
         }
         }
 #else
 #else
+        AddOutPt(e1, pt);
         AddOutPt(e2, pt);
         AddOutPt(e2, pt);
 #endif
 #endif
         SwapOutrecs(e1, e2);
         SwapOutrecs(e1, e2);
@@ -1964,17 +1987,21 @@ namespace Clipper2Lib {
     }
     }
     else if (IsHotEdge(e1))
     else if (IsHotEdge(e1))
     {
     {
-      resultOp = AddOutPt(e1, pt);
 #ifdef USINGZ
 #ifdef USINGZ
+      resultOp = AddOutPt(e1, pt);
       if (zCallback_) SetZ(e1, e2, resultOp->pt);
       if (zCallback_) SetZ(e1, e2, resultOp->pt);
+#else
+      AddOutPt(e1, pt);
 #endif
 #endif
       SwapOutrecs(e1, e2);
       SwapOutrecs(e1, e2);
     }
     }
     else if (IsHotEdge(e2))
     else if (IsHotEdge(e2))
     {
     {
-      resultOp = AddOutPt(e2, pt);
 #ifdef USINGZ
 #ifdef USINGZ
+      resultOp = AddOutPt(e2, pt);
       if (zCallback_) SetZ(e1, e2, resultOp->pt);
       if (zCallback_) SetZ(e1, e2, resultOp->pt);
+#else
+      AddOutPt(e2, pt);
 #endif
 #endif
       SwapOutrecs(e1, e2);
       SwapOutrecs(e1, e2);
     }
     }
@@ -2004,33 +2031,53 @@ namespace Clipper2Lib {
 
 
       if (!IsSamePolyType(e1, e2))
       if (!IsSamePolyType(e1, e2))
       {
       {
-        resultOp = AddLocalMinPoly(e1, e2, pt, false);
 #ifdef USINGZ
 #ifdef USINGZ
+        resultOp = AddLocalMinPoly(e1, e2, pt, false);
         if (zCallback_) SetZ(e1, e2, resultOp->pt);
         if (zCallback_) SetZ(e1, e2, resultOp->pt);
+#else
+        AddLocalMinPoly(e1, e2, pt, false);
 #endif
 #endif
       }
       }
       else if (old_e1_windcnt == 1 && old_e2_windcnt == 1)
       else if (old_e1_windcnt == 1 && old_e2_windcnt == 1)
       {
       {
+#ifdef USINGZ
         resultOp = nullptr;
         resultOp = nullptr;
+#endif
         switch (cliptype_)
         switch (cliptype_)
         {
         {
         case ClipType::Union:
         case ClipType::Union:
           if (e1Wc2 <= 0 && e2Wc2 <= 0)
           if (e1Wc2 <= 0 && e2Wc2 <= 0)
+#ifdef USINGZ
             resultOp = AddLocalMinPoly(e1, e2, pt, false);
             resultOp = AddLocalMinPoly(e1, e2, pt, false);
+#else
+            AddLocalMinPoly(e1, e2, pt, false);
+#endif
           break;
           break;
         case ClipType::Difference:
         case ClipType::Difference:
           if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
           if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
             ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
             ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
           {
           {
+#ifdef USINGZ
             resultOp = AddLocalMinPoly(e1, e2, pt, false);
             resultOp = AddLocalMinPoly(e1, e2, pt, false);
+#else
+            AddLocalMinPoly(e1, e2, pt, false);
+#endif
           }
           }
           break;
           break;
         case ClipType::Xor:
         case ClipType::Xor:
+#ifdef USINGZ
           resultOp = AddLocalMinPoly(e1, e2, pt, false);
           resultOp = AddLocalMinPoly(e1, e2, pt, false);
+#else
+          AddLocalMinPoly(e1, e2, pt, false);
+#endif
           break;
           break;
         default:
         default:
           if (e1Wc2 > 0 && e2Wc2 > 0)
           if (e1Wc2 > 0 && e2Wc2 > 0)
+#ifdef USINGZ
             resultOp = AddLocalMinPoly(e1, e2, pt, false);
             resultOp = AddLocalMinPoly(e1, e2, pt, false);
+#else
+            AddLocalMinPoly(e1, e2, pt, false);
+#endif
           break;
           break;
         }
         }
 #ifdef USINGZ
 #ifdef USINGZ
@@ -2038,7 +2085,6 @@ namespace Clipper2Lib {
 #endif
 #endif
       }
       }
     }
     }
-    return resultOp;
   }
   }
 
 
   inline void ClipperBase::DeleteFromAEL(Active& e)
   inline void ClipperBase::DeleteFromAEL(Active& e)
@@ -2065,7 +2111,7 @@ namespace Clipper2Lib {
       e->next_in_sel = e->next_in_ael;
       e->next_in_sel = e->next_in_ael;
       e->jump = e->next_in_sel;
       e->jump = e->next_in_sel;
       if (e->join_with == JoinWith::Left)
       if (e->join_with == JoinWith::Left)
-        e->curr_x = e->prev_in_ael->curr_x; // also avoids complications      
+        e->curr_x = e->prev_in_ael->curr_x; // also avoids complications
       else
       else
         e->curr_x = TopX(*e, top_y);
         e->curr_x = TopX(*e, top_y);
       e = e->next_in_ael;
       e = e->next_in_ael;
@@ -2138,7 +2184,7 @@ namespace Clipper2Lib {
     if (outrecHasEdges)
     if (outrecHasEdges)
     {
     {
       OutPt* opA = outrec->pts, * opZ = opA->next;
       OutPt* opA = outrec->pts, * opZ = opA->next;
-      while (opP != opZ && opP->prev->pt.y == curr_y) 
+      while (opP != opZ && opP->prev->pt.y == curr_y)
         opP = opP->prev;
         opP = opP->prev;
       while (opN != opA && opN->next->pt.y == curr_y)
       while (opN != opA && opN->next->pt.y == curr_y)
         opN = opN->next;
         opN = opN->next;
@@ -2150,7 +2196,7 @@ namespace Clipper2Lib {
       while (opN->next != opP && opN->next->pt.y == curr_y)
       while (opN->next != opP && opN->next->pt.y == curr_y)
         opN = opN->next;
         opN = opN->next;
     }
     }
-    bool result = 
+    bool result =
       SetHorzSegHeadingForward(hs, opP, opN) &&
       SetHorzSegHeadingForward(hs, opP, opN) &&
       !hs.left_op->horz;
       !hs.left_op->horz;
 
 
@@ -2160,13 +2206,14 @@ namespace Clipper2Lib {
       hs.right_op = nullptr; // (for sorting)
       hs.right_op = nullptr; // (for sorting)
     return result;
     return result;
   }
   }
-  
+
   void ClipperBase::ConvertHorzSegsToJoins()
   void ClipperBase::ConvertHorzSegsToJoins()
   {
   {
-    auto j = std::count_if(horz_seg_list_.begin(), 
+    auto j = std::count_if(horz_seg_list_.begin(),
       horz_seg_list_.end(),
       horz_seg_list_.end(),
       [](HorzSegment& hs) { return UpdateHorzSegment(hs); });
       [](HorzSegment& hs) { return UpdateHorzSegment(hs); });
     if (j < 2) return;
     if (j < 2) return;
+
     std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter());
     std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter());
 
 
     HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2;
     HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2;
@@ -2207,8 +2254,8 @@ namespace Clipper2Lib {
             DuplicateOp(hs1->left_op, false));
             DuplicateOp(hs1->left_op, false));
           horz_join_list_.push_back(join);
           horz_join_list_.push_back(join);
         }
         }
-      } 
-    } 
+      }
+    }
   }
   }
 
 
   void MoveSplits(OutRec* fromOr, OutRec* toOr)
   void MoveSplits(OutRec* fromOr, OutRec* toOr)
@@ -2301,7 +2348,7 @@ namespace Clipper2Lib {
   void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
   void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
   {
   {
     Point64 ip;
     Point64 ip;
-    if (!GetIntersectPoint(e1.bot, e1.top, e2.bot, e2.top, ip))
+    if (!GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip))
       ip = Point64(e1.curr_x, top_y); //parallel edges
       ip = Point64(e1.curr_x, top_y); //parallel edges
 
 
     //rounding errors can occasionally place the calculated intersection
     //rounding errors can occasionally place the calculated intersection
@@ -2321,7 +2368,7 @@ namespace Clipper2Lib {
         ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
         ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
       else if (abs_dx2 > 100)
       else if (abs_dx2 > 100)
         ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
         ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
-      else 
+      else
       {
       {
         if (ip.y < top_y) ip.y = top_y;
         if (ip.y < top_y) ip.y = top_y;
         else ip.y = bot_y_;
         else ip.y = bot_y_;
@@ -2453,7 +2500,7 @@ namespace Clipper2Lib {
     horz_seg_list_.push_back(HorzSegment(op));
     horz_seg_list_.push_back(HorzSegment(op));
   }
   }
 
 
-  bool ClipperBase::ResetHorzDirection(const Active& horz, 
+  bool ClipperBase::ResetHorzDirection(const Active& horz,
     const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right)
     const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right)
   {
   {
     if (horz.bot.x == horz.top.x)
     if (horz.bot.x == horz.top.x)
@@ -2536,8 +2583,8 @@ namespace Clipper2Lib {
           if (IsHotEdge(horz) && IsJoined(*e))
           if (IsHotEdge(horz) && IsJoined(*e))
             Split(*e, e->top);
             Split(*e, e->top);
 
 
-          //if (IsHotEdge(horz) != IsHotEdge(*e)) 
-          //  DoError(undefined_error_i);
+          //if (IsHotEdge(horz) != IsHotEdge(*e))
+          //    DoError(undefined_error_i);
 
 
           if (IsHotEdge(horz))
           if (IsHotEdge(horz))
           {
           {
@@ -2641,7 +2688,7 @@ namespace Clipper2Lib {
         ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
         ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
     }
     }
 
 
-    if (IsHotEdge(horz)) 
+    if (IsHotEdge(horz))
     {
     {
       OutPt* op = AddOutPt(horz, horz.top);
       OutPt* op = AddOutPt(horz, horz.top);
       AddTrialHorzJoin(op);
       AddTrialHorzJoin(op);
@@ -2754,21 +2801,23 @@ namespace Clipper2Lib {
     }
     }
   }
   }
 
 
-  void ClipperBase::CheckJoinLeft(Active& e, 
+  void ClipperBase::CheckJoinLeft(Active& e,
     const Point64& pt, bool check_curr_x)
     const Point64& pt, bool check_curr_x)
   {
   {
     Active* prev = e.prev_in_ael;
     Active* prev = e.prev_in_ael;
-    if (IsOpen(e) || !IsHotEdge(e) || !prev ||
-      IsOpen(*prev) || !IsHotEdge(*prev)) return;
+    if (!prev ||
+      !IsHotEdge(e) || !IsHotEdge(*prev) ||
+      IsHorizontal(e) || IsHorizontal(*prev) ||
+      IsOpen(e) || IsOpen(*prev) ) return;
     if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) &&
     if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) &&
-      ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins              
+      ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins
 
 
     if (check_curr_x)
     if (check_curr_x)
     {
     {
-      if (DistanceFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
+      if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
     }
     }
     else if (e.curr_x != prev->curr_x) return;
     else if (e.curr_x != prev->curr_x) return;
-    if (CrossProduct(e.top, pt, prev->top)) return;
+    if (!IsCollinear(e.top, pt, prev->top)) return;
 
 
     if (e.outrec->idx == prev->outrec->idx)
     if (e.outrec->idx == prev->outrec->idx)
       AddLocalMaxPoly(*prev, e, pt);
       AddLocalMaxPoly(*prev, e, pt);
@@ -2780,22 +2829,24 @@ namespace Clipper2Lib {
     e.join_with = JoinWith::Left;
     e.join_with = JoinWith::Left;
   }
   }
 
 
-  void ClipperBase::CheckJoinRight(Active& e, 
+  void ClipperBase::CheckJoinRight(Active& e,
     const Point64& pt, bool check_curr_x)
     const Point64& pt, bool check_curr_x)
   {
   {
     Active* next = e.next_in_ael;
     Active* next = e.next_in_ael;
-    if (IsOpen(e) || !IsHotEdge(e) ||
-      !next || IsOpen(*next) || !IsHotEdge(*next)) return;
+    if (!next ||
+      !IsHotEdge(e) || !IsHotEdge(*next) ||
+      IsHorizontal(e) || IsHorizontal(*next) ||
+      IsOpen(e) || IsOpen(*next)) return;
     if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) &&
     if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) &&
-      ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins              
+      ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins
 
 
     if (check_curr_x)
     if (check_curr_x)
     {
     {
-      if (DistanceFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
+      if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
     }
     }
     else if (e.curr_x != next->curr_x) return;
     else if (e.curr_x != next->curr_x) return;
-    if (CrossProduct(e.top, pt, next->top)) return;
-      
+    if (!IsCollinear(e.top, pt, next->top)) return;
+
     if (e.outrec->idx == next->outrec->idx)
     if (e.outrec->idx == next->outrec->idx)
       AddLocalMaxPoly(e, *next, pt);
       AddLocalMaxPoly(e, *next, pt);
     else if (e.outrec->idx < next->outrec->idx)
     else if (e.outrec->idx < next->outrec->idx)
@@ -2863,7 +2914,7 @@ namespace Clipper2Lib {
         op2 = op2->next;
         op2 = op2->next;
     }
     }
 
 
-    if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
+    if (!isOpen && path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
     else return true;
     else return true;
   }
   }
 
 
@@ -2872,8 +2923,8 @@ namespace Clipper2Lib {
     if (!outrec->pts) return false;
     if (!outrec->pts) return false;
     if (!outrec->bounds.IsEmpty()) return true;
     if (!outrec->bounds.IsEmpty()) return true;
     CleanCollinear(outrec);
     CleanCollinear(outrec);
-    if (!outrec->pts || 
-      !BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){ 
+    if (!outrec->pts ||
+      !BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){
         return false;}
         return false;}
     outrec->bounds = GetBounds(outrec->path);
     outrec->bounds = GetBounds(outrec->path);
     return true;
     return true;
@@ -2887,10 +2938,10 @@ namespace Clipper2Lib {
       if(!split || split == outrec || split->recursive_split == outrec) continue;
       if(!split || split == outrec || split->recursive_split == outrec) continue;
       split->recursive_split = outrec; // prevent infinite loops
       split->recursive_split = outrec; // prevent infinite loops
 
 
-      if (split->splits && CheckSplitOwner(outrec, split->splits)) 
+      if (split->splits && CheckSplitOwner(outrec, split->splits))
           return true;
           return true;
-      else if (CheckBounds(split) && 
-        IsValidOwner(outrec, split) && 
+      else if (CheckBounds(split) &&
+        IsValidOwner(outrec, split) &&
         split->bounds.Contains(outrec->bounds) &&
         split->bounds.Contains(outrec->bounds) &&
         Path1InsidePath2(outrec->pts, split->pts))
         Path1InsidePath2(outrec->pts, split->pts))
       {
       {
@@ -2919,7 +2970,7 @@ namespace Clipper2Lib {
 
 
     if (outrec->owner)
     if (outrec->owner)
     {
     {
-      if (!outrec->owner->polypath) 
+      if (!outrec->owner->polypath)
         RecursiveCheckOwners(outrec->owner, polypath);
         RecursiveCheckOwners(outrec->owner, polypath);
       outrec->polypath = outrec->owner->polypath->AddChild(outrec->path);
       outrec->polypath = outrec->owner->polypath->AddChild(outrec->path);
     }
     }
@@ -2968,7 +3019,7 @@ namespace Clipper2Lib {
     open_paths.resize(0);
     open_paths.resize(0);
     if (has_open_paths_)
     if (has_open_paths_)
       open_paths.reserve(outrec_list_.size());
       open_paths.reserve(outrec_list_.size());
-    
+
     // outrec_list_.size() is not static here because
     // outrec_list_.size() is not static here because
     // CheckBounds below can indirectly add additional
     // CheckBounds below can indirectly add additional
     // OutRec (via FixOutRecPts & CleanCollinear)
     // OutRec (via FixOutRecPts & CleanCollinear)
@@ -2991,7 +3042,7 @@ namespace Clipper2Lib {
 
 
   bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale)
   bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale)
   {
   {
-    if (!op || op->next == op || (!isOpen && op->next == op->prev)) 
+    if (!op || op->next == op || (!isOpen && op->next == op->prev))
       return false;
       return false;
 
 
     path.resize(0);
     path.resize(0);
@@ -3024,7 +3075,7 @@ namespace Clipper2Lib {
 #else
 #else
         path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
         path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
 #endif
 #endif
-        
+
       }
       }
       if (reverse)
       if (reverse)
         op2 = op2->prev;
         op2 = op2->prev;

+ 143 - 221
thirdparty/clipper2/src/clipper.offset.cpp

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  28 November 2023                                                *
+* Date      :  17 April 2024                                                   *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  Path Offset (Inflate/Shrink)                                    *
 * Purpose   :  Path Offset (Inflate/Shrink)                                    *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -20,60 +20,19 @@ const double floating_point_tolerance = 1e-12;
 // Miscellaneous methods
 // Miscellaneous methods
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 
 
-inline bool ToggleBoolIf(bool val, bool condition)
+std::optional<size_t> GetLowestClosedPathIdx(const Paths64& paths)
 {
 {
-	return condition ? !val : val;
-}
-
-void GetMultiBounds(const Paths64& paths, std::vector<Rect64>& recList)
-{
-	recList.reserve(paths.size());
-	for (const Path64& path : paths)
-	{ 
-		if (path.size() < 1)
-		{
-			recList.push_back(InvalidRect64);
-			continue;
-		}
-		int64_t x = path[0].x, y = path[0].y;
-		Rect64 r = Rect64(x, y, x, y);
-		for (const Point64& pt : path)
-		{
-			if (pt.y > r.bottom) r.bottom = pt.y;
-			else if (pt.y < r.top) r.top = pt.y;
-			if (pt.x > r.right) r.right = pt.x;
-			else if (pt.x < r.left) r.left = pt.x;
-		}
-		recList.push_back(r);
-	}
-}
-
-bool ValidateBounds(std::vector<Rect64>& recList, double delta)
-{
-	int64_t int_delta = static_cast<int64_t>(delta);
-	int64_t big = MAX_COORD - int_delta;
-	int64_t small = MIN_COORD + int_delta;
-	for (const Rect64& r : recList)
-	{
-		if (!r.IsValid()) continue; // ignore invalid paths
-		else if (r.left < small || r.right > big ||
-			r.top < small || r.bottom > big) return false;
-	}
-	return true;
-}
-
-int GetLowestClosedPathIdx(std::vector<Rect64>& boundsList)
-{
-	int i = -1, result = -1;
+    std::optional<size_t> result;
 	Point64 botPt = Point64(INT64_MAX, INT64_MIN);
 	Point64 botPt = Point64(INT64_MAX, INT64_MIN);
-	for (const Rect64& r : boundsList)
-	{		
-		++i;
-		if (!r.IsValid()) continue; // ignore invalid paths
-		else if (r.bottom > botPt.y || (r.bottom == botPt.y && r.left < botPt.x))
+	for (size_t i = 0; i < paths.size(); ++i)
+	{
+		for (const Point64& pt : paths[i])
 		{
 		{
-			botPt = Point64(r.left, r.bottom);
-			result = static_cast<int>(i);
+			if ((pt.y < botPt.y) || 
+				((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
+            result = i;
+			botPt.x = pt.x;
+			botPt.y = pt.y;
 		}
 		}
 	}
 	}
 	return result;
 	return result;
@@ -96,14 +55,14 @@ inline bool AlmostZero(double value, double epsilon = 0.001)
 	return std::fabs(value) < epsilon;
 	return std::fabs(value) < epsilon;
 }
 }
 
 
-inline double Hypot(double x, double y) 
+inline double Hypot(double x, double y)
 {
 {
 	//see https://stackoverflow.com/a/32436148/359538
 	//see https://stackoverflow.com/a/32436148/359538
 	return std::sqrt(x * x + y * y);
 	return std::sqrt(x * x + y * y);
 }
 }
 
 
 inline PointD NormalizeVector(const PointD& vec)
 inline PointD NormalizeVector(const PointD& vec)
-{	
+{
 	double h = Hypot(vec.x, vec.y);
 	double h = Hypot(vec.x, vec.y);
 	if (AlmostZero(h)) return PointD(0,0);
 	if (AlmostZero(h)) return PointD(0,0);
 	double inverseHypot = 1 / h;
 	double inverseHypot = 1 / h;
@@ -164,30 +123,21 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType
 	for (Path64& p: paths_in)
 	for (Path64& p: paths_in)
 	  StripDuplicates(p, is_joined);
 	  StripDuplicates(p, is_joined);
 
 
-	// get bounds of each path --> bounds_list
-	GetMultiBounds(paths_in, bounds_list);
-
 	if (end_type == EndType::Polygon)
 	if (end_type == EndType::Polygon)
 	{
 	{
-		is_hole_list.reserve(paths_in.size());
-		for (const Path64& path : paths_in)
-			is_hole_list.push_back(Area(path) < 0);
-		lowest_path_idx = GetLowestClosedPathIdx(bounds_list);
+		lowest_path_idx = GetLowestClosedPathIdx(paths_in);
 		// the lowermost path must be an outer path, so if its orientation is negative,
 		// 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.)
 		// then flag the whole group is 'reversed' (will negate delta etc.)
 		// as this is much more efficient than reversing every path.
 		// as this is much more efficient than reversing every path.
-		is_reversed = (lowest_path_idx >= 0) && is_hole_list[lowest_path_idx];
-		if (is_reversed) is_hole_list.flip();
+        is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0;
 	}
 	}
 	else
 	else
 	{
 	{
-		lowest_path_idx = -1;
+        lowest_path_idx = std::nullopt;
 		is_reversed = false;
 		is_reversed = false;
-		is_hole_list.resize(paths_in.size());
 	}
 	}
 }
 }
 
 
-
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 // ClipperOffset methods
 // ClipperOffset methods
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -216,66 +166,29 @@ void ClipperOffset::BuildNormals(const Path64& path)
 	norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
 	norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin())));
 }
 }
 
 
-inline PointD TranslatePoint(const PointD& pt, double dx, double dy)
-{
-#ifdef USINGZ
-	return PointD(pt.x + dx, pt.y + dy, pt.z);
-#else
-	return PointD(pt.x + dx, pt.y + dy);
-#endif
-}
-
-inline PointD ReflectPoint(const PointD& pt, const PointD& pivot)
-{
-#ifdef USINGZ
-	return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z);
-#else
-	return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y));
-#endif
-}
-
-PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b,
-	const PointD& pt2a, const PointD& pt2b)
-{
-	if (pt1a.x == pt1b.x) //vertical
-	{
-		if (pt2a.x == pt2b.x) return PointD(0, 0);
-
-		double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x);
-		double b2 = pt2a.y - m2 * pt2a.x;
-		return PointD(pt1a.x, m2 * pt1a.x + b2);
-	}
-	else if (pt2a.x == pt2b.x) //vertical
-	{
-		double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
-		double b1 = pt1a.y - m1 * pt1a.x;
-		return PointD(pt2a.x, m1 * pt2a.x + b1);
-	}
-	else
-	{
-		double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x);
-		double b1 = pt1a.y - m1 * pt1a.x;
-		double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x);
-		double b2 = pt2a.y - m2 * pt2a.x;
-		if (m1 == m2) return PointD(0, 0);
-		double x = (b2 - b1) / (m1 - m2);
-		return PointD(x, m1 * x + b1);
-	}
-}
-
 void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
 void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
 {
 {
 	PointD pt1, pt2;
 	PointD pt1, pt2;
 	if (j == k)
 	if (j == k)
 	{
 	{
 		double abs_delta = std::abs(group_delta_);
 		double abs_delta = std::abs(group_delta_);
+#ifdef USINGZ
+		pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y, path[j].z);
+		pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y, path[j].z);
+#else
 		pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y);
 		pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y);
 		pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y);
 		pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y);
-	} 
+#endif
+	}
 	else
 	else
 	{
 	{
+#ifdef USINGZ
+		pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y, path[j].z);
+		pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y, path[j].z);
+#else
 		pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y);
 		pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y);
 		pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y);
 		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(pt1));
 	path_out.push_back(Point64(pt2));
 	path_out.push_back(Point64(pt2));
@@ -284,7 +197,7 @@ void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k)
 void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
 void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
 {
 {
 	PointD vec;
 	PointD vec;
-	if (j == k) 
+	if (j == k)
 		vec = PointD(norms[j].y, -norms[j].x);
 		vec = PointD(norms[j].y, -norms[j].x);
 	else
 	else
 		vec = GetAvgUnitVector(
 		vec = GetAvgUnitVector(
@@ -304,10 +217,8 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
 	if (j == k)
 	if (j == k)
 	{
 	{
 		PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
 		PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
-		PointD pt = IntersectPoint(pt1, pt2, pt3, pt4);
-#ifdef USINGZ
-		pt.z = ptQ.z;
-#endif
+		PointD pt = ptQ;
+		GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
 		//get the second intersect point through reflecion
 		//get the second intersect point through reflecion
 		path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
 		path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
 		path_out.push_back(Point64(pt));
 		path_out.push_back(Point64(pt));
@@ -315,10 +226,8 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
 	else
 	else
 	{
 	{
 		PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
 		PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
-		PointD pt = IntersectPoint(pt1, pt2, pt3, pt4);
-#ifdef USINGZ
-		pt.z = ptQ.z;
-#endif
+		PointD pt = ptQ;
+		GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
 		path_out.push_back(Point64(pt));
 		path_out.push_back(Point64(pt));
 		//get the second intersect point through reflecion
 		//get the second intersect point through reflecion
 		path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
 		path_out.push_back(Point64(ReflectPoint(pt, ptQ)));
@@ -343,7 +252,7 @@ void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a
 void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle)
 void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle)
 {
 {
 	if (deltaCallback64_) {
 	if (deltaCallback64_) {
-		// when deltaCallback64_ is assigned, group_delta_ won't be constant, 
+		// when deltaCallback64_ is assigned, group_delta_ won't be constant,
 		// so we'll need to do the following calculations for *every* vertex.
 		// so we'll need to do the following calculations for *every* vertex.
 		double abs_delta = std::fabs(group_delta_);
 		double abs_delta = std::fabs(group_delta_);
 		double arcTol = (arc_tolerance_ > floating_point_tolerance ?
 		double arcTol = (arc_tolerance_ > floating_point_tolerance ?
@@ -387,7 +296,7 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
 	// sin(A) < 0: right turning
 	// sin(A) < 0: right turning
 	// cos(A) < 0: change in angle is more than 90 degree
 	// cos(A) < 0: change in angle is more than 90 degree
 
 
-	if (path[j] == path[k]) { k = j; return; }
+	if (path[j] == path[k]) return;
 
 
 	double sin_a = CrossProduct(norms[j], norms[k]);
 	double sin_a = CrossProduct(norms[j], norms[k]);
 	double cos_a = DotProduct(norms[j], norms[k]);
 	double cos_a = DotProduct(norms[j], norms[k]);
@@ -404,18 +313,29 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
 		return;
 		return;
 	}
 	}
 
 
-	if (cos_a > -0.99 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
+	if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
 	{
 	{
-		// is concave
+		// 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_));
 		path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_));
-		// this extra point is the only (simple) way to ensure that
-	  // path reversals are fully cleaned with the trailing clipper		
-		path_out.push_back(path[j]); // (#405)
+#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(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z));
+#else
 		path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
 		path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_));
+#endif
 	}
 	}
-	else if (cos_a > 0.999 && join_type_ != JoinType::Round) 
+	else if (cos_a > 0.999 && join_type_ != JoinType::Round)
 	{
 	{
-		// almost straight - less than 2.5 degree (#424, #482, #526 & #724) 
+		// almost straight - less than 2.5 degree (#424, #482, #526 & #724)
 		DoMiter(path, j, k, cos_a);
 		DoMiter(path, j, k, cos_a);
 	}
 	}
 	else if (join_type_ == JoinType::Miter)
 	else if (join_type_ == JoinType::Miter)
@@ -435,9 +355,9 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
 void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
 void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
 {
 {
 	path_out.clear();
 	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);
+	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);
 }
 }
 
 
 void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
 void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
@@ -445,8 +365,8 @@ void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
 	OffsetPolygon(group, path);
 	OffsetPolygon(group, path);
 	Path64 reverse_path(path);
 	Path64 reverse_path(path);
 	std::reverse(reverse_path.begin(), reverse_path.end());
 	std::reverse(reverse_path.begin(), reverse_path.end());
-	
-	//rebuild normals // BuildNormals(path);
+
+	//rebuild normals 
 	std::reverse(norms.begin(), norms.end());
 	std::reverse(norms.begin(), norms.end());
 	norms.push_back(norms[0]);
 	norms.push_back(norms[0]);
 	norms.erase(norms.begin());
 	norms.erase(norms.begin());
@@ -459,7 +379,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
 {
 {
 	// do the line start cap
 	// do the line start cap
 	if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
 	if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0);
-	
+
 	if (std::fabs(group_delta_) <= floating_point_tolerance)
 	if (std::fabs(group_delta_) <= floating_point_tolerance)
 		path_out.push_back(path[0]);
 		path_out.push_back(path[0]);
 	else
 	else
@@ -477,13 +397,13 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
 			break;
 			break;
 		}
 		}
 	}
 	}
-	
+
 	size_t highI = path.size() - 1;
 	size_t highI = path.size() - 1;
 	// offset the left side going forward
 	// offset the left side going forward
 	for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
 	for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j)
 		OffsetPoint(group, path, j, k);
 		OffsetPoint(group, path, j, k);
 
 
-	// reverse normals 
+	// reverse normals
 	for (size_t i = highI; i > 0; --i)
 	for (size_t i = highI; i > 0; --i)
 		norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y);
 		norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y);
 	norms[0] = norms[highI];
 	norms[0] = norms[highI];
@@ -510,41 +430,34 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path)
 		}
 		}
 	}
 	}
 
 
-	for (size_t j = highI, k = 0; j > 0; k = j, --j)
+	for (size_t j = highI -1, k = highI; j > 0; k = j, --j)
 		OffsetPoint(group, path, j, k);
 		OffsetPoint(group, path, j, k);
-	solution.push_back(path_out);
+	solution->push_back(path_out);
 }
 }
 
 
 void ClipperOffset::DoGroupOffset(Group& group)
 void ClipperOffset::DoGroupOffset(Group& group)
 {
 {
 	if (group.end_type == EndType::Polygon)
 	if (group.end_type == EndType::Polygon)
 	{
 	{
-		// a straight path (2 points) can now also be 'polygon' offset 
+		// a straight path (2 points) can now also be 'polygon' offset
 		// where the ends will be treated as (180 deg.) joins
 		// where the ends will be treated as (180 deg.) joins
-		if (group.lowest_path_idx < 0) delta_ = std::abs(delta_);
+        if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_);
 		group_delta_ = (group.is_reversed) ? -delta_ : delta_;
 		group_delta_ = (group.is_reversed) ? -delta_ : delta_;
 	}
 	}
 	else
 	else
 		group_delta_ = std::abs(delta_);// *0.5;
 		group_delta_ = std::abs(delta_);// *0.5;
 
 
 	double abs_delta = std::fabs(group_delta_);
 	double abs_delta = std::fabs(group_delta_);
-	if (!ValidateBounds(group.bounds_list, abs_delta))
-	{
-		DoError(range_error_i);
-		error_code_ |= range_error_i;
-		return;
-	}
-
 	join_type_	= group.join_type;
 	join_type_	= group.join_type;
 	end_type_ = group.end_type;
 	end_type_ = group.end_type;
 
 
 	if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
 	if (group.join_type == JoinType::Round || group.end_type == EndType::Round)
 	{
 	{
-		// calculate a sensible number of steps (for 360 deg for the given offset)
-		// arcTol - when arc_tolerance_ is undefined (0), the amount of 
-		// curve imprecision that's allowed is based on the size of the 
-		// offset (delta). Obviously very large offsets will almost always 
-		// require much less precision. See also offset_triginometry2.svg
+		// calculate the number of steps required to approximate a circle
+		// (see http://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 ?
 		double arcTol = (arc_tolerance_ > floating_point_tolerance ?
 			std::min(abs_delta, arc_tolerance_) :
 			std::min(abs_delta, arc_tolerance_) :
 			std::log10(2 + abs_delta) * default_arc_tolerance);
 			std::log10(2 + abs_delta) * default_arc_tolerance);
@@ -556,24 +469,29 @@ void ClipperOffset::DoGroupOffset(Group& group)
 		steps_per_rad_ = steps_per_360 / (2 * PI);
 		steps_per_rad_ = steps_per_360 / (2 * PI);
 	}
 	}
 
 
-	std::vector<Rect64>::const_iterator path_rect_it = group.bounds_list.cbegin();
-	std::vector<bool>::const_iterator is_hole_it = group.is_hole_list.cbegin();
+	//double min_area = PI * Sqr(group_delta_);
 	Paths64::const_iterator path_in_it = group.paths_in.cbegin();
 	Paths64::const_iterator path_in_it = group.paths_in.cbegin();
-	for ( ; path_in_it != group.paths_in.cend(); ++path_in_it, ++path_rect_it, ++is_hole_it)
+	for ( ; path_in_it != group.paths_in.cend(); ++path_in_it)
 	{
 	{
-		if (!path_rect_it->IsValid()) continue;
 		Path64::size_type pathLen = path_in_it->size();
 		Path64::size_type pathLen = path_in_it->size();
 		path_out.clear();
 		path_out.clear();
 
 
 		if (pathLen == 1) // single point
 		if (pathLen == 1) // single point
 		{
 		{
+			if (deltaCallback64_)
+			{
+				group_delta_ = deltaCallback64_(*path_in_it, norms, 0, 0);
+				if (group.is_reversed) group_delta_ = -group_delta_;
+				abs_delta = std::fabs(group_delta_);
+			}
+
 			if (group_delta_ < 1) continue;
 			if (group_delta_ < 1) continue;
 			const Point64& pt = (*path_in_it)[0];
 			const Point64& pt = (*path_in_it)[0];
 			//single vertex so build a circle or square ...
 			//single vertex so build a circle or square ...
 			if (group.join_type == JoinType::Round)
 			if (group.join_type == JoinType::Round)
 			{
 			{
 				double radius = abs_delta;
 				double radius = abs_delta;
-				int steps = static_cast<int>(std::ceil(steps_per_rad_ * 2 * PI)); //#617
+                size_t steps = steps_per_rad_ > 0 ? static_cast<size_t>(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617
 				path_out = Ellipse(pt, radius, radius, steps);
 				path_out = Ellipse(pt, radius, radius, steps);
 #ifdef USINGZ
 #ifdef USINGZ
 				for (auto& p : path_out) p.z = pt.z;
 				for (auto& p : path_out) p.z = pt.z;
@@ -588,19 +506,14 @@ void ClipperOffset::DoGroupOffset(Group& group)
 				for (auto& p : path_out) p.z = pt.z;
 				for (auto& p : path_out) p.z = pt.z;
 #endif
 #endif
 			}
 			}
-			solution.push_back(path_out);
-			continue;
-		} // end of offsetting a single point 
 
 
-		// when shrinking outer paths, make sure they can shrink this far (#593)
-		// also when shrinking holes, make sure they too can shrink this far (#715)
-		if ((group_delta_ > 0) == ToggleBoolIf(*is_hole_it, group.is_reversed) &&
-			(std::min(path_rect_it->Width(), path_rect_it->Height()) <= -group_delta_ * 2) )
-				  continue;
+			solution->push_back(path_out);
+			continue;
+		} // end of offsetting a single point
 
 
 		if ((pathLen == 2) && (group.end_type == EndType::Joined))
 		if ((pathLen == 2) && (group.end_type == EndType::Joined))
-			end_type_ = (group.join_type == JoinType::Round) ? 
-			  EndType::Round : 
+			end_type_ = (group.join_type == JoinType::Round) ?
+			  EndType::Round :
 			  EndType::Square;
 			  EndType::Square;
 
 
 		BuildNormals(*path_in_it);
 		BuildNormals(*path_in_it);
@@ -610,6 +523,16 @@ void ClipperOffset::DoGroupOffset(Group& group)
 	}
 	}
 }
 }
 
 
+#ifdef USINGZ
+void ClipperOffset::ZCB(const Point64& bot1, const Point64& top1,
+	const Point64& bot2, const Point64& top2, Point64& ip)
+{
+	if (bot1.z && ((bot1.z == bot2.z) || (bot1.z == top2.z))) ip.z = bot1.z;
+	else if (bot2.z && (bot2.z == top1.z)) ip.z = bot2.z;
+	else if (top1.z && (top1.z == top2.z)) ip.z = top1.z;
+	else if (zCallback64_) zCallback64_(bot1, top1, bot2, top2, ip);
+}
+#endif
 
 
 size_t ClipperOffset::CalcSolutionCapacity()
 size_t ClipperOffset::CalcSolutionCapacity()
 {
 {
@@ -635,40 +558,35 @@ bool ClipperOffset::CheckReverseOrientation()
 void ClipperOffset::ExecuteInternal(double delta)
 void ClipperOffset::ExecuteInternal(double delta)
 {
 {
 	error_code_ = 0;
 	error_code_ = 0;
-	solution.clear();
 	if (groups_.size() == 0) return;
 	if (groups_.size() == 0) return;
-	solution.reserve(CalcSolutionCapacity());
+	solution->reserve(CalcSolutionCapacity());
 
 
-	if (std::abs(delta) < 0.5) // ie: offset is insignificant 
+	if (std::abs(delta) < 0.5) // ie: offset is insignificant
 	{
 	{
 		Paths64::size_type sol_size = 0;
 		Paths64::size_type sol_size = 0;
 		for (const Group& group : groups_) sol_size += group.paths_in.size();
 		for (const Group& group : groups_) sol_size += group.paths_in.size();
-		solution.reserve(sol_size);
+		solution->reserve(sol_size);
 		for (const Group& group : groups_)
 		for (const Group& group : groups_)
-			copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(solution));
-		return;
+			copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(*solution));
 	}
 	}
+	else
+	{
 
 
-	temp_lim_ = (miter_limit_ <= 1) ?
-		2.0 :
-		2.0 / (miter_limit_ * miter_limit_);
+		temp_lim_ = (miter_limit_ <= 1) ?
+			2.0 :
+			2.0 / (miter_limit_ * miter_limit_);
 
 
-	delta_ = delta;
-	std::vector<Group>::iterator git;
-	for (git = groups_.begin(); git != groups_.end(); ++git)
-	{
-		DoGroupOffset(*git);
-		if (!error_code_) continue; // all OK
-		solution.clear();
+		delta_ = delta;
+		std::vector<Group>::iterator git;
+		for (git = groups_.begin(); git != groups_.end(); ++git)
+		{
+			DoGroupOffset(*git);
+			if (!error_code_) continue; // all OK
+			solution->clear();
+		}
 	}
 	}
-}
 
 
-void ClipperOffset::Execute(double delta, Paths64& paths)
-{
-	paths.clear();
-
-	ExecuteInternal(delta);
-	if (!solution.size()) return;
+	if (!solution->size()) return;
 
 
 	bool paths_reversed = CheckReverseOrientation();
 	bool paths_reversed = CheckReverseOrientation();
 	//clean up self-intersections ...
 	//clean up self-intersections ...
@@ -677,41 +595,45 @@ void ClipperOffset::Execute(double delta, Paths64& paths)
 	//the solution should retain the orientation of the input
 	//the solution should retain the orientation of the input
 	c.ReverseSolution(reverse_solution_ != paths_reversed);
 	c.ReverseSolution(reverse_solution_ != paths_reversed);
 #ifdef USINGZ
 #ifdef USINGZ
-	if (zCallback64_) { c.SetZCallback(zCallback64_); }
+	auto fp = std::bind(&ClipperOffset::ZCB, this, std::placeholders::_1,
+		std::placeholders::_2, std::placeholders::_3,
+		std::placeholders::_4, std::placeholders::_5);
+	c.SetZCallback(fp);
 #endif
 #endif
-	c.AddSubject(solution);
-	if (paths_reversed)
-		c.Execute(ClipType::Union, FillRule::Negative, paths);
+	c.AddSubject(*solution);
+	if (solution_tree)
+	{
+		if (paths_reversed)
+			c.Execute(ClipType::Union, FillRule::Negative, *solution_tree);
+		else
+			c.Execute(ClipType::Union, FillRule::Positive, *solution_tree);
+	}
 	else
 	else
-		c.Execute(ClipType::Union, FillRule::Positive, paths);
+	{
+		if (paths_reversed)
+			c.Execute(ClipType::Union, FillRule::Negative, *solution);
+		else
+			c.Execute(ClipType::Union, FillRule::Positive, *solution);
+	}
+}
+
+void ClipperOffset::Execute(double delta, Paths64& paths)
+{
+	paths.clear();
+	solution = &paths;
+	solution_tree = nullptr;
+	ExecuteInternal(delta);
 }
 }
 
 
 
 
 void ClipperOffset::Execute(double delta, PolyTree64& polytree)
 void ClipperOffset::Execute(double delta, PolyTree64& polytree)
 {
 {
 	polytree.Clear();
 	polytree.Clear();
-
+	solution_tree = &polytree;
+	solution = new Paths64();
 	ExecuteInternal(delta);
 	ExecuteInternal(delta);
-	if (!solution.size()) return;
-
-	bool paths_reversed = CheckReverseOrientation();
-	//clean up self-intersections ...
-	Clipper64 c;
-	c.PreserveCollinear(false);
-	//the solution should retain the orientation of the input
-	c.ReverseSolution (reverse_solution_ != paths_reversed);
-#ifdef USINGZ
-	if (zCallback64_) {
-		c.SetZCallback(zCallback64_);
-	}
-#endif
-	c.AddSubject(solution);
-
-
-	if (paths_reversed)
-		c.Execute(ClipType::Union, FillRule::Negative, polytree);
-	else
-		c.Execute(ClipType::Union, FillRule::Positive, polytree);
+	delete solution;
+	solution = nullptr;
 }
 }
 
 
 void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths)
 void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths)

+ 67 - 44
thirdparty/clipper2/src/clipper.rectclip.cpp

@@ -1,8 +1,8 @@
 /*******************************************************************************
 /*******************************************************************************
 * Author    :  Angus Johnson                                                   *
 * Author    :  Angus Johnson                                                   *
-* Date      :  8 September 2023                                                *
+* Date      :  5 July 2024                                                     *
 * Website   :  http://www.angusj.com                                           *
 * Website   :  http://www.angusj.com                                           *
-* Copyright :  Angus Johnson 2010-2023                                         *
+* Copyright :  Angus Johnson 2010-2024                                         *
 * Purpose   :  FAST rectangular clipping                                       *
 * Purpose   :  FAST rectangular clipping                                       *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
 *******************************************************************************/
 *******************************************************************************/
@@ -71,7 +71,7 @@ namespace Clipper2Lib {
     return pt1.y == pt2.y;
     return pt1.y == pt2.y;
   }
   }
 
 
-  inline bool GetSegmentIntersection(const Point64& p1,
+  bool GetSegmentIntersection(const Point64& p1,
     const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
     const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
   {
   {
     double res1 = CrossProduct(p1, p3, p4);
     double res1 = CrossProduct(p1, p3, p4);
@@ -113,7 +113,7 @@ namespace Clipper2Lib {
     if ((res3 > 0) == (res4 > 0)) return false;
     if ((res3 > 0) == (res4 > 0)) return false;
 
 
     // segments must intersect to get here
     // segments must intersect to get here
-    return GetIntersectPoint(p1, p2, p3, p4, ip);
+    return GetSegmentIntersectPt(p1, p2, p3, p4, ip);
   }
   }
 
 
   inline bool GetIntersection(const Path64& rectPath,
   inline bool GetIntersection(const Path64& rectPath,
@@ -125,7 +125,7 @@ namespace Clipper2Lib {
     {
     {
     case Location::Left:
     case Location::Left:
       if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true;
       if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true;
-      else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))        
+      else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
       {
       {
         loc = Location::Top;
         loc = Location::Top;
         return true;
         return true;
@@ -180,7 +180,7 @@ namespace Clipper2Lib {
       else return false;
       else return false;
 
 
     default: // loc == rInside
     default: // loc == rInside
-      if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) 
+      if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
       {
       {
         loc = Location::Left;
         loc = Location::Left;
         return true;
         return true;
@@ -320,9 +320,9 @@ namespace Clipper2Lib {
     // this method is only called by InternalExecute.
     // this method is only called by InternalExecute.
     // Later splitting & rejoining won't create additional op's,
     // Later splitting & rejoining won't create additional op's,
     // though they will change the (non-storage) results_ count.
     // though they will change the (non-storage) results_ count.
-    int curr_idx = static_cast<int>(results_.size()) - 1;
+    size_t curr_idx = results_.size();
     OutPt2* result;
     OutPt2* result;
-    if (curr_idx < 0 || start_new)
+    if (curr_idx == 0 || start_new)
     {
     {
       result = &op_container_.emplace_back(OutPt2());
       result = &op_container_.emplace_back(OutPt2());
       result->pt = pt;
       result->pt = pt;
@@ -332,6 +332,7 @@ namespace Clipper2Lib {
     }
     }
     else
     else
     {
     {
+      --curr_idx;
       OutPt2* prevOp = results_[curr_idx];
       OutPt2* prevOp = results_[curr_idx];
       if (prevOp->pt == pt)  return prevOp;
       if (prevOp->pt == pt)  return prevOp;
       result = &op_container_.emplace_back(OutPt2());
       result = &op_container_.emplace_back(OutPt2());
@@ -349,27 +350,27 @@ namespace Clipper2Lib {
   void RectClip64::AddCorner(Location prev, Location curr)
   void RectClip64::AddCorner(Location prev, Location curr)
   {
   {
     if (HeadingClockwise(prev, curr))
     if (HeadingClockwise(prev, curr))
-      Add(rect_as_path_[static_cast<int>(prev)]);
+      Add(rect_as_path_[static_cast<size_t>(prev)]);
     else
     else
-      Add(rect_as_path_[static_cast<int>(curr)]);
+      Add(rect_as_path_[static_cast<size_t>(curr)]);
   }
   }
 
 
   void RectClip64::AddCorner(Location& loc, bool isClockwise)
   void RectClip64::AddCorner(Location& loc, bool isClockwise)
   {
   {
     if (isClockwise)
     if (isClockwise)
     {
     {
-      Add(rect_as_path_[static_cast<int>(loc)]);
+      Add(rect_as_path_[static_cast<size_t>(loc)]);
       loc = GetAdjacentLocation(loc, true);
       loc = GetAdjacentLocation(loc, true);
     }
     }
     else
     else
     {
     {
       loc = GetAdjacentLocation(loc, false);
       loc = GetAdjacentLocation(loc, false);
-      Add(rect_as_path_[static_cast<int>(loc)]);
+      Add(rect_as_path_[static_cast<size_t>(loc)]);
     }
     }
   }
   }
 
 
   void RectClip64::GetNextLocation(const Path64& path,
   void RectClip64::GetNextLocation(const Path64& path,
-    Location& loc, int& i, int highI)
+    Location& loc, size_t& i, size_t highI)
   {
   {
     switch (loc)
     switch (loc)
     {
     {
@@ -420,31 +421,52 @@ namespace Clipper2Lib {
         break; //inner loop
         break; //inner loop
       }
       }
       break;
       break;
-    } //switch          
+    } //switch
+  }
+
+  bool StartLocsAreClockwise(const std::vector<Location>& startlocs)
+  {
+    int result = 0;
+    for (size_t i = 1; i < startlocs.size(); ++i)
+    {
+      int d = static_cast<int>(startlocs[i]) - static_cast<int>(startlocs[i - 1]);
+      switch (d)
+      {
+        case -1: result -= 1; break;
+        case 1: result += 1; break;
+        case -3: result += 1; break;
+        case 3: result -= 1; break;
+      }
+    }
+    return result > 0;
   }
   }
 
 
   void RectClip64::ExecuteInternal(const Path64& path)
   void RectClip64::ExecuteInternal(const Path64& path)
   {
   {
-    int i = 0, highI = static_cast<int>(path.size()) - 1;
+    if (path.size() < 1)
+      return;
+
+    size_t highI = path.size() - 1;
     Location prev = Location::Inside, loc;
     Location prev = Location::Inside, loc;
     Location crossing_loc = Location::Inside;
     Location crossing_loc = Location::Inside;
     Location first_cross_ = Location::Inside;
     Location first_cross_ = Location::Inside;
     if (!GetLocation(rect_, path[highI], loc))
     if (!GetLocation(rect_, path[highI], loc))
     {
     {
-      i = highI - 1;
-      while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i;
-      if (i < 0) 
+      size_t i = highI;
+      while (i > 0 && !GetLocation(rect_, path[i - 1], prev))
+        --i;
+      if (i == 0)
       {
       {
         // all of path must be inside fRect
         // all of path must be inside fRect
         for (const auto& pt : path) Add(pt);
         for (const auto& pt : path) Add(pt);
         return;
         return;
       }
       }
       if (prev == Location::Inside) loc = Location::Inside;
       if (prev == Location::Inside) loc = Location::Inside;
-      i = 0;
     }
     }
-    Location startingLoc = loc;
+    Location starting_loc = loc;
 
 
     ///////////////////////////////////////////////////
     ///////////////////////////////////////////////////
+    size_t i = 0;
     while (i <= highI)
     while (i <= highI)
     {
     {
       prev = loc;
       prev = loc;
@@ -454,12 +476,12 @@ namespace Clipper2Lib {
 
 
       if (i > highI) break;
       if (i > highI) break;
       Point64 ip, ip2;
       Point64 ip, ip2;
-      Point64 prev_pt = (i) ? 
-        path[static_cast<size_t>(i - 1)] : 
+      Point64 prev_pt = (i) ?
+        path[static_cast<size_t>(i - 1)] :
         path[highI];
         path[highI];
 
 
       crossing_loc = loc;
       crossing_loc = loc;
-      if (!GetIntersection(rect_as_path_, 
+      if (!GetIntersection(rect_as_path_,
         path[i], prev_pt, crossing_loc, ip))
         path[i], prev_pt, crossing_loc, ip))
       {
       {
         // ie remaining outside
         // ie remaining outside
@@ -470,7 +492,7 @@ namespace Clipper2Lib {
             start_locs_.push_back(prev);
             start_locs_.push_back(prev);
             prev = GetAdjacentLocation(prev, isClockw);
             prev = GetAdjacentLocation(prev, isClockw);
           } while (prev != loc);
           } while (prev != loc);
-          crossing_loc = crossing_prev; // still not crossed 
+          crossing_loc = crossing_prev; // still not crossed
         }
         }
         else if (prev != Location::Inside && prev != loc)
         else if (prev != Location::Inside && prev != loc)
         {
         {
@@ -504,7 +526,7 @@ namespace Clipper2Lib {
       }
       }
       else if (prev != Location::Inside)
       else if (prev != Location::Inside)
       {
       {
-        // passing right through rect. 'ip' here will be the second 
+        // passing right through rect. 'ip' here will be the second
         // intersect pt but we'll also need the first intersect pt (ip2)
         // intersect pt but we'll also need the first intersect pt (ip2)
         loc = prev;
         loc = prev;
         GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
         GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
@@ -543,7 +565,7 @@ namespace Clipper2Lib {
     if (first_cross_ == Location::Inside)
     if (first_cross_ == Location::Inside)
     {
     {
       // path never intersects
       // path never intersects
-      if (startingLoc != Location::Inside)
+      if (starting_loc != Location::Inside)
       {
       {
         // path is outside rect
         // path is outside rect
         // but being outside, it still may not contain rect
         // but being outside, it still may not contain rect
@@ -552,11 +574,13 @@ namespace Clipper2Lib {
         {
         {
           // yep, the path does fully contain rect
           // yep, the path does fully contain rect
           // so add rect to the solution
           // so add rect to the solution
+          bool is_clockwise_path = StartLocsAreClockwise(start_locs_);
           for (size_t j = 0; j < 4; ++j)
           for (size_t j = 0; j < 4; ++j)
           {
           {
-            Add(rect_as_path_[j]);
+            size_t k = is_clockwise_path ? j : 3 - j; // reverses result path
+            Add(rect_as_path_[k]);
             // we may well need to do some splitting later, so
             // we may well need to do some splitting later, so
-            AddToEdge(edges_[j * 2], results_[0]);
+            AddToEdge(edges_[k * 2], results_[0]);
           }
           }
         }
         }
       }
       }
@@ -589,8 +613,7 @@ namespace Clipper2Lib {
       OutPt2* op2 = op;
       OutPt2* op2 = op;
       do
       do
       {
       {
-        if (!CrossProduct(op2->prev->pt,
-          op2->pt, op2->next->pt))
+        if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt))
         {
         {
           if (op2 == op)
           if (op2 == op)
           {
           {
@@ -640,7 +663,7 @@ namespace Clipper2Lib {
     }
     }
   }
   }
 
 
-  void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw)
+  void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw)
   {
   {
     if (ccw.empty()) return;
     if (ccw.empty()) return;
     bool isHorz = ((idx == 1) || (idx == 3));
     bool isHorz = ((idx == 1) || (idx == 3));
@@ -648,7 +671,7 @@ namespace Clipper2Lib {
     size_t i = 0, j = 0;
     size_t i = 0, j = 0;
     OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;
     OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;
 
 
-    while (i < cw.size()) 
+    while (i < cw.size())
     {
     {
       p1 = cw[i];
       p1 = cw[i];
       if (!p1 || p1->next == p1->prev)
       if (!p1 || p1->next == p1->prev)
@@ -825,8 +848,8 @@ namespace Clipper2Lib {
     OutPt2* op2 = op->next;
     OutPt2* op2 = op->next;
     while (op2 && op2 != op)
     while (op2 && op2 != op)
     {
     {
-      if (CrossProduct(op2->prev->pt, 
-        op2->pt, op2->next->pt) == 0)
+      if (IsCollinear(op2->prev->pt,
+        op2->pt, op2->next->pt))
       {
       {
         op = op2->prev;
         op = op2->prev;
         op2 = UnlinkOp(op2);
         op2 = UnlinkOp(op2);
@@ -854,7 +877,7 @@ namespace Clipper2Lib {
     if (rect_.IsEmpty()) return result;
     if (rect_.IsEmpty()) return result;
 
 
     for (const Path64& path : paths)
     for (const Path64& path : paths)
-    {      
+    {
       if (path.size() < 3) continue;
       if (path.size() < 3) continue;
       path_bounds_ = GetBounds(path);
       path_bounds_ = GetBounds(path);
       if (!rect_.Intersects(path_bounds_))
       if (!rect_.Intersects(path_bounds_))
@@ -868,9 +891,9 @@ namespace Clipper2Lib {
 
 
       ExecuteInternal(path);
       ExecuteInternal(path);
       CheckEdges();
       CheckEdges();
-      for (int i = 0; i < 4; ++i)
+      for (size_t i = 0; i < 4; ++i)
         TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
         TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
-  
+
       for (OutPt2*& op :  results_)
       for (OutPt2*& op :  results_)
       {
       {
         Path64 tmp = GetPath(op);
         Path64 tmp = GetPath(op);
@@ -925,14 +948,14 @@ namespace Clipper2Lib {
     op_container_ = std::deque<OutPt2>();
     op_container_ = std::deque<OutPt2>();
     start_locs_.clear();
     start_locs_.clear();
 
 
-    int i = 1, highI = static_cast<int>(path.size()) - 1;
+    size_t i = 1, highI = path.size() - 1;
 
 
     Location prev = Location::Inside, loc;
     Location prev = Location::Inside, loc;
     Location crossing_loc;
     Location crossing_loc;
     if (!GetLocation(rect_, path[0], loc))
     if (!GetLocation(rect_, path[0], loc))
     {
     {
       while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
       while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
-      if (i > highI) 
+      if (i > highI)
       {
       {
         // all of path must be inside fRect
         // all of path must be inside fRect
         for (const auto& pt : path) Add(pt);
         for (const auto& pt : path) Add(pt);
@@ -953,7 +976,7 @@ namespace Clipper2Lib {
       Point64 prev_pt = path[static_cast<size_t>(i - 1)];
       Point64 prev_pt = path[static_cast<size_t>(i - 1)];
 
 
       crossing_loc = loc;
       crossing_loc = loc;
-      if (!GetIntersection(rect_as_path_, 
+      if (!GetIntersection(rect_as_path_,
         path[i], prev_pt, crossing_loc, ip))
         path[i], prev_pt, crossing_loc, ip))
       {
       {
         // ie remaining outside
         // ie remaining outside
@@ -971,10 +994,10 @@ namespace Clipper2Lib {
       }
       }
       else if (prev != Location::Inside)
       else if (prev != Location::Inside)
       {
       {
-        // passing right through rect. 'ip' here will be the second 
+        // passing right through rect. 'ip' here will be the second
         // intersect pt but we'll also need the first intersect pt (ip2)
         // intersect pt but we'll also need the first intersect pt (ip2)
         crossing_loc = prev;
         crossing_loc = prev;
-        GetIntersection(rect_as_path_, 
+        GetIntersection(rect_as_path_,
           prev_pt, path[i], crossing_loc, ip2);
           prev_pt, path[i], crossing_loc, ip2);
         Add(ip2, true);
         Add(ip2, true);
         Add(ip);
         Add(ip);
@@ -991,14 +1014,14 @@ namespace Clipper2Lib {
   {
   {
     Path64 result;
     Path64 result;
     if (!op || op == op->next) return result;
     if (!op || op == op->next) return result;
-    op = op->next; // starting at path beginning 
+    op = op->next; // starting at path beginning
     result.push_back(op->pt);
     result.push_back(op->pt);
     OutPt2 *op2 = op->next;
     OutPt2 *op2 = op->next;
     while (op2 != op)
     while (op2 != op)
     {
     {
       result.push_back(op2->pt);
       result.push_back(op2->pt);
       op2 = op2->next;
       op2 = op2->next;
-    }        
+    }
     return result;
     return result;
   }
   }