summaryrefslogtreecommitdiffstats
path: root/thirdparty/clipper2/include
diff options
context:
space:
mode:
authorsmix8 <52464204+smix8@users.noreply.github.com>2023-08-17 18:32:30 +0200
committersmix8 <52464204+smix8@users.noreply.github.com>2023-09-25 19:48:14 +0200
commit0ee7e3102b6072d2f5a9d157c8afdb99e13624e6 (patch)
tree80e45613d1cdc8a850d6ceb1f9bce7f63d0db94e /thirdparty/clipper2/include
parent82f6e9be5ea06bfef1adb315f15a409939b4a100 (diff)
downloadredot-engine-0ee7e3102b6072d2f5a9d157c8afdb99e13624e6.tar.gz
Add 2D navigation mesh baking
Adds 2D navigation mesh baking.
Diffstat (limited to 'thirdparty/clipper2/include')
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.core.h846
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.engine.h610
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.export.h774
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.h776
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.minkowski.h120
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.offset.h114
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.rectclip.h82
7 files changed, 3322 insertions, 0 deletions
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
new file mode 100644
index 0000000000..086d1b659c
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -0,0 +1,846 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 22 March 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : Core Clipper Library structures and functions *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+#ifndef CLIPPER_CORE_H
+#define CLIPPER_CORE_H
+
+#include <cstdint>
+#include <cstdlib>
+#include <cmath>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <algorithm>
+#include <climits>
+#include <numeric>
+
+#define CLIPPER2_THROW(exception) std::abort()
+
+namespace Clipper2Lib
+{
+
+#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
+
+ class Clipper2Exception : public std::exception {
+ public:
+ explicit Clipper2Exception(const char* description) :
+ m_descr(description) {}
+ virtual const char* what() const throw() override { return m_descr.c_str(); }
+ private:
+ std::string m_descr;
+ };
+
+ static const char* precision_error =
+ "Precision exceeds the permitted range";
+ static const char* range_error =
+ "Values exceed permitted range";
+ static const char* scale_error =
+ "Invalid scale (either 0 or too large)";
+ static const char* non_pair_error =
+ "There must be 2 values for each coordinate";
+#endif
+
+ // error codes (2^n)
+ 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 range_error_i = 64;
+
+ static const double PI = 3.141592653589793238;
+ static const int64_t MAX_COORD = INT64_MAX >> 2;
+ static const int64_t MIN_COORD = -MAX_COORD;
+ static const int64_t INVALID = INT64_MAX;
+ const double max_coord = static_cast<double>(MAX_COORD);
+ const double min_coord = static_cast<double>(MIN_COORD);
+
+ static const double MAX_DBL = (std::numeric_limits<double>::max)();
+
+ static void DoError(int error_code)
+ {
+#if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS)
+ switch (error_code)
+ {
+ case precision_error_i:
+ CLIPPER2_THROW(Clipper2Exception(precision_error));
+ case scale_error_i:
+ CLIPPER2_THROW(Clipper2Exception(scale_error));
+ case non_pair_error_i:
+ CLIPPER2_THROW(Clipper2Exception(non_pair_error));
+ case range_error_i:
+ CLIPPER2_THROW(Clipper2Exception(range_error));
+ }
+#else
+ if(error_code) {}; // only to stop compiler 'parameter not used' warning
+#endif
+ }
+
+ //By far the most widely used filling rules for polygons are EvenOdd
+ //and NonZero, sometimes called Alternate and Winding respectively.
+ //https://en.wikipedia.org/wiki/Nonzero-rule
+ enum class FillRule { EvenOdd, NonZero, Positive, Negative };
+
+ // Point ------------------------------------------------------------------------
+
+ template <typename T>
+ struct Point {
+ T x;
+ T y;
+#ifdef USINGZ
+ int64_t z;
+
+ template <typename T2>
+ 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)
+ {
+ x = static_cast<T>(std::round(x_));
+ y = static_cast<T>(std::round(y_));
+ z = z_;
+ }
+ else
+ {
+ x = static_cast<T>(x_);
+ y = static_cast<T>(y_);
+ z = z_;
+ }
+ }
+
+ explicit Point() : x(0), y(0), z(0) {};
+
+ template <typename T2>
+ Point(const T2 x_, const T2 y_, const int64_t z_ = 0)
+ {
+ Init(x_, y_);
+ z = z_;
+ }
+
+ template <typename T2>
+ explicit Point<T>(const Point<T2>& p)
+ {
+ Init(p.x, p.y, p.z);
+ }
+
+ Point operator * (const double scale) const
+ {
+ return Point(x * scale, y * scale, z);
+ }
+
+
+ friend std::ostream& operator<<(std::ostream& os, const Point& point)
+ {
+ os << point.x << "," << point.y << "," << point.z << " ";
+ return os;
+ }
+
+#else
+
+ template <typename T2>
+ 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)
+ {
+ x = static_cast<T>(std::round(x_));
+ y = static_cast<T>(std::round(y_));
+ }
+ else
+ {
+ x = static_cast<T>(x_);
+ y = static_cast<T>(y_);
+ }
+ }
+
+ explicit Point() : x(0), y(0) {};
+
+ template <typename T2>
+ 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); }
+
+ Point operator * (const double scale) const
+ {
+ return Point(x * scale, y * scale);
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const Point& point)
+ {
+ os << point.x << "," << point.y << " ";
+ return os;
+ }
+#endif
+
+ friend bool operator==(const Point& a, const Point& b)
+ {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ friend bool operator!=(const Point& a, const Point& b)
+ {
+ return !(a == b);
+ }
+
+ inline Point<T> operator-() const
+ {
+ return Point<T>(-x, -y);
+ }
+
+ inline Point operator+(const Point& b) const
+ {
+ return Point(x + b.x, y + b.y);
+ }
+
+ inline Point operator-(const Point& b) const
+ {
+ return Point(x - b.x, y - b.y);
+ }
+
+ inline void Negate() { x = -x; y = -y; }
+
+ };
+
+ //nb: using 'using' here (instead of typedef) as they can be used in templates
+ using Point64 = Point<int64_t>;
+ using PointD = Point<double>;
+
+ template <typename T>
+ using Path = std::vector<Point<T>>;
+ template <typename T>
+ using Paths = std::vector<Path<T>>;
+
+ using Path64 = Path<int64_t>;
+ using PathD = Path<double>;
+ using Paths64 = std::vector< Path64>;
+ using PathsD = std::vector< PathD>;
+
+ // Rect ------------------------------------------------------------------------
+
+ template <typename T>
+ struct Rect;
+
+ using Rect64 = Rect<int64_t>;
+ using RectD = Rect<double>;
+
+ template <typename T>
+ struct Rect {
+ T left;
+ T top;
+ T right;
+ T bottom;
+
+ Rect() :
+ left(0),
+ top(0),
+ right(0),
+ bottom(0) {}
+
+ Rect(T l, T t, T r, T b) :
+ left(l),
+ top(t),
+ right(r),
+ bottom(b) {}
+
+ Rect(bool is_valid)
+ {
+ if (is_valid)
+ {
+ left = right = top = bottom = 0;
+ }
+ else
+ {
+ left = top = std::numeric_limits<T>::max();
+ right = bottom = -std::numeric_limits<int64_t>::max();
+ }
+ }
+
+ T Width() const { return right - left; }
+ T Height() const { return bottom - top; }
+ void Width(T width) { right = left + width; }
+ void Height(T height) { bottom = top + height; }
+
+ Point<T> MidPoint() const
+ {
+ return Point<T>((left + right) / 2, (top + bottom) / 2);
+ }
+
+ Path<T> AsPath() const
+ {
+ Path<T> result;
+ result.reserve(4);
+ result.push_back(Point<T>(left, top));
+ result.push_back(Point<T>(right, top));
+ result.push_back(Point<T>(right, bottom));
+ result.push_back(Point<T>(left, bottom));
+ return result;
+ }
+
+ bool Contains(const Point<T>& pt) const
+ {
+ return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom;
+ }
+
+ bool Contains(const Rect<T>& rec) const
+ {
+ return rec.left >= left && rec.right <= right &&
+ rec.top >= top && rec.bottom <= bottom;
+ }
+
+ void Scale(double scale) {
+ left *= scale;
+ top *= scale;
+ right *= scale;
+ bottom *= scale;
+ }
+
+ bool IsEmpty() const { return bottom <= top || right <= left; };
+
+ bool Intersects(const Rect<T>& rec) const
+ {
+ return ((std::max)(left, rec.left) <= (std::min)(right, rec.right)) &&
+ ((std::max)(top, rec.top) <= (std::min)(bottom, rec.bottom));
+ };
+
+ friend std::ostream& operator<<(std::ostream& os, const Rect<T>& rect) {
+ os << "("
+ << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom
+ << ")";
+ return os;
+ }
+ };
+
+ template <typename T1, typename T2>
+ inline Rect<T1> ScaleRect(const Rect<T2>& rect, double scale)
+ {
+ Rect<T1> result;
+
+ if constexpr (std::numeric_limits<T1>::is_integer &&
+ !std::numeric_limits<T2>::is_integer)
+ {
+ result.left = static_cast<T1>(std::round(rect.left * scale));
+ result.top = static_cast<T1>(std::round(rect.top * scale));
+ result.right = static_cast<T1>(std::round(rect.right * scale));
+ result.bottom = static_cast<T1>(std::round(rect.bottom * scale));
+ }
+ else
+ {
+ result.left = rect.left * scale;
+ result.top = rect.top * scale;
+ result.right = rect.right * scale;
+ result.bottom = rect.bottom * scale;
+ }
+ return result;
+ }
+
+ static const Rect64 MaxInvalidRect64 = Rect64(
+ INT64_MAX, INT64_MAX, INT64_MIN, INT64_MIN);
+ static const RectD MaxInvalidRectD = RectD(
+ MAX_DBL, MAX_DBL, -MAX_DBL, -MAX_DBL);
+
+ template <typename T>
+ 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();
+ for (const auto& 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;
+ }
+ return Rect<T>(xmin, ymin, xmax, ymax);
+ }
+
+ template <typename T>
+ 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();
+ for (const Path<T>& path : paths)
+ 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;
+ }
+ return Rect<T>(xmin, ymin, xmax, ymax);
+ }
+
+ template <typename T>
+ std::ostream& operator << (std::ostream& outstream, const Path<T>& path)
+ {
+ if (!path.empty())
+ {
+ auto pt = path.cbegin(), last = path.cend() - 1;
+ while (pt != last)
+ outstream << *pt++ << ", ";
+ outstream << *last << std::endl;
+ }
+ return outstream;
+ }
+
+ template <typename T>
+ std::ostream& operator << (std::ostream& outstream, const Paths<T>& paths)
+ {
+ for (auto p : paths)
+ outstream << p;
+ return outstream;
+ }
+
+
+ template <typename T1, typename T2>
+ inline Path<T1> ScalePath(const Path<T2>& path,
+ double scale_x, double scale_y, int& error_code)
+ {
+ Path<T1> result;
+ if (scale_x == 0 || scale_y == 0)
+ {
+ error_code |= scale_error_i;
+ DoError(scale_error_i);
+ // if no exception, treat as non-fatal error
+ if (scale_x == 0) scale_x = 1.0;
+ if (scale_y == 0) scale_y = 1.0;
+ }
+
+ result.reserve(path.size());
+#ifdef USINGZ
+ std::transform(path.begin(), path.end(), back_inserter(result),
+ [scale_x, scale_y](const auto& pt)
+ { return Point<T1>(pt.x * scale_x, pt.y * scale_y, pt.z); });
+#else
+ std::transform(path.begin(), path.end(), back_inserter(result),
+ [scale_x, scale_y](const auto& pt)
+ { return Point<T1>(pt.x * scale_x, pt.y * scale_y); });
+#endif
+ return result;
+ }
+
+ template <typename T1, typename T2>
+ inline Path<T1> ScalePath(const Path<T2>& path,
+ double scale, int& error_code)
+ {
+ return ScalePath<T1, T2>(path, scale, scale, error_code);
+ }
+
+ template <typename T1, typename T2>
+ inline Paths<T1> ScalePaths(const Paths<T2>& paths,
+ double scale_x, double scale_y, int& error_code)
+ {
+ Paths<T1> result;
+
+ if constexpr (std::numeric_limits<T1>::is_integer &&
+ !std::numeric_limits<T2>::is_integer)
+ {
+ RectD r = GetBounds(paths);
+ if ((r.left * scale_x) < min_coord ||
+ (r.right * scale_x) > max_coord ||
+ (r.top * scale_y) < min_coord ||
+ (r.bottom * scale_y) > max_coord)
+ {
+ error_code |= range_error_i;
+ DoError(range_error_i);
+ return result; // empty path
+ }
+ }
+
+ result.reserve(paths.size());
+ std::transform(paths.begin(), paths.end(), back_inserter(result),
+ [=, &error_code](const auto& path)
+ { return ScalePath<T1, T2>(path, scale_x, scale_y, error_code); });
+ return result;
+ }
+
+ template <typename T1, typename T2>
+ inline Paths<T1> ScalePaths(const Paths<T2>& paths,
+ double scale, int& error_code)
+ {
+ return ScalePaths<T1, T2>(paths, scale, scale, error_code);
+ }
+
+ template <typename T1, typename T2>
+ inline Path<T1> TransformPath(const Path<T2>& path)
+ {
+ Path<T1> result;
+ result.reserve(path.size());
+ std::transform(path.cbegin(), path.cend(), std::back_inserter(result),
+ [](const Point<T2>& pt) {return Point<T1>(pt); });
+ return result;
+ }
+
+ template <typename T1, typename T2>
+ inline Paths<T1> TransformPaths(const Paths<T2>& paths)
+ {
+ Paths<T1> result;
+ std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result),
+ [](const Path<T2>& path) {return TransformPath<T1, T2>(path); });
+ return result;
+ }
+
+ inline PathD Path64ToPathD(const Path64& path)
+ {
+ return TransformPath<double, int64_t>(path);
+ }
+
+ inline PathsD Paths64ToPathsD(const Paths64& paths)
+ {
+ return TransformPaths<double, int64_t>(paths);
+ }
+
+ inline Path64 PathDToPath64(const PathD& path)
+ {
+ return TransformPath<int64_t, double>(path);
+ }
+
+ inline Paths64 PathsDToPaths64(const PathsD& paths)
+ {
+ return TransformPaths<int64_t, double>(paths);
+ }
+
+ template<typename T>
+ inline double Sqr(T val)
+ {
+ return static_cast<double>(val) * static_cast<double>(val);
+ }
+
+ template<typename T>
+ inline bool NearEqual(const Point<T>& p1,
+ const Point<T>& p2, double max_dist_sqrd)
+ {
+ return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd;
+ }
+
+ template<typename T>
+ inline Path<T> StripNearEqual(const Path<T>& path,
+ double max_dist_sqrd, bool is_closed_path)
+ {
+ if (path.size() == 0) return Path<T>();
+ Path<T> result;
+ result.reserve(path.size());
+ typename Path<T>::const_iterator path_iter = path.cbegin();
+ Point<T> first_pt = *path_iter++, last_pt = first_pt;
+ result.push_back(first_pt);
+ for (; path_iter != path.cend(); ++path_iter)
+ {
+ if (!NearEqual(*path_iter, last_pt, max_dist_sqrd))
+ {
+ last_pt = *path_iter;
+ result.push_back(last_pt);
+ }
+ }
+ if (!is_closed_path) return result;
+ while (result.size() > 1 &&
+ NearEqual(result.back(), first_pt, max_dist_sqrd)) result.pop_back();
+ return result;
+ }
+
+ template<typename T>
+ inline Paths<T> StripNearEqual(const Paths<T>& paths,
+ double max_dist_sqrd, bool is_closed_path)
+ {
+ Paths<T> result;
+ result.reserve(paths.size());
+ for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
+ paths_citer != paths.cend(); ++paths_citer)
+ {
+ result.push_back(StripNearEqual(*paths_citer, max_dist_sqrd, is_closed_path));
+ }
+ return result;
+ }
+
+ template<typename T>
+ inline Path<T> StripDuplicates(const Path<T>& path, bool is_closed_path)
+ {
+ if (path.size() == 0) return Path<T>();
+ Path<T> result;
+ result.reserve(path.size());
+ typename Path<T>::const_iterator path_iter = path.cbegin();
+ Point<T> first_pt = *path_iter++, last_pt = first_pt;
+ result.push_back(first_pt);
+ for (; path_iter != path.cend(); ++path_iter)
+ {
+ if (*path_iter != last_pt)
+ {
+ last_pt = *path_iter;
+ result.push_back(last_pt);
+ }
+ }
+ if (!is_closed_path) return result;
+ while (result.size() > 1 && result.back() == first_pt) result.pop_back();
+ return result;
+ }
+
+ template<typename T>
+ inline Paths<T> StripDuplicates(const Paths<T>& paths, bool is_closed_path)
+ {
+ Paths<T> result;
+ result.reserve(paths.size());
+ for (typename Paths<T>::const_iterator paths_citer = paths.cbegin();
+ paths_citer != paths.cend(); ++paths_citer)
+ {
+ result.push_back(StripDuplicates(*paths_citer, is_closed_path));
+ }
+ return result;
+ }
+
+ // Miscellaneous ------------------------------------------------------------
+
+ inline void CheckPrecision(int& precision, int& error_code)
+ {
+ if (precision >= -8 && precision <= 8) return;
+ error_code |= precision_error_i; // non-fatal error
+ DoError(precision_error_i); // unless exceptions enabled
+ precision = precision > 8 ? 8 : -8;
+ }
+
+ inline void CheckPrecision(int& precision)
+ {
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ }
+
+ template <typename T>
+ 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 -
+ pt2.y) - static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.x - pt2.x));
+ }
+
+ template <typename T>
+ inline double CrossProduct(const Point<T>& vec1, const Point<T>& vec2)
+ {
+ return static_cast<double>(vec1.y * vec2.x) - static_cast<double>(vec2.y * vec1.x);
+ }
+
+ template <typename T>
+ inline double DotProduct(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3) {
+ return (static_cast<double>(pt2.x - pt1.x) * static_cast<double>(pt3.x - pt2.x) +
+ static_cast<double>(pt2.y - pt1.y) * static_cast<double>(pt3.y - pt2.y));
+ }
+
+ template <typename T>
+ inline double DotProduct(const Point<T>& vec1, const Point<T>& vec2)
+ {
+ return static_cast<double>(vec1.x * vec2.x) + static_cast<double>(vec1.y * vec2.y);
+ }
+
+ template <typename T>
+ inline double DistanceSqr(const Point<T> pt1, const Point<T> pt2)
+ {
+ return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y);
+ }
+
+ template <typename T>
+ inline double DistanceFromLineSqrd(const Point<T>& pt, const Point<T>& ln1, const Point<T>& ln2)
+ {
+ //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
+ //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);
+ }
+
+ template <typename T>
+ inline double Area(const Path<T>& path)
+ {
+ size_t cnt = path.size();
+ if (cnt < 3) return 0.0;
+ double a = 0.0;
+ typename Path<T>::const_iterator it1, it2 = path.cend() - 1, stop = it2;
+ if (!(cnt & 1)) ++stop;
+ for (it1 = path.cbegin(); it1 != stop;)
+ {
+ a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
+ it2 = it1 + 1;
+ a += static_cast<double>(it1->y + it2->y) * (it1->x - it2->x);
+ it1 += 2;
+ }
+ if (cnt & 1)
+ a += static_cast<double>(it2->y + it1->y) * (it2->x - it1->x);
+ return a * 0.5;
+ }
+
+ template <typename T>
+ inline double Area(const Paths<T>& paths)
+ {
+ double a = 0.0;
+ for (typename Paths<T>::const_iterator paths_iter = paths.cbegin();
+ paths_iter != paths.cend(); ++paths_iter)
+ {
+ a += Area<T>(*paths_iter);
+ }
+ return a;
+ }
+
+ template <typename T>
+ inline bool IsPositive(const Path<T>& poly)
+ {
+ // A curve has positive orientation [and area] if a region 'R'
+ // is on the left when traveling around the outside of 'R'.
+ //https://mathworld.wolfram.com/CurveOrientation.html
+ //nb: This statement is premised on using Cartesian coordinates
+ return Area<T>(poly) >= 0;
+ }
+
+ inline int64_t CheckCastInt64(double val)
+ {
+ if ((val >= max_coord) || (val <= min_coord)) return INVALID;
+ else return static_cast<int64_t>(val);
+ }
+
+ inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b,
+ const Point64& ln2a, const Point64& ln2b, Point64& ip)
+ {
+ // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
+
+ double dx1 = static_cast<double>(ln1b.x - ln1a.x);
+ double dy1 = static_cast<double>(ln1b.y - ln1a.y);
+ double dx2 = static_cast<double>(ln2b.x - ln2a.x);
+ double dy2 = static_cast<double>(ln2b.y - ln2a.y);
+ double det = dy1 * dx2 - dy2 * dx1;
+ if (det == 0.0) return 0;
+ double qx = dx1 * ln1a.y - dy1 * ln1a.x;
+ double qy = dx2 * ln2a.y - dy2 * ln2a.x;
+ ip.x = CheckCastInt64((dx1 * qy - dx2 * qx) / det);
+ ip.y = CheckCastInt64((dy1 * qy - dy2 * qx) / det);
+ return (ip.x != INVALID && ip.y != INVALID);
+ }
+
+ inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
+ const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
+ {
+ if (inclusive)
+ {
+ double res1 = CrossProduct(seg1a, seg2a, seg2b);
+ double res2 = CrossProduct(seg1b, seg2a, seg2b);
+ if (res1 * res2 > 0) return false;
+ double res3 = CrossProduct(seg2a, seg1a, seg1b);
+ double res4 = CrossProduct(seg2b, seg1a, seg1b);
+ if (res3 * res4 > 0) return false;
+ return (res1 || res2 || res3 || res4); // ensures not collinear
+ }
+ else {
+ return (CrossProduct(seg1a, seg2a, seg2b) *
+ CrossProduct(seg1b, seg2a, seg2b) < 0) &&
+ (CrossProduct(seg2a, seg1a, seg1b) *
+ CrossProduct(seg2b, seg1a, seg1b) < 0);
+ }
+ }
+
+ inline Point64 GetClosestPointOnSegment(const Point64& offPt,
+ const Point64& seg1, const Point64& seg2)
+ {
+ if (seg1.x == seg2.x && seg1.y == seg2.y) return seg1;
+ double dx = static_cast<double>(seg2.x - seg1.x);
+ double dy = static_cast<double>(seg2.y - seg1.y);
+ double q =
+ (static_cast<double>(offPt.x - seg1.x) * dx +
+ static_cast<double>(offPt.y - seg1.y) * dy) /
+ (Sqr(dx) + Sqr(dy));
+ if (q < 0) q = 0; else if (q > 1) q = 1;
+ return Point64(
+ seg1.x + static_cast<int64_t>(nearbyint(q * dx)),
+ seg1.y + static_cast<int64_t>(nearbyint(q * dy)));
+ }
+
+ enum class PointInPolygonResult { IsOn, IsInside, IsOutside };
+
+ template <typename T>
+ inline PointInPolygonResult PointInPolygon(const Point<T>& pt, const Path<T>& polygon)
+ {
+ if (polygon.size() < 3)
+ return PointInPolygonResult::IsOutside;
+
+ int val = 0;
+ typename Path<T>::const_iterator cbegin = polygon.cbegin(), first = cbegin, curr, prev;
+ typename Path<T>::const_iterator cend = polygon.cend();
+
+ while (first != cend && first->y == pt.y) ++first;
+ if (first == cend) // not a proper polygon
+ return PointInPolygonResult::IsOutside;
+
+ bool is_above = first->y < pt.y, starting_above = is_above;
+ curr = first +1;
+ while (true)
+ {
+ if (curr == cend)
+ {
+ if (cend == first || first == cbegin) break;
+ cend = first;
+ curr = cbegin;
+ }
+
+ if (is_above)
+ {
+ while (curr != cend && curr->y < pt.y) ++curr;
+ if (curr == cend) continue;
+ }
+ else
+ {
+ while (curr != cend && curr->y > pt.y) ++curr;
+ if (curr == cend) continue;
+ }
+
+ if (curr == cbegin)
+ prev = polygon.cend() - 1; //nb: NOT cend (since might equal first)
+ else
+ prev = curr - 1;
+
+ if (curr->y == pt.y)
+ {
+ if (curr->x == pt.x ||
+ (curr->y == prev->y &&
+ ((pt.x < prev->x) != (pt.x < curr->x))))
+ return PointInPolygonResult::IsOn;
+ ++curr;
+ if (curr == first) break;
+ continue;
+ }
+
+ if (pt.x < curr->x && pt.x < prev->x)
+ {
+ // we're only interested in edges crossing on the left
+ }
+ else if (pt.x > prev->x && pt.x > curr->x)
+ val = 1 - val; // toggle val
+ else
+ {
+ double d = CrossProduct(*prev, *curr, pt);
+ if (d == 0) return PointInPolygonResult::IsOn;
+ if ((d < 0) == is_above) val = 1 - val;
+ }
+ is_above = !is_above;
+ ++curr;
+ }
+
+ if (is_above != starting_above)
+ {
+ cend = polygon.cend();
+ if (curr == cend) curr = cbegin;
+ if (curr == cbegin) prev = cend - 1;
+ else prev = curr - 1;
+ double d = CrossProduct(*prev, *curr, pt);
+ if (d == 0) return PointInPolygonResult::IsOn;
+ if ((d < 0) == is_above) val = 1 - val;
+ }
+
+ return (val == 0) ?
+ PointInPolygonResult::IsOutside :
+ PointInPolygonResult::IsInside;
+ }
+
+} // namespace
+
+#endif // CLIPPER_CORE_H
diff --git a/thirdparty/clipper2/include/clipper2/clipper.engine.h b/thirdparty/clipper2/include/clipper2/clipper.engine.h
new file mode 100644
index 0000000000..30dc6c86ff
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.engine.h
@@ -0,0 +1,610 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 26 March 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : This is the main polygon clipping module *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+#ifndef CLIPPER_ENGINE_H
+#define CLIPPER_ENGINE_H
+
+constexpr auto CLIPPER2_VERSION = "1.2.2";
+
+#include <cstdlib>
+#include <iostream>
+#include <queue>
+#include <vector>
+#include <functional>
+#include <numeric>
+#include <memory>
+
+#include "clipper.core.h"
+
+namespace Clipper2Lib {
+
+ struct Scanline;
+ struct IntersectNode;
+ struct Active;
+ struct Vertex;
+ struct LocalMinima;
+ struct OutRec;
+ struct HorzSegment;
+
+ //Note: all clipping operations except for Difference are commutative.
+ enum class ClipType { None, Intersection, Union, Difference, Xor };
+
+ enum class PathType { Subject, Clip };
+ enum class JoinWith { None, Left, Right };
+
+ enum class VertexFlags : uint32_t {
+ None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8
+ };
+
+ constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b)
+ {
+ return (enum VertexFlags)(uint32_t(a) & uint32_t(b));
+ }
+
+ constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b)
+ {
+ return (enum VertexFlags)(uint32_t(a) | uint32_t(b));
+ }
+
+ struct Vertex {
+ Point64 pt;
+ Vertex* next = nullptr;
+ Vertex* prev = nullptr;
+ VertexFlags flags = VertexFlags::None;
+ };
+
+ struct OutPt {
+ Point64 pt;
+ OutPt* next = nullptr;
+ OutPt* prev = nullptr;
+ OutRec* outrec;
+ HorzSegment* horz = nullptr;
+
+ OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) {
+ next = this;
+ prev = this;
+ }
+ };
+
+ class PolyPath;
+ class PolyPath64;
+ class PolyPathD;
+ using PolyTree64 = PolyPath64;
+ using PolyTreeD = PolyPathD;
+
+ struct OutRec;
+ typedef std::vector<OutRec*> OutRecList;
+
+ //OutRec: contains a path in the clipping solution. Edges in the AEL will
+ //have OutRec pointers assigned when they form part of the clipping solution.
+ struct OutRec {
+ size_t idx = 0;
+ OutRec* owner = nullptr;
+ Active* front_edge = nullptr;
+ Active* back_edge = nullptr;
+ OutPt* pts = nullptr;
+ PolyPath* polypath = nullptr;
+ OutRecList* splits = nullptr;
+ Rect64 bounds = {};
+ Path64 path;
+ bool is_open = false;
+ bool horz_done = false;
+ ~OutRec() {
+ if (splits) delete splits;
+ // nb: don't delete the split pointers
+ // as these are owned by ClipperBase's outrec_list_
+ };
+ };
+
+ ///////////////////////////////////////////////////////////////////
+ //Important: UP and DOWN here are premised on Y-axis positive down
+ //displays, which is the orientation used in Clipper's development.
+ ///////////////////////////////////////////////////////////////////
+
+ struct Active {
+ Point64 bot;
+ Point64 top;
+ int64_t curr_x = 0; //current (updated at every new scanline)
+ double dx = 0.0;
+ int wind_dx = 1; //1 or -1 depending on winding direction
+ int wind_cnt = 0;
+ int wind_cnt2 = 0; //winding count of the opposite polytype
+ OutRec* outrec = nullptr;
+ //AEL: 'active edge list' (Vatti's AET - active edge table)
+ // a linked list of all edges (from left to right) that are present
+ // (or 'active') within the current scanbeam (a horizontal 'beam' that
+ // sweeps from bottom to top over the paths in the clipping operation).
+ Active* prev_in_ael = nullptr;
+ Active* next_in_ael = nullptr;
+ //SEL: 'sorted edge list' (Vatti's ST - sorted table)
+ // linked list used when sorting edges into their new positions at the
+ // top of scanbeams, but also (re)used to process horizontals.
+ Active* prev_in_sel = nullptr;
+ Active* next_in_sel = nullptr;
+ Active* jump = nullptr;
+ Vertex* vertex_top = nullptr;
+ LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti)
+ bool is_left_bound = false;
+ JoinWith join_with = JoinWith::None;
+ };
+
+ struct LocalMinima {
+ Vertex* vertex;
+ PathType polytype;
+ bool is_open;
+ LocalMinima(Vertex* v, PathType pt, bool open) :
+ vertex(v), polytype(pt), is_open(open){}
+ };
+
+ struct IntersectNode {
+ Point64 pt;
+ Active* edge1;
+ Active* edge2;
+ IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {}
+ IntersectNode(Active* e1, Active* e2, Point64& pt_) :
+ pt(pt_), edge1(e1), edge2(e2) {}
+ };
+
+ struct HorzSegment {
+ OutPt* left_op;
+ OutPt* right_op = nullptr;
+ bool left_to_right = true;
+ HorzSegment() : left_op(nullptr) { }
+ explicit HorzSegment(OutPt* op) : left_op(op) { }
+ };
+
+ struct HorzJoin {
+ OutPt* op1 = nullptr;
+ OutPt* op2 = nullptr;
+ HorzJoin() {};
+ explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { }
+ };
+
+#ifdef USINGZ
+ typedef std::function<void(const Point64& e1bot, const Point64& e1top,
+ const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64;
+
+ typedef std::function<void(const PointD& e1bot, const PointD& e1top,
+ const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD;
+#endif
+
+ typedef std::vector<HorzSegment> HorzSegmentList;
+ typedef std::unique_ptr<LocalMinima> LocalMinima_ptr;
+ typedef std::vector<LocalMinima_ptr> LocalMinimaList;
+ typedef std::vector<IntersectNode> IntersectNodeList;
+
+ // ClipperBase -------------------------------------------------------------
+
+ class ClipperBase {
+ private:
+ ClipType cliptype_ = ClipType::None;
+ FillRule fillrule_ = FillRule::EvenOdd;
+ FillRule fillpos = FillRule::Positive;
+ int64_t bot_y_ = 0;
+ bool minima_list_sorted_ = false;
+ bool using_polytree_ = false;
+ Active* actives_ = nullptr;
+ Active *sel_ = nullptr;
+ LocalMinimaList minima_list_; //pointers in case of memory reallocs
+ LocalMinimaList::iterator current_locmin_iter_;
+ std::vector<Vertex*> vertex_lists_;
+ std::priority_queue<int64_t> scanline_list_;
+ IntersectNodeList intersect_nodes_;
+ HorzSegmentList horz_seg_list_;
+ std::vector<HorzJoin> horz_join_list_;
+ void Reset();
+ inline void InsertScanline(int64_t y);
+ inline bool PopScanline(int64_t &y);
+ inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima);
+ void DisposeAllOutRecs();
+ void DisposeVerticesAndLocalMinima();
+ void DeleteEdges(Active*& e);
+ inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open);
+ bool IsContributingClosed(const Active &e) const;
+ inline bool IsContributingOpen(const Active &e) const;
+ void SetWindCountForClosedPathEdge(Active &edge);
+ void SetWindCountForOpenPathEdge(Active &e);
+ void InsertLocalMinimaIntoAEL(int64_t bot_y);
+ void InsertLeftEdge(Active &e);
+ inline void PushHorz(Active &e);
+ inline bool PopHorz(Active *&e);
+ inline OutPt* StartOpenPath(Active &e, const Point64& pt);
+ inline void UpdateEdgeIntoAEL(Active *e);
+ OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt);
+ inline void DeleteFromAEL(Active &e);
+ inline void AdjustCurrXAndCopyToSEL(const int64_t top_y);
+ void DoIntersections(const int64_t top_y);
+ void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y);
+ bool BuildIntersectList(const int64_t top_y);
+ void ProcessIntersectList();
+ void SwapPositionsInAEL(Active& edge1, Active& edge2);
+ OutRec* NewOutRec();
+ OutPt* AddOutPt(const Active &e, const Point64& pt);
+ OutPt* AddLocalMinPoly(Active &e1, Active &e2,
+ const Point64& pt, bool is_new = false);
+ OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt);
+ void DoHorizontal(Active &horz);
+ bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex,
+ int64_t &horz_left, int64_t &horz_right);
+ void DoTopOfScanbeam(const int64_t top_y);
+ Active *DoMaxima(Active &e);
+ void JoinOutrecPaths(Active &e1, Active &e2);
+ void CompleteSplit(OutPt* op1, OutPt* op2, OutRec& outrec);
+ void FixSelfIntersects(OutRec* outrec);
+ void DoSplitOp(OutRec* outRec, OutPt* splitOp);
+
+ inline void AddTrialHorzJoin(OutPt* op);
+ void ConvertHorzSegsToJoins();
+ void ProcessHorzJoins();
+
+ void Split(Active& e, const Point64& pt);
+ inline void CheckJoinLeft(Active& e,
+ const Point64& pt, bool check_curr_x = false);
+ inline void CheckJoinRight(Active& e,
+ const Point64& pt, bool check_curr_x = false);
+ protected:
+ int error_code_ = 0;
+ bool has_open_paths_ = false;
+ bool succeeded_ = true;
+ OutRecList outrec_list_; //pointers in case list memory reallocated
+ bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees);
+ void CleanCollinear(OutRec* outrec);
+ bool CheckBounds(OutRec* outrec);
+ void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath);
+ void DeepCheckOwners(OutRec* outrec, PolyPath* polypath);
+#ifdef USINGZ
+ ZCallback64 zCallback_ = nullptr;
+ void SetZ(const Active& e1, const Active& e2, Point64& pt);
+#endif
+ void CleanUp(); // unlike Clear, CleanUp preserves added paths
+ void AddPath(const Path64& path, PathType polytype, bool is_open);
+ void AddPaths(const Paths64& paths, PathType polytype, bool is_open);
+ public:
+ virtual ~ClipperBase();
+ int ErrorCode() { return error_code_; };
+ bool PreserveCollinear = true;
+ bool ReverseSolution = false;
+ void Clear();
+#ifdef USINGZ
+ int64_t DefaultZ = 0;
+#endif
+ };
+
+ // PolyPath / PolyTree --------------------------------------------------------
+
+ //PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned
+ //by clipping operations. While this structure is more complex than the
+ //alternative Paths structure, it does preserve path 'ownership' - ie those
+ //paths that contain (or own) other paths. This will be useful to some users.
+
+ class PolyPath {
+ protected:
+ PolyPath* parent_;
+ public:
+ PolyPath(PolyPath* parent = nullptr): parent_(parent){}
+ virtual ~PolyPath() {};
+ //https://en.cppreference.com/w/cpp/language/rule_of_three
+ PolyPath(const PolyPath&) = delete;
+ PolyPath& operator=(const PolyPath&) = delete;
+
+ unsigned Level() const
+ {
+ unsigned result = 0;
+ const PolyPath* p = parent_;
+ while (p) { ++result; p = p->parent_; }
+ return result;
+ }
+
+ virtual PolyPath* AddChild(const Path64& path) = 0;
+
+ virtual void Clear() = 0;
+ virtual size_t Count() const { return 0; }
+
+ const PolyPath* Parent() const { return parent_; }
+
+ bool IsHole() const
+ {
+ unsigned lvl = Level();
+ //Even levels except level 0
+ return lvl && !(lvl & 1);
+ }
+ };
+
+ typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
+ typedef typename std::vector<std::unique_ptr<PolyPathD>> PolyPathDList;
+
+ class PolyPath64 : public PolyPath {
+ private:
+ PolyPath64List childs_;
+ Path64 polygon_;
+ public:
+ explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
+
+ ~PolyPath64() {
+ childs_.resize(0);
+ }
+
+ const PolyPath64* operator [] (size_t index) const
+ {
+ return childs_[index].get();
+ }
+
+ const PolyPath64* Child(size_t index) const
+ {
+ return childs_[index].get();
+ }
+
+ PolyPath64List::const_iterator begin() const { return childs_.cbegin(); }
+ PolyPath64List::const_iterator end() const { return childs_.cend(); }
+
+ 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;
+ }
+
+ void Clear() override
+ {
+ childs_.resize(0);
+ }
+
+ size_t Count() const override
+ {
+ return childs_.size();
+ }
+
+ const Path64& Polygon() const { return polygon_; };
+
+ double Area() const
+ {
+ return std::accumulate(childs_.cbegin(), childs_.cend(),
+ Clipper2Lib::Area<int64_t>(polygon_),
+ [](double a, const auto& child) {return a + child->Area(); });
+ }
+
+ };
+
+ class PolyPathD : public PolyPath {
+ private:
+ PolyPathDList childs_;
+ double inv_scale_;
+ PathD polygon_;
+ public:
+ explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent)
+ {
+ inv_scale_ = parent ? parent->inv_scale_ : 1.0;
+ }
+
+ ~PolyPathD() {
+ childs_.resize(0);
+ }
+
+ const PolyPathD* operator [] (size_t index) const
+ {
+ return childs_[index].get();
+ }
+
+ const PolyPathD* Child(size_t index) const
+ {
+ return childs_[index].get();
+ }
+
+ PolyPathDList::const_iterator begin() const { return childs_.cbegin(); }
+ PolyPathDList::const_iterator end() const { return childs_.cend(); }
+
+ void SetInvScale(double value) { inv_scale_ = value; }
+ double InvScale() { return inv_scale_; }
+ 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, inv_scale_, error_code);
+ return result;
+ }
+
+ void Clear() override
+ {
+ childs_.resize(0);
+ }
+
+ size_t Count() const override
+ {
+ return childs_.size();
+ }
+
+ const PathD& Polygon() const { return polygon_; };
+
+ double Area() const
+ {
+ return std::accumulate(childs_.begin(), childs_.end(),
+ Clipper2Lib::Area<double>(polygon_),
+ [](double a, const auto& child) {return a + child->Area(); });
+ }
+ };
+
+ class Clipper64 : public ClipperBase
+ {
+ private:
+ void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen);
+ void BuildTree64(PolyPath64& polytree, Paths64& open_paths);
+ public:
+#ifdef USINGZ
+ void SetZCallback(ZCallback64 cb) { zCallback_ = cb; }
+#endif
+
+ void AddSubject(const Paths64& subjects)
+ {
+ AddPaths(subjects, PathType::Subject, false);
+ }
+ void AddOpenSubject(const Paths64& open_subjects)
+ {
+ AddPaths(open_subjects, PathType::Subject, true);
+ }
+ void AddClip(const Paths64& clips)
+ {
+ AddPaths(clips, PathType::Clip, false);
+ }
+
+ bool Execute(ClipType clip_type,
+ FillRule fill_rule, Paths64& closed_paths)
+ {
+ Paths64 dummy;
+ return Execute(clip_type, fill_rule, closed_paths, dummy);
+ }
+
+ bool Execute(ClipType clip_type, FillRule fill_rule,
+ Paths64& closed_paths, Paths64& open_paths)
+ {
+ closed_paths.clear();
+ open_paths.clear();
+ if (ExecuteInternal(clip_type, fill_rule, false))
+ BuildPaths64(closed_paths, &open_paths);
+ CleanUp();
+ return succeeded_;
+ }
+
+ bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree)
+ {
+ Paths64 dummy;
+ return Execute(clip_type, fill_rule, polytree, dummy);
+ }
+
+ bool Execute(ClipType clip_type,
+ FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths)
+ {
+ if (ExecuteInternal(clip_type, fill_rule, true))
+ {
+ open_paths.clear();
+ polytree.Clear();
+ BuildTree64(polytree, open_paths);
+ }
+ CleanUp();
+ return succeeded_;
+ }
+ };
+
+ class ClipperD : public ClipperBase {
+ private:
+ double scale_ = 1.0, invScale_ = 1.0;
+#ifdef USINGZ
+ ZCallbackD zCallbackD_ = nullptr;
+#endif
+ void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen);
+ void BuildTreeD(PolyPathD& polytree, PathsD& open_paths);
+ public:
+ explicit ClipperD(int precision = 2) : ClipperBase()
+ {
+ CheckPrecision(precision, error_code_);
+ // to optimize scaling / descaling precision
+ // set the scale to a power of double's radix (2) (#25)
+ scale_ = std::pow(std::numeric_limits<double>::radix,
+ std::ilogb(std::pow(10, precision)) + 1);
+ invScale_ = 1 / scale_;
+ }
+
+#ifdef USINGZ
+ void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; };
+
+ void ZCB(const Point64& e1bot, const Point64& e1top,
+ const Point64& e2bot, const Point64& e2top, Point64& pt)
+ {
+ // de-scale (x & y)
+ // temporarily convert integers to their initial float values
+ // this will slow clipping marginally but will make it much easier
+ // to understand the coordinates passed to the callback function
+ PointD tmp = PointD(pt) * invScale_;
+ PointD e1b = PointD(e1bot) * invScale_;
+ PointD e1t = PointD(e1top) * invScale_;
+ PointD e2b = PointD(e2bot) * invScale_;
+ PointD e2t = PointD(e2top) * invScale_;
+ zCallbackD_(e1b,e1t, e2b, e2t, tmp);
+ pt.z = tmp.z; // only update 'z'
+ };
+
+ void CheckCallback()
+ {
+ if(zCallbackD_)
+ // if the user defined float point callback has been assigned
+ // then assign the proxy callback function
+ ClipperBase::zCallback_ =
+ std::bind(&ClipperD::ZCB, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3,
+ std::placeholders::_4, std::placeholders::_5);
+ else
+ ClipperBase::zCallback_ = nullptr;
+ }
+
+#endif
+
+ void AddSubject(const PathsD& subjects)
+ {
+ AddPaths(ScalePaths<int64_t, double>(subjects, scale_, error_code_), PathType::Subject, false);
+ }
+
+ void AddOpenSubject(const PathsD& open_subjects)
+ {
+ AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_, error_code_), PathType::Subject, true);
+ }
+
+ void AddClip(const PathsD& clips)
+ {
+ AddPaths(ScalePaths<int64_t, double>(clips, scale_, error_code_), PathType::Clip, false);
+ }
+
+ bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths)
+ {
+ PathsD dummy;
+ return Execute(clip_type, fill_rule, closed_paths, dummy);
+ }
+
+ bool Execute(ClipType clip_type,
+ FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths)
+ {
+#ifdef USINGZ
+ CheckCallback();
+#endif
+ if (ExecuteInternal(clip_type, fill_rule, false))
+ {
+ BuildPathsD(closed_paths, &open_paths);
+ }
+ CleanUp();
+ return succeeded_;
+ }
+
+ bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree)
+ {
+ PathsD dummy;
+ return Execute(clip_type, fill_rule, polytree, dummy);
+ }
+
+ bool Execute(ClipType clip_type,
+ FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths)
+ {
+#ifdef USINGZ
+ CheckCallback();
+#endif
+ if (ExecuteInternal(clip_type, fill_rule, true))
+ {
+ polytree.Clear();
+ polytree.SetInvScale(invScale_);
+ open_paths.clear();
+ BuildTreeD(polytree, open_paths);
+ }
+ CleanUp();
+ return succeeded_;
+ }
+
+ };
+
+} // namespace
+
+#endif // CLIPPER_ENGINE_H
diff --git a/thirdparty/clipper2/include/clipper2/clipper.export.h b/thirdparty/clipper2/include/clipper2/clipper.export.h
new file mode 100644
index 0000000000..e8d678a41d
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.export.h
@@ -0,0 +1,774 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 23 March 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : This module exports the Clipper2 Library (ie DLL/so) *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+// The exported functions below refer to simple structures that
+// can be understood across multiple languages. Consequently
+// Path64, PathD, Polytree64 etc are converted from C++ classes
+// (std::vector<> etc) into the following data structures:
+//
+// CPath64 (int64_t*) & CPathD (double_t*):
+// Path64 and PathD are converted into arrays of x,y coordinates.
+// However in these arrays the first x,y coordinate pair is a
+// counter with 'x' containing the number of following coordinate
+// pairs. ('y' should be 0, with one exception explained below.)
+// __________________________________
+// |counter|coord1|coord2|...|coordN|
+// |N ,0 |x1, y1|x2, y2|...|xN, yN|
+// __________________________________
+//
+// CPaths64 (int64_t**) & CPathsD (double_t**):
+// These are arrays of pointers to CPath64 and CPathD where
+// the first pointer is to a 'counter path'. This 'counter
+// path' has a single x,y coord pair with 'y' (not 'x')
+// containing the number of paths that follow. ('x' = 0).
+// _______________________________
+// |counter|path1|path2|...|pathN|
+// |addr0 |addr1|addr2|...|addrN| (*addr0[0]=0; *addr0[1]=N)
+// _______________________________
+//
+// The structures of CPolytree64 and CPolytreeD are defined
+// below and these structures don't need to be explained here.
+
+#ifndef CLIPPER2_EXPORT_H
+#define CLIPPER2_EXPORT_H
+
+#include <cstdlib>
+#include <vector>
+
+#include "clipper2/clipper.core.h"
+#include "clipper2/clipper.engine.h"
+#include "clipper2/clipper.offset.h"
+#include "clipper2/clipper.rectclip.h"
+
+namespace Clipper2Lib {
+
+typedef int64_t* CPath64;
+typedef int64_t** CPaths64;
+typedef double* CPathD;
+typedef double** CPathsD;
+
+typedef struct CPolyPath64 {
+ CPath64 polygon;
+ uint32_t is_hole;
+ uint32_t child_count;
+ CPolyPath64* childs;
+}
+CPolyTree64;
+
+typedef struct CPolyPathD {
+ CPathD polygon;
+ uint32_t is_hole;
+ uint32_t child_count;
+ CPolyPathD* childs;
+}
+CPolyTreeD;
+
+template <typename T>
+struct CRect {
+ T left;
+ T top;
+ T right;
+ T bottom;
+};
+
+typedef CRect<int64_t> CRect64;
+typedef CRect<double> CRectD;
+
+template <typename T>
+inline bool CRectIsEmpty(const CRect<T>& rect)
+{
+ return (rect.right <= rect.left) || (rect.bottom <= rect.top);
+}
+
+template <typename T>
+inline Rect<T> CRectToRect(const CRect<T>& rect)
+{
+ Rect<T> result;
+ result.left = rect.left;
+ result.top = rect.top;
+ result.right = rect.right;
+ result.bottom = rect.bottom;
+ return result;
+}
+
+#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)
+
+//////////////////////////////////////////////////////
+// EXPORTED FUNCTION DEFINITIONS
+//////////////////////////////////////////////////////
+
+EXTERN_DLL_EXPORT const char* Version();
+
+// Some of the functions below will return data in the various CPath
+// and CPolyTree structures which are pointers to heap allocated
+// memory. Eventually this memory will need to be released with one
+// of the following 'DisposeExported' functions. (This may be the
+// only safe way to release this memory since the executable
+// accessing these exported functions may use a memory manager that
+// allocates and releases heap memory in a different way. Also,
+// CPath structures that have been constructed by the executable
+// should not be destroyed using these 'DisposeExported' functions.)
+EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p);
+EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp);
+EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p);
+EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp);
+EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt);
+EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt);
+
+// Boolean clipping:
+// cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4
+// fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3
+EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
+ uint8_t fillrule, const CPaths64 subjects,
+ const CPaths64 subjects_open, const CPaths64 clips,
+ CPaths64& solution, CPaths64& solution_open,
+ bool preserve_collinear = true, bool reverse_solution = false);
+EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype,
+ uint8_t fillrule, const CPaths64 subjects,
+ const CPaths64 subjects_open, const CPaths64 clips,
+ CPolyTree64*& solution, CPaths64& solution_open,
+ bool preserve_collinear = true, bool reverse_solution = false);
+EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
+ uint8_t fillrule, const CPathsD subjects,
+ const CPathsD subjects_open, const CPathsD clips,
+ CPathsD& solution, CPathsD& solution_open, int precision = 2,
+ bool preserve_collinear = true, bool reverse_solution = false);
+EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype,
+ uint8_t fillrule, const CPathsD subjects,
+ const CPathsD subjects_open, const CPathsD clips,
+ CPolyTreeD*& solution, CPathsD& solution_open, int precision = 2,
+ bool preserve_collinear = true, bool reverse_solution = false);
+
+// Polygon offsetting (inflate/deflate):
+// jointype: Square=0, Round=1, Miter=2
+// endtype: Polygon=0, Joined=1, Butt=2, Square=3, Round=4
+EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
+ double delta, uint8_t jointype, uint8_t endtype,
+ double miter_limit = 2.0, double arc_tolerance = 0.0,
+ bool reverse_solution = false);
+EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
+ double delta, uint8_t jointype, uint8_t endtype,
+ int precision = 2, double miter_limit = 2.0,
+ double arc_tolerance = 0.0, bool reverse_solution = false);
+
+// ExecuteRectClip & ExecuteRectClipLines:
+EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect,
+ const CPaths64 paths, bool convex_only = false);
+EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
+ const CPathsD paths, int precision = 2, bool convex_only = false);
+EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect,
+ const CPaths64 paths);
+EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect,
+ const CPathsD paths, int precision = 2);
+
+//////////////////////////////////////////////////////
+// INTERNAL FUNCTIONS
+//////////////////////////////////////////////////////
+
+inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2);
+inline CPath64 CreateCPath64(const Path64& p);
+inline CPaths64 CreateCPaths64(const Paths64& pp);
+inline Path64 ConvertCPath64(const CPath64& p);
+inline Paths64 ConvertCPaths64(const CPaths64& pp);
+
+inline CPathD CreateCPathD(size_t cnt1, size_t cnt2);
+inline CPathD CreateCPathD(const PathD& p);
+inline CPathsD CreateCPathsD(const PathsD& pp);
+inline PathD ConvertCPathD(const CPathD& p);
+inline PathsD ConvertCPathsD(const CPathsD& pp);
+
+// the following function avoid multiple conversions
+inline CPathD CreateCPathD(const Path64& p, double scale);
+inline CPathsD CreateCPathsD(const Paths64& pp, double scale);
+inline Path64 ConvertCPathD(const CPathD& p, double scale);
+inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale);
+
+inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt);
+inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale);
+
+EXTERN_DLL_EXPORT const char* Version()
+{
+ return CLIPPER2_VERSION;
+}
+
+EXTERN_DLL_EXPORT void DisposeExportedCPath64(CPath64 p)
+{
+ if (p) delete[] p;
+}
+
+EXTERN_DLL_EXPORT void DisposeExportedCPaths64(CPaths64& pp)
+{
+ if (!pp) return;
+ CPaths64 v = pp;
+ CPath64 cnts = *v;
+ const size_t cnt = static_cast<size_t>(cnts[1]);
+ for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1
+ DisposeExportedCPath64(*v++);
+ delete[] pp;
+ pp = nullptr;
+}
+
+EXTERN_DLL_EXPORT void DisposeExportedCPathD(CPathD p)
+{
+ if (p) delete[] p;
+}
+
+EXTERN_DLL_EXPORT void DisposeExportedCPathsD(CPathsD& pp)
+{
+ if (!pp) return;
+ CPathsD v = pp;
+ CPathD cnts = *v;
+ size_t cnt = static_cast<size_t>(cnts[1]);
+ for (size_t i = 0; i <= cnt; ++i) //nb: cnt +1
+ DisposeExportedCPathD(*v++);
+ delete[] pp;
+ pp = nullptr;
+}
+
+EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype,
+ uint8_t fillrule, const CPaths64 subjects,
+ const CPaths64 subjects_open, const CPaths64 clips,
+ CPaths64& solution, CPaths64& solution_open,
+ bool preserve_collinear, bool reverse_solution)
+{
+ if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
+ if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
+
+ Paths64 sub, sub_open, clp, sol, sol_open;
+ sub = ConvertCPaths64(subjects);
+ sub_open = ConvertCPaths64(subjects_open);
+ clp = ConvertCPaths64(clips);
+
+ Clipper64 clipper;
+ clipper.PreserveCollinear = preserve_collinear;
+ clipper.ReverseSolution = reverse_solution;
+ if (sub.size() > 0) clipper.AddSubject(sub);
+ if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
+ if (clp.size() > 0) clipper.AddClip(clp);
+ if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open))
+ return -1; // clipping bug - should never happen :)
+ solution = CreateCPaths64(sol);
+ solution_open = CreateCPaths64(sol_open);
+ return 0; //success !!
+}
+
+EXTERN_DLL_EXPORT int BooleanOpPt64(uint8_t cliptype,
+ uint8_t fillrule, const CPaths64 subjects,
+ const CPaths64 subjects_open, const CPaths64 clips,
+ CPolyTree64*& solution, CPaths64& solution_open,
+ bool preserve_collinear, bool reverse_solution)
+{
+ if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
+ if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
+ Paths64 sub, sub_open, clp, sol_open;
+ sub = ConvertCPaths64(subjects);
+ sub_open = ConvertCPaths64(subjects_open);
+ clp = ConvertCPaths64(clips);
+
+ PolyTree64 pt;
+ Clipper64 clipper;
+ clipper.PreserveCollinear = preserve_collinear;
+ clipper.ReverseSolution = reverse_solution;
+ if (sub.size() > 0) clipper.AddSubject(sub);
+ if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open);
+ if (clp.size() > 0) clipper.AddClip(clp);
+ if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), pt, sol_open))
+ return -1; // clipping bug - should never happen :)
+
+ solution = CreateCPolyTree64(pt);
+ solution_open = CreateCPaths64(sol_open);
+ return 0; //success !!
+}
+
+EXTERN_DLL_EXPORT int BooleanOpD(uint8_t cliptype,
+ uint8_t fillrule, const CPathsD subjects,
+ const CPathsD subjects_open, const CPathsD clips,
+ CPathsD& solution, CPathsD& solution_open, int precision,
+ bool preserve_collinear, bool reverse_solution)
+{
+ if (precision < -8 || precision > 8) return -5;
+ if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
+ if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
+ const double scale = std::pow(10, precision);
+
+ Paths64 sub, sub_open, clp, sol, sol_open;
+ sub = ConvertCPathsD(subjects, scale);
+ sub_open = ConvertCPathsD(subjects_open, scale);
+ clp = ConvertCPathsD(clips, scale);
+
+ Clipper64 clipper;
+ clipper.PreserveCollinear = preserve_collinear;
+ clipper.ReverseSolution = reverse_solution;
+ if (sub.size() > 0) clipper.AddSubject(sub);
+ if (sub_open.size() > 0)
+ clipper.AddOpenSubject(sub_open);
+ if (clp.size() > 0) clipper.AddClip(clp);
+ if (!clipper.Execute(ClipType(cliptype),
+ FillRule(fillrule), sol, sol_open)) return -1;
+
+ if (sol.size() > 0) solution = CreateCPathsD(sol, 1 / scale);
+ if (sol_open.size() > 0)
+ solution_open = CreateCPathsD(sol_open, 1 / scale);
+ return 0;
+}
+
+EXTERN_DLL_EXPORT int BooleanOpPtD(uint8_t cliptype,
+ uint8_t fillrule, const CPathsD subjects,
+ const CPathsD subjects_open, const CPathsD clips,
+ CPolyTreeD*& solution, CPathsD& solution_open, int precision,
+ bool preserve_collinear, bool reverse_solution)
+{
+ if (precision < -8 || precision > 8) return -5;
+ if (cliptype > static_cast<uint8_t>(ClipType::Xor)) return -4;
+ if (fillrule > static_cast<uint8_t>(FillRule::Negative)) return -3;
+
+ const double scale = std::pow(10, precision);
+ Paths64 sub, sub_open, clp, sol_open;
+ sub = ConvertCPathsD(subjects, scale);
+ sub_open = ConvertCPathsD(subjects_open, scale);
+ clp = ConvertCPathsD(clips, scale);
+
+ PolyTree64 sol;
+ Clipper64 clipper;
+ clipper.PreserveCollinear = preserve_collinear;
+ clipper.ReverseSolution = reverse_solution;
+ if (sub.size() > 0) clipper.AddSubject(sub);
+ if (sub_open.size() > 0)
+ clipper.AddOpenSubject(sub_open);
+ if (clp.size() > 0) clipper.AddClip(clp);
+ if (!clipper.Execute(ClipType(cliptype),
+ FillRule(fillrule), sol, sol_open)) return -1;
+
+ solution = CreateCPolyTreeD(sol, 1 / scale);
+ if (sol_open.size() > 0)
+ solution_open = CreateCPathsD(sol_open, 1 / scale);
+ return 0;
+}
+
+EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths,
+ double delta, uint8_t jointype, uint8_t endtype, double miter_limit,
+ double arc_tolerance, bool reverse_solution)
+{
+ Paths64 pp;
+ pp = ConvertCPaths64(paths);
+
+ ClipperOffset clip_offset( miter_limit,
+ arc_tolerance, reverse_solution);
+ clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
+ Paths64 result;
+ clip_offset.Execute(delta, result);
+ return CreateCPaths64(result);
+}
+
+EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths,
+ double delta, uint8_t jointype, uint8_t endtype,
+ int precision, double miter_limit,
+ double arc_tolerance, bool reverse_solution)
+{
+ if (precision < -8 || precision > 8 || !paths) return nullptr;
+ const double scale = std::pow(10, precision);
+ ClipperOffset clip_offset(miter_limit, arc_tolerance, reverse_solution);
+ Paths64 pp = ConvertCPathsD(paths, scale);
+ clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype));
+ Paths64 result;
+ clip_offset.Execute(delta * scale, result);
+ return CreateCPathsD(result, 1/scale);
+}
+
+EXTERN_DLL_EXPORT CPaths64 ExecuteRectClip64(const CRect64& rect,
+ const CPaths64 paths, bool convex_only)
+{
+ if (CRectIsEmpty(rect) || !paths) return nullptr;
+ Rect64 r64 = CRectToRect(rect);
+ class RectClip rc(r64);
+ Paths64 pp = ConvertCPaths64(paths);
+ Paths64 result = rc.Execute(pp, convex_only);
+ return CreateCPaths64(result);
+}
+
+EXTERN_DLL_EXPORT CPathsD ExecuteRectClipD(const CRectD& rect,
+ const CPathsD paths, int precision, bool convex_only)
+{
+ if (CRectIsEmpty(rect) || !paths) return nullptr;
+ if (precision < -8 || precision > 8) return nullptr;
+ const double scale = std::pow(10, precision);
+
+ RectD r = CRectToRect(rect);
+ Rect64 rec = ScaleRect<int64_t, double>(r, scale);
+ Paths64 pp = ConvertCPathsD(paths, scale);
+ class RectClip rc(rec);
+ Paths64 result = rc.Execute(pp, convex_only);
+ return CreateCPathsD(result, 1/scale);
+}
+
+EXTERN_DLL_EXPORT CPaths64 ExecuteRectClipLines64(const CRect64& rect,
+ const CPaths64 paths)
+{
+ if (CRectIsEmpty(rect) || !paths) return nullptr;
+ Rect64 r = CRectToRect(rect);
+ class RectClipLines rcl (r);
+ Paths64 pp = ConvertCPaths64(paths);
+ Paths64 result = rcl.Execute(pp);
+ return CreateCPaths64(result);
+}
+
+EXTERN_DLL_EXPORT CPathsD ExecuteRectClipLinesD(const CRectD& rect,
+ const CPathsD paths, int precision)
+{
+ if (CRectIsEmpty(rect) || !paths) return nullptr;
+ if (precision < -8 || precision > 8) return nullptr;
+ const double scale = std::pow(10, precision);
+ Rect64 r = ScaleRect<int64_t, double>(CRectToRect(rect), scale);
+ class RectClipLines rcl(r);
+ Paths64 pp = ConvertCPathsD(paths, scale);
+ Paths64 result = rcl.Execute(pp);
+ return CreateCPathsD(result, 1/scale);
+}
+
+inline CPath64 CreateCPath64(size_t cnt1, size_t cnt2)
+{
+ // allocates memory for CPath64, fills in the counter, and
+ // returns the structure ready to be filled with path data
+ CPath64 result = new int64_t[2 + cnt1 *2];
+ result[0] = cnt1;
+ result[1] = cnt2;
+ return result;
+}
+
+inline CPath64 CreateCPath64(const Path64& p)
+{
+ // allocates memory for CPath64, fills the counter
+ // and returns the memory filled with path data
+ size_t cnt = p.size();
+ if (!cnt) return nullptr;
+ CPath64 result = CreateCPath64(cnt, 0);
+ CPath64 v = result;
+ v += 2; // skip counters
+ for (const Point64& pt : p)
+ {
+ *v++ = pt.x;
+ *v++ = pt.y;
+ }
+ return result;
+}
+
+inline Path64 ConvertCPath64(const CPath64& p)
+{
+ Path64 result;
+ if (p && *p)
+ {
+ CPath64 v = p;
+ const size_t cnt = static_cast<size_t>(p[0]);
+ v += 2; // skip counters
+ result.reserve(cnt);
+ for (size_t i = 0; i < cnt; ++i)
+ {
+ // x,y here avoids right to left function evaluation
+ // result.push_back(Point64(*v++, *v++));
+ int64_t x = *v++;
+ int64_t y = *v++;
+ result.push_back(Point64(x, y));
+ }
+ }
+ return result;
+}
+
+inline CPaths64 CreateCPaths64(const Paths64& pp)
+{
+ // allocates memory for multiple CPath64 and
+ // and returns this memory filled with path data
+ size_t cnt = pp.size(), cnt2 = cnt;
+
+ // don't allocate space for empty paths
+ for (size_t i = 0; i < cnt; ++i)
+ if (!pp[i].size()) --cnt2;
+ if (!cnt2) return nullptr;
+
+ CPaths64 result = new int64_t* [cnt2 + 1];
+ CPaths64 v = result;
+ *v++ = CreateCPath64(0, cnt2); // assign a counter path
+ for (const Path64& p : pp)
+ {
+ *v = CreateCPath64(p);
+ if (*v) ++v;
+ }
+ return result;
+}
+
+inline Paths64 ConvertCPaths64(const CPaths64& pp)
+{
+ Paths64 result;
+ if (pp)
+ {
+ CPaths64 v = pp;
+ CPath64 cnts = pp[0];
+ const size_t cnt = static_cast<size_t>(cnts[1]); // nb 2nd cnt
+ ++v; // skip cnts
+ result.reserve(cnt);
+ for (size_t i = 0; i < cnt; ++i)
+ result.push_back(ConvertCPath64(*v++));
+ }
+ return result;
+}
+
+inline CPathD CreateCPathD(size_t cnt1, size_t cnt2)
+{
+ // allocates memory for CPathD, fills in the counter, and
+ // returns the structure ready to be filled with path data
+ CPathD result = new double[2 + cnt1 * 2];
+ result[0] = static_cast<double>(cnt1);
+ result[1] = static_cast<double>(cnt2);
+ return result;
+}
+
+inline CPathD CreateCPathD(const PathD& p)
+{
+ // allocates memory for CPath, fills the counter
+ // and returns the memory fills with path data
+ size_t cnt = p.size();
+ if (!cnt) return nullptr;
+ CPathD result = CreateCPathD(cnt, 0);
+ CPathD v = result;
+ v += 2; // skip counters
+ for (const PointD& pt : p)
+ {
+ *v++ = pt.x;
+ *v++ = pt.y;
+ }
+ return result;
+}
+
+inline PathD ConvertCPathD(const CPathD& p)
+{
+ PathD result;
+ if (p)
+ {
+ CPathD v = p;
+ size_t cnt = static_cast<size_t>(v[0]);
+ v += 2; // skip counters
+ result.reserve(cnt);
+ for (size_t i = 0; i < cnt; ++i)
+ {
+ // x,y here avoids right to left function evaluation
+ // result.push_back(PointD(*v++, *v++));
+ double x = *v++;
+ double y = *v++;
+ result.push_back(PointD(x, y));
+ }
+ }
+ return result;
+}
+
+inline CPathsD CreateCPathsD(const PathsD& pp)
+{
+ size_t cnt = pp.size(), cnt2 = cnt;
+ // don't allocate space for empty paths
+ for (size_t i = 0; i < cnt; ++i)
+ if (!pp[i].size()) --cnt2;
+ if (!cnt2) return nullptr;
+ CPathsD result = new double * [cnt2 + 1];
+ CPathsD v = result;
+ *v++ = CreateCPathD(0, cnt2); // assign counter path
+ for (const PathD& p : pp)
+ {
+ *v = CreateCPathD(p);
+ if (*v) { ++v; }
+ }
+ return result;
+}
+
+inline PathsD ConvertCPathsD(const CPathsD& pp)
+{
+ PathsD result;
+ if (pp)
+ {
+ CPathsD v = pp;
+ CPathD cnts = v[0];
+ size_t cnt = static_cast<size_t>(cnts[1]);
+ ++v; // skip cnts path
+ result.reserve(cnt);
+ for (size_t i = 0; i < cnt; ++i)
+ result.push_back(ConvertCPathD(*v++));
+ }
+ return result;
+}
+
+inline Path64 ConvertCPathD(const CPathD& p, double scale)
+{
+ Path64 result;
+ if (p)
+ {
+ CPathD v = p;
+ size_t cnt = static_cast<size_t>(*v);
+ v += 2; // skip counters
+ result.reserve(cnt);
+ for (size_t i = 0; i < cnt; ++i)
+ {
+ // x,y here avoids right to left function evaluation
+ // result.push_back(PointD(*v++, *v++));
+ double x = *v++ * scale;
+ double y = *v++ * scale;
+ result.push_back(Point64(x, y));
+ }
+ }
+ return result;
+}
+
+inline Paths64 ConvertCPathsD(const CPathsD& pp, double scale)
+{
+ Paths64 result;
+ if (pp)
+ {
+ CPathsD v = pp;
+ CPathD cnts = v[0];
+ size_t cnt = static_cast<size_t>(cnts[1]);
+ result.reserve(cnt);
+ ++v; // skip cnts path
+ for (size_t i = 0; i < cnt; ++i)
+ result.push_back(ConvertCPathD(*v++, scale));
+ }
+ return result;
+}
+
+inline CPathD CreateCPathD(const Path64& p, double scale)
+{
+ // allocates memory for CPathD, fills in the counter, and
+ // returns the structure filled with *scaled* path data
+ size_t cnt = p.size();
+ if (!cnt) return nullptr;
+ CPathD result = CreateCPathD(cnt, 0);
+ CPathD v = result;
+ v += 2; // skip cnts
+ for (const Point64& pt : p)
+ {
+ *v++ = pt.x * scale;
+ *v++ = pt.y * scale;
+ }
+ return result;
+}
+
+inline CPathsD CreateCPathsD(const Paths64& pp, double scale)
+{
+ // allocates memory for *multiple* CPathD, and
+ // returns the structure filled with scaled path data
+ size_t cnt = pp.size(), cnt2 = cnt;
+ // don't allocate space for empty paths
+ for (size_t i = 0; i < cnt; ++i)
+ if (!pp[i].size()) --cnt2;
+ if (!cnt2) return nullptr;
+ CPathsD result = new double* [cnt2 + 1];
+ CPathsD v = result;
+ *v++ = CreateCPathD(0, cnt2);
+ for (const Path64& p : pp)
+ {
+ *v = CreateCPathD(p, scale);
+ if (*v) ++v;
+ }
+ return result;
+}
+
+inline void InitCPolyPath64(CPolyTree64* cpt,
+ bool is_hole, const std::unique_ptr <PolyPath64>& pp)
+{
+ cpt->polygon = CreateCPath64(pp->Polygon());
+ cpt->is_hole = is_hole;
+ size_t child_cnt = pp->Count();
+ cpt->child_count = static_cast<uint32_t>(child_cnt);
+ cpt->childs = nullptr;
+ if (!child_cnt) return;
+ cpt->childs = new CPolyPath64[child_cnt];
+ CPolyPath64* child = cpt->childs;
+ for (const std::unique_ptr <PolyPath64>& pp_child : *pp)
+ InitCPolyPath64(child++, !is_hole, pp_child);
+}
+
+inline CPolyTree64* CreateCPolyTree64(const PolyTree64& pt)
+{
+ CPolyTree64* result = new CPolyTree64();
+ result->polygon = nullptr;
+ result->is_hole = false;
+ size_t child_cnt = pt.Count();
+ result->childs = nullptr;
+ result->child_count = static_cast<uint32_t>(child_cnt);
+ if (!child_cnt) return result;
+ result->childs = new CPolyPath64[child_cnt];
+ CPolyPath64* child = result->childs;
+ for (const std::unique_ptr <PolyPath64>& pp : pt)
+ InitCPolyPath64(child++, true, pp);
+ return result;
+}
+
+inline void DisposeCPolyPath64(CPolyPath64* cpp)
+{
+ if (!cpp->child_count) return;
+ CPolyPath64* child = cpp->childs;
+ for (size_t i = 0; i < cpp->child_count; ++i)
+ DisposeCPolyPath64(child);
+ delete[] cpp->childs;
+}
+
+EXTERN_DLL_EXPORT void DisposeExportedCPolyTree64(CPolyTree64*& cpt)
+{
+ if (!cpt) return;
+ DisposeCPolyPath64(cpt);
+ delete cpt;
+ cpt = nullptr;
+}
+
+inline void InitCPolyPathD(CPolyTreeD* cpt,
+ bool is_hole, const std::unique_ptr <PolyPath64>& pp, double scale)
+{
+ cpt->polygon = CreateCPathD(pp->Polygon(), scale);
+ cpt->is_hole = is_hole;
+ size_t child_cnt = pp->Count();
+ cpt->child_count = static_cast<uint32_t>(child_cnt);
+ cpt->childs = nullptr;
+ if (!child_cnt) return;
+ cpt->childs = new CPolyPathD[child_cnt];
+ CPolyPathD* child = cpt->childs;
+ for (const std::unique_ptr <PolyPath64>& pp_child : *pp)
+ InitCPolyPathD(child++, !is_hole, pp_child, scale);
+}
+
+inline CPolyTreeD* CreateCPolyTreeD(const PolyTree64& pt, double scale)
+{
+ CPolyTreeD* result = new CPolyTreeD();
+ result->polygon = nullptr;
+ result->is_hole = false;
+ size_t child_cnt = pt.Count();
+ result->child_count = static_cast<uint32_t>(child_cnt);
+ result->childs = nullptr;
+ if (!child_cnt) return result;
+ result->childs = new CPolyPathD[child_cnt];
+ CPolyPathD* child = result->childs;
+ for (const std::unique_ptr <PolyPath64>& pp : pt)
+ InitCPolyPathD(child++, true, pp, scale);
+ return result;
+}
+
+inline void DisposeCPolyPathD(CPolyPathD* cpp)
+{
+ if (!cpp->child_count) return;
+ CPolyPathD* child = cpp->childs;
+ for (size_t i = 0; i < cpp->child_count; ++i)
+ DisposeCPolyPathD(child++);
+ delete[] cpp->childs;
+}
+
+EXTERN_DLL_EXPORT void DisposeExportedCPolyTreeD(CPolyTreeD*& cpt)
+{
+ if (!cpt) return;
+ DisposeCPolyPathD(cpt);
+ delete cpt;
+ cpt = nullptr;
+}
+
+} // end Clipper2Lib namespace
+
+#endif // CLIPPER2_EXPORT_H
diff --git a/thirdparty/clipper2/include/clipper2/clipper.h b/thirdparty/clipper2/include/clipper2/clipper.h
new file mode 100644
index 0000000000..6579f59c18
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.h
@@ -0,0 +1,776 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 23 March 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : This module provides a simple interface to the Clipper Library *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+#ifndef CLIPPER_H
+#define CLIPPER_H
+
+#include <cstdlib>
+#include <type_traits>
+#include <vector>
+
+#include "clipper.core.h"
+#include "clipper.engine.h"
+#include "clipper.offset.h"
+#include "clipper.minkowski.h"
+#include "clipper.rectclip.h"
+
+namespace Clipper2Lib {
+
+ inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
+ const Paths64& subjects, const Paths64& clips)
+ {
+ Paths64 result;
+ Clipper64 clipper;
+ clipper.AddSubject(subjects);
+ clipper.AddClip(clips);
+ clipper.Execute(cliptype, fillrule, result);
+ return result;
+ }
+
+ inline void BooleanOp(ClipType cliptype, FillRule fillrule,
+ const Paths64& subjects, const Paths64& clips, PolyTree64& solution)
+ {
+ Paths64 sol_open;
+ Clipper64 clipper;
+ clipper.AddSubject(subjects);
+ clipper.AddClip(clips);
+ clipper.Execute(cliptype, fillrule, solution, sol_open);
+ }
+
+ inline PathsD BooleanOp(ClipType cliptype, FillRule fillrule,
+ const PathsD& subjects, const PathsD& clips, int precision = 2)
+ {
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ PathsD result;
+ if (error_code) return result;
+ ClipperD clipper(precision);
+ clipper.AddSubject(subjects);
+ clipper.AddClip(clips);
+ clipper.Execute(cliptype, fillrule, result);
+ return result;
+ }
+
+ inline void BooleanOp(ClipType cliptype, FillRule fillrule,
+ const PathsD& subjects, const PathsD& clips,
+ PolyTreeD& polytree, int precision = 2)
+ {
+ polytree.Clear();
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ if (error_code) return;
+ ClipperD clipper(precision);
+ clipper.AddSubject(subjects);
+ clipper.AddClip(clips);
+ clipper.Execute(cliptype, fillrule, polytree);
+ }
+
+ inline Paths64 Intersect(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
+ {
+ return BooleanOp(ClipType::Intersection, fillrule, subjects, clips);
+ }
+
+ inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
+ {
+ return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec);
+ }
+
+ inline Paths64 Union(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
+ {
+ return BooleanOp(ClipType::Union, fillrule, subjects, clips);
+ }
+
+ inline PathsD Union(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
+ {
+ return BooleanOp(ClipType::Union, fillrule, subjects, clips, decimal_prec);
+ }
+
+ inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
+ {
+ Paths64 result;
+ Clipper64 clipper;
+ clipper.AddSubject(subjects);
+ clipper.Execute(ClipType::Union, fillrule, result);
+ return result;
+ }
+
+ inline PathsD Union(const PathsD& subjects, FillRule fillrule, int precision = 2)
+ {
+ PathsD result;
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ if (error_code) return result;
+ ClipperD clipper(precision);
+ clipper.AddSubject(subjects);
+ clipper.Execute(ClipType::Union, fillrule, result);
+ return result;
+ }
+
+ inline Paths64 Difference(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
+ {
+ return BooleanOp(ClipType::Difference, fillrule, subjects, clips);
+ }
+
+ inline PathsD Difference(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
+ {
+ return BooleanOp(ClipType::Difference, fillrule, subjects, clips, decimal_prec);
+ }
+
+ inline Paths64 Xor(const Paths64& subjects, const Paths64& clips, FillRule fillrule)
+ {
+ return BooleanOp(ClipType::Xor, fillrule, subjects, clips);
+ }
+
+ inline PathsD Xor(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2)
+ {
+ return BooleanOp(ClipType::Xor, fillrule, subjects, clips, decimal_prec);
+ }
+
+ inline Paths64 InflatePaths(const Paths64& paths, double delta,
+ JoinType jt, EndType et, double miter_limit = 2.0,
+ double arc_tolerance = 0.0)
+ {
+ if (!delta) return paths;
+ ClipperOffset clip_offset(miter_limit, arc_tolerance);
+ clip_offset.AddPaths(paths, jt, et);
+ Paths64 solution;
+ clip_offset.Execute(delta, solution);
+ return solution;
+ }
+
+ inline PathsD InflatePaths(const PathsD& paths, double delta,
+ JoinType jt, EndType et, double miter_limit = 2.0,
+ int precision = 2, double arc_tolerance = 0.0)
+ {
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ if (!delta) return paths;
+ if (error_code) return PathsD();
+ const double scale = std::pow(10, precision);
+ ClipperOffset clip_offset(miter_limit, arc_tolerance);
+ clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
+ if (error_code) return PathsD();
+ Paths64 solution;
+ clip_offset.Execute(delta * scale, solution);
+ return ScalePaths<double, int64_t>(solution, 1 / scale, error_code);
+ }
+
+ inline Path64 TranslatePath(const Path64& path, int64_t dx, int64_t dy)
+ {
+ Path64 result;
+ result.reserve(path.size());
+ std::transform(path.begin(), path.end(), back_inserter(result),
+ [dx, dy](const auto& pt) { return Point64(pt.x + dx, pt.y +dy); });
+ return result;
+ }
+
+ inline PathD TranslatePath(const PathD& path, double dx, double dy)
+ {
+ PathD result;
+ result.reserve(path.size());
+ std::transform(path.begin(), path.end(), back_inserter(result),
+ [dx, dy](const auto& pt) { return PointD(pt.x + dx, pt.y + dy); });
+ return result;
+ }
+
+ inline Paths64 TranslatePaths(const Paths64& paths, int64_t dx, int64_t dy)
+ {
+ Paths64 result;
+ result.reserve(paths.size());
+ std::transform(paths.begin(), paths.end(), back_inserter(result),
+ [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
+ return result;
+ }
+
+ inline PathsD TranslatePaths(const PathsD& paths, double dx, double dy)
+ {
+ PathsD result;
+ result.reserve(paths.size());
+ std::transform(paths.begin(), paths.end(), back_inserter(result),
+ [dx, dy](const auto& path) { return TranslatePath(path, dx, dy); });
+ return result;
+ }
+
+ inline Paths64 ExecuteRectClip(const Rect64& rect,
+ const Paths64& paths, bool convex_only = false)
+ {
+ if (rect.IsEmpty() || paths.empty()) return Paths64();
+ RectClip rc(rect);
+ return rc.Execute(paths, convex_only);
+ }
+
+ inline Paths64 ExecuteRectClip(const Rect64& rect,
+ const Path64& path, bool convex_only = false)
+ {
+ if (rect.IsEmpty() || path.empty()) return Paths64();
+ RectClip rc(rect);
+ return rc.Execute(Paths64{ path }, convex_only);
+ }
+
+ inline PathsD ExecuteRectClip(const RectD& rect,
+ const PathsD& paths, bool convex_only = false, int precision = 2)
+ {
+ if (rect.IsEmpty() || paths.empty()) return PathsD();
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ if (error_code) return PathsD();
+ const double scale = std::pow(10, precision);
+ Rect64 r = ScaleRect<int64_t, double>(rect, scale);
+ RectClip rc(r);
+ Paths64 pp = ScalePaths<int64_t, double>(paths, scale, error_code);
+ if (error_code) return PathsD(); // ie: error_code result is lost
+ return ScalePaths<double, int64_t>(
+ rc.Execute(pp, convex_only), 1 / scale, error_code);
+ }
+
+ inline PathsD ExecuteRectClip(const RectD& rect,
+ const PathD& path, bool convex_only = false, int precision = 2)
+ {
+ return ExecuteRectClip(rect, PathsD{ path }, convex_only, precision);
+ }
+
+ inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Paths64& lines)
+ {
+ if (rect.IsEmpty() || lines.empty()) return Paths64();
+ RectClipLines rcl(rect);
+ return rcl.Execute(lines);
+ }
+
+ inline Paths64 ExecuteRectClipLines(const Rect64& rect, const Path64& line)
+ {
+ return ExecuteRectClipLines(rect, Paths64{ line });
+ }
+
+ inline PathsD ExecuteRectClipLines(const RectD& rect, const PathD& line, int precision = 2)
+ {
+ return ExecuteRectClip(rect, PathsD{ line }, precision);
+ }
+
+ inline PathsD ExecuteRectClipLines(const RectD& rect, const PathsD& lines, int precision = 2)
+ {
+ if (rect.IsEmpty() || lines.empty()) return PathsD();
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ if (error_code) return PathsD();
+ const double scale = std::pow(10, precision);
+ Rect64 r = ScaleRect<int64_t, double>(rect, scale);
+ RectClipLines rcl(r);
+ Paths64 p = ScalePaths<int64_t, double>(lines, scale, error_code);
+ if (error_code) return PathsD();
+ p = rcl.Execute(p);
+ return ScalePaths<double, int64_t>(p, 1 / scale, error_code);
+ }
+
+ namespace details
+ {
+
+ inline void PolyPathToPaths64(const PolyPath64& polypath, Paths64& paths)
+ {
+ paths.push_back(polypath.Polygon());
+ for (const auto& child : polypath)
+ PolyPathToPaths64(*child, paths);
+ }
+
+ inline void PolyPathToPathsD(const PolyPathD& polypath, PathsD& paths)
+ {
+ paths.push_back(polypath.Polygon());
+ for (const auto& child : polypath)
+ PolyPathToPathsD(*child, paths);
+ }
+
+ inline bool PolyPath64ContainsChildren(const PolyPath64& pp)
+ {
+ for (const auto& child : pp)
+ {
+ // return false if this child isn't fully contained by its parent
+
+ // the following algorithm is a bit too crude, and doesn't account
+ // for rounding errors. A better algorithm is to return false when
+ // consecutive vertices are found outside the parent's polygon.
+
+ //const Path64& path = pp.Polygon();
+ //if (std::any_of(child->Polygon().cbegin(), child->Polygon().cend(),
+ // [path](const auto& pt) {return (PointInPolygon(pt, path) ==
+ // PointInPolygonResult::IsOutside); })) return false;
+
+ int outsideCnt = 0;
+ for (const Point64& pt : child->Polygon())
+ {
+ PointInPolygonResult result = PointInPolygon(pt, pp.Polygon());
+ if (result == PointInPolygonResult::IsInside) --outsideCnt;
+ else if (result == PointInPolygonResult::IsOutside) ++outsideCnt;
+ if (outsideCnt > 1) return false;
+ else if (outsideCnt < -1) break;
+ }
+
+ // now check any nested children too
+ if (child->Count() > 0 && !PolyPath64ContainsChildren(*child))
+ return false;
+ }
+ return true;
+ }
+
+ static void OutlinePolyPath(std::ostream& os,
+ bool isHole, size_t count, const std::string& preamble)
+ {
+ std::string plural = (count == 1) ? "." : "s.";
+ if (isHole)
+ {
+ if (count)
+ os << preamble << "+- Hole with " << count <<
+ " nested polygon" << plural << std::endl;
+ else
+ os << preamble << "+- Hole" << std::endl;
+ }
+ else
+ {
+ if (count)
+ os << preamble << "+- Polygon with " << count <<
+ " hole" << plural << std::endl;
+ else
+ os << preamble << "+- Polygon" << std::endl;
+ }
+ }
+
+ static void OutlinePolyPath64(std::ostream& os, const PolyPath64& pp,
+ std::string preamble, bool last_child)
+ {
+ OutlinePolyPath(os, pp.IsHole(), pp.Count(), preamble);
+ preamble += (!last_child) ? "| " : " ";
+ if (pp.Count())
+ {
+ PolyPath64List::const_iterator it = pp.begin();
+ for (; it < pp.end() - 1; ++it)
+ OutlinePolyPath64(os, **it, preamble, false);
+ OutlinePolyPath64(os, **it, preamble, true);
+ }
+ }
+
+ static void OutlinePolyPathD(std::ostream& os, const PolyPathD& pp,
+ std::string preamble, bool last_child)
+ {
+ OutlinePolyPath(os, pp.IsHole(), pp.Count(), preamble);
+ preamble += (!last_child) ? "| " : " ";
+ if (pp.Count())
+ {
+ PolyPathDList::const_iterator it = pp.begin();
+ for (; it < pp.end() - 1; ++it)
+ OutlinePolyPathD(os, **it, preamble, false);
+ OutlinePolyPathD(os, **it, preamble, true);
+ }
+ }
+
+ } // end details namespace
+
+ inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
+ {
+ PolyPath64List::const_iterator it = pp.begin();
+ for (; it < pp.end() - 1; ++it)
+ details::OutlinePolyPath64(os, **it, " ", false);
+ details::OutlinePolyPath64(os, **it, " ", true);
+ os << std::endl << std::endl;
+ if (!pp.Level()) os << std::endl;
+ return os;
+ }
+
+ inline std::ostream& operator<< (std::ostream& os, const PolyTreeD& pp)
+ {
+ PolyPathDList::const_iterator it = pp.begin();
+ for (; it < pp.end() - 1; ++it)
+ details::OutlinePolyPathD(os, **it, " ", false);
+ details::OutlinePolyPathD(os, **it, " ", true);
+ os << std::endl << std::endl;
+ if (!pp.Level()) os << std::endl;
+ return os;
+ }
+
+ inline Paths64 PolyTreeToPaths64(const PolyTree64& polytree)
+ {
+ Paths64 result;
+ for (const auto& child : polytree)
+ details::PolyPathToPaths64(*child, result);
+ return result;
+ }
+
+ inline PathsD PolyTreeToPathsD(const PolyTreeD& polytree)
+ {
+ PathsD result;
+ for (const auto& child : polytree)
+ details::PolyPathToPathsD(*child, result);
+ return result;
+ }
+
+ inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree)
+ {
+ for (const auto& child : polytree)
+ if (child->Count() > 0 &&
+ !details::PolyPath64ContainsChildren(*child))
+ return false;
+ return true;
+ }
+
+ namespace details {
+
+ template<typename T, typename U>
+ inline constexpr void MakePathGeneric(const T list, size_t size,
+ std::vector<U>& result)
+ {
+ for (size_t i = 0; i < size; ++i)
+#ifdef USINGZ
+ result[i / 2] = U{list[i], list[++i], 0};
+#else
+ result[i / 2] = U{list[i], list[++i]};
+#endif
+ }
+
+ } // end details namespace
+
+ template<typename T,
+ typename std::enable_if<
+ std::is_integral<T>::value &&
+ !std::is_same<char, T>::value, bool
+ >::type = true>
+ inline Path64 MakePath(const std::vector<T>& list)
+ {
+ const auto size = list.size() - list.size() % 2;
+ if (list.size() != size)
+ DoError(non_pair_error_i); // non-fatal without exception handling
+ Path64 result(size / 2); // else ignores unpaired value
+ details::MakePathGeneric(list, size, result);
+ return result;
+ }
+
+ template<typename T, std::size_t N,
+ typename std::enable_if<
+ std::is_integral<T>::value &&
+ !std::is_same<char, T>::value, bool
+ >::type = true>
+ inline Path64 MakePath(const T(&list)[N])
+ {
+ // Make the compiler error on unpaired value (i.e. no runtime effects).
+ static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
+ Path64 result(N / 2);
+ details::MakePathGeneric(list, N, result);
+ return result;
+ }
+
+ template<typename T,
+ typename std::enable_if<
+ std::is_arithmetic<T>::value &&
+ !std::is_same<char, T>::value, bool
+ >::type = true>
+ inline PathD MakePathD(const std::vector<T>& list)
+ {
+ const auto size = list.size() - list.size() % 2;
+ if (list.size() != size)
+ DoError(non_pair_error_i); // non-fatal without exception handling
+ PathD result(size / 2); // else ignores unpaired value
+ details::MakePathGeneric(list, size, result);
+ return result;
+ }
+
+ template<typename T, std::size_t N,
+ typename std::enable_if<
+ std::is_arithmetic<T>::value &&
+ !std::is_same<char, T>::value, bool
+ >::type = true>
+ inline PathD MakePathD(const T(&list)[N])
+ {
+ // Make the compiler error on unpaired value (i.e. no runtime effects).
+ static_assert(N % 2 == 0, "MakePath requires an even number of arguments");
+ PathD result(N / 2);
+ details::MakePathGeneric(list, N, result);
+ return result;
+ }
+
+ inline Path64 TrimCollinear(const Path64& p, bool is_open_path = false)
+ {
+ size_t len = p.size();
+ if (len < 3)
+ {
+ if (!is_open_path || len < 2 || p[0] == p[1]) return Path64();
+ else return p;
+ }
+
+ Path64 dst;
+ dst.reserve(len);
+ Path64::const_iterator srcIt = p.cbegin(), prevIt, stop = p.cend() - 1;
+
+ if (!is_open_path)
+ {
+ while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1)))
+ ++srcIt;
+ while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt))
+ --stop;
+ if (srcIt == stop) return Path64();
+ }
+
+ prevIt = srcIt++;
+ dst.push_back(*prevIt);
+ for (; srcIt != stop; ++srcIt)
+ {
+ if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1)))
+ {
+ prevIt = srcIt;
+ dst.push_back(*prevIt);
+ }
+ }
+
+ if (is_open_path)
+ dst.push_back(*srcIt);
+ else if (CrossProduct(*prevIt, *stop, dst[0]))
+ dst.push_back(*stop);
+ else
+ {
+ while (dst.size() > 2 &&
+ !CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0]))
+ dst.pop_back();
+ if (dst.size() < 3) return Path64();
+ }
+ return dst;
+ }
+
+ inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false)
+ {
+ int error_code = 0;
+ CheckPrecision(precision, error_code);
+ if (error_code) return PathD();
+ const double scale = std::pow(10, precision);
+ Path64 p = ScalePath<int64_t, double>(path, scale, error_code);
+ if (error_code) return PathD();
+ p = TrimCollinear(p, is_open_path);
+ return ScalePath<double, int64_t>(p, 1/scale, error_code);
+ }
+
+ template <typename T>
+ inline double Distance(const Point<T> pt1, const Point<T> pt2)
+ {
+ return std::sqrt(DistanceSqr(pt1, pt2));
+ }
+
+ template <typename T>
+ inline double Length(const Path<T>& path, bool is_closed_path = false)
+ {
+ double result = 0.0;
+ if (path.size() < 2) return result;
+ auto it = path.cbegin(), stop = path.end() - 1;
+ for (; it != stop; ++it)
+ result += Distance(*it, *(it + 1));
+ if (is_closed_path)
+ result += Distance(*stop, *path.cbegin());
+ return result;
+ }
+
+
+ template <typename T>
+ inline bool NearCollinear(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3, double sin_sqrd_min_angle_rads)
+ {
+ double cp = std::abs(CrossProduct(pt1, pt2, pt3));
+ return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads;
+ }
+
+ template <typename T>
+ inline Path<T> Ellipse(const Rect<T>& rect, int steps = 0)
+ {
+ return Ellipse(rect.MidPoint(),
+ static_cast<double>(rect.Width()) *0.5,
+ static_cast<double>(rect.Height()) * 0.5, steps);
+ }
+
+ template <typename T>
+ inline Path<T> Ellipse(const Point<T>& center,
+ double radiusX, double radiusY = 0, int steps = 0)
+ {
+ if (radiusX <= 0) return Path<T>();
+ if (radiusY <= 0) radiusY = radiusX;
+ if (steps <= 2)
+ steps = static_cast<int>(PI * sqrt((radiusX + radiusY) / 2));
+
+ double si = std::sin(2 * PI / steps);
+ double co = std::cos(2 * PI / steps);
+ double dx = co, dy = si;
+ Path<T> result;
+ result.reserve(steps);
+ result.push_back(Point<T>(center.x + radiusX, static_cast<double>(center.y)));
+ for (int i = 1; i < steps; ++i)
+ {
+ result.push_back(Point<T>(center.x + radiusX * dx, center.y + radiusY * dy));
+ double x = dx * co - dy * si;
+ dy = dy * co + dx * si;
+ dx = x;
+ }
+ 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,
+ const std::vector<bool>& flags)
+ {
+ ++current;
+ while (current <= high && flags[current]) ++current;
+ if (current <= high) return current;
+ current = 0;
+ while (flags[current]) ++current;
+ return current;
+ }
+
+ inline size_t GetPrior(size_t current, size_t high,
+ const std::vector<bool>& flags)
+ {
+ if (current == 0) current = high;
+ else --current;
+ while (current > 0 && flags[current]) --current;
+ if (!flags[current]) return current;
+ current = high;
+ while (flags[current]) --current;
+ return current;
+ }
+
+ template <typename T>
+ inline Path<T> SimplifyPath(const Path<T> path,
+ double epsilon, bool isOpenPath = false)
+ {
+ const size_t len = path.size(), high = len -1;
+ const double epsSqr = Sqr(epsilon);
+ if (len < 4) return Path<T>(path);
+
+ std::vector<bool> flags(len);
+ std::vector<double> distSqr(len);
+ size_t prior = high, curr = 0, start, next, prior2, next2;
+ if (isOpenPath)
+ {
+ distSqr[0] = MAX_DBL;
+ distSqr[high] = MAX_DBL;
+ }
+ else
+ {
+ distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]);
+ distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]);
+ }
+ for (size_t i = 1; i < high; ++i)
+ distSqr[i] = PerpendicDistFromLineSqrd(path[i], path[i - 1], path[i + 1]);
+
+ for (;;)
+ {
+ if (distSqr[curr] > epsSqr)
+ {
+ start = curr;
+ do
+ {
+ curr = GetNext(curr, high, flags);
+ } while (curr != start && distSqr[curr] > epsSqr);
+ if (curr == start) break;
+ }
+
+ prior = GetPrior(curr, high, flags);
+ next = GetNext(curr, high, flags);
+ if (next == prior) break;
+
+ if (distSqr[next] < distSqr[curr])
+ {
+ flags[next] = true;
+ next = GetNext(next, high, flags);
+ next2 = GetNext(next, high, flags);
+ distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
+ if (next != high || !isOpenPath)
+ distSqr[next] = PerpendicDistFromLineSqrd(path[next], path[curr], path[next2]);
+ curr = next;
+ }
+ else
+ {
+ flags[curr] = true;
+ curr = next;
+ next = GetNext(next, high, flags);
+ prior2 = GetPrior(prior, high, flags);
+ distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
+ if (prior != 0 || !isOpenPath)
+ distSqr[prior] = PerpendicDistFromLineSqrd(path[prior], path[prior2], path[curr]);
+ }
+ }
+ Path<T> result;
+ result.reserve(len);
+ for (typename Path<T>::size_type i = 0; i < len; ++i)
+ if (!flags[i]) result.push_back(path[i]);
+ return result;
+ }
+
+ template <typename T>
+ inline Paths<T> SimplifyPaths(const Paths<T> paths,
+ double epsilon, bool isOpenPath = false)
+ {
+ Paths<T> result;
+ result.reserve(paths.size());
+ for (const auto& path : paths)
+ result.push_back(SimplifyPath(path, epsilon, isOpenPath));
+ return result;
+ }
+
+ template <typename T>
+ inline void RDP(const Path<T> path, std::size_t begin,
+ std::size_t end, double epsSqrd, std::vector<bool>& flags)
+ {
+ typename Path<T>::size_type idx = 0;
+ double max_d = 0;
+ while (end > begin && path[begin] == path[end]) flags[end--] = false;
+ for (typename Path<T>::size_type i = begin + 1; i < end; ++i)
+ {
+ // PerpendicDistFromLineSqrd - avoids expensive Sqrt()
+ double d = PerpendicDistFromLineSqrd(path[i], path[begin], path[end]);
+ if (d <= max_d) continue;
+ max_d = d;
+ idx = i;
+ }
+ if (max_d <= epsSqrd) return;
+ flags[idx] = true;
+ if (idx > begin + 1) RDP(path, begin, idx, epsSqrd, flags);
+ if (idx < end - 1) RDP(path, idx, end, epsSqrd, flags);
+ }
+
+ template <typename T>
+ inline Path<T> RamerDouglasPeucker(const Path<T>& path, double epsilon)
+ {
+ const typename Path<T>::size_type len = path.size();
+ if (len < 5) return Path<T>(path);
+ std::vector<bool> flags(len);
+ flags[0] = true;
+ flags[len - 1] = true;
+ RDP(path, 0, len - 1, Sqr(epsilon), flags);
+ Path<T> result;
+ result.reserve(len);
+ for (typename Path<T>::size_type i = 0; i < len; ++i)
+ if (flags[i])
+ result.push_back(path[i]);
+ return result;
+ }
+
+ template <typename T>
+ inline Paths<T> RamerDouglasPeucker(const Paths<T>& paths, double epsilon)
+ {
+ Paths<T> result;
+ result.reserve(paths.size());
+ std::transform(paths.begin(), paths.end(), back_inserter(result),
+ [epsilon](const auto& path)
+ { return RamerDouglasPeucker<T>(path, epsilon); });
+ return result;
+ }
+
+} // end Clipper2Lib namespace
+
+#endif // CLIPPER_H
diff --git a/thirdparty/clipper2/include/clipper2/clipper.minkowski.h b/thirdparty/clipper2/include/clipper2/clipper.minkowski.h
new file mode 100644
index 0000000000..71c221bb50
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.minkowski.h
@@ -0,0 +1,120 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 28 January 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : Minkowski Sum and Difference *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+#ifndef CLIPPER_MINKOWSKI_H
+#define CLIPPER_MINKOWSKI_H
+
+#include <cstdlib>
+#include <vector>
+#include <string>
+#include "clipper.core.h"
+
+namespace Clipper2Lib
+{
+
+ namespace detail
+ {
+ inline Paths64 Minkowski(const Path64& pattern, const Path64& path, bool isSum, bool isClosed)
+ {
+ size_t delta = isClosed ? 0 : 1;
+ size_t patLen = pattern.size(), pathLen = path.size();
+ if (patLen == 0 || pathLen == 0) return Paths64();
+ Paths64 tmp;
+ tmp.reserve(pathLen);
+
+ if (isSum)
+ {
+ for (const Point64& p : path)
+ {
+ Path64 path2(pattern.size());
+ std::transform(pattern.cbegin(), pattern.cend(),
+ path2.begin(), [p](const Point64& pt2) {return p + pt2; });
+ tmp.push_back(path2);
+ }
+ }
+ else
+ {
+ for (const Point64& p : path)
+ {
+ Path64 path2(pattern.size());
+ std::transform(pattern.cbegin(), pattern.cend(),
+ path2.begin(), [p](const Point64& pt2) {return p - pt2; });
+ tmp.push_back(path2);
+ }
+ }
+
+ Paths64 result;
+ result.reserve((pathLen - delta) * patLen);
+ size_t g = isClosed ? pathLen - 1 : 0;
+ for (size_t h = patLen - 1, i = delta; i < pathLen; ++i)
+ {
+ for (size_t j = 0; j < patLen; j++)
+ {
+ Path64 quad;
+ quad.reserve(4);
+ {
+ quad.push_back(tmp[g][h]);
+ quad.push_back(tmp[i][h]);
+ quad.push_back(tmp[i][j]);
+ quad.push_back(tmp[g][j]);
+ };
+ if (!IsPositive(quad))
+ std::reverse(quad.begin(), quad.end());
+ result.push_back(quad);
+ h = j;
+ }
+ g = i;
+ }
+ return result;
+ }
+
+ inline Paths64 Union(const Paths64& subjects, FillRule fillrule)
+ {
+ Paths64 result;
+ Clipper64 clipper;
+ clipper.AddSubject(subjects);
+ clipper.Execute(ClipType::Union, fillrule, result);
+ return result;
+ }
+
+ } // namespace internal
+
+ inline Paths64 MinkowskiSum(const Path64& pattern, const Path64& path, bool isClosed)
+ {
+ return detail::Union(detail::Minkowski(pattern, path, true, isClosed), FillRule::NonZero);
+ }
+
+ inline PathsD MinkowskiSum(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
+ {
+ int error_code = 0;
+ double scale = pow(10, decimalPlaces);
+ Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
+ Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
+ Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero);
+ return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
+ }
+
+ inline Paths64 MinkowskiDiff(const Path64& pattern, const Path64& path, bool isClosed)
+ {
+ return detail::Union(detail::Minkowski(pattern, path, false, isClosed), FillRule::NonZero);
+ }
+
+ inline PathsD MinkowskiDiff(const PathD& pattern, const PathD& path, bool isClosed, int decimalPlaces = 2)
+ {
+ int error_code = 0;
+ double scale = pow(10, decimalPlaces);
+ Path64 pat64 = ScalePath<int64_t, double>(pattern, scale, error_code);
+ Path64 path64 = ScalePath<int64_t, double>(path, scale, error_code);
+ Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero);
+ return ScalePaths<double, int64_t>(tmp, 1 / scale, error_code);
+ }
+
+} // Clipper2Lib namespace
+
+#endif // CLIPPER_MINKOWSKI_H
diff --git a/thirdparty/clipper2/include/clipper2/clipper.offset.h b/thirdparty/clipper2/include/clipper2/clipper.offset.h
new file mode 100644
index 0000000000..f5d47e07ee
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.offset.h
@@ -0,0 +1,114 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 22 March 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : Path Offset (Inflate/Shrink) *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+#ifndef CLIPPER_OFFSET_H_
+#define CLIPPER_OFFSET_H_
+
+#include "clipper.core.h"
+#include "clipper.engine.h"
+
+namespace Clipper2Lib {
+
+enum class JoinType { Square, Round, Miter };
+
+enum class EndType {Polygon, Joined, Butt, Square, Round};
+//Butt : offsets both sides of a path, with square blunt ends
+//Square : offsets both sides of a path, with square extended ends
+//Round : offsets both sides of a path, with round extended ends
+//Joined : offsets both sides of a path, with joined ends
+//Polygon: offsets only one side of a closed path
+
+
+class ClipperOffset {
+private:
+
+ class Group {
+ public:
+ Paths64 paths_in;
+ Paths64 paths_out;
+ Path64 path;
+ bool is_reversed = false;
+ JoinType join_type;
+ EndType end_type;
+ Group(const Paths64& _paths, JoinType _join_type, EndType _end_type) :
+ paths_in(_paths), join_type(_join_type), end_type(_end_type) {}
+ };
+
+ int error_code_ = 0;
+ double delta_ = 0.0;
+ double group_delta_ = 0.0;
+ double abs_group_delta_ = 0.0;
+ double temp_lim_ = 0.0;
+ double steps_per_rad_ = 0.0;
+ double step_sin_ = 0.0;
+ double step_cos_ = 0.0;
+ PathD norms;
+ Paths64 solution;
+ std::vector<Group> groups_;
+ JoinType join_type_ = JoinType::Square;
+ EndType end_type_ = EndType::Polygon;
+
+ double miter_limit_ = 0.0;
+ double arc_tolerance_ = 0.0;
+ bool preserve_collinear_ = false;
+ bool reverse_solution_ = false;
+
+#ifdef USINGZ
+ ZCallback64 zCallback64_ = nullptr;
+#endif
+
+ void DoSquare(Group& group, const Path64& path, size_t j, size_t k);
+ void DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a);
+ void DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle);
+ void BuildNormals(const Path64& path);
+ void OffsetPolygon(Group& group, Path64& path);
+ void OffsetOpenJoined(Group& group, Path64& path);
+ void OffsetOpenPath(Group& group, Path64& path);
+ void OffsetPoint(Group& group, Path64& path, size_t j, size_t& k);
+ void DoGroupOffset(Group &group);
+ void ExecuteInternal(double delta);
+public:
+ explicit ClipperOffset(double miter_limit = 2.0,
+ double arc_tolerance = 0.0,
+ bool preserve_collinear = false,
+ bool reverse_solution = false) :
+ miter_limit_(miter_limit), arc_tolerance_(arc_tolerance),
+ preserve_collinear_(preserve_collinear),
+ reverse_solution_(reverse_solution) { };
+
+ ~ClipperOffset() { Clear(); };
+
+ int ErrorCode() { return error_code_; };
+ void AddPath(const Path64& path, JoinType jt_, EndType et_);
+ void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
+ void Clear() { groups_.clear(); norms.clear(); };
+
+ void Execute(double delta, Paths64& paths);
+ void Execute(double delta, PolyTree64& polytree);
+
+ double MiterLimit() const { return miter_limit_; }
+ void MiterLimit(double miter_limit) { miter_limit_ = miter_limit; }
+
+ //ArcTolerance: needed for rounded offsets (See offset_triginometry2.svg)
+ double ArcTolerance() const { return arc_tolerance_; }
+ void ArcTolerance(double arc_tolerance) { arc_tolerance_ = arc_tolerance; }
+
+ bool PreserveCollinear() const { return preserve_collinear_; }
+ void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
+
+ bool ReverseSolution() const { return reverse_solution_; }
+ void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;}
+
+#ifdef USINGZ
+ void SetZCallback(ZCallback64 cb) { zCallback64_ = cb; }
+#endif
+};
+
+}
+#endif /* CLIPPER_OFFSET_H_ */
diff --git a/thirdparty/clipper2/include/clipper2/clipper.rectclip.h b/thirdparty/clipper2/include/clipper2/clipper.rectclip.h
new file mode 100644
index 0000000000..2a9bb35d08
--- /dev/null
+++ b/thirdparty/clipper2/include/clipper2/clipper.rectclip.h
@@ -0,0 +1,82 @@
+/*******************************************************************************
+* Author : Angus Johnson *
+* Date : 9 February 2023 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2023 *
+* Purpose : FAST rectangular clipping *
+* License : http://www.boost.org/LICENSE_1_0.txt *
+*******************************************************************************/
+
+#ifndef CLIPPER_RECTCLIP_H
+#define CLIPPER_RECTCLIP_H
+
+#include <cstdlib>
+#include <vector>
+#include <queue>
+#include "clipper.h"
+#include "clipper.core.h"
+
+namespace Clipper2Lib
+{
+
+ enum class Location { Left, Top, Right, Bottom, Inside };
+
+ class OutPt2;
+ typedef std::vector<OutPt2*> OutPt2List;
+
+ class OutPt2 {
+ public:
+ Point64 pt;
+ size_t owner_idx;
+ OutPt2List* edge;
+ OutPt2* next;
+ OutPt2* prev;
+ };
+
+ //------------------------------------------------------------------------------
+ // RectClip
+ //------------------------------------------------------------------------------
+
+ class RectClip {
+ private:
+ void ExecuteInternal(const Path64& path);
+ Path64 GetPath(OutPt2*& op);
+ protected:
+ const Rect64 rect_;
+ const Path64 rect_as_path_;
+ const Point64 rect_mp_;
+ Rect64 path_bounds_;
+ std::deque<OutPt2> op_container_;
+ OutPt2List results_; // each path can be broken into multiples
+ OutPt2List edges_[8]; // clockwise and counter-clockwise
+ std::vector<Location> start_locs_;
+ void CheckEdges();
+ void TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw);
+ void GetNextLocation(const Path64& path,
+ Location& loc, int& i, int highI);
+ OutPt2* Add(Point64 pt, bool start_new = false);
+ void AddCorner(Location prev, Location curr);
+ void AddCorner(Location& loc, bool isClockwise);
+ public:
+ explicit RectClip(const Rect64& rect) :
+ rect_(rect),
+ rect_as_path_(rect.AsPath()),
+ rect_mp_(rect.MidPoint()) {}
+ Paths64 Execute(const Paths64& paths, bool convex_only = false);
+ };
+
+ //------------------------------------------------------------------------------
+ // RectClipLines
+ //------------------------------------------------------------------------------
+
+ class RectClipLines : public RectClip {
+ private:
+ void ExecuteInternal(const Path64& path);
+ Path64 GetPath(OutPt2*& op);
+ public:
+ explicit RectClipLines(const Rect64& rect) : RectClip(rect) {};
+ Paths64 Execute(const Paths64& paths);
+ };
+
+} // Clipper2Lib namespace
+#endif // CLIPPER_RECTCLIP_H