From 6f88656bc7496b89b00708becc4fa59e659eb2d7 Mon Sep 17 00:00:00 2001 From: Relintai Date: Wed, 7 Jun 2023 15:33:59 +0200 Subject: [PATCH] Removed the Clipper2 module, and also the remaining c++17 defines. --- modules/clipper2/.gitignore | 8 - modules/clipper2/COPYRIGHT.txt | 42 - modules/clipper2/SCsub | 15 - modules/clipper2/config.py | 17 - modules/clipper2/geometry_new.h | 187 - modules/clipper2/lib/LICENSE | 23 - .../lib/include/clipper2/clipper.core.h | 808 ----- .../lib/include/clipper2/clipper.engine.h | 610 ---- .../lib/include/clipper2/clipper.export.h | 830 ----- .../clipper2/lib/include/clipper2/clipper.h | 911 ----- .../lib/include/clipper2/clipper.minkowski.h | 120 - .../lib/include/clipper2/clipper.offset.h | 101 - .../lib/include/clipper2/clipper.rectclip.h | 50 - modules/clipper2/lib/src/clipper.engine.cpp | 2996 ----------------- modules/clipper2/lib/src/clipper.offset.cpp | 494 --- modules/clipper2/lib/src/clipper.rectclip.cpp | 513 --- modules/clipper2/polypartition.cpp | 1851 ---------- modules/clipper2/polypartition.h | 378 --- modules/clipper2/register_types.cpp | 7 - modules/clipper2/register_types.h | 9 - modules/navigation_geometry_parsers/SCsub | 6 - 21 files changed, 9976 deletions(-) delete mode 100644 modules/clipper2/.gitignore delete mode 100644 modules/clipper2/COPYRIGHT.txt delete mode 100644 modules/clipper2/SCsub delete mode 100644 modules/clipper2/config.py delete mode 100644 modules/clipper2/geometry_new.h delete mode 100644 modules/clipper2/lib/LICENSE delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.core.h delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.engine.h delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.export.h delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.h delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.minkowski.h delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.offset.h delete mode 100644 modules/clipper2/lib/include/clipper2/clipper.rectclip.h delete mode 100644 modules/clipper2/lib/src/clipper.engine.cpp delete mode 100644 modules/clipper2/lib/src/clipper.offset.cpp delete mode 100644 modules/clipper2/lib/src/clipper.rectclip.cpp delete mode 100644 modules/clipper2/polypartition.cpp delete mode 100644 modules/clipper2/polypartition.h delete mode 100644 modules/clipper2/register_types.cpp delete mode 100644 modules/clipper2/register_types.h diff --git a/modules/clipper2/.gitignore b/modules/clipper2/.gitignore deleted file mode 100644 index 00d0f2904..000000000 --- a/modules/clipper2/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.import -*.d -*.o -*.meta -*.obj -*.pyc -*.bc -*.os diff --git a/modules/clipper2/COPYRIGHT.txt b/modules/clipper2/COPYRIGHT.txt deleted file mode 100644 index 7cbe3e545..000000000 --- a/modules/clipper2/COPYRIGHT.txt +++ /dev/null @@ -1,42 +0,0 @@ -# Exhaustive licensing information for files in the Godot Engine repository -# ========================================================================= -# -# This file aims at documenting the copyright and license for every source -# file in the Godot Engine repository, and especially outline the files -# whose license differs from the MIT/Expat license used by Godot Engine. -# -# It is written as a machine-readable format following the debian/copyright -# specification. Globbing patterns (e.g. "Files: *") mean that they affect -# all corresponding files (also recursively in subfolders), apart from those -# with a more explicit copyright statement. -# -# Licenses are given with their debian/copyright short name (or SPDX identifier -# if no standard short name exists) and are all included in plain text at the -# end of this file (in alphabetical order). -# -# Disclaimer for thirdparty libraries: -# ------------------------------------ -# -# Licensing details for thirdparty libraries in the 'thirdparty/' directory -# are given in summarized form, i.e. with only the "main" license described -# in the library's license statement. Different licenses of single files or -# code snippets in thirdparty libraries are not documented here. -# For example: -# Files: ./thirdparty/zlib/ -# Copyright: 1995-2017, Jean-loup Gailly and Mark Adler -# License: Zlib -# The exact copyright for each file in that library *may* differ, and some -# files or code snippets might be distributed under other compatible licenses -# (e.g. a public domain dedication), but as far as Godot Engine is concerned -# the library is considered as a whole under the Zlib license. -# -# Nota: When linking dynamically against thirdparty libraries instead of -# building them into the Godot binary, you may remove the corresponding -# license details from this file. - ------------------------------------------------------------------------ - -Files: modules/clipper2/lib/* -Comment: FastNoise Library -Copyright: 2010-2023, Angus Johnson. -License: BSL-1.0 diff --git a/modules/clipper2/SCsub b/modules/clipper2/SCsub deleted file mode 100644 index 80992f782..000000000 --- a/modules/clipper2/SCsub +++ /dev/null @@ -1,15 +0,0 @@ -Import('env') - -module_env = env.Clone() - - -module_env.add_source_files(env.modules_sources,"*.cpp") -module_env.add_source_files(env.modules_sources,"lib/src/*.cpp") - -# TODO this won't work as a custom module like this! -module_env.Prepend(CPPPATH=["#modules/clipper2/lib/include"]) - -if env.msvc: - module_env.Append(CXXFLAGS=['/std:c++17']) -else: - module_env.Append(CXXFLAGS=['-std=c++17']) \ No newline at end of file diff --git a/modules/clipper2/config.py b/modules/clipper2/config.py deleted file mode 100644 index bb417b86d..000000000 --- a/modules/clipper2/config.py +++ /dev/null @@ -1,17 +0,0 @@ - - -def get_doc_classes(): - return [ - ] - -def get_doc_path(): - return "doc_classes" - -def can_build(env, platform): - return True - -def configure(env): - pass - -def get_license_file(): - return "COPYRIGHT.txt" diff --git a/modules/clipper2/geometry_new.h b/modules/clipper2/geometry_new.h deleted file mode 100644 index a91846a20..000000000 --- a/modules/clipper2/geometry_new.h +++ /dev/null @@ -1,187 +0,0 @@ - -#include "thirdparty/clipper2/include/clipper2/clipper.h" - -Vector> Geometry2D::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector &p_polypath_a, const Vector &p_polypath_b, bool is_a_open) { - Vector> finished_polygons; - -#ifdef CLIPPER_ENABLED - using namespace Clipper2Lib; - - Paths64 polyon_paths_solution; - - ClipType clipper_cliptype = ClipType::Union; - FillRule clipper_fillrule = FillRule::EvenOdd; - - if (is_a_open) { - // Polyline with Polygon - Clipper64 clipper_64; - - Paths64 polyline_paths; - Paths64 polygon_paths_b; - - Path64 polyline_path_a; - Path64 polygon_path_b; - - for (const Vector2 &polyline_point : p_polypath_a) { - const Point64 &point = Point64(polyline_point.x * (real_t)SCALE_FACTOR, polyline_point.y * (real_t)SCALE_FACTOR); - polyline_path_a.push_back(point); - } - polyline_paths.push_back(polyline_path_a); - - for (const Vector2 &polypath_outline_point : p_polypath_b) { - const Point64 &point = Point64(polypath_outline_point.x * (real_t)SCALE_FACTOR, polypath_outline_point.y * (real_t)SCALE_FACTOR); - polygon_path_b.push_back(point); - } - polygon_paths_b.push_back(polygon_path_b); - - switch (p_op) { - case OPERATION_UNION: - // not supported for polyline (in Godot) - return finished_polygons; - - case OPERATION_DIFFERENCE: - clipper_cliptype = ClipType::Difference; - clipper_fillrule = FillRule::EvenOdd; - clipper_64.AddOpenSubject(polyline_paths); - clipper_64.AddClip(polygon_paths_b); - break; - - case OPERATION_INTERSECTION: - clipper_cliptype = ClipType::Intersection; - clipper_fillrule = FillRule::EvenOdd; - clipper_64.AddOpenSubject(polyline_paths); - clipper_64.AddClip(polygon_paths_b); - break; - - case OPERATION_XOR: - // not supported for polyline - return finished_polygons; - } - - Paths64 polygon_solution, polyline_solution; - clipper_64.Execute(clipper_cliptype, clipper_fillrule, polygon_solution, polyline_solution); - polyon_paths_solution = polyline_solution; - - } else { - // Polygon with Polygon - Paths64 polygon_paths; - Paths64 polygon_clip_paths; - - Path64 polygon_path_a; - Path64 polygon_path_b; - - for (const Vector2 &polypath_outline_point : p_polypath_a) { - const Point64 &point = Point64(polypath_outline_point.x * (real_t)SCALE_FACTOR, polypath_outline_point.y * (real_t)SCALE_FACTOR); - polygon_path_a.push_back(point); - } - polygon_paths.push_back(polygon_path_a); - - for (const Vector2 &polypath_outline_point : p_polypath_b) { - const Point64 &point = Point64(polypath_outline_point.x * (real_t)SCALE_FACTOR, polypath_outline_point.y * (real_t)SCALE_FACTOR); - polygon_path_b.push_back(point); - } - polygon_clip_paths.push_back(polygon_path_b); - - switch (p_op) { - case OPERATION_UNION: - clipper_cliptype = ClipType::Union; - clipper_fillrule = FillRule::NonZero; - - polyon_paths_solution = Union(polygon_paths, polygon_clip_paths, clipper_fillrule); - break; - case OPERATION_DIFFERENCE: - clipper_cliptype = ClipType::Difference; - clipper_fillrule = FillRule::EvenOdd; - - polyon_paths_solution = Difference(polygon_paths, polygon_clip_paths, clipper_fillrule); - break; - case OPERATION_INTERSECTION: - clipper_cliptype = ClipType::Intersection; - clipper_fillrule = FillRule::NonZero; - - polyon_paths_solution = Intersect(polygon_paths, polygon_clip_paths, clipper_fillrule); - break; - case OPERATION_XOR: - clipper_cliptype = ClipType::Xor; - clipper_fillrule = FillRule::NonZero; - - polyon_paths_solution = Xor(polygon_paths, polygon_clip_paths, clipper_fillrule); - break; - } - } - - for (const Path64 &polyon_path : polyon_paths_solution) { - Vector finished_polygon; - for (const Point64 &polyon_path_point : polyon_path) { - finished_polygon.push_back(Vector2(static_cast(polyon_path_point.x), static_cast(polyon_path_point.y)) / (real_t)SCALE_FACTOR); - } - finished_polygons.push_back(finished_polygon); - } -#endif // CLIPPER_ENABLED - - return finished_polygons; -} - -Vector> Geometry2D::_polypath_offset(const Vector &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - Vector> finished_polygons; - -#ifdef CLIPPER_ENABLED - using namespace Clipper2Lib; - - JoinType clipper_jointype = JoinType::Miter; - - switch (p_join_type) { - case JOIN_SQUARE: - clipper_jointype = JoinType::Square; - break; - case JOIN_ROUND: - clipper_jointype = JoinType::Round; - break; - case JOIN_MITER: - clipper_jointype = JoinType::Miter; - break; - } - - EndType clipper_endtype = EndType::Polygon; - - switch (p_end_type) { - case END_POLYGON: - clipper_endtype = EndType::Polygon; - break; - case END_JOINED: - clipper_endtype = EndType::Joined; - break; - case END_BUTT: - clipper_endtype = EndType::Butt; - break; - case END_SQUARE: - clipper_endtype = EndType::Square; - break; - case END_ROUND: - clipper_endtype = EndType::Round; - break; - } - - Paths64 polygon_paths; - - Path64 polygon_path; - for (const Vector2 &polypath_outline_point : p_polypath) { - const Point64 &point = Point64(polypath_outline_point.x * (real_t)SCALE_FACTOR, polypath_outline_point.y * (real_t)SCALE_FACTOR); - polygon_path.push_back(point); - } - polygon_paths.push_back(polygon_path); - - Paths64 paths_solution = InflatePaths(polygon_paths, p_delta, clipper_jointype, clipper_endtype); - - for (const Path64 &scaled_path : paths_solution) { - Vector polypath; - for (const Point64 &scaled_point : scaled_path) { - polypath.push_back(Vector2(static_cast(scaled_point.x), static_cast(scaled_point.y)) / (real_t)SCALE_FACTOR); - } - finished_polygons.push_back(polypath); - } -#endif // CLIPPER_ENABLED - - return finished_polygons; -} - diff --git a/modules/clipper2/lib/LICENSE b/modules/clipper2/lib/LICENSE deleted file mode 100644 index 36b7cd93c..000000000 --- a/modules/clipper2/lib/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/modules/clipper2/lib/include/clipper2/clipper.core.h b/modules/clipper2/lib/include/clipper2/clipper.core.h deleted file mode 100644 index c7e8844bc..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.core.h +++ /dev/null @@ -1,808 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 28 January 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 -#include -#include -#include -#include -#include -#include - -namespace Clipper2Lib -{ - -#if __cpp_exceptions - - class Clipper2Exception : public std::exception { - public: - explicit Clipper2Exception(const char* description) : - m_descr(description) {} - virtual const char* what() const throw() { 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"; -#endif - - // error codes (2^n) - const int precision_error_i = 1; // non-fatal - const int range_error_i = 2; - - 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; - - static const double MAX_DBL = (std::numeric_limits::max)(); - static const double MIN_DBL = (std::numeric_limits::min)(); - - //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 - struct Point { - T x; - T y; -#ifdef USINGZ - int64_t z; - - template - inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0) - { - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - x = static_cast(std::round(x_)); - y = static_cast(std::round(y_)); - z = z_; - } - else - { - x = static_cast(x_); - y = static_cast(y_); - z = z_; - } - } - - explicit Point() : x(0), y(0), z(0) {}; - - template - Point(const T2 x_, const T2 y_, const int64_t z_ = 0) - { - Init(x_, y_); - z = z_; - } - - template - explicit Point(const Point& 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 - inline void Init(const T2 x_ = 0, const T2 y_ = 0) - { - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - x = static_cast(std::round(x_)); - y = static_cast(std::round(y_)); - } - else - { - x = static_cast(x_); - y = static_cast(y_); - } - } - - explicit Point() : x(0), y(0) {}; - - template - Point(const T2 x_, const T2 y_) { Init(x_, y_); } - - template - explicit Point(const Point& 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 operator-() const - { - return Point(-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; - using PointD = Point; - - template - using Path = std::vector>; - template - using Paths = std::vector>; - - using Path64 = Path; - using PathD = Path; - using Paths64 = std::vector< Path64>; - using PathsD = std::vector< PathD>; - - // Rect ------------------------------------------------------------------------ - - template - struct Rect; - - using Rect64 = Rect; - using RectD = Rect; - - template - 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) {} - - - 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 MidPoint() const - { - return Point((left + right) / 2, (top + bottom) / 2); - } - - Path AsPath() const - { - Path result; - result.reserve(4); - result.push_back(Point(left, top)); - result.push_back(Point(right, top)); - result.push_back(Point(right, bottom)); - result.push_back(Point(left, bottom)); - return result; - } - - bool Contains(const Point& pt) const - { - return pt.x > left && pt.x < right&& pt.y > top && pt.y < bottom; - } - - bool Contains(const Rect& 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& 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& rect) { - os << "(" - << rect.left << "," << rect.top << "," << rect.right << "," << rect.bottom - << ")"; - return os; - } - }; - - template - inline Rect ScaleRect(const Rect& rect, double scale) - { - Rect result; - - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - result.left = static_cast(std::round(rect.left * scale)); - result.top = static_cast(std::round(rect.top * scale)); - result.right = static_cast(std::round(rect.right * scale)); - result.bottom = static_cast(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, MIN_DBL, MIN_DBL); - - inline Rect64 Bounds(const Path64& path) - { - Rect64 rec = MaxInvalidRect64; - for (const Point64& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return Rect64(); - return rec; - } - - inline Rect64 Bounds(const Paths64& paths) - { - Rect64 rec = MaxInvalidRect64; - for (const Path64& path : paths) - for (const Point64& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return Rect64(); - return rec; - } - - inline RectD Bounds(const PathD& path) - { - RectD rec = MaxInvalidRectD; - for (const PointD& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return RectD(); - return rec; - } - - inline RectD Bounds(const PathsD& paths) - { - RectD rec = MaxInvalidRectD; - for (const PathD& path : paths) - for (const PointD& pt : path) - { - if (pt.x < rec.left) rec.left = pt.x; - if (pt.x > rec.right) rec.right = pt.x; - if (pt.y < rec.top) rec.top = pt.y; - if (pt.y > rec.bottom) rec.bottom = pt.y; - } - if (rec.IsEmpty()) return RectD(); - return rec; - } - - - template - std::ostream& operator << (std::ostream& outstream, const Path& path) - { - if (!path.empty()) - { - auto pt = path.cbegin(), last = path.cend() - 1; - while (pt != last) - outstream << *pt++ << ", "; - outstream << *last << std::endl; - } - return outstream; - } - - template - std::ostream& operator << (std::ostream& outstream, const Paths& paths) - { - for (auto p : paths) - outstream << p; - return outstream; - } - - - template - inline Path ScalePath(const Path& path, double scale_x, double scale_y) - { - Path result; - result.reserve(path.size()); -#ifdef USINGZ - std::transform(path.begin(), path.end(), back_inserter(result), - [scale_x, scale_y](const auto& pt) - { return Point(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(pt.x * scale_x, pt.y * scale_y); }); -#endif - return result; - } - - template - inline Path ScalePath(const Path& path, double scale) - { - return ScalePath(path, scale, scale); - } - - template - inline Paths ScalePaths(const Paths& paths, - double scale_x, double scale_y, int& error_code) - { - Paths result; - - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) - { - RectD r = Bounds(paths); - const double max_coord_d = static_cast(MAX_COORD); - const double min_coord_d = static_cast(MIN_COORD); - if ((r.left * scale_x) < min_coord_d || - (r.right * scale_x) > max_coord_d || - (r.top * scale_y) < min_coord_d || - (r.bottom * scale_y) > max_coord_d) - { - error_code |= range_error_i; -#if __cpp_exceptions - throw Clipper2Exception(range_error); -#else - // error_code = range_error_i; // compiler complains if here - return result; // empty -#endif - } - } - - result.reserve(paths.size()); - std::transform(paths.begin(), paths.end(), back_inserter(result), - [scale_x, scale_y](const auto& path) - { return ScalePath(path, scale_x, scale_y); }); - return result; - } - - template - inline Paths ScalePaths(const Paths& paths, - double scale, int& error_code) - { - return ScalePaths(paths, scale, scale, error_code); - } - - template - inline Path TransformPath(const Path& path) - { - Path result; - result.reserve(path.size()); - std::transform(path.cbegin(), path.cend(), std::back_inserter(result), - [](const Point& pt) {return Point(pt); }); - return result; - } - - template - inline Paths TransformPaths(const Paths& paths) - { - Paths result; - std::transform(paths.cbegin(), paths.cend(), std::back_inserter(result), - [](const Path& path) {return TransformPath(path); }); - return result; - } - - inline PathD Path64ToPathD(const Path64& path) - { - return TransformPath(path); - } - - inline PathsD Paths64ToPathsD(const Paths64& paths) - { - return TransformPaths(paths); - } - - inline Path64 PathDToPath64(const PathD& path) - { - return TransformPath(path); - } - - inline Paths64 PathsDToPaths64(const PathsD& paths) - { - return TransformPaths(paths); - } - - template - inline double Sqr(T val) - { - return static_cast(val) * static_cast(val); - } - - template - inline bool NearEqual(const Point& p1, - const Point& p2, double max_dist_sqrd) - { - return Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y) < max_dist_sqrd; - } - - template - inline Path StripNearEqual(const Path& path, - double max_dist_sqrd, bool is_closed_path) - { - if (path.size() == 0) return Path(); - Path result; - result.reserve(path.size()); - typename Path::const_iterator path_iter = path.cbegin(); - Point 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 - inline Paths StripNearEqual(const Paths& paths, - double max_dist_sqrd, bool is_closed_path) - { - Paths result; - result.reserve(paths.size()); - for (typename Paths::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 - inline Path StripDuplicates(const Path& path, bool is_closed_path) - { - if (path.size() == 0) return Path(); - Path result; - result.reserve(path.size()); - typename Path::const_iterator path_iter = path.cbegin(); - Point 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 - inline Paths StripDuplicates(const Paths& paths, bool is_closed_path) - { - Paths result; - result.reserve(paths.size()); - for (typename Paths::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 -#if __cpp_exceptions - throw Clipper2Exception(precision_error); -#else - precision = precision > 8 ? 8 : -8; -#endif - } - - inline void CheckPrecision(int& precision) - { - int error_code = 0; - CheckPrecision(precision, error_code); - } - - template - inline double CrossProduct(const Point& pt1, const Point& pt2, const Point& pt3) { - return (static_cast(pt2.x - pt1.x) * static_cast(pt3.y - - pt2.y) - static_cast(pt2.y - pt1.y) * static_cast(pt3.x - pt2.x)); - } - - template - inline double CrossProduct(const Point& vec1, const Point& vec2) - { - return static_cast(vec1.y * vec2.x) - static_cast(vec2.y * vec1.x); - } - - template - inline double DotProduct(const Point& pt1, const Point& pt2, const Point& pt3) { - return (static_cast(pt2.x - pt1.x) * static_cast(pt3.x - pt2.x) + - static_cast(pt2.y - pt1.y) * static_cast(pt3.y - pt2.y)); - } - - template - inline double DotProduct(const Point& vec1, const Point& vec2) - { - return static_cast(vec1.x * vec2.x) + static_cast(vec1.y * vec2.y); - } - - template - inline double DistanceSqr(const Point pt1, const Point pt2) - { - return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y); - } - - template - inline double DistanceFromLineSqrd(const Point& pt, const Point& ln1, const Point& 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(ln1.y - ln2.y); - double B = static_cast(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 - inline double Area(const Path& path) - { - size_t cnt = path.size(); - if (cnt < 3) return 0.0; - double a = 0.0; - typename Path::const_iterator it1, it2 = path.cend() - 1, stop = it2; - if (!(cnt & 1)) ++stop; - for (it1 = path.cbegin(); it1 != stop;) - { - a += static_cast(it2->y + it1->y) * (it2->x - it1->x); - it2 = it1 + 1; - a += static_cast(it1->y + it2->y) * (it1->x - it2->x); - it1 += 2; - } - if (cnt & 1) - a += static_cast(it2->y + it1->y) * (it2->x - it1->x); - return a * 0.5; - } - - template - inline double Area(const Paths& paths) - { - double a = 0.0; - for (typename Paths::const_iterator paths_iter = paths.cbegin(); - paths_iter != paths.cend(); ++paths_iter) - { - a += Area(*paths_iter); - } - return a; - } - - template - inline bool IsPositive(const Path& 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(poly) >= 0; - } - - static const double max_coord = static_cast(MAX_COORD); - static const double min_coord = static_cast(MIN_COORD); - - inline int64_t CheckCastInt64(double val) - { - if ((val >= max_coord) || (val <= min_coord)) return INVALID; - else return static_cast(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(ln1b.x - ln1a.x); - double dy1 = static_cast(ln1b.y - ln1a.y); - double dx2 = static_cast(ln2b.x - ln2a.x); - double dy2 = static_cast(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(seg2.x - seg1.x); - double dy = static_cast(seg2.y - seg1.y); - double q = - (static_cast(offPt.x - seg1.x) * dx + - static_cast(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(nearbyint(q * dx)), - seg1.y + static_cast(nearbyint(q * dy))); - } - - enum class PointInPolygonResult { IsOn, IsInside, IsOutside }; - - template - inline PointInPolygonResult PointInPolygon(const Point& pt, const Path& polygon) - { - if (polygon.size() < 3) - return PointInPolygonResult::IsOutside; - - int val = 0; - typename Path::const_iterator start = polygon.cbegin(), cit = start; - typename Path::const_iterator cend = polygon.cend(), pit = cend - 1; - - while (pit->y == pt.y) - { - if (pit == start) return PointInPolygonResult::IsOutside; - --pit; - } - bool is_above = pit->y < pt.y; - - while (cit != cend) - { - if (is_above) - { - while (cit != cend && cit->y < pt.y) ++cit; - if (cit == cend) break; - } - else - { - while (cit != cend && cit->y > pt.y) ++cit; - if (cit == cend) break; - } - - if (cit == start) pit = cend - 1; - else pit = cit - 1; - - if (cit->y == pt.y) - { - if (cit->x == pt.x || (cit->y == pit->y && - ((pt.x < pit->x) != (pt.x < cit->x)))) - return PointInPolygonResult::IsOn; - ++cit; - continue; - } - - if (pt.x < cit->x && pt.x < pit->x) - { - // we're only interested in edges crossing on the left - } - else if (pt.x > pit->x && pt.x > cit->x) - val = 1 - val; // toggle val - else - { - double d = CrossProduct(*pit, *cit, pt); - if (d == 0) return PointInPolygonResult::IsOn; - if ((d < 0) == is_above) val = 1 - val; - } - is_above = !is_above; - ++cit; - } - return (val == 0) ? - PointInPolygonResult::IsOutside : - PointInPolygonResult::IsInside; - } - -} // namespace - -#endif // CLIPPER_CORE_H diff --git a/modules/clipper2/lib/include/clipper2/clipper.engine.h b/modules/clipper2/lib/include/clipper2/clipper.engine.h deleted file mode 100644 index 18110578c..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.engine.h +++ /dev/null @@ -1,610 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 28 January 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.1.0"; - -#include -#include -#include -#include -#include -#include -#include - -#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 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 ZCallback64; - - typedef std::function ZCallbackD; -#endif - - typedef std::vector HorzSegmentList; - typedef std::unique_ptr LocalMinima_ptr; - typedef std::vector LocalMinimaList; - typedef std::vector 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_lists_; - std::priority_queue scanline_list_; - IntersectNodeList intersect_nodes_; - HorzSegmentList horz_seg_list_; - std::vector 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> PolyPath64List; - typedef typename std::vector> 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(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(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(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = ScalePath(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(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::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(subjects, scale_, error_code_), PathType::Subject, false); - } - - void AddOpenSubject(const PathsD& open_subjects) - { - AddPaths(ScalePaths(open_subjects, scale_, error_code_), PathType::Subject, true); - } - - void AddClip(const PathsD& clips) - { - AddPaths(ScalePaths(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/modules/clipper2/lib/include/clipper2/clipper.export.h b/modules/clipper2/lib/include/clipper2/clipper.export.h deleted file mode 100644 index 3698408a6..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.export.h +++ /dev/null @@ -1,830 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 11 December 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* 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 -#include - -#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 -struct CRect { - T left; - T top; - T right; - T bottom; -}; - -typedef CRect CRect64; -typedef CRect CRectD; - -template -inline bool CRectIsEmpty(const CRect& rect) -{ - return (rect.right <= rect.left) || (rect.bottom <= rect.top); -} - -template -inline Rect CRectToRect(const CRect& rect) -{ - Rect 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); - -// RectClip & RectClipLines: -EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, - const CPaths64 paths); -EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, - const CPathsD paths, int precision = 2); -EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, - const CPaths64 paths); -EXTERN_DLL_EXPORT CPathsD RectClipLinesD(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(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(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(ClipType::Xor)) return -4; - if (fillrule > static_cast(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(ClipType::Xor)) return -4; - if (fillrule > static_cast(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(ClipType::Xor)) return -4; - if (fillrule > static_cast(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(ClipType::Xor)) return -4; - if (fillrule > static_cast(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); - 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); - return CreateCPathsD(result, 1/scale); -} - -EXTERN_DLL_EXPORT CPaths64 RectClip64(const CRect64& rect, - const CPaths64 paths) -{ - if (CRectIsEmpty(rect) || !paths) return nullptr; - Rect64 r64 = CRectToRect(rect); - class RectClip rc(r64); - Paths64 pp = ConvertCPaths64(paths); - Paths64 result; - result.reserve(pp.size()); - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r64.Intersects(pathRec)) continue; - - if (r64.Contains(pathRec)) - result.push_back(p); - else - { - Path64 p2 = rc.Execute(p); - if (!p2.empty()) result.push_back(std::move(p2)); - } - } - return CreateCPaths64(result); -} - -EXTERN_DLL_EXPORT CPathsD RectClipD(const CRectD& rect, - const CPathsD paths, int precision) -{ - if (CRectIsEmpty(rect) || !paths) return nullptr; - if (precision < -8 || precision > 8) return nullptr; - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(CRectToRect(rect), scale); - Paths64 pp = ConvertCPathsD(paths, scale); - class RectClip rc(r); - Paths64 result; - result.reserve(pp.size()); - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r.Intersects(pathRec)) continue; - - if (r.Contains(pathRec)) - result.push_back(p); - else - { - Path64 p2 = rc.Execute(p); - if (!p2.empty()) result.push_back(std::move(p2)); - } - } - return CreateCPathsD(result, 1/scale); -} - -EXTERN_DLL_EXPORT CPaths64 RectClipLines64(const CRect64& rect, - const CPaths64 paths) -{ - if (CRectIsEmpty(rect) || !paths) return nullptr; - Rect64 r = CRectToRect(rect); - class RectClipLines rcl (r); - Paths64 pp = ConvertCPaths64(paths); - Paths64 result; - result.reserve(pp.size()); - - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r.Intersects(pathRec)) continue; - - if (r.Contains(pathRec)) - result.push_back(p); - else - { - Paths64 pp2 = rcl.Execute(p); - if (!pp2.empty()) - result.insert(result.end(), pp2.begin(), pp2.end()); - } - } - return CreateCPaths64(result); -} - -EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, - const CPathsD paths, int precision) -{ - Paths64 result; - if (CRectIsEmpty(rect) || !paths) return nullptr; - if (precision < -8 || precision > 8) return nullptr; - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(CRectToRect(rect), scale); - class RectClipLines rcl(r); - Paths64 pp = ConvertCPathsD(paths, scale); - - result.reserve(pp.size()); - for (const Path64& p : pp) - { - Rect64 pathRec = Bounds(p); - if (!r.Intersects(pathRec)) continue; - - if (r.Contains(pathRec)) - result.push_back(p); - else - { - Paths64 pp2 = rcl.Execute(p); - if (pp2.empty()) continue; - result.insert(result.end(), pp2.begin(), pp2.end()); - } - } - 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(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(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(cnt1); - result[1] = static_cast(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(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(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(*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(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 & pp) -{ - cpt->polygon = CreateCPath64(pp->Polygon()); - cpt->is_hole = is_hole; - size_t child_cnt = pp->Count(); - cpt->child_count = static_cast(child_cnt); - cpt->childs = nullptr; - if (!child_cnt) return; - cpt->childs = new CPolyPath64[child_cnt]; - CPolyPath64* child = cpt->childs; - for (const std::unique_ptr & 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(child_cnt); - if (!child_cnt) return result; - result->childs = new CPolyPath64[child_cnt]; - CPolyPath64* child = result->childs; - for (const std::unique_ptr & 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 & 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(child_cnt); - cpt->childs = nullptr; - if (!child_cnt) return; - cpt->childs = new CPolyPathD[child_cnt]; - CPolyPathD* child = cpt->childs; - for (const std::unique_ptr & 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(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 & 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/modules/clipper2/lib/include/clipper2/clipper.h b/modules/clipper2/lib/include/clipper2/clipper.h deleted file mode 100644 index b58e0e7f2..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.h +++ /dev/null @@ -1,911 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 28 January 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 -#include - -#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) - { - ClipperOffset clip_offset(miter_limit); - clip_offset.AddPaths(paths, jt, et); - return clip_offset.Execute(delta); - } - - inline PathsD InflatePaths(const PathsD& paths, double delta, - JoinType jt, EndType et, double miter_limit = 2.0, int precision = 2) - { - int error_code = 0; - CheckPrecision(precision, error_code); - if (error_code) return PathsD(); - const double scale = std::pow(10, precision); - ClipperOffset clip_offset(miter_limit); - clip_offset.AddPaths(ScalePaths(paths, scale, error_code), jt, et); - if (error_code) return PathsD(); - Paths64 tmp = clip_offset.Execute(delta * scale); - return ScalePaths(tmp, 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 Path64 RectClip(const Rect64& rect, const Path64& path) - { - if (rect.IsEmpty() || path.empty()) return Path64(); - Rect64 pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) return Path64(); - if (rect.Contains(pathRec)) return path; - class RectClip rc(rect); - return rc.Execute(path); - } - - inline Paths64 RectClip(const Rect64& rect, const Paths64& paths) - { - if (rect.IsEmpty() || paths.empty()) return Paths64(); - class RectClip rc(rect); - Paths64 result; - result.reserve(paths.size()); - - for (const Path64& p : paths) - { - Rect64 pathRec = Bounds(p); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(p); - else - { - Path64 p2 = rc.Execute(p); - if (!p2.empty()) result.push_back(std::move(p2)); - } - } - return result; - } - - inline PathD RectClip(const RectD& rect, const PathD& path, int precision = 2) - { - if (rect.IsEmpty() || path.empty() || - !rect.Contains(Bounds(path))) return PathD(); - int error_code = 0; - CheckPrecision(precision, error_code); - if (error_code) return PathD(); - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(rect, scale); - class RectClip rc(r); - Path64 p = ScalePath(path, scale, error_code); - if (error_code) return PathD(); - return ScalePath(rc.Execute(p), 1 / scale, error_code); - } - - inline PathsD RectClip(const RectD& rect, const PathsD& paths, 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(rect, scale); - class RectClip rc(r); - PathsD result; - result.reserve(paths.size()); - for (const PathD& path : paths) - { - RectD pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(path); - else - { - Path64 p = ScalePath(path, scale, error_code); - if (error_code) return PathsD(); - p = rc.Execute(p); - if (p.empty()) continue; - result.push_back(ScalePath(p, 1/scale, error_code)); - if (error_code) return PathsD(); - } - } - return result; - } - - inline Paths64 RectClipLines(const Rect64& rect, const Path64& path) - { - Paths64 result; - if (rect.IsEmpty() || path.empty()) return result; - Rect64 pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) return result; - if (rect.Contains(pathRec)) - { - result.push_back(path); - return result; - } - class RectClipLines rcl(rect); - return rcl.Execute(path); - } - - inline Paths64 RectClipLines(const Rect64& rect, const Paths64& paths) - { - Paths64 result; - if (rect.IsEmpty() || paths.empty()) return result; - class RectClipLines rcl(rect); - for (const Path64& p : paths) - { - Rect64 pathRec = Bounds(p); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(p); - else - { - Paths64 pp = rcl.Execute(p); - if (!pp.empty()) - result.insert(result.end(), pp.begin(), pp.end()); - } - } - return result; - } - - inline PathsD RectClipLines(const RectD& rect, const PathD& path, int precision = 2) - { - if (rect.IsEmpty() || path.empty() || - !rect.Contains(Bounds(path))) 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(rect, scale); - class RectClipLines rcl(r); - Path64 p = ScalePath(path, scale, error_code); - if (error_code) return PathsD(); - return ScalePaths(rcl.Execute(p), 1 / scale, error_code); - } - - inline PathsD RectClipLines(const RectD& rect, const PathsD& paths, int precision = 2) - { - PathsD result; - if (rect.IsEmpty() || paths.empty()) return result; - int error_code = 0; - CheckPrecision(precision, error_code); - if (error_code) return PathsD(); - const double scale = std::pow(10, precision); - Rect64 r = ScaleRect(rect, scale); - class RectClipLines rcl(r); - result.reserve(paths.size()); - for (const PathD& path : paths) - { - RectD pathRec = Bounds(path); - if (!rect.Intersects(pathRec)) - continue; - else if (rect.Contains(pathRec)) - result.push_back(path); - else - { - Path64 p = ScalePath(path, scale, error_code); - if (error_code) return PathsD(); - Paths64 pp = rcl.Execute(p); - if (pp.empty()) continue; - PathsD ppd = ScalePaths(pp, 1 / scale, error_code); - if (error_code) return PathsD(); - result.insert(result.end(), ppd.begin(), ppd.end()); - } - } - return result; - } - - 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; - } - - inline bool GetInt(std::string::const_iterator& iter, const - std::string::const_iterator& end_iter, int64_t& val) - { - val = 0; - bool is_neg = *iter == '-'; - if (is_neg) ++iter; - std::string::const_iterator start_iter = iter; - while (iter != end_iter && - ((*iter >= '0') && (*iter <= '9'))) - { - val = val * 10 + (static_cast(*iter++) - '0'); - } - if (is_neg) val = -val; - return (iter != start_iter); - } - - inline bool GetFloat(std::string::const_iterator& iter, const - std::string::const_iterator& end_iter, double& val) - { - val = 0; - bool is_neg = *iter == '-'; - if (is_neg) ++iter; - int dec_pos = 1; - const std::string::const_iterator start_iter = iter; - while (iter != end_iter && (*iter == '.' || - ((*iter >= '0') && (*iter <= '9')))) - { - if (*iter == '.') - { - if (dec_pos != 1) break; - dec_pos = 0; - ++iter; - continue; - } - if (dec_pos != 1) --dec_pos; - val = val * 10 + ((int64_t)(*iter++) - '0'); - } - if (iter == start_iter || dec_pos == 0) return false; - if (dec_pos < 0) - val *= std::pow(10, dec_pos); - if (is_neg) - val *= -1; - return true; - } - - inline void SkipWhiteSpace(std::string::const_iterator& iter, - const std::string::const_iterator& end_iter) - { - while (iter != end_iter && *iter <= ' ') ++iter; - } - - inline void SkipSpacesWithOptionalComma(std::string::const_iterator& iter, - const std::string::const_iterator& end_iter) - { - bool comma_seen = false; - while (iter != end_iter) - { - if (*iter == ' ') ++iter; - else if (*iter == ',') - { - if (comma_seen) return; // don't skip 2 commas! - comma_seen = true; - ++iter; - } - else return; - } - } - - inline bool has_one_match(const char c, char* chrs) - { - while (*chrs > 0 && c != *chrs) ++chrs; - if (!*chrs) return false; - *chrs = ' '; // only match once per char - return true; - } - - - inline void SkipUserDefinedChars(std::string::const_iterator& iter, - const std::string::const_iterator& end_iter, const std::string& skip_chars) - { - const size_t MAX_CHARS = 16; - char buff[MAX_CHARS] = {0}; - std::copy(skip_chars.cbegin(), skip_chars.cend(), &buff[0]); - while (iter != end_iter && - (*iter <= ' ' || has_one_match(*iter, buff))) ++iter; - return; - } - - 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; - } - - inline Path64 MakePath(const std::string& s) - { - const std::string skip_chars = " ,(){}[]"; - Path64 result; - std::string::const_iterator s_iter = s.cbegin(); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - while (s_iter != s.cend()) - { - int64_t y = 0, x = 0; - if (!details::GetInt(s_iter, s.cend(), x)) break; - details::SkipSpacesWithOptionalComma(s_iter, s.cend()); - if (!details::GetInt(s_iter, s.cend(), y)) break; - result.push_back(Point64(x, y)); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - } - return result; - } - - inline PathD MakePathD(const std::string& s) - { - const std::string skip_chars = " ,(){}[]"; - PathD result; - std::string::const_iterator s_iter = s.cbegin(); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - while (s_iter != s.cend()) - { - double y = 0, x = 0; - if (!details::GetFloat(s_iter, s.cend(), x)) break; - details::SkipSpacesWithOptionalComma(s_iter, s.cend()); - if (!details::GetFloat(s_iter, s.cend(), y)) break; - result.push_back(PointD(x, y)); - details::SkipUserDefinedChars(s_iter, s.cend(), skip_chars); - } - 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(path, scale, error_code); - if (error_code) return PathD(); - p = TrimCollinear(p, is_open_path); - return ScalePath(p, 1/scale, error_code); - } - - template - inline double Distance(const Point pt1, const Point pt2) - { - return std::sqrt(DistanceSqr(pt1, pt2)); - } - - template - inline double Length(const Path& 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 - inline bool NearCollinear(const Point& pt1, const Point& pt2, const Point& 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 - inline Path Ellipse(const Rect& rect, int steps = 0) - { - return Ellipse(rect.MidPoint(), - static_cast(rect.Width()) *0.5, - static_cast(rect.Height()) * 0.5, steps); - } - - template - inline Path Ellipse(const Point& center, - double radiusX, double radiusY = 0, int steps = 0) - { - if (radiusX <= 0) return Path(); - if (radiusY <= 0) radiusY = radiusX; - if (steps <= 2) - steps = static_cast(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 result; - result.reserve(steps); - result.push_back(Point(center.x + radiusX, static_cast(center.y))); - for (int i = 1; i < steps; ++i) - { - result.push_back(Point(center.x + radiusX * dx, center.y + radiusY * dy)); - double x = dx * co - dy * si; - dy = dy * co + dx * si; - dx = x; - } - return result; - } - - template - inline double PerpendicDistFromLineSqrd(const Point& pt, - const Point& line1, const Point& line2) - { - double a = static_cast(pt.x - line1.x); - double b = static_cast(pt.y - line1.y); - double c = static_cast(line2.x - line1.x); - double d = static_cast(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& 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& 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 - inline Path SimplifyPath(const Path 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(path); - - std::vector flags(len); - std::vector 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 result; - result.reserve(len); - for (typename Path::size_type i = 0; i < len; ++i) - if (!flags[i]) result.push_back(path[i]); - return result; - } - - template - inline Paths SimplifyPaths(const Paths paths, - double epsilon, bool isOpenPath = false) - { - Paths result; - result.reserve(paths.size()); - for (const auto& path : paths) - result.push_back(SimplifyPath(path, epsilon, isOpenPath)); - return result; - } - - template - inline void RDP(const Path path, std::size_t begin, - std::size_t end, double epsSqrd, std::vector& flags) - { - typename Path::size_type idx = 0; - double max_d = 0; - while (end > begin && path[begin] == path[end]) flags[end--] = false; - for (typename Path::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 - inline Path RamerDouglasPeucker(const Path& path, double epsilon) - { - const typename Path::size_type len = path.size(); - if (len < 5) return Path(path); - std::vector flags(len); - flags[0] = true; - flags[len - 1] = true; - RDP(path, 0, len - 1, Sqr(epsilon), flags); - Path result; - result.reserve(len); - for (typename Path::size_type i = 0; i < len; ++i) - if (flags[i]) - result.push_back(path[i]); - return result; - } - - template - inline Paths RamerDouglasPeucker(const Paths& paths, double epsilon) - { - Paths result; - result.reserve(paths.size()); - std::transform(paths.begin(), paths.end(), back_inserter(result), - [epsilon](const auto& path) - { return RamerDouglasPeucker(path, epsilon); }); - return result; - } - -} // end Clipper2Lib namespace - -#endif // CLIPPER_H diff --git a/modules/clipper2/lib/include/clipper2/clipper.minkowski.h b/modules/clipper2/lib/include/clipper2/clipper.minkowski.h deleted file mode 100644 index 71c221bb5..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.minkowski.h +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* -* 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 -#include -#include -#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(pattern, scale, error_code); - Path64 path64 = ScalePath(path, scale, error_code); - Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, true, isClosed), FillRule::NonZero); - return ScalePaths(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(pattern, scale, error_code); - Path64 path64 = ScalePath(path, scale, error_code); - Paths64 tmp = detail::Union(detail::Minkowski(pat64, path64, false, isClosed), FillRule::NonZero); - return ScalePaths(tmp, 1 / scale, error_code); - } - -} // Clipper2Lib namespace - -#endif // CLIPPER_MINKOWSKI_H diff --git a/modules/clipper2/lib/include/clipper2/clipper.offset.h b/modules/clipper2/lib/include/clipper2/clipper.offset.h deleted file mode 100644 index a89232683..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.offset.h +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 27 January 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" - -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 group_delta_ = 0.0; - double abs_group_delta_ = 0.0; - double temp_lim_ = 0.0; - double steps_per_rad_ = 0.0; - PathD norms; - Paths64 solution; - std::vector groups_; - JoinType join_type_ = JoinType::Square; - - double miter_limit_ = 0.0; - double arc_tolerance_ = 0.0; - bool preserve_collinear_ = false; - bool reverse_solution_ = false; - - 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, EndType endType); - void OffsetPoint(Group& group, Path64& path, - size_t j, size_t& k, bool reversing = false); - void DoGroupOffset(Group &group, 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 AddPath(const PathD &p, JoinType jt_, EndType et_); - void AddPaths(const PathsD &p, JoinType jt_, EndType et_); - void Clear() { groups_.clear(); norms.clear(); }; - - Paths64 Execute(double delta); - - 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;} -}; - -} -#endif /* CLIPPER_OFFSET_H_ */ diff --git a/modules/clipper2/lib/include/clipper2/clipper.rectclip.h b/modules/clipper2/lib/include/clipper2/clipper.rectclip.h deleted file mode 100644 index 1e6354859..000000000 --- a/modules/clipper2/lib/include/clipper2/clipper.rectclip.h +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 26 October 2022 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#ifndef CLIPPER_RECTCLIP_H -#define CLIPPER_RECTCLIP_H - -#include -#include -#include "clipper.h" -#include "clipper.core.h" - -namespace Clipper2Lib -{ - - enum class Location { Left, Top, Right, Bottom, Inside }; - - class RectClip { - protected: - const Rect64 rect_; - const Point64 mp_; - const Path64 rectPath_; - Path64 result_; - std::vector start_locs_; - - void GetNextLocation(const Path64& path, - Location& loc, int& i, int highI); - void AddCorner(Location prev, Location curr); - void AddCorner(Location& loc, bool isClockwise); - public: - explicit RectClip(const Rect64& rect) : - rect_(rect), - mp_(rect.MidPoint()), - rectPath_(rect.AsPath()) {} - Path64 Execute(const Path64& path); - }; - - class RectClipLines : public RectClip { - public: - explicit RectClipLines(const Rect64& rect) : RectClip(rect) {}; - Paths64 Execute(const Path64& path); - }; - -} // Clipper2Lib namespace -#endif // CLIPPER_RECTCLIP_H diff --git a/modules/clipper2/lib/src/clipper.engine.cpp b/modules/clipper2/lib/src/clipper.engine.cpp deleted file mode 100644 index a635ac60f..000000000 --- a/modules/clipper2/lib/src/clipper.engine.cpp +++ /dev/null @@ -1,2996 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 27 January 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 * -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include "clipper2/clipper.engine.h" - -// https://github.com/AngusJohnson/Clipper2/discussions/334 -// #discussioncomment-4248602 -#if defined(_MSC_VER) && ( defined(_M_AMD64) || defined(_M_X64) ) -#include -#include -#define fmin(a,b) _mm_cvtsd_f64(_mm_min_sd(_mm_set_sd(a),_mm_set_sd(b))) -#define fmax(a,b) _mm_cvtsd_f64(_mm_max_sd(_mm_set_sd(a),_mm_set_sd(b))) -#define nearbyint(a) _mm_cvtsd_si64(_mm_set_sd(a)) /* Note: expression type is (int64_t) */ -#endif - -namespace Clipper2Lib { - - static const Rect64 invalid_rect = Rect64( - std::numeric_limits::max(), - std::numeric_limits::max(), - -std::numeric_limits::max(), - -std::numeric_limits::max() - ); - - // Every closed path (or polygon) is made up of a series of vertices forming - // edges that alternate between going up (relative to the Y-axis) and going - // down. Edges consecutively going up or consecutively going down are called - // 'bounds' (ie sides if they're simple polygons). 'Local Minima' refer to - // vertices where descending bounds become ascending ones. - - struct Scanline { - int64_t y = 0; - Scanline* next = nullptr; - - explicit Scanline(int64_t y_) : y(y_) {} - }; - - struct HorzSegSorter { - inline bool operator()(const HorzSegment& hs1, const HorzSegment& hs2) - { - if (!hs1.right_op || !hs2.right_op) return (hs1.right_op); - return hs2.left_op->pt.x > hs1.left_op->pt.x; - } - }; - - struct LocMinSorter { - inline bool operator()(const LocalMinima_ptr& locMin1, - const LocalMinima_ptr& locMin2) - { - if (locMin2->vertex->pt.y != locMin1->vertex->pt.y) - return locMin2->vertex->pt.y < locMin1->vertex->pt.y; - else - return locMin2->vertex->pt.x > locMin1->vertex->pt.x; - } - }; - - inline bool IsOdd(int val) - { - return (val & 1) ? true : false; - } - - - inline bool IsHotEdge(const Active& e) - { - return (e.outrec); - } - - - inline bool IsOpen(const Active& e) - { - return (e.local_min->is_open); - } - - - inline bool IsOpenEnd(const Vertex& v) - { - return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) != - VertexFlags::None; - } - - - inline bool IsOpenEnd(const Active& ae) - { - return IsOpenEnd(*ae.vertex_top); - } - - - inline Active* GetPrevHotEdge(const Active& e) - { - Active* prev = e.prev_in_ael; - while (prev && (IsOpen(*prev) || !IsHotEdge(*prev))) - prev = prev->prev_in_ael; - return prev; - } - - inline bool IsFront(const Active& e) - { - return (&e == e.outrec->front_edge); - } - - inline bool IsInvalidPath(OutPt* op) - { - return (!op || op->next == op); - } - - /******************************************************************************* - * Dx: 0(90deg) * - * | * - * +inf (180deg) <--- o ---> -inf (0deg) * - *******************************************************************************/ - - inline double GetDx(const Point64& pt1, const Point64& pt2) - { - double dy = double(pt2.y - pt1.y); - if (dy != 0) - return double(pt2.x - pt1.x) / dy; - else if (pt2.x > pt1.x) - return -std::numeric_limits::max(); - else - return std::numeric_limits::max(); - } - - inline int64_t TopX(const Active& ae, const int64_t currentY) - { - if ((currentY == ae.top.y) || (ae.top.x == ae.bot.x)) return ae.top.x; - else if (currentY == ae.bot.y) return ae.bot.x; - else return ae.bot.x + static_cast(nearbyint(ae.dx * (currentY - ae.bot.y))); - // nb: std::nearbyint (or std::round) substantially *improves* performance here - // as it greatly improves the likelihood of edge adjacency in ProcessIntersectList(). - } - - - inline bool IsHorizontal(const Active& e) - { - return (e.top.y == e.bot.y); - } - - - inline bool IsHeadingRightHorz(const Active& e) - { - return e.dx == -std::numeric_limits::max(); - } - - - inline bool IsHeadingLeftHorz(const Active& e) - { - return e.dx == std::numeric_limits::max(); - } - - - inline void SwapActives(Active*& e1, Active*& e2) - { - Active* e = e1; - e1 = e2; - e2 = e; - } - - inline PathType GetPolyType(const Active& e) - { - return e.local_min->polytype; - } - - inline bool IsSamePolyType(const Active& e1, const Active& e2) - { - return e1.local_min->polytype == e2.local_min->polytype; - } - - inline void SetDx(Active& e) - { - e.dx = GetDx(e.bot, e.top); - } - - inline Vertex* NextVertex(const Active& e) - { - if (e.wind_dx > 0) - return e.vertex_top->next; - else - return e.vertex_top->prev; - } - - //PrevPrevVertex: useful to get the (inverted Y-axis) top of the - //alternate edge (ie left or right bound) during edge insertion. - inline Vertex* PrevPrevVertex(const Active& ae) - { - if (ae.wind_dx > 0) - return ae.vertex_top->prev->prev; - else - return ae.vertex_top->next->next; - } - - - inline Active* ExtractFromSEL(Active* ae) - { - Active* res = ae->next_in_sel; - if (res) - res->prev_in_sel = ae->prev_in_sel; - ae->prev_in_sel->next_in_sel = res; - return res; - } - - - inline void Insert1Before2InSEL(Active* ae1, Active* ae2) - { - ae1->prev_in_sel = ae2->prev_in_sel; - if (ae1->prev_in_sel) - ae1->prev_in_sel->next_in_sel = ae1; - ae1->next_in_sel = ae2; - ae2->prev_in_sel = ae1; - } - - inline bool IsMaxima(const Vertex& v) - { - return ((v.flags & VertexFlags::LocalMax) != VertexFlags::None); - } - - - inline bool IsMaxima(const Active& e) - { - return IsMaxima(*e.vertex_top); - } - - inline Vertex* GetCurrYMaximaVertex_Open(const Active& e) - { - Vertex* result = e.vertex_top; - if (e.wind_dx > 0) - while ((result->next->pt.y == result->pt.y) && - ((result->flags & (VertexFlags::OpenEnd | - VertexFlags::LocalMax)) == VertexFlags::None)) - result = result->next; - else - while (result->prev->pt.y == result->pt.y && - ((result->flags & (VertexFlags::OpenEnd | - VertexFlags::LocalMax)) == VertexFlags::None)) - result = result->prev; - if (!IsMaxima(*result)) result = nullptr; // not a maxima - return result; - } - - inline Vertex* GetCurrYMaximaVertex(const Active& e) - { - Vertex* result = e.vertex_top; - if (e.wind_dx > 0) - while (result->next->pt.y == result->pt.y) result = result->next; - else - while (result->prev->pt.y == result->pt.y) result = result->prev; - if (!IsMaxima(*result)) result = nullptr; // not a maxima - return result; - } - - Active* GetMaximaPair(const Active& e) - { - Active* e2; - e2 = e.next_in_ael; - while (e2) - { - if (e2->vertex_top == e.vertex_top) return e2; // Found! - e2 = e2->next_in_ael; - } - return nullptr; - } - - inline int PointCount(OutPt* op) - { - OutPt* op2 = op; - int cnt = 0; - do - { - op2 = op2->next; - ++cnt; - } while (op2 != op); - return cnt; - } - - inline OutPt* DuplicateOp(OutPt* op, bool insert_after) - { - OutPt* result = new OutPt(op->pt, op->outrec); - if (insert_after) - { - result->next = op->next; - result->next->prev = result; - result->prev = op; - op->next = result; - } - else - { - result->prev = op->prev; - result->prev->next = result; - result->next = op; - op->prev = result; - } - return result; - } - - inline OutPt* DisposeOutPt(OutPt* op) - { - OutPt* result = op->next; - op->prev->next = op->next; - op->next->prev = op->prev; - delete op; - return result; - } - - - inline void DisposeOutPts(OutRec* outrec) - { - OutPt* op = outrec->pts; - op->prev->next = nullptr; - while (op) - { - OutPt* tmp = op; - op = op->next; - delete tmp; - }; - outrec->pts = nullptr; - } - - - bool IntersectListSort(const IntersectNode& a, const IntersectNode& b) - { - //note different inequality tests ... - return (a.pt.y == b.pt.y) ? (a.pt.x < b.pt.x) : (a.pt.y > b.pt.y); - } - - - inline void SetSides(OutRec& outrec, Active& start_edge, Active& end_edge) - { - outrec.front_edge = &start_edge; - outrec.back_edge = &end_edge; - } - - - void SwapOutrecs(Active& e1, Active& e2) - { - OutRec* or1 = e1.outrec; - OutRec* or2 = e2.outrec; - if (or1 == or2) - { - Active* e = or1->front_edge; - or1->front_edge = or1->back_edge; - or1->back_edge = e; - return; - } - if (or1) - { - if (&e1 == or1->front_edge) - or1->front_edge = &e2; - else - or1->back_edge = &e2; - } - if (or2) - { - if (&e2 == or2->front_edge) - or2->front_edge = &e1; - else - or2->back_edge = &e1; - } - e1.outrec = or2; - e2.outrec = or1; - } - - - double Area(OutPt* op) - { - //https://en.wikipedia.org/wiki/Shoelace_formula - double result = 0.0; - OutPt* op2 = op; - do - { - result += static_cast(op2->prev->pt.y + op2->pt.y) * - static_cast(op2->prev->pt.x - op2->pt.x); - op2 = op2->next; - } while (op2 != op); - return result * 0.5; - } - - inline double AreaTriangle(const Point64& pt1, - const Point64& pt2, const Point64& pt3) - { - return (static_cast(pt3.y + pt1.y) * static_cast(pt3.x - pt1.x) + - static_cast(pt1.y + pt2.y) * static_cast(pt1.x - pt2.x) + - static_cast(pt2.y + pt3.y) * static_cast(pt2.x - pt3.x)); - } - - void ReverseOutPts(OutPt* op) - { - if (!op) return; - - OutPt* op1 = op; - OutPt* op2; - - do - { - op2 = op1->next; - op1->next = op1->prev; - op1->prev = op2; - op1 = op2; - } while (op1 != op); - } - - inline void SwapSides(OutRec& outrec) - { - Active* e2 = outrec.front_edge; - outrec.front_edge = outrec.back_edge; - outrec.back_edge = e2; - outrec.pts = outrec.pts->next; - } - - inline OutRec* GetRealOutRec(OutRec* outrec) - { - while (outrec && !outrec->pts) outrec = outrec->owner; - return outrec; - } - - - inline void UncoupleOutRec(Active ae) - { - OutRec* outrec = ae.outrec; - if (!outrec) return; - outrec->front_edge->outrec = nullptr; - outrec->back_edge->outrec = nullptr; - outrec->front_edge = nullptr; - outrec->back_edge = nullptr; - } - - - inline bool PtsReallyClose(const Point64& pt1, const Point64& pt2) - { - return (std::llabs(pt1.x - pt2.x) < 2) && (std::llabs(pt1.y - pt2.y) < 2); - } - - inline bool IsVerySmallTriangle(const OutPt& op) - { - return op.next->next == op.prev && - (PtsReallyClose(op.prev->pt, op.next->pt) || - PtsReallyClose(op.pt, op.next->pt) || - PtsReallyClose(op.pt, op.prev->pt)); - } - - inline bool IsValidClosedPath(const OutPt* op) - { - return op && (op->next != op) && (op->next != op->prev) && - !IsVerySmallTriangle(*op); - } - - inline bool OutrecIsAscending(const Active* hotEdge) - { - return (hotEdge == hotEdge->outrec->front_edge); - } - - inline void SwapFrontBackSides(OutRec& outrec) - { - Active* tmp = outrec.front_edge; - outrec.front_edge = outrec.back_edge; - outrec.back_edge = tmp; - outrec.pts = outrec.pts->next; - } - - inline bool EdgesAdjacentInAEL(const IntersectNode& inode) - { - return (inode.edge1->next_in_ael == inode.edge2) || (inode.edge1->prev_in_ael == inode.edge2); - } - - inline bool IsJoined(const Active& e) - { - return e.join_with != JoinWith::None; - } - - inline void SetOwner(OutRec* outrec, OutRec* new_owner) - { - //precondition1: new_owner is never null - while (new_owner->owner && !new_owner->owner->pts) - new_owner->owner = new_owner->owner->owner; - OutRec* tmp = new_owner; - while (tmp && tmp != outrec) tmp = tmp->owner; - if (tmp) new_owner->owner = outrec->owner; - outrec->owner = new_owner; - } - - //------------------------------------------------------------------------------ - // ClipperBase methods ... - //------------------------------------------------------------------------------ - - ClipperBase::~ClipperBase() - { - Clear(); - } - - void ClipperBase::DeleteEdges(Active*& e) - { - while (e) - { - Active* e2 = e; - e = e->next_in_ael; - delete e2; - } - } - - void ClipperBase::CleanUp() - { - DeleteEdges(actives_); - scanline_list_ = std::priority_queue(); - intersect_nodes_.clear(); - DisposeAllOutRecs(); - horz_seg_list_.clear(); - horz_join_list_.clear(); - } - - - void ClipperBase::Clear() - { - CleanUp(); - DisposeVerticesAndLocalMinima(); - current_locmin_iter_ = minima_list_.begin(); - minima_list_sorted_ = false; - has_open_paths_ = false; - } - - - void ClipperBase::Reset() - { - if (!minima_list_sorted_) - { - std::sort(minima_list_.begin(), minima_list_.end(), LocMinSorter()); - minima_list_sorted_ = true; - } - LocalMinimaList::const_reverse_iterator i; - for (i = minima_list_.rbegin(); i != minima_list_.rend(); ++i) - InsertScanline((*i)->vertex->pt.y); - - current_locmin_iter_ = minima_list_.begin(); - actives_ = nullptr; - sel_ = nullptr; - succeeded_ = true; - } - - -#ifdef USINGZ - void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip) - { - if (!zCallback_) return; - // prioritize subject over clip vertices by passing - // subject vertices before clip vertices in the callback - if (GetPolyType(e1) == PathType::Subject) - { - if (ip == e1.bot) ip.z = e1.bot.z; - else if (ip == e1.top) ip.z = e1.top.z; - else if (ip == e2.bot) ip.z = e2.bot.z; - else if (ip == e2.top) ip.z = e2.top.z; - else ip.z = DefaultZ; - zCallback_(e1.bot, e1.top, e2.bot, e2.top, ip); - } - else - { - if (ip == e2.bot) ip.z = e2.bot.z; - else if (ip == e2.top) ip.z = e2.top.z; - else if (ip == e1.bot) ip.z = e1.bot.z; - else if (ip == e1.top) ip.z = e1.top.z; - else ip.z = DefaultZ; - zCallback_(e2.bot, e2.top, e1.bot, e1.top, ip); - } - } -#endif - - void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open) - { - Paths64 tmp; - tmp.push_back(path); - AddPaths(tmp, polytype, is_open); - } - - - void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open) - { - if (is_open) has_open_paths_ = true; - minima_list_sorted_ = false; - - const auto total_vertex_count = - std::accumulate(paths.begin(), paths.end(), 0, - [](const auto& a, const Path64& path) - {return a + static_cast(path.size());}); - if (total_vertex_count == 0) return; - - Vertex* vertices = new Vertex[total_vertex_count], * v = vertices; - for (const Path64& path : paths) - { - //for each path create a circular double linked list of vertices - Vertex* v0 = v, * curr_v = v, * prev_v = nullptr; - - v->prev = nullptr; - int cnt = 0; - for (const Point64& pt : path) - { - if (prev_v) - { - if (prev_v->pt == pt) continue; // ie skips duplicates - prev_v->next = curr_v; - } - curr_v->prev = prev_v; - curr_v->pt = pt; - curr_v->flags = VertexFlags::None; - prev_v = curr_v++; - cnt++; - } - if (!prev_v || !prev_v->prev) continue; - if (!is_open && prev_v->pt == v0->pt) - prev_v = prev_v->prev; - prev_v->next = v0; - v0->prev = prev_v; - v = curr_v; // ie get ready for next path - if (cnt < 2 || (cnt == 2 && !is_open)) continue; - - //now find and assign local minima - bool going_up, going_up0; - if (is_open) - { - curr_v = v0->next; - while (curr_v != v0 && curr_v->pt.y == v0->pt.y) - curr_v = curr_v->next; - going_up = curr_v->pt.y <= v0->pt.y; - if (going_up) - { - v0->flags = VertexFlags::OpenStart; - AddLocMin(*v0, polytype, true); - } - else - v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax; - } - else // closed path - { - prev_v = v0->prev; - while (prev_v != v0 && prev_v->pt.y == v0->pt.y) - prev_v = prev_v->prev; - if (prev_v == v0) - continue; // only open paths can be completely flat - going_up = prev_v->pt.y > v0->pt.y; - } - - going_up0 = going_up; - prev_v = v0; - curr_v = v0->next; - while (curr_v != v0) - { - if (curr_v->pt.y > prev_v->pt.y && going_up) - { - prev_v->flags = (prev_v->flags | VertexFlags::LocalMax); - going_up = false; - } - else if (curr_v->pt.y < prev_v->pt.y && !going_up) - { - going_up = true; - AddLocMin(*prev_v, polytype, is_open); - } - prev_v = curr_v; - curr_v = curr_v->next; - } - - if (is_open) - { - prev_v->flags = prev_v->flags | VertexFlags::OpenEnd; - if (going_up) - prev_v->flags = prev_v->flags | VertexFlags::LocalMax; - else - AddLocMin(*prev_v, polytype, is_open); - } - else if (going_up != going_up0) - { - if (going_up0) AddLocMin(*prev_v, polytype, false); - else prev_v->flags = prev_v->flags | VertexFlags::LocalMax; - } - } // end processing current path - - vertex_lists_.emplace_back(vertices); - } // end AddPaths - - - void ClipperBase::InsertScanline(int64_t y) - { - scanline_list_.push(y); - } - - - bool ClipperBase::PopScanline(int64_t& y) - { - if (scanline_list_.empty()) return false; - y = scanline_list_.top(); - scanline_list_.pop(); - while (!scanline_list_.empty() && y == scanline_list_.top()) - scanline_list_.pop(); // Pop duplicates. - return true; - } - - - bool ClipperBase::PopLocalMinima(int64_t y, LocalMinima*& local_minima) - { - if (current_locmin_iter_ == minima_list_.end() || (*current_locmin_iter_)->vertex->pt.y != y) return false; - local_minima = (current_locmin_iter_++)->get(); - return true; - } - - void ClipperBase::DisposeAllOutRecs() - { - for (auto outrec : outrec_list_) - { - if (outrec->pts) DisposeOutPts(outrec); - delete outrec; - } - outrec_list_.resize(0); - } - - void ClipperBase::DisposeVerticesAndLocalMinima() - { - minima_list_.clear(); - for (auto v : vertex_lists_) delete[] v; - vertex_lists_.clear(); - } - - - void ClipperBase::AddLocMin(Vertex& vert, PathType polytype, bool is_open) - { - //make sure the vertex is added only once ... - if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return; - - vert.flags = (vert.flags | VertexFlags::LocalMin); - minima_list_.push_back(std::make_unique (&vert, polytype, is_open)); - } - - bool ClipperBase::IsContributingClosed(const Active& e) const - { - switch (fillrule_) - { - case FillRule::EvenOdd: - break; - case FillRule::NonZero: - if (abs(e.wind_cnt) != 1) return false; - break; - case FillRule::Positive: - if (e.wind_cnt != 1) return false; - break; - case FillRule::Negative: - if (e.wind_cnt != -1) return false; - break; - } - - switch (cliptype_) - { - case ClipType::None: - return false; - case ClipType::Intersection: - switch (fillrule_) - { - case FillRule::Positive: - return (e.wind_cnt2 > 0); - case FillRule::Negative: - return (e.wind_cnt2 < 0); - default: - return (e.wind_cnt2 != 0); - } - break; - - case ClipType::Union: - switch (fillrule_) - { - case FillRule::Positive: - return (e.wind_cnt2 <= 0); - case FillRule::Negative: - return (e.wind_cnt2 >= 0); - default: - return (e.wind_cnt2 == 0); - } - break; - - case ClipType::Difference: - bool result; - switch (fillrule_) - { - case FillRule::Positive: - result = (e.wind_cnt2 <= 0); - break; - case FillRule::Negative: - result = (e.wind_cnt2 >= 0); - break; - default: - result = (e.wind_cnt2 == 0); - } - if (GetPolyType(e) == PathType::Subject) - return result; - else - return !result; - break; - - case ClipType::Xor: return true; break; - } - return false; // we should never get here - } - - - inline bool ClipperBase::IsContributingOpen(const Active& e) const - { - bool is_in_clip, is_in_subj; - switch (fillrule_) - { - case FillRule::Positive: - is_in_clip = e.wind_cnt2 > 0; - is_in_subj = e.wind_cnt > 0; - break; - case FillRule::Negative: - is_in_clip = e.wind_cnt2 < 0; - is_in_subj = e.wind_cnt < 0; - break; - default: - is_in_clip = e.wind_cnt2 != 0; - is_in_subj = e.wind_cnt != 0; - } - - switch (cliptype_) - { - case ClipType::Intersection: return is_in_clip; - case ClipType::Union: return (!is_in_subj && !is_in_clip); - default: return !is_in_clip; - } - } - - - void ClipperBase::SetWindCountForClosedPathEdge(Active& e) - { - //Wind counts refer to polygon regions not edges, so here an edge's WindCnt - //indicates the higher of the wind counts for the two regions touching the - //edge. (NB Adjacent regions can only ever have their wind counts differ by - //one. Also, open paths have no meaningful wind directions or counts.) - - Active* e2 = e.prev_in_ael; - //find the nearest closed path edge of the same PolyType in AEL (heading left) - PathType pt = GetPolyType(e); - while (e2 && (GetPolyType(*e2) != pt || IsOpen(*e2))) e2 = e2->prev_in_ael; - - if (!e2) - { - e.wind_cnt = e.wind_dx; - e2 = actives_; - } - else if (fillrule_ == FillRule::EvenOdd) - { - e.wind_cnt = e.wind_dx; - e.wind_cnt2 = e2->wind_cnt2; - e2 = e2->next_in_ael; - } - else - { - //NonZero, positive, or negative filling here ... - //if e's WindCnt is in the SAME direction as its WindDx, then polygon - //filling will be on the right of 'e'. - //NB neither e2.WindCnt nor e2.WindDx should ever be 0. - if (e2->wind_cnt * e2->wind_dx < 0) - { - //opposite directions so 'e' is outside 'e2' ... - if (abs(e2->wind_cnt) > 1) - { - //outside prev poly but still inside another. - if (e2->wind_dx * e.wind_dx < 0) - //reversing direction so use the same WC - e.wind_cnt = e2->wind_cnt; - else - //otherwise keep 'reducing' the WC by 1 (ie towards 0) ... - e.wind_cnt = e2->wind_cnt + e.wind_dx; - } - else - //now outside all polys of same polytype so set own WC ... - e.wind_cnt = (IsOpen(e) ? 1 : e.wind_dx); - } - else - { - //'e' must be inside 'e2' - if (e2->wind_dx * e.wind_dx < 0) - //reversing direction so use the same WC - e.wind_cnt = e2->wind_cnt; - else - //otherwise keep 'increasing' the WC by 1 (ie away from 0) ... - e.wind_cnt = e2->wind_cnt + e.wind_dx; - } - e.wind_cnt2 = e2->wind_cnt2; - e2 = e2->next_in_ael; // ie get ready to calc WindCnt2 - } - - //update wind_cnt2 ... - if (fillrule_ == FillRule::EvenOdd) - while (e2 != &e) - { - if (GetPolyType(*e2) != pt && !IsOpen(*e2)) - e.wind_cnt2 = (e.wind_cnt2 == 0 ? 1 : 0); - e2 = e2->next_in_ael; - } - else - while (e2 != &e) - { - if (GetPolyType(*e2) != pt && !IsOpen(*e2)) - e.wind_cnt2 += e2->wind_dx; - e2 = e2->next_in_ael; - } - } - - - void ClipperBase::SetWindCountForOpenPathEdge(Active& e) - { - Active* e2 = actives_; - if (fillrule_ == FillRule::EvenOdd) - { - int cnt1 = 0, cnt2 = 0; - while (e2 != &e) - { - if (GetPolyType(*e2) == PathType::Clip) - cnt2++; - else if (!IsOpen(*e2)) - cnt1++; - e2 = e2->next_in_ael; - } - e.wind_cnt = (IsOdd(cnt1) ? 1 : 0); - e.wind_cnt2 = (IsOdd(cnt2) ? 1 : 0); - } - else - { - while (e2 != &e) - { - if (GetPolyType(*e2) == PathType::Clip) - e.wind_cnt2 += e2->wind_dx; - else if (!IsOpen(*e2)) - e.wind_cnt += e2->wind_dx; - e2 = e2->next_in_ael; - } - } - } - - - bool IsValidAelOrder(const Active& resident, const Active& newcomer) - { - if (newcomer.curr_x != resident.curr_x) - return newcomer.curr_x > resident.curr_x; - - //get the turning direction a1.top, a2.bot, a2.top - double d = CrossProduct(resident.top, newcomer.bot, newcomer.top); - if (d != 0) return d < 0; - - //edges must be collinear to get here - //for starting open paths, place them according to - //the direction they're about to turn - if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y)) - { - return CrossProduct(newcomer.bot, - resident.top, NextVertex(resident)->pt) <= 0; - } - else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y)) - { - return CrossProduct(newcomer.bot, - newcomer.top, NextVertex(newcomer)->pt) >= 0; - } - - int64_t y = newcomer.bot.y; - bool newcomerIsLeft = newcomer.is_left_bound; - - if (resident.bot.y != y || resident.local_min->vertex->pt.y != y) - return newcomer.is_left_bound; - //resident must also have just been inserted - else if (resident.is_left_bound != newcomerIsLeft) - return newcomerIsLeft; - else if (CrossProduct(PrevPrevVertex(resident)->pt, - resident.bot, resident.top) == 0) return true; - else - //compare turning direction of the alternate bound - return (CrossProduct(PrevPrevVertex(resident)->pt, - newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft; - } - - - void ClipperBase::InsertLeftEdge(Active& e) - { - Active* e2; - if (!actives_) - { - e.prev_in_ael = nullptr; - e.next_in_ael = nullptr; - actives_ = &e; - } - else if (!IsValidAelOrder(*actives_, e)) - { - e.prev_in_ael = nullptr; - e.next_in_ael = actives_; - actives_->prev_in_ael = &e; - actives_ = &e; - } - else - { - e2 = actives_; - while (e2->next_in_ael && IsValidAelOrder(*e2->next_in_ael, e)) - e2 = e2->next_in_ael; - if (e2->join_with == JoinWith::Right) - e2 = e2->next_in_ael; - if (!e2) return; // should never happen and stops compiler warning :) - e.next_in_ael = e2->next_in_ael; - if (e2->next_in_ael) e2->next_in_ael->prev_in_ael = &e; - e.prev_in_ael = e2; - e2->next_in_ael = &e; - } - } - - - void InsertRightEdge(Active& e, Active& e2) - { - e2.next_in_ael = e.next_in_ael; - if (e.next_in_ael) e.next_in_ael->prev_in_ael = &e2; - e2.prev_in_ael = &e; - e.next_in_ael = &e2; - } - - - void ClipperBase::InsertLocalMinimaIntoAEL(int64_t bot_y) - { - LocalMinima* local_minima; - Active* left_bound, * right_bound; - //Add any local minima (if any) at BotY ... - //nb: horizontal local minima edges should contain locMin.vertex.prev - - while (PopLocalMinima(bot_y, local_minima)) - { - if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::None) - { - left_bound = nullptr; - } - else - { - left_bound = new Active(); - left_bound->bot = local_minima->vertex->pt; - left_bound->curr_x = left_bound->bot.x; - left_bound->wind_dx = -1; - left_bound->vertex_top = local_minima->vertex->prev; // ie descending - left_bound->top = left_bound->vertex_top->pt; - left_bound->local_min = local_minima; - SetDx(*left_bound); - } - - if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::None) - { - right_bound = nullptr; - } - else - { - right_bound = new Active(); - right_bound->bot = local_minima->vertex->pt; - right_bound->curr_x = right_bound->bot.x; - right_bound->wind_dx = 1; - right_bound->vertex_top = local_minima->vertex->next; // ie ascending - right_bound->top = right_bound->vertex_top->pt; - right_bound->local_min = local_minima; - SetDx(*right_bound); - } - - //Currently LeftB is just the descending bound and RightB is the ascending. - //Now if the LeftB isn't on the left of RightB then we need swap them. - if (left_bound && right_bound) - { - if (IsHorizontal(*left_bound)) - { - if (IsHeadingRightHorz(*left_bound)) SwapActives(left_bound, right_bound); - } - else if (IsHorizontal(*right_bound)) - { - if (IsHeadingLeftHorz(*right_bound)) SwapActives(left_bound, right_bound); - } - else if (left_bound->dx < right_bound->dx) - SwapActives(left_bound, right_bound); - } - else if (!left_bound) - { - left_bound = right_bound; - right_bound = nullptr; - } - - bool contributing; - left_bound->is_left_bound = true; - InsertLeftEdge(*left_bound); - - if (IsOpen(*left_bound)) - { - SetWindCountForOpenPathEdge(*left_bound); - contributing = IsContributingOpen(*left_bound); - } - else - { - SetWindCountForClosedPathEdge(*left_bound); - contributing = IsContributingClosed(*left_bound); - } - - if (right_bound) - { - right_bound->is_left_bound = false; - right_bound->wind_cnt = left_bound->wind_cnt; - right_bound->wind_cnt2 = left_bound->wind_cnt2; - InsertRightEdge(*left_bound, *right_bound); /////// - if (contributing) - { - AddLocalMinPoly(*left_bound, *right_bound, left_bound->bot, true); - if (!IsHorizontal(*left_bound)) - CheckJoinLeft(*left_bound, left_bound->bot); - } - - while (right_bound->next_in_ael && - IsValidAelOrder(*right_bound->next_in_ael, *right_bound)) - { - IntersectEdges(*right_bound, *right_bound->next_in_ael, right_bound->bot); - SwapPositionsInAEL(*right_bound, *right_bound->next_in_ael); - } - - if (IsHorizontal(*right_bound)) - PushHorz(*right_bound); - else - { - CheckJoinRight(*right_bound, right_bound->bot); - InsertScanline(right_bound->top.y); - } - } - else if (contributing) - { - StartOpenPath(*left_bound, left_bound->bot); - } - - if (IsHorizontal(*left_bound)) - PushHorz(*left_bound); - else - InsertScanline(left_bound->top.y); - } // while (PopLocalMinima()) - } - - - inline void ClipperBase::PushHorz(Active& e) - { - e.next_in_sel = (sel_ ? sel_ : nullptr); - sel_ = &e; - } - - - inline bool ClipperBase::PopHorz(Active*& e) - { - e = sel_; - if (!e) return false; - sel_ = sel_->next_in_sel; - return true; - } - - - OutPt* ClipperBase::AddLocalMinPoly(Active& e1, Active& e2, - const Point64& pt, bool is_new) - { - OutRec* outrec = NewOutRec(); - e1.outrec = outrec; - e2.outrec = outrec; - - if (IsOpen(e1)) - { - outrec->owner = nullptr; - outrec->is_open = true; - if (e1.wind_dx > 0) - SetSides(*outrec, e1, e2); - else - SetSides(*outrec, e2, e1); - } - else - { - Active* prevHotEdge = GetPrevHotEdge(e1); - //e.windDx is the winding direction of the **input** paths - //and unrelated to the winding direction of output polygons. - //Output orientation is determined by e.outrec.frontE which is - //the ascending edge (see AddLocalMinPoly). - if (prevHotEdge) - { - if (using_polytree_) - SetOwner(outrec, prevHotEdge->outrec); - if (OutrecIsAscending(prevHotEdge) == is_new) - SetSides(*outrec, e2, e1); - else - SetSides(*outrec, e1, e2); - } - else - { - outrec->owner = nullptr; - if (is_new) - SetSides(*outrec, e1, e2); - else - SetSides(*outrec, e2, e1); - } - } - - OutPt* op = new OutPt(pt, outrec); - outrec->pts = op; - return op; - } - - - OutPt* ClipperBase::AddLocalMaxPoly(Active& e1, Active& e2, const Point64& pt) - { - if (IsJoined(e1)) Split(e1, pt); - if (IsJoined(e2)) Split(e2, pt); - - if (IsFront(e1) == IsFront(e2)) - { - if (IsOpenEnd(e1)) - SwapFrontBackSides(*e1.outrec); - else if (IsOpenEnd(e2)) - SwapFrontBackSides(*e2.outrec); - else - { - succeeded_ = false; - return nullptr; - } - } - - OutPt* result = AddOutPt(e1, pt); - if (e1.outrec == e2.outrec) - { - OutRec& outrec = *e1.outrec; - outrec.pts = result; - - if (using_polytree_) - { - Active* e = GetPrevHotEdge(e1); - if (!e) - outrec.owner = nullptr; - else - SetOwner(&outrec, e->outrec); - // nb: outRec.owner here is likely NOT the real - // owner but this will be checked in DeepCheckOwner() - } - - UncoupleOutRec(e1); - result = outrec.pts; - if (outrec.owner && !outrec.owner->front_edge) - outrec.owner = GetRealOutRec(outrec.owner); - } - //and to preserve the winding orientation of outrec ... - else if (IsOpen(e1)) - { - if (e1.wind_dx < 0) - JoinOutrecPaths(e1, e2); - else - JoinOutrecPaths(e2, e1); - } - else if (e1.outrec->idx < e2.outrec->idx) - JoinOutrecPaths(e1, e2); - else - JoinOutrecPaths(e2, e1); - return result; - } - - void ClipperBase::JoinOutrecPaths(Active& e1, Active& e2) - { - //join e2 outrec path onto e1 outrec path and then delete e2 outrec path - //pointers. (NB Only very rarely do the joining ends share the same coords.) - OutPt* p1_st = e1.outrec->pts; - OutPt* p2_st = e2.outrec->pts; - OutPt* p1_end = p1_st->next; - OutPt* p2_end = p2_st->next; - if (IsFront(e1)) - { - p2_end->prev = p1_st; - p1_st->next = p2_end; - p2_st->next = p1_end; - p1_end->prev = p2_st; - e1.outrec->pts = p2_st; - e1.outrec->front_edge = e2.outrec->front_edge; - if (e1.outrec->front_edge) - e1.outrec->front_edge->outrec = e1.outrec; - } - else - { - p1_end->prev = p2_st; - p2_st->next = p1_end; - p1_st->next = p2_end; - p2_end->prev = p1_st; - e1.outrec->back_edge = e2.outrec->back_edge; - if (e1.outrec->back_edge) - e1.outrec->back_edge->outrec = e1.outrec; - } - - //after joining, the e2.OutRec must contains no vertices ... - e2.outrec->front_edge = nullptr; - e2.outrec->back_edge = nullptr; - e2.outrec->pts = nullptr; - SetOwner(e2.outrec, e1.outrec); - - if (IsOpenEnd(e1)) - { - e2.outrec->pts = e1.outrec->pts; - e1.outrec->pts = nullptr; - } - - //and e1 and e2 are maxima and are about to be dropped from the Actives list. - e1.outrec = nullptr; - e2.outrec = nullptr; - } - - OutRec* ClipperBase::NewOutRec() - { - OutRec* result = new OutRec(); - result->idx = outrec_list_.size(); - outrec_list_.push_back(result); - result->pts = nullptr; - result->owner = nullptr; - result->polypath = nullptr; - result->is_open = false; - return result; - } - - - OutPt* ClipperBase::AddOutPt(const Active& e, const Point64& pt) - { - OutPt* new_op = nullptr; - - //Outrec.OutPts: a circular doubly-linked-list of POutPt where ... - //op_front[.Prev]* ~~~> op_back & op_back == op_front.Next - OutRec* outrec = e.outrec; - bool to_front = IsFront(e); - OutPt* op_front = outrec->pts; - OutPt* op_back = op_front->next; - - if (to_front) - { - if (pt == op_front->pt) - return op_front; - } - else if (pt == op_back->pt) - return op_back; - - new_op = new OutPt(pt, outrec); - op_back->prev = new_op; - new_op->prev = op_front; - new_op->next = op_back; - op_front->next = new_op; - if (to_front) outrec->pts = new_op; - return new_op; - } - - - void ClipperBase::CleanCollinear(OutRec* outrec) - { - outrec = GetRealOutRec(outrec); - if (!outrec || outrec->is_open) return; - if (!IsValidClosedPath(outrec->pts)) - { - DisposeOutPts(outrec); - return; - } - - OutPt* startOp = outrec->pts, * op2 = startOp; - for (; ; ) - { - //NB if preserveCollinear == true, then only remove 180 deg. spikes - if ((CrossProduct(op2->prev->pt, op2->pt, op2->next->pt) == 0) && - (op2->pt == op2->prev->pt || - op2->pt == op2->next->pt || !PreserveCollinear || - DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0)) - { - - if (op2 == outrec->pts) outrec->pts = op2->prev; - - op2 = DisposeOutPt(op2); - if (!IsValidClosedPath(op2)) - { - DisposeOutPts(outrec); - return; - } - startOp = op2; - continue; - } - op2 = op2->next; - if (op2 == startOp) break; - } - FixSelfIntersects(outrec); - } - - void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp) - { - // splitOp.prev -> splitOp && - // splitOp.next -> splitOp.next.next are intersecting - OutPt* prevOp = splitOp->prev; - OutPt* nextNextOp = splitOp->next->next; - outrec->pts = prevOp; - - Point64 ip; - GetIntersectPoint(prevOp->pt, splitOp->pt, - splitOp->next->pt, nextNextOp->pt, ip); - -#ifdef USINGZ - if (zCallback_) zCallback_(prevOp->pt, splitOp->pt, - splitOp->next->pt, nextNextOp->pt, ip); -#endif - double area1 = Area(outrec->pts); - double absArea1 = std::fabs(area1); - if (absArea1 < 2) - { - DisposeOutPts(outrec); - return; - } - - // nb: area1 is the path's area *before* splitting, whereas area2 is - // the area of the triangle containing splitOp & splitOp.next. - // So the only way for these areas to have the same sign is if - // the split triangle is larger than the path containing prevOp or - // if there's more than one self=intersection. - double area2 = AreaTriangle(ip, splitOp->pt, splitOp->next->pt); - double absArea2 = std::fabs(area2); - - // de-link splitOp and splitOp.next from the path - // while inserting the intersection point - if (ip == prevOp->pt || ip == nextNextOp->pt) - { - nextNextOp->prev = prevOp; - prevOp->next = nextNextOp; - } - else - { - OutPt* newOp2 = new OutPt(ip, prevOp->outrec); - newOp2->prev = prevOp; - newOp2->next = nextNextOp; - nextNextOp->prev = newOp2; - prevOp->next = newOp2; - } - - if (absArea2 >= 1 && - (absArea2 > absArea1 || (area2 > 0) == (area1 > 0))) - { - OutRec* newOr = NewOutRec(); - newOr->owner = outrec->owner; - - if (using_polytree_) - { - if (!outrec->splits) outrec->splits = new OutRecList(); - outrec->splits->push_back(newOr); - } - - splitOp->outrec = newOr; - splitOp->next->outrec = newOr; - OutPt* newOp = new OutPt(ip, newOr); - newOp->prev = splitOp->next; - newOp->next = splitOp; - newOr->pts = newOp; - splitOp->prev = newOp; - splitOp->next->next = newOp; - } - else - { - delete splitOp->next; - delete splitOp; - } - } - - void ClipperBase::FixSelfIntersects(OutRec* outrec) - { - OutPt* op2 = outrec->pts; - for (; ; ) - { - // triangles can't self-intersect - if (op2->prev == op2->next->next) break; - if (SegmentsIntersect(op2->prev->pt, - op2->pt, op2->next->pt, op2->next->next->pt)) - { - if (op2 == outrec->pts || op2->next == outrec->pts) - outrec->pts = outrec->pts->prev; - DoSplitOp(outrec, op2); - if (!outrec->pts) break; - op2 = outrec->pts; - continue; - } - else - op2 = op2->next; - - if (op2 == outrec->pts) break; - } - } - - - inline void UpdateOutrecOwner(OutRec* outrec) - { - OutPt* opCurr = outrec->pts; - for (; ; ) - { - opCurr->outrec = outrec; - opCurr = opCurr->next; - if (opCurr == outrec->pts) return; - } - } - - - OutPt* ClipperBase::StartOpenPath(Active& e, const Point64& pt) - { - OutRec* outrec = NewOutRec(); - outrec->is_open = true; - - if (e.wind_dx > 0) - { - outrec->front_edge = &e; - outrec->back_edge = nullptr; - } - else - { - outrec->front_edge = nullptr; - outrec->back_edge = &e; - } - - e.outrec = outrec; - - OutPt* op = new OutPt(pt, outrec); - outrec->pts = op; - return op; - } - - - inline void ClipperBase::UpdateEdgeIntoAEL(Active* e) - { - e->bot = e->top; - e->vertex_top = NextVertex(*e); - e->top = e->vertex_top->pt; - e->curr_x = e->bot.x; - SetDx(*e); - - if (IsJoined(*e)) Split(*e, e->bot); - - if (IsHorizontal(*e)) return; - InsertScanline(e->top.y); - - CheckJoinLeft(*e, e->bot); - CheckJoinRight(*e, e->bot); - } - - Active* FindEdgeWithMatchingLocMin(Active* e) - { - Active* result = e->next_in_ael; - while (result) - { - if (result->local_min == e->local_min) return result; - else if (!IsHorizontal(*result) && e->bot != result->bot) result = nullptr; - else result = result->next_in_ael; - } - result = e->prev_in_ael; - while (result) - { - if (result->local_min == e->local_min) return result; - else if (!IsHorizontal(*result) && e->bot != result->bot) return nullptr; - else result = result->prev_in_ael; - } - return result; - } - - - OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) - { - //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... - if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2))) - { - if (IsOpen(e1) && IsOpen(e2)) return nullptr; - Active* edge_o, * edge_c; - if (IsOpen(e1)) - { - edge_o = &e1; - edge_c = &e2; - } - else - { - edge_o = &e2; - edge_c = &e1; - } - if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety - - if (abs(edge_c->wind_cnt) != 1) return nullptr; - switch (cliptype_) - { - case ClipType::Union: - if (!IsHotEdge(*edge_c)) return nullptr; - break; - default: - if (edge_c->local_min->polytype == PathType::Subject) - return nullptr; - } - - switch (fillrule_) - { - case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break; - case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break; - default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break; - } - - //toggle contribution ... - if (IsHotEdge(*edge_o)) - { - OutPt* resultOp = AddOutPt(*edge_o, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr; - else edge_o->outrec->back_edge = nullptr; - edge_o->outrec = nullptr; - return resultOp; - } - - //horizontal edges can pass under open paths at a LocMins - else if (pt == edge_o->local_min->vertex->pt && - !IsOpenEnd(*edge_o->local_min->vertex)) - { - //find the other side of the LocMin and - //if it's 'hot' join up with it ... - Active* e3 = FindEdgeWithMatchingLocMin(edge_o); - if (e3 && IsHotEdge(*e3)) - { - edge_o->outrec = e3->outrec; - if (edge_o->wind_dx > 0) - SetSides(*e3->outrec, *edge_o, *e3); - else - SetSides(*e3->outrec, *e3, *edge_o); - return e3->outrec->pts; - } - else - return StartOpenPath(*edge_o, pt); - } - else - return StartOpenPath(*edge_o, pt); - } - - //MANAGING CLOSED PATHS FROM HERE ON - - if (IsJoined(e1)) Split(e1, pt); - if (IsJoined(e2)) Split(e2, pt); - - //UPDATE WINDING COUNTS... - - int old_e1_windcnt, old_e2_windcnt; - if (e1.local_min->polytype == e2.local_min->polytype) - { - if (fillrule_ == FillRule::EvenOdd) - { - old_e1_windcnt = e1.wind_cnt; - e1.wind_cnt = e2.wind_cnt; - e2.wind_cnt = old_e1_windcnt; - } - else - { - if (e1.wind_cnt + e2.wind_dx == 0) - e1.wind_cnt = -e1.wind_cnt; - else - e1.wind_cnt += e2.wind_dx; - if (e2.wind_cnt - e1.wind_dx == 0) - e2.wind_cnt = -e2.wind_cnt; - else - e2.wind_cnt -= e1.wind_dx; - } - } - else - { - if (fillrule_ != FillRule::EvenOdd) - { - e1.wind_cnt2 += e2.wind_dx; - e2.wind_cnt2 -= e1.wind_dx; - } - else - { - e1.wind_cnt2 = (e1.wind_cnt2 == 0 ? 1 : 0); - e2.wind_cnt2 = (e2.wind_cnt2 == 0 ? 1 : 0); - } - } - - switch (fillrule_) - { - case FillRule::EvenOdd: - case FillRule::NonZero: - old_e1_windcnt = abs(e1.wind_cnt); - old_e2_windcnt = abs(e2.wind_cnt); - break; - default: - if (fillrule_ == fillpos) - { - old_e1_windcnt = e1.wind_cnt; - old_e2_windcnt = e2.wind_cnt; - } - else - { - old_e1_windcnt = -e1.wind_cnt; - old_e2_windcnt = -e2.wind_cnt; - } - break; - } - - const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1; - const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1; - - if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01)) - { - return nullptr; - } - - //NOW PROCESS THE INTERSECTION ... - OutPt* resultOp = nullptr; - //if both edges are 'hot' ... - if (IsHotEdge(e1) && IsHotEdge(e2)) - { - if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) || - (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor)) - { - resultOp = AddLocalMaxPoly(e1, e2, pt); -#ifdef USINGZ - if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); -#endif - } - else if (IsFront(e1) || (e1.outrec == e2.outrec)) - { - //this 'else if' condition isn't strictly needed but - //it's sensible to split polygons that ony touch at - //a common vertex (not at common edges). - - resultOp = AddLocalMaxPoly(e1, e2, pt); -#ifdef USINGZ - OutPt* op2 = AddLocalMinPoly(e1, e2, pt); - if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); - if (zCallback_) SetZ(e1, e2, op2->pt); -#else - AddLocalMinPoly(e1, e2, pt); -#endif - } - else - { - resultOp = AddOutPt(e1, pt); -#ifdef USINGZ - OutPt* op2 = AddOutPt(e2, pt); - if (zCallback_) - { - SetZ(e1, e2, resultOp->pt); - SetZ(e1, e2, op2->pt); - } -#else - AddOutPt(e2, pt); -#endif - SwapOutrecs(e1, e2); - } - } - else if (IsHotEdge(e1)) - { - resultOp = AddOutPt(e1, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - SwapOutrecs(e1, e2); - } - else if (IsHotEdge(e2)) - { - resultOp = AddOutPt(e2, pt); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - SwapOutrecs(e1, e2); - } - else - { - int64_t e1Wc2, e2Wc2; - switch (fillrule_) - { - case FillRule::EvenOdd: - case FillRule::NonZero: - e1Wc2 = abs(e1.wind_cnt2); - e2Wc2 = abs(e2.wind_cnt2); - break; - default: - if (fillrule_ == fillpos) - { - e1Wc2 = e1.wind_cnt2; - e2Wc2 = e2.wind_cnt2; - } - else - { - e1Wc2 = -e1.wind_cnt2; - e2Wc2 = -e2.wind_cnt2; - } - break; - } - - if (!IsSamePolyType(e1, e2)) - { - resultOp = AddLocalMinPoly(e1, e2, pt, false); -#ifdef USINGZ - if (zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - } - else if (old_e1_windcnt == 1 && old_e2_windcnt == 1) - { - resultOp = nullptr; - switch (cliptype_) - { - case ClipType::Union: - if (e1Wc2 <= 0 && e2Wc2 <= 0) - resultOp = AddLocalMinPoly(e1, e2, pt, false); - break; - case ClipType::Difference: - if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - { - resultOp = AddLocalMinPoly(e1, e2, pt, false); - } - break; - case ClipType::Xor: - resultOp = AddLocalMinPoly(e1, e2, pt, false); - break; - default: - if (e1Wc2 > 0 && e2Wc2 > 0) - resultOp = AddLocalMinPoly(e1, e2, pt, false); - break; - } -#ifdef USINGZ - if (resultOp && zCallback_) SetZ(e1, e2, resultOp->pt); -#endif - } - } - return resultOp; - } - - - inline void ClipperBase::DeleteFromAEL(Active& e) - { - Active* prev = e.prev_in_ael; - Active* next = e.next_in_ael; - if (!prev && !next && (&e != actives_)) return; // already deleted - if (prev) - prev->next_in_ael = next; - else - actives_ = next; - if (next) next->prev_in_ael = prev; - delete& e; - } - - - inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y) - { - Active* e = actives_; - sel_ = e; - while (e) - { - e->prev_in_sel = e->prev_in_ael; - e->next_in_sel = e->next_in_ael; - e->jump = e->next_in_sel; - if (e->join_with == JoinWith::Left) - e->curr_x = e->prev_in_ael->curr_x; // also avoids complications - else - e->curr_x = TopX(*e, top_y); - e = e->next_in_ael; - } - } - - bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees) - { - cliptype_ = ct; - fillrule_ = fillrule; - using_polytree_ = use_polytrees; - Reset(); - int64_t y; - if (ct == ClipType::None || !PopScanline(y)) return true; - - while (succeeded_) - { - InsertLocalMinimaIntoAEL(y); - Active* e; - while (PopHorz(e)) DoHorizontal(*e); - if (horz_seg_list_.size() > 0) - { - ConvertHorzSegsToJoins(); - horz_seg_list_.clear(); - } - bot_y_ = y; // bot_y_ == bottom of scanbeam - if (!PopScanline(y)) break; // y new top of scanbeam - DoIntersections(y); - DoTopOfScanbeam(y); - while (PopHorz(e)) DoHorizontal(*e); - } - if (succeeded_) ProcessHorzJoins(); - return succeeded_; - } - - inline void FixOutRecPts(OutRec* outrec) - { - OutPt* op = outrec->pts; - do { - op->outrec = outrec; - op = op->next; - } while (op != outrec->pts); - } - - inline Rect64 GetBounds(OutPt* op) - { - Rect64 result(op->pt.x, op->pt.y, op->pt.x, op->pt.y); - OutPt* op2 = op->next; - while (op2 != op) - { - if (op2->pt.x < result.left) result.left = op2->pt.x; - else if (op2->pt.x > result.right) result.right = op2->pt.x; - if (op2->pt.y < result.top) result.top = op2->pt.y; - else if (op2->pt.y > result.bottom) result.bottom = op2->pt.y; - op2 = op2->next; - } - return result; - } - - static PointInPolygonResult PointInOpPolygon(const Point64& pt, OutPt* op) - { - if (op == op->next || op->prev == op->next) - return PointInPolygonResult::IsOutside; - OutPt* op2 = op; - do - { - if (op->pt.y != pt.y) break; - op = op->next; - } while (op != op2); - if (op == op2) return PointInPolygonResult::IsOutside; - - // must be above or below to get here - bool isAbove = op->pt.y < pt.y; - int val = 0; - op2 = op->next; - while (op2 != op) - { - if (isAbove) - while (op2 != op && op2->pt.y < pt.y) op2 = op2->next; - else - while (op2 != op && op2->pt.y > pt.y) op2 = op2->next; - if (op2 == op) break; - - // must have touched or crossed the pt.Y horizonal - // and this must happen an even number of times - - if (op2->pt.y == pt.y) // touching the horizontal - { - if (op2->pt.x == pt.x || (op2->pt.y == op2->prev->pt.y && - (pt.x < op2->prev->pt.x) != (pt.x < op2->pt.x))) - return PointInPolygonResult::IsOn; - - op2 = op2->next; - continue; - } - - if (pt.x < op2->pt.x && pt.x < op2->prev->pt.x); - // do nothing because - // we're only interested in edges crossing on the left - else if ((pt.x > op2->prev->pt.x && pt.x > op2->pt.x)) - val = 1 - val; // toggle val - else - { - double d = CrossProduct(op2->prev->pt, op2->pt, pt); - if (d == 0) return PointInPolygonResult::IsOn; - if ((d < 0) == isAbove) val = 1 - val; - } - isAbove = !isAbove; - op2 = op2->next; - } - if (val == 0) return PointInPolygonResult::IsOutside; - else return PointInPolygonResult::IsInside; - } - - inline bool Path1InsidePath2(OutPt* op1, OutPt* op2) - { - // we need to make some accommodation for rounding errors - // so we won't jump if the first vertex is found outside - int outside_cnt = 0; - OutPt* op = op1; - do - { - PointInPolygonResult result = PointInOpPolygon(op->pt, op2); - if (result == PointInPolygonResult::IsOutside) ++outside_cnt; - else if (result == PointInPolygonResult::IsInside) --outside_cnt; - op = op->next; - } while (op != op1 && std::abs(outside_cnt) < 2); - if (std::abs(outside_cnt) > 1) return (outside_cnt < 0); - // since path1's location is still equivocal, check its midpoint - Point64 mp = GetBounds(op).MidPoint(); - return PointInOpPolygon(mp, op2) == PointInPolygonResult::IsInside; - } - - inline bool Path1InsidePath2(const OutRec* or1, const OutRec* or2) - { - // we need to make some accommodation for rounding errors - // so we won't jump if the first vertex is found outside - int outside_cnt = 0; - OutPt* op = or1->pts; - do - { - PointInPolygonResult result = PointInPolygon(op->pt, or2->path); - if (result == PointInPolygonResult::IsOutside) ++outside_cnt; - else if (result == PointInPolygonResult::IsInside) --outside_cnt; - op = op->next; - } while (op != or1->pts && std::abs(outside_cnt) < 2); - if (std::abs(outside_cnt) > 1) return (outside_cnt < 0); - // since path1's location is still equivocal, check its midpoint - Point64 mp = GetBounds(op).MidPoint(); - return PointInPolygon(mp, or2->path) == PointInPolygonResult::IsInside; - } - - inline bool SetHorzSegHeadingForward(HorzSegment& hs, OutPt* opP, OutPt* opN) - { - if (opP->pt.x == opN->pt.x) return false; - if (opP->pt.x < opN->pt.x) - { - hs.left_op = opP; - hs.right_op = opN; - hs.left_to_right = true; - } - else - { - hs.left_op = opN; - hs.right_op = opP; - hs.left_to_right = false; - } - return true; - } - - inline bool UpdateHorzSegment(HorzSegment& hs) - { - OutPt* op = hs.left_op; - OutRec* outrec = GetRealOutRec(op->outrec); - bool outrecHasEdges = outrec->front_edge; - int64_t curr_y = op->pt.y; - OutPt* opP = op, * opN = op; - if (outrecHasEdges) - { - OutPt* opA = outrec->pts, * opZ = opA->next; - while (opP != opZ && opP->prev->pt.y == curr_y) - opP = opP->prev; - while (opN != opA && opN->next->pt.y == curr_y) - opN = opN->next; - } - else - { - while (opP->prev != opN && opP->prev->pt.y == curr_y) - opP = opP->prev; - while (opN->next != opP && opN->next->pt.y == curr_y) - opN = opN->next; - } - bool result = - SetHorzSegHeadingForward(hs, opP, opN) && - !hs.left_op->horz; - - if (result) - hs.left_op->horz = &hs; - else - hs.right_op = nullptr; // (for sorting) - return result; - } - - void ClipperBase::ConvertHorzSegsToJoins() - { - auto j = std::count_if(horz_seg_list_.begin(), - horz_seg_list_.end(), - [](HorzSegment& hs) { return UpdateHorzSegment(hs); }); - if (j < 2) return; - std::sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter()); - - HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2; - HorzSegmentList::iterator hs_end = hs1 +j; - HorzSegmentList::iterator hs_end1 = hs_end - 1; - - for (; hs1 != hs_end1; ++hs1) - { - for (hs2 = hs1 + 1; hs2 != hs_end; ++hs2) - { - if (hs2->left_op->pt.x >= hs1->right_op->pt.x) break; - if (hs2->left_to_right == hs1->left_to_right || - (hs2->right_op->pt.x <= hs1->left_op->pt.x)) continue; - int64_t curr_y = hs1->left_op->pt.y; - if (hs1->left_to_right) - { - while (hs1->left_op->next->pt.y == curr_y && - hs1->left_op->next->pt.x <= hs2->left_op->pt.x) - hs1->left_op = hs1->left_op->next; - while (hs2->left_op->prev->pt.y == curr_y && - hs2->left_op->prev->pt.x <= hs1->left_op->pt.x) - hs2->left_op = hs2->left_op->prev; - HorzJoin join = HorzJoin( - DuplicateOp(hs1->left_op, true), - DuplicateOp(hs2->left_op, false)); - horz_join_list_.push_back(join); - } - else - { - while (hs1->left_op->prev->pt.y == curr_y && - hs1->left_op->prev->pt.x <= hs2->left_op->pt.x) - hs1->left_op = hs1->left_op->prev; - while (hs2->left_op->next->pt.y == curr_y && - hs2->left_op->next->pt.x <= hs1->left_op->pt.x) - hs2->left_op = hs2->left_op->next; - HorzJoin join = HorzJoin( - DuplicateOp(hs2->left_op, true), - DuplicateOp(hs1->left_op, false)); - horz_join_list_.push_back(join); - } - } - } - } - - void ClipperBase::ProcessHorzJoins() - { - for (const HorzJoin& j : horz_join_list_) - { - OutRec* or1 = GetRealOutRec(j.op1->outrec); - OutRec* or2 = GetRealOutRec(j.op2->outrec); - - OutPt* op1b = j.op1->next; - OutPt* op2b = j.op2->prev; - j.op1->next = j.op2; - j.op2->prev = j.op1; - op1b->prev = op2b; - op2b->next = op1b; - - if (or1 == or2) - { - or2 = new OutRec(); - or2->pts = op1b; - FixOutRecPts(or2); - if (or1->pts->outrec == or2) - { - or1->pts = j.op1; - or1->pts->outrec = or1; - } - - if (using_polytree_) - { - if (Path1InsidePath2(or2->pts, or1->pts)) - SetOwner(or2, or1); - else if (Path1InsidePath2(or1->pts, or2->pts)) - SetOwner(or1, or2); - else - or2->owner = or1; - } - else - or2->owner = or1; - - outrec_list_.push_back(or2); - } - else - { - or2->pts = nullptr; - if (using_polytree_) - SetOwner(or2, or1); - else - or2->owner = or1; - } - } - } - - void ClipperBase::DoIntersections(const int64_t top_y) - { - if (BuildIntersectList(top_y)) - { - ProcessIntersectList(); - intersect_nodes_.clear(); - } - } - - void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y) - { - Point64 ip; - if (!GetIntersectPoint(e1.bot, e1.top, e2.bot, e2.top, ip)) - ip = Point64(e1.curr_x, top_y); //parallel edges - - //rounding errors can occasionally place the calculated intersection - //point either below or above the scanbeam, so check and correct ... - if (ip.y > bot_y_ || ip.y < top_y) - { - double abs_dx1 = std::fabs(e1.dx); - double abs_dx2 = std::fabs(e2.dx); - if (abs_dx1 > 100 && abs_dx2 > 100) - { - if (abs_dx1 > abs_dx2) - ip = GetClosestPointOnSegment(ip, e1.bot, e1.top); - else - ip = GetClosestPointOnSegment(ip, e2.bot, e2.top); - } - else if (abs_dx1 > 100) - ip = GetClosestPointOnSegment(ip, e1.bot, e1.top); - else if (abs_dx2 > 100) - ip = GetClosestPointOnSegment(ip, e2.bot, e2.top); - else - { - if (ip.y < top_y) ip.y = top_y; - else ip.y = bot_y_; - if (abs_dx1 < abs_dx2) ip.x = TopX(e1, ip.y); - else ip.x = TopX(e2, ip.y); - } - } - intersect_nodes_.push_back(IntersectNode(&e1, &e2, ip)); - } - - bool ClipperBase::BuildIntersectList(const int64_t top_y) - { - if (!actives_ || !actives_->next_in_ael) return false; - - //Calculate edge positions at the top of the current scanbeam, and from this - //we will determine the intersections required to reach these new positions. - AdjustCurrXAndCopyToSEL(top_y); - //Find all edge intersections in the current scanbeam using a stable merge - //sort that ensures only adjacent edges are intersecting. Intersect info is - //stored in FIntersectList ready to be processed in ProcessIntersectList. - //Re merge sorts see https://stackoverflow.com/a/46319131/359538 - - Active* left = sel_, * right, * l_end, * r_end, * curr_base, * tmp; - - while (left && left->jump) - { - Active* prev_base = nullptr; - while (left && left->jump) - { - curr_base = left; - right = left->jump; - l_end = right; - r_end = right->jump; - left->jump = r_end; - while (left != l_end && right != r_end) - { - if (right->curr_x < left->curr_x) - { - tmp = right->prev_in_sel; - for (; ; ) - { - AddNewIntersectNode(*tmp, *right, top_y); - if (tmp == left) break; - tmp = tmp->prev_in_sel; - } - - tmp = right; - right = ExtractFromSEL(tmp); - l_end = right; - Insert1Before2InSEL(tmp, left); - if (left == curr_base) - { - curr_base = tmp; - curr_base->jump = r_end; - if (!prev_base) sel_ = curr_base; - else prev_base->jump = curr_base; - } - } - else left = left->next_in_sel; - } - prev_base = curr_base; - left = r_end; - } - left = sel_; - } - return intersect_nodes_.size() > 0; - } - - void ClipperBase::ProcessIntersectList() - { - //We now have a list of intersections required so that edges will be - //correctly positioned at the top of the scanbeam. However, it's important - //that edge intersections are processed from the bottom up, but it's also - //crucial that intersections only occur between adjacent edges. - - //First we do a quicksort so intersections proceed in a bottom up order ... - std::sort(intersect_nodes_.begin(), intersect_nodes_.end(), IntersectListSort); - //Now as we process these intersections, we must sometimes adjust the order - //to ensure that intersecting edges are always adjacent ... - - IntersectNodeList::iterator node_iter, node_iter2; - for (node_iter = intersect_nodes_.begin(); - node_iter != intersect_nodes_.end(); ++node_iter) - { - if (!EdgesAdjacentInAEL(*node_iter)) - { - node_iter2 = node_iter + 1; - while (!EdgesAdjacentInAEL(*node_iter2)) ++node_iter2; - std::swap(*node_iter, *node_iter2); - } - - IntersectNode& node = *node_iter; - IntersectEdges(*node.edge1, *node.edge2, node.pt); - SwapPositionsInAEL(*node.edge1, *node.edge2); - - node.edge1->curr_x = node.pt.x; - node.edge2->curr_x = node.pt.x; - CheckJoinLeft(*node.edge2, node.pt); - CheckJoinRight(*node.edge1, node.pt, true); - } - } - - void ClipperBase::SwapPositionsInAEL(Active& e1, Active& e2) - { - //preconditon: e1 must be immediately to the left of e2 - Active* next = e2.next_in_ael; - if (next) next->prev_in_ael = &e1; - Active* prev = e1.prev_in_ael; - if (prev) prev->next_in_ael = &e2; - e2.prev_in_ael = prev; - e2.next_in_ael = &e1; - e1.prev_in_ael = &e2; - e1.next_in_ael = next; - if (!e2.prev_in_ael) actives_ = &e2; - } - - inline OutPt* GetLastOp(const Active& hot_edge) - { - OutRec* outrec = hot_edge.outrec; - OutPt* result = outrec->pts; - if (&hot_edge != outrec->front_edge) - result = result->next; - return result; - } - - void ClipperBase::AddTrialHorzJoin(OutPt* op) - { - if (op->outrec->is_open) return; - horz_seg_list_.push_back(HorzSegment(op)); - } - - bool ClipperBase::ResetHorzDirection(const Active& horz, - const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right) - { - if (horz.bot.x == horz.top.x) - { - //the horizontal edge is going nowhere ... - horz_left = horz.curr_x; - horz_right = horz.curr_x; - Active* e = horz.next_in_ael; - while (e && e->vertex_top != max_vertex) e = e->next_in_ael; - return e != nullptr; - } - else if (horz.curr_x < horz.top.x) - { - horz_left = horz.curr_x; - horz_right = horz.top.x; - return true; - } - else - { - horz_left = horz.top.x; - horz_right = horz.curr_x; - return false; // right to left - } - } - - inline bool HorzIsSpike(const Active& horzEdge) - { - Point64 nextPt = NextVertex(horzEdge)->pt; - return (nextPt.y == horzEdge.bot.y) && - (horzEdge.bot.x < horzEdge.top.x) != (horzEdge.top.x < nextPt.x); - } - - inline void TrimHorz(Active& horzEdge, bool preserveCollinear) - { - bool wasTrimmed = false; - Point64 pt = NextVertex(horzEdge)->pt; - while (pt.y == horzEdge.top.y) - { - //always trim 180 deg. spikes (in closed paths) - //but otherwise break if preserveCollinear = true - if (preserveCollinear && - ((pt.x < horzEdge.top.x) != (horzEdge.bot.x < horzEdge.top.x))) - break; - - horzEdge.vertex_top = NextVertex(horzEdge); - horzEdge.top = pt; - wasTrimmed = true; - if (IsMaxima(horzEdge)) break; - pt = NextVertex(horzEdge)->pt; - } - - if (wasTrimmed) SetDx(horzEdge); // +/-infinity - } - - void ClipperBase::DoHorizontal(Active& horz) - /******************************************************************************* - * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or * - * bottom of a scanbeam) are processed as if layered.The order in which HEs * - * are processed doesn't matter. HEs intersect with the bottom vertices of * - * other HEs[#] and with non-horizontal edges [*]. Once these intersections * - * are completed, intermediate HEs are 'promoted' to the next edge in their * - * bounds, and they in turn may be intersected[%] by other HEs. * - * * - * eg: 3 horizontals at a scanline: / | / / * - * | / | (HE3)o ========%========== o * - * o ======= o(HE2) / | / / * - * o ============#=========*======*========#=========o (HE1) * - * / | / | / * - *******************************************************************************/ - { - Point64 pt; - bool horzIsOpen = IsOpen(horz); - int64_t y = horz.bot.y; - Vertex* vertex_max; - if (horzIsOpen) - vertex_max = GetCurrYMaximaVertex_Open(horz); - else - vertex_max = GetCurrYMaximaVertex(horz); - - // remove 180 deg.spikes and also simplify - // consecutive horizontals when PreserveCollinear = true - if (vertex_max && !horzIsOpen && vertex_max != horz.vertex_top) - TrimHorz(horz, PreserveCollinear); - - int64_t horz_left, horz_right; - bool is_left_to_right = - ResetHorzDirection(horz, vertex_max, horz_left, horz_right); - - if (IsHotEdge(horz)) - { -#ifdef USINGZ - OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y, horz.bot.z)); -#else - OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y)); -#endif - AddTrialHorzJoin(op); - } - OutRec* currHorzOutrec = horz.outrec; - - while (true) // loop through consec. horizontal edges - { - Active* e; - if (is_left_to_right) e = horz.next_in_ael; - else e = horz.prev_in_ael; - - while (e) - { - if (e->vertex_top == vertex_max) - { - if (IsHotEdge(horz) && IsJoined(*e)) - Split(*e, e->top); - - if (IsHotEdge(horz)) - { - while (horz.vertex_top != vertex_max) - { - AddOutPt(horz, horz.top); - UpdateEdgeIntoAEL(&horz); - } - if (is_left_to_right) - AddLocalMaxPoly(horz, *e, horz.top); - else - AddLocalMaxPoly(*e, horz, horz.top); - } - DeleteFromAEL(*e); - DeleteFromAEL(horz); - return; - } - - //if horzEdge is a maxima, keep going until we reach - //its maxima pair, otherwise check for break conditions - if (vertex_max != horz.vertex_top || IsOpenEnd(horz)) - { - //otherwise stop when 'ae' is beyond the end of the horizontal line - if ((is_left_to_right && e->curr_x > horz_right) || - (!is_left_to_right && e->curr_x < horz_left)) break; - - if (e->curr_x == horz.top.x && !IsHorizontal(*e)) - { - pt = NextVertex(horz)->pt; - if (is_left_to_right) - { - //with open paths we'll only break once past horz's end - if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e)) - { - if (TopX(*e, pt.y) > pt.x) break; - } - //otherwise we'll only break when horz's outslope is greater than e's - else if (TopX(*e, pt.y) >= pt.x) break; - } - else - { - if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e)) - { - if (TopX(*e, pt.y) < pt.x) break; - } - else if (TopX(*e, pt.y) <= pt.x) break; - } - } - } - - pt = Point64(e->curr_x, horz.bot.y); - if (is_left_to_right) - { - IntersectEdges(horz, *e, pt); - SwapPositionsInAEL(horz, *e); - horz.curr_x = e->curr_x; - e = horz.next_in_ael; - } - else - { - IntersectEdges(*e, horz, pt); - SwapPositionsInAEL(*e, horz); - horz.curr_x = e->curr_x; - e = horz.prev_in_ael; - } - - if (horz.outrec && horz.outrec != currHorzOutrec) - { - currHorzOutrec = horz.outrec; - //nb: The outrec containining the op returned by IntersectEdges - //above may no longer be associated with horzEdge. - AddTrialHorzJoin(GetLastOp(horz)); - } - } - - //check if we've finished with (consecutive) horizontals ... - if (horzIsOpen && IsOpenEnd(horz)) // ie open at top - { - if (IsHotEdge(horz)) - { - AddOutPt(horz, horz.top); - if (IsFront(horz)) - horz.outrec->front_edge = nullptr; - else - horz.outrec->back_edge = nullptr; - horz.outrec = nullptr; - } - DeleteFromAEL(horz); - return; - } - else if (NextVertex(horz)->pt.y != horz.top.y) - break; - - //still more horizontals in bound to process ... - if (IsHotEdge(horz)) - AddOutPt(horz, horz.top); - UpdateEdgeIntoAEL(&horz); - - if (PreserveCollinear && !horzIsOpen && HorzIsSpike(horz)) - TrimHorz(horz, true); - - is_left_to_right = - ResetHorzDirection(horz, vertex_max, horz_left, horz_right); - } - - if (IsHotEdge(horz)) AddOutPt(horz, horz.top); - UpdateEdgeIntoAEL(&horz); // end of an intermediate horiz. - } - - void ClipperBase::DoTopOfScanbeam(const int64_t y) - { - sel_ = nullptr; // sel_ is reused to flag horizontals (see PushHorz below) - Active* e = actives_; - while (e) - { - //nb: 'e' will never be horizontal here - if (e->top.y == y) - { - e->curr_x = e->top.x; - if (IsMaxima(*e)) - { - e = DoMaxima(*e); // TOP OF BOUND (MAXIMA) - continue; - } - else - { - //INTERMEDIATE VERTEX ... - if (IsHotEdge(*e)) AddOutPt(*e, e->top); - UpdateEdgeIntoAEL(e); - if (IsHorizontal(*e)) - PushHorz(*e); // horizontals are processed later - } - } - else // i.e. not the top of the edge - e->curr_x = TopX(*e, y); - - e = e->next_in_ael; - } - } - - - Active* ClipperBase::DoMaxima(Active& e) - { - Active* next_e, * prev_e, * max_pair; - prev_e = e.prev_in_ael; - next_e = e.next_in_ael; - if (IsOpenEnd(e)) - { - if (IsHotEdge(e)) AddOutPt(e, e.top); - if (!IsHorizontal(e)) - { - if (IsHotEdge(e)) - { - if (IsFront(e)) - e.outrec->front_edge = nullptr; - else - e.outrec->back_edge = nullptr; - e.outrec = nullptr; - } - DeleteFromAEL(e); - } - return next_e; - } - - max_pair = GetMaximaPair(e); - if (!max_pair) return next_e; // eMaxPair is horizontal - - if (IsJoined(e)) Split(e, e.top); - if (IsJoined(*max_pair)) Split(*max_pair, max_pair->top); - - //only non-horizontal maxima here. - //process any edges between maxima pair ... - while (next_e != max_pair) - { - IntersectEdges(e, *next_e, e.top); - SwapPositionsInAEL(e, *next_e); - next_e = e.next_in_ael; - } - - if (IsOpen(e)) - { - if (IsHotEdge(e)) - AddLocalMaxPoly(e, *max_pair, e.top); - DeleteFromAEL(*max_pair); - DeleteFromAEL(e); - return (prev_e ? prev_e->next_in_ael : actives_); - } - - // e.next_in_ael== max_pair ... - if (IsHotEdge(e)) - AddLocalMaxPoly(e, *max_pair, e.top); - - DeleteFromAEL(e); - DeleteFromAEL(*max_pair); - return (prev_e ? prev_e->next_in_ael : actives_); - } - - void ClipperBase::Split(Active& e, const Point64& pt) - { - if (e.join_with == JoinWith::Right) - { - e.join_with = JoinWith::None; - e.next_in_ael->join_with = JoinWith::None; - AddLocalMinPoly(e, *e.next_in_ael, pt, true); - } - else - { - e.join_with = JoinWith::None; - e.prev_in_ael->join_with = JoinWith::None; - AddLocalMinPoly(*e.prev_in_ael, e, pt, true); - } - } - - void ClipperBase::CheckJoinLeft(Active& e, - const Point64& pt, bool check_curr_x) - { - Active* prev = e.prev_in_ael; - if (IsOpen(e) || !IsHotEdge(e) || !prev || - IsOpen(*prev) || !IsHotEdge(*prev) || - pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) // avoid trivial joins - return; - if (check_curr_x) prev->curr_x = TopX(*prev, pt.y); - if (e.curr_x != prev->curr_x || CrossProduct(e.top, pt, prev->top)) - return; - - if (e.outrec->idx == prev->outrec->idx) - AddLocalMaxPoly(*prev, e, pt); - else if (e.outrec->idx < prev->outrec->idx) - JoinOutrecPaths(e, *prev); - else - JoinOutrecPaths(*prev, e); - prev->join_with = JoinWith::Right; - e.join_with = JoinWith::Left; - } - - void ClipperBase::CheckJoinRight(Active& e, - const Point64& pt, bool check_curr_x) - { - Active* next = e.next_in_ael; - if (IsOpen(e) || !IsHotEdge(e) || - !next || IsOpen(*next) || !IsHotEdge(*next) || - pt.y < e.top.y +2 || pt.y < next->top.y +2) // avoids trivial joins - return; - - if (check_curr_x) next->curr_x = TopX(*next, pt.y); - if (e.curr_x != next->curr_x || - CrossProduct(e.top, pt, next->top)) return; - - if (e.outrec->idx == next->outrec->idx) - AddLocalMaxPoly(e, *next, pt); - else if (e.outrec->idx < next->outrec->idx) - JoinOutrecPaths(e, *next); - else - JoinOutrecPaths(*next, e); - e.join_with = JoinWith::Right; - next->join_with = JoinWith::Left; - } - - inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2) - { - OutRec* outrec = GetRealOutRec(op->outrec); - op2 = op; - if (outrec->front_edge) - { - while (op->prev != outrec->pts && - op->prev->pt.y == op->pt.y) op = op->prev; - while (op2 != outrec->pts && - op2->next->pt.y == op2->pt.y) op2 = op2->next; - return op2 != op; - } - else - { - while (op->prev != op2 && op->prev->pt.y == op->pt.y) - op = op->prev; - while (op2->next != op && op2->next->pt.y == op2->pt.y) - op2 = op2->next; - return op2 != op && op2->next != op; - } - } - - inline Rect64 GetBounds(const Path64& path) - { - if (path.empty()) return Rect64(); - Rect64 result = invalid_rect; - for (const Point64& pt : path) - { - if (pt.x < result.left) result.left = pt.x; - if (pt.x > result.right) result.right = pt.x; - if (pt.y < result.top) result.top = pt.y; - if (pt.y > result.bottom) result.bottom = pt.y; - } - return result; - } - - bool BuildPath64(OutPt* op, bool reverse, bool isOpen, Path64& path) - { - if (!op || op->next == op || (!isOpen && op->next == op->prev)) - return false; - - path.resize(0); - Point64 lastPt; - OutPt* op2; - if (reverse) - { - lastPt = op->pt; - op2 = op->prev; - } - else - { - op = op->next; - lastPt = op->pt; - op2 = op->next; - } - path.push_back(lastPt); - - while (op2 != op) - { - if (op2->pt != lastPt) - { - lastPt = op2->pt; - path.push_back(lastPt); - } - if (reverse) - op2 = op2->prev; - else - op2 = op2->next; - } - - if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false; - else return true; - } - - bool ClipperBase::CheckBounds(OutRec* outrec) - { - if (!outrec->pts) return false; - if (!outrec->bounds.IsEmpty()) return true; - CleanCollinear(outrec); - if (!outrec->pts || - !BuildPath64(outrec->pts, ReverseSolution, false, outrec->path)) - return false; - outrec->bounds = GetBounds(outrec->path); - return true; - } - - void ClipperBase::RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath) - { - // pre-condition: outrec will have valid bounds - // post-condition: if a valid path, outrec will have a polypath - - if (outrec->polypath || outrec->bounds.IsEmpty()) return; - - while (outrec->owner && - (!outrec->owner->pts || !CheckBounds(outrec->owner))) - outrec->owner = outrec->owner->owner; - - if (outrec->owner && !outrec->owner->polypath) - RecursiveCheckOwners(outrec->owner, polypath); - - while (outrec->owner) - if (outrec->owner->bounds.Contains(outrec->bounds) && - Path1InsidePath2(outrec, outrec->owner)) - break; // found - owner contain outrec! - else - outrec->owner = outrec->owner->owner; - - if (outrec->owner) - outrec->polypath = outrec->owner->polypath->AddChild(outrec->path); - else - outrec->polypath = polypath->AddChild(outrec->path); - } - - void ClipperBase::DeepCheckOwners(OutRec* outrec, PolyPath* polypath) - { - RecursiveCheckOwners(outrec, polypath); - - while (outrec->owner && outrec->owner->splits) - { - OutRec* split = nullptr; - for (auto s : *outrec->owner->splits) - { - split = GetRealOutRec(s); - if (split && split != outrec && - split != outrec->owner && CheckBounds(split) && - split->bounds.Contains(outrec->bounds) && - Path1InsidePath2(outrec, split)) - { - RecursiveCheckOwners(split, polypath); - outrec->owner = split; //found in split - break; // inner 'for' loop - } - else - split = nullptr; - } - if (!split) break; - } - } - - void Clipper64::BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen) - { - solutionClosed.resize(0); - solutionClosed.reserve(outrec_list_.size()); - if (solutionOpen) - { - solutionOpen->resize(0); - solutionOpen->reserve(outrec_list_.size()); - } - - // nb: outrec_list_.size() may change in the following - // while loop because polygons may be split during - // calls to CleanCollinear which calls FixSelfIntersects - size_t i = 0; - while (i < outrec_list_.size()) - { - OutRec* outrec = outrec_list_[i++]; - if (outrec->pts == nullptr) continue; - - Path64 path; - if (solutionOpen && outrec->is_open) - { - if (BuildPath64(outrec->pts, ReverseSolution, true, path)) - solutionOpen->emplace_back(std::move(path)); - } - else - { - // nb: CleanCollinear can add to outrec_list_ - CleanCollinear(outrec); - //closed paths should always return a Positive orientation - if (BuildPath64(outrec->pts, ReverseSolution, false, path)) - solutionClosed.emplace_back(std::move(path)); - } - } - } - - void Clipper64::BuildTree64(PolyPath64& polytree, Paths64& open_paths) - { - polytree.Clear(); - open_paths.resize(0); - if (has_open_paths_) - open_paths.reserve(outrec_list_.size()); - - size_t i = 0; - // outrec_list_.size() is not static here because - // CheckBounds below can indirectly add additional - // OutRec (via FixOutRecPts & CleanCollinear) - while (i < outrec_list_.size()) - { - OutRec* outrec = outrec_list_[i++]; - if (!outrec || !outrec->pts) continue; - if (outrec->is_open) - { - Path64 path; - if (BuildPath64(outrec->pts, ReverseSolution, true, path)) - open_paths.push_back(path); - continue; - } - - if (CheckBounds(outrec)) - DeepCheckOwners(outrec, &polytree); - } - } - - bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale) - { - if (!op || op->next == op || (!isOpen && op->next == op->prev)) - return false; - - path.resize(0); - Point64 lastPt; - OutPt* op2; - if (reverse) - { - lastPt = op->pt; - op2 = op->prev; - } - else - { - op = op->next; - lastPt = op->pt; - op2 = op->next; - } -#ifdef USINGZ - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z)); -#else - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); -#endif - - while (op2 != op) - { - if (op2->pt != lastPt) - { - lastPt = op2->pt; -#ifdef USINGZ - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z)); -#else - path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); -#endif - - } - if (reverse) - op2 = op2->prev; - else - op2 = op2->next; - } - if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false; - return true; - } - - void ClipperD::BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen) - { - solutionClosed.resize(0); - solutionClosed.reserve(outrec_list_.size()); - if (solutionOpen) - { - solutionOpen->resize(0); - solutionOpen->reserve(outrec_list_.size()); - } - - for (OutRec* outrec : outrec_list_) - { - if (outrec->pts == nullptr) continue; - - PathD path; - if (solutionOpen && outrec->is_open) - { - if (BuildPathD(outrec->pts, ReverseSolution, true, path, invScale_)) - solutionOpen->emplace_back(std::move(path)); - } - else - { - CleanCollinear(outrec); - //closed paths should always return a Positive orientation - if (BuildPathD(outrec->pts, ReverseSolution, false, path, invScale_)) - solutionClosed.emplace_back(std::move(path)); - } - } - } - - void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths) - { - polytree.Clear(); - open_paths.resize(0); - if (has_open_paths_) - open_paths.reserve(outrec_list_.size()); - - for (OutRec* outrec : outrec_list_) - { - if (!outrec || !outrec->pts) continue; - if (outrec->is_open) - { - PathD path; - if (BuildPathD(outrec->pts, ReverseSolution, true, path, invScale_)) - open_paths.push_back(path); - continue; - } - - if (CheckBounds(outrec)) - DeepCheckOwners(outrec, &polytree); - } - } - -} // namespace clipper2lib diff --git a/modules/clipper2/lib/src/clipper.offset.cpp b/modules/clipper2/lib/src/clipper.offset.cpp deleted file mode 100644 index 870ee7538..000000000 --- a/modules/clipper2/lib/src/clipper.offset.cpp +++ /dev/null @@ -1,494 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 27 January 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 * -*******************************************************************************/ - -#include -#include "clipper2/clipper.h" -#include "clipper2/clipper.offset.h" - -namespace Clipper2Lib { - -const double default_arc_tolerance = 0.25; -const double floating_point_tolerance = 1e-12; - -//------------------------------------------------------------------------------ -// Miscellaneous methods -//------------------------------------------------------------------------------ - -void GetBoundsAndLowestPolyIdx(const Paths64& paths, Rect64& r, int & idx) -{ - idx = -1; - r = MaxInvalidRect64; - int64_t lpx = 0; - for (int i = 0; i < static_cast(paths.size()); ++i) - for (const Point64& p : paths[i]) - { - if (p.y >= r.bottom) - { - if (p.y > r.bottom || p.x < lpx) - { - idx = i; - lpx = p.x; - r.bottom = p.y; - } - } - else if (p.y < r.top) r.top = p.y; - if (p.x > r.right) r.right = p.x; - else if (p.x < r.left) r.left = p.x; - } - if (idx < 0) r = Rect64(0, 0, 0, 0); - if (r.top == INT64_MIN) r.bottom = r.top; - if (r.left == INT64_MIN) r.left = r.right; -} - -bool IsSafeOffset(const Rect64& r, int64_t delta) -{ - if (delta < 0) return true; - return r.left > INT64_MIN + delta && - r.right < INT64_MAX - delta && - r.top > INT64_MIN + delta && - r.bottom < INT64_MAX - delta; -} - -PointD GetUnitNormal(const Point64& pt1, const Point64& pt2) -{ - double dx, dy, inverse_hypot; - if (pt1 == pt2) return PointD(0.0, 0.0); - dx = static_cast(pt2.x - pt1.x); - dy = static_cast(pt2.y - pt1.y); - inverse_hypot = 1.0 / hypot(dx, dy); - dx *= inverse_hypot; - dy *= inverse_hypot; - return PointD(dy, -dx); -} - -inline bool AlmostZero(double value, double epsilon = 0.001) -{ - return std::fabs(value) < epsilon; -} - -inline double Hypot(double x, double y) -{ - //see https://stackoverflow.com/a/32436148/359538 - return std::sqrt(x * x + y * y); -} - -inline PointD NormalizeVector(const PointD& vec) -{ - - double h = Hypot(vec.x, vec.y); - if (AlmostZero(h)) return PointD(0,0); - double inverseHypot = 1 / h; - return PointD(vec.x * inverseHypot, vec.y * inverseHypot); -} - -inline PointD GetAvgUnitVector(const PointD& vec1, const PointD& vec2) -{ - return NormalizeVector(PointD(vec1.x + vec2.x, vec1.y + vec2.y)); -} - -inline bool IsClosedPath(EndType et) -{ - return et == EndType::Polygon || et == EndType::Joined; -} - -inline Point64 GetPerpendic(const Point64& pt, const PointD& norm, double delta) -{ - return Point64(pt.x + norm.x * delta, pt.y + norm.y * delta); -} - -inline PointD GetPerpendicD(const Point64& pt, const PointD& norm, double delta) -{ - return PointD(pt.x + norm.x * delta, pt.y + norm.y * delta); -} - -//------------------------------------------------------------------------------ -// ClipperOffset methods -//------------------------------------------------------------------------------ - -void ClipperOffset::AddPath(const Path64& path, JoinType jt_, EndType et_) -{ - Paths64 paths; - paths.push_back(path); - AddPaths(paths, jt_, et_); -} - -void ClipperOffset::AddPaths(const Paths64 &paths, JoinType jt_, EndType et_) -{ - if (paths.size() == 0) return; - groups_.push_back(Group(paths, jt_, et_)); -} - -void ClipperOffset::AddPath(const Clipper2Lib::PathD& path, JoinType jt_, EndType et_) -{ - PathsD paths; - paths.push_back(path); - AddPaths(paths, jt_, et_); -} - -void ClipperOffset::AddPaths(const PathsD& paths, JoinType jt_, EndType et_) -{ - if (paths.size() == 0) return; - groups_.push_back(Group(PathsDToPaths64(paths), jt_, et_)); -} - -void ClipperOffset::BuildNormals(const Path64& path) -{ - norms.clear(); - norms.reserve(path.size()); - if (path.size() == 0) return; - Path64::const_iterator path_iter, path_last_iter = --path.cend(); - for (path_iter = path.cbegin(); path_iter != path_last_iter; ++path_iter) - norms.push_back(GetUnitNormal(*path_iter,*(path_iter +1))); - norms.push_back(GetUnitNormal(*path_last_iter, *(path.cbegin()))); -} - -inline PointD TranslatePoint(const PointD& pt, double dx, double dy) -{ - return PointD(pt.x + dx, pt.y + dy); -} - -inline PointD ReflectPoint(const PointD& pt, const PointD& pivot) -{ - return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y)); -} - -PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b, - const PointD& pt2a, const PointD& pt2b) -{ - if (pt1a.x == pt1b.x) //vertical - { - if (pt2a.x == pt2b.x) return PointD(0, 0); - - double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); - double b2 = pt2a.y - m2 * pt2a.x; - return PointD(pt1a.x, m2 * pt1a.x + b2); - } - else if (pt2a.x == pt2b.x) //vertical - { - double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); - double b1 = pt1a.y - m1 * pt1a.x; - return PointD(pt2a.x, m1 * pt2a.x + b1); - } - else - { - double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); - double b1 = pt1a.y - m1 * pt1a.x; - double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); - double b2 = pt2a.y - m2 * pt2a.x; - if (m1 == m2) return PointD(0, 0); - double x = (b2 - b1) / (m1 - m2); - return PointD(x, m1 * x + b1); - } -} - -void ClipperOffset::DoSquare(Group& group, const Path64& path, size_t j, size_t k) -{ - PointD vec; - if (j == k) - vec = PointD(norms[0].y, -norms[0].x); - else - vec = GetAvgUnitVector( - PointD(-norms[k].y, norms[k].x), - PointD(norms[j].y, -norms[j].x)); - - // now offset the original vertex delta units along unit vector - PointD ptQ = PointD(path[j]); - ptQ = TranslatePoint(ptQ, abs_group_delta_ * vec.x, abs_group_delta_ * vec.y); - // get perpendicular vertices - PointD pt1 = TranslatePoint(ptQ, group_delta_ * vec.y, group_delta_ * -vec.x); - PointD pt2 = TranslatePoint(ptQ, group_delta_ * -vec.y, group_delta_ * vec.x); - // get 2 vertices along one edge offset - PointD pt3 = GetPerpendicD(path[k], norms[k], group_delta_); - if (j == k) - { - PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_); - PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); - //get the second intersect point through reflecion - group.path_.push_back(Point64(ReflectPoint(pt, ptQ))); - group.path_.push_back(Point64(pt)); - } - else - { - PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_); - PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); - group.path_.push_back(Point64(pt)); - //get the second intersect point through reflecion - group.path_.push_back(Point64(ReflectPoint(pt, ptQ))); - } -} - -void ClipperOffset::DoMiter(Group& group, const Path64& path, size_t j, size_t k, double cos_a) -{ - double q = group_delta_ / (cos_a + 1); - group.path_.push_back(Point64( - path[j].x + (norms[k].x + norms[j].x) * q, - path[j].y + (norms[k].y + norms[j].y) * q)); -} - -void ClipperOffset::DoRound(Group& group, const Path64& path, size_t j, size_t k, double angle) -{ - //even though angle may be negative this is a convex join - Point64 pt = path[j]; - int steps = static_cast(std::floor(steps_per_rad_ * std::abs(angle))); - double step_sin = std::sin(angle / steps); - double step_cos = std::cos(angle / steps); - - PointD pt2 = PointD(norms[k].x * group_delta_, norms[k].y * group_delta_); - if (j == k) pt2.Negate(); - - group.path_.push_back(Point64(pt.x + pt2.x, pt.y + pt2.y)); - for (int i = 0; i < steps; ++i) - { - pt2 = PointD(pt2.x * step_cos - step_sin * pt2.y, - pt2.x * step_sin + pt2.y * step_cos); - group.path_.push_back(Point64(pt.x + pt2.x, pt.y + pt2.y)); - } - group.path_.push_back(GetPerpendic(path[j], norms[j], group_delta_)); -} - -void ClipperOffset::OffsetPoint(Group& group, - Path64& path, size_t j, size_t& k, bool reversing) -{ - // Let A = change in angle where edges join - // A == 0: ie no change in angle (flat join) - // A == PI: edges 'spike' - // sin(A) < 0: right turning - // cos(A) < 0: change in angle is more than 90 degree - - if (path[j] == path[k]) { k = j; return; } - - double sin_a = CrossProduct(norms[j], norms[k]); - double cos_a = DotProduct(norms[j], norms[k]); - if (sin_a > 1.0) sin_a = 1.0; - else if (sin_a < -1.0) sin_a = -1.0; - - bool almostNoAngle = AlmostZero(cos_a - 1); - bool is180DegSpike = AlmostZero(cos_a + 1) && reversing; - // when there's almost no angle of deviation or it's concave - if (almostNoAngle || is180DegSpike || (sin_a * group_delta_ < 0)) - { - //almost no angle or concave - group.path_.push_back(GetPerpendic(path[j], norms[k], group_delta_)); - // create a simple self-intersection that will be cleaned up later - if (!almostNoAngle) group.path_.push_back(path[j]); - group.path_.push_back(GetPerpendic(path[j], norms[j], group_delta_)); - } - else - { - // it's convex - if (join_type_ == JoinType::Round) - DoRound(group, path, j, k, std::atan2(sin_a, cos_a)); - else if (join_type_ == JoinType::Miter) - { - // miter unless the angle is so acute the miter would exceeds ML - if (cos_a > temp_lim_ - 1) DoMiter(group, path, j, k, cos_a); - else DoSquare(group, path, j, k); - } - // don't bother squaring angles that deviate < ~20 degrees because - // squaring will be indistinguishable from mitering and just be a lot slower - else if (cos_a > 0.9) - DoMiter(group, path, j, k, cos_a); - else - DoSquare(group, path, j, k); - } - k = j; -} - -void ClipperOffset::OffsetPolygon(Group& group, Path64& path) -{ - group.path_.clear(); - for (Path64::size_type i = 0, j = path.size() -1; i < path.size(); j = i, ++i) - OffsetPoint(group, path, i, j); - group.paths_out_.push_back(group.path_); -} - -void ClipperOffset::OffsetOpenJoined(Group& group, Path64& path) -{ - OffsetPolygon(group, path); - std::reverse(path.begin(), path.end()); - BuildNormals(path); - OffsetPolygon(group, path); -} - -void ClipperOffset::OffsetOpenPath(Group& group, Path64& path, EndType end_type) -{ - group.path_.clear(); - - // do the line start cap - switch (end_type) - { - case EndType::Butt: - group.path_.push_back(Point64( - path[0].x - norms[0].x * group_delta_, - path[0].y - norms[0].y * group_delta_)); - group.path_.push_back(GetPerpendic(path[0], norms[0], group_delta_)); - break; - case EndType::Round: - DoRound(group, path, 0,0, PI); - break; - default: - DoSquare(group, path, 0, 0); - break; - } - - size_t highI = path.size() - 1; - - // offset the left side going forward - for (Path64::size_type i = 1, k = 0; i < highI; ++i) - OffsetPoint(group, path, i, k); - - // reverse normals - for (size_t i = highI; i > 0; --i) - norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y); - norms[0] = norms[highI]; - - // do the line end cap - switch (end_type) - { - case EndType::Butt: - group.path_.push_back(Point64( - path[highI].x - norms[highI].x * group_delta_, - path[highI].y - norms[highI].y * group_delta_)); - group.path_.push_back(GetPerpendic(path[highI], norms[highI], group_delta_)); - break; - case EndType::Round: - DoRound(group, path, highI, highI, PI); - break; - default: - DoSquare(group, path, highI, highI); - break; - } - - for (size_t i = highI, k = 0; i > 0; --i) - OffsetPoint(group, path, i, k, true); - group.paths_out_.push_back(group.path_); -} - -void ClipperOffset::DoGroupOffset(Group& group, double delta) -{ - if (group.end_type_ != EndType::Polygon) delta = std::abs(delta) * 0.5; - bool isClosedPaths = IsClosedPath(group.end_type_); - - //the lowermost polygon must be an outer polygon. So we can use that as the - //designated orientation for outer polygons (needed for tidy-up clipping) - Rect64 r; - int idx = 0; - GetBoundsAndLowestPolyIdx(group.paths_in_, r, idx); - if (!IsSafeOffset(r, static_cast(std::ceil(delta)))) -#if __cpp_exceptions - throw Clipper2Exception(range_error); -#else - error_code_ |= range_error_i; - return; -#endif - if (isClosedPaths) - { - double area = Area(group.paths_in_[idx]); - if (area == 0) return; - group.is_reversed_ = (area < 0); - if (group.is_reversed_) delta = -delta; - } - else - group.is_reversed_ = false; - - group_delta_ = delta; - abs_group_delta_ = std::abs(group_delta_); - join_type_ = group.join_type_; - - double arcTol = (arc_tolerance_ > floating_point_tolerance ? arc_tolerance_ - : std::log10(2 + abs_group_delta_) * default_arc_tolerance); // empirically derived - -//calculate a sensible number of steps (for 360 deg for the given offset - if (group.join_type_ == JoinType::Round || group.end_type_ == EndType::Round) - { - steps_per_rad_ = PI / std::acos(1 - arcTol / abs_group_delta_) / (PI *2); - } - - bool is_closed_path = IsClosedPath(group.end_type_); - Paths64::const_iterator path_iter; - for(path_iter = group.paths_in_.cbegin(); path_iter != group.paths_in_.cend(); ++path_iter) - { - Path64 path = StripDuplicates(*path_iter, is_closed_path); - Path64::size_type cnt = path.size(); - if (cnt == 0) continue; - - if (cnt == 1) // single point - only valid with open paths - { - group.path_ = Path64(); - //single vertex so build a circle or square ... - if (group.join_type_ == JoinType::Round) - { - double radius = abs_group_delta_; - group.path_ = Ellipse(path[0], radius, radius); - } - else - { - int d = (int)std::ceil(abs_group_delta_); - r = Rect64(path[0].x - d, path[0].y - d, path[0].x + d, path[0].y + d); - group.path_ = r.AsPath(); - } - group.paths_out_.push_back(group.path_); - } - else - { - BuildNormals(path); - if (group.end_type_ == EndType::Polygon) OffsetPolygon(group, path); - else if (group.end_type_ == EndType::Joined) OffsetOpenJoined(group, path); - else OffsetOpenPath(group, path, group.end_type_); - } - } - solution.reserve(solution.size() + group.paths_out_.size()); - copy(group.paths_out_.begin(), group.paths_out_.end(), back_inserter(solution)); - group.paths_out_.clear(); -} - -Paths64 ClipperOffset::Execute(double delta) -{ - error_code_ = 0; - solution.clear(); - if (groups_.size() == 0) return solution; - - if (std::abs(delta) < default_arc_tolerance) - { - for (const Group& group : groups_) - { - solution.reserve(solution.size() + group.paths_in_.size()); - copy(group.paths_in_.begin(), group.paths_in_.end(), back_inserter(solution)); - } - return solution; - } - - temp_lim_ = (miter_limit_ <= 1) ? - 2.0 : - 2.0 / (miter_limit_ * miter_limit_); - - std::vector::iterator git; - for (git = groups_.begin(); git != groups_.end(); ++git) - { - DoGroupOffset(*git, delta); - if (!error_code_) continue; - solution.clear(); - return solution; - } - - //clean up self-intersections ... - Clipper64 c; - c.PreserveCollinear = false; - //the solution should retain the orientation of the input - c.ReverseSolution = reverse_solution_ != groups_[0].is_reversed_; - c.AddSubject(solution); - if (groups_[0].is_reversed_) - c.Execute(ClipType::Union, FillRule::Negative, solution); - else - c.Execute(ClipType::Union, FillRule::Positive, solution); - - return solution; -} - -} // namespace diff --git a/modules/clipper2/lib/src/clipper.rectclip.cpp b/modules/clipper2/lib/src/clipper.rectclip.cpp deleted file mode 100644 index 634988148..000000000 --- a/modules/clipper2/lib/src/clipper.rectclip.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/******************************************************************************* -* Author : Angus Johnson * -* Date : 14 January 2023 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2022 * -* Purpose : FAST rectangular clipping * -* License : http://www.boost.org/LICENSE_1_0.txt * -*******************************************************************************/ - -#include -#include "clipper2/clipper.h" -#include "clipper2/clipper.rectclip.h" - -namespace Clipper2Lib { - - //------------------------------------------------------------------------------ - // Miscellaneous methods - //------------------------------------------------------------------------------ - - inline PointInPolygonResult Path1ContainsPath2(const Path64& path1, const Path64& path2) - { - PointInPolygonResult result = PointInPolygonResult::IsOn; - for(const Point64& pt : path2) - { - result = PointInPolygon(pt, path1); - if (result != PointInPolygonResult::IsOn) break; - } - return result; - } - - inline bool GetLocation(const Rect64& rec, - const Point64& pt, Location& loc) - { - if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom) - { - loc = Location::Left; - return false; - } - else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom) - { - loc = Location::Right; - return false; - } - else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right) - { - loc = Location::Top; - return false; - } - else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right) - { - loc = Location::Bottom; - return false; - } - else if (pt.x < rec.left) loc = Location::Left; - else if (pt.x > rec.right) loc = Location::Right; - else if (pt.y < rec.top) loc = Location::Top; - else if (pt.y > rec.bottom) loc = Location::Bottom; - else loc = Location::Inside; - return true; - } - - inline bool GetIntersection(const Path64& rectPath, - const Point64& p, const Point64& p2, Location& loc, Point64& ip) - { - // gets the intersection closest to 'p' - // when Result = false, loc will remain unchanged - switch (loc) - { - case Location::Left: - if (SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip); - else if (p.y < rectPath[0].y && - SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - { - GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip); - loc = Location::Top; - } - else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - { - GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip); - loc = Location::Bottom; - } - else return false; - break; - - case Location::Top: - if (SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip); - else if (p.x < rectPath[0].x && - SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - { - GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip); - loc = Location::Left; - } - else if (p.x > rectPath[1].x && - SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - { - GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip); - loc = Location::Right; - } - else return false; - break; - - case Location::Right: - if (SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip); - else if (p.y < rectPath[0].y && - SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - { - GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip); - loc = Location::Top; - } - else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - { - GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip); - loc = Location::Bottom; - } - else return false; - break; - - case Location::Bottom: - if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip); - else if (p.x < rectPath[3].x && - SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - { - GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip); - loc = Location::Left; - } - else if (p.x > rectPath[2].x && - SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - { - GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip); - loc = Location::Right; - } - else return false; - break; - - default: // loc == rInside - if (SegmentsIntersect(p, p2, rectPath[0], rectPath[3], true)) - { - GetIntersectPoint(p, p2, rectPath[0], rectPath[3], ip); - loc = Location::Left; - } - else if (SegmentsIntersect(p, p2, rectPath[0], rectPath[1], true)) - { - GetIntersectPoint(p, p2, rectPath[0], rectPath[1], ip); - loc = Location::Top; - } - else if (SegmentsIntersect(p, p2, rectPath[1], rectPath[2], true)) - { - GetIntersectPoint(p, p2, rectPath[1], rectPath[2], ip); - loc = Location::Right; - } - else if (SegmentsIntersect(p, p2, rectPath[2], rectPath[3], true)) - { - GetIntersectPoint(p, p2, rectPath[2], rectPath[3], ip); - loc = Location::Bottom; - } - else return false; - break; - } - return true; - } - - inline Location GetAdjacentLocation(Location loc, bool isClockwise) - { - int delta = (isClockwise) ? 1 : 3; - return static_cast((static_cast(loc) + delta) % 4); - } - - inline bool HeadingClockwise(Location prev, Location curr) - { - return (static_cast(prev) + 1) % 4 == static_cast(curr); - } - - inline bool AreOpposites(Location prev, Location curr) - { - return abs(static_cast(prev) - static_cast(curr)) == 2; - } - - inline bool IsClockwise(Location prev, Location curr, - const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp) - { - if (AreOpposites(prev, curr)) - return CrossProduct(prev_pt, rect_mp, curr_pt) < 0; - else - return HeadingClockwise(prev, curr); - } - - //---------------------------------------------------------------------------- - // RectClip64 - //---------------------------------------------------------------------------- - - void RectClip::AddCorner(Location prev, Location curr) - { - if (HeadingClockwise(prev, curr)) - result_.push_back(rectPath_[static_cast(prev)]); - else - result_.push_back(rectPath_[static_cast(curr)]); - } - - void RectClip::AddCorner(Location& loc, bool isClockwise) - { - if (isClockwise) - { - result_.push_back(rectPath_[static_cast(loc)]); - loc = GetAdjacentLocation(loc, true); - } - else - { - loc = GetAdjacentLocation(loc, false); - result_.push_back(rectPath_[static_cast(loc)]); - } - } - - void RectClip::GetNextLocation(const Path64& path, - Location& loc, int& i, int highI) - { - switch (loc) - { - case Location::Left: - while (i <= highI && path[i].x <= rect_.left) ++i; - if (i > highI) break; - else if (path[i].x >= rect_.right) loc = Location::Right; - else if (path[i].y <= rect_.top) loc = Location::Top; - else if (path[i].y >= rect_.bottom) loc = Location::Bottom; - else loc = Location::Inside; - break; - - case Location::Top: - while (i <= highI && path[i].y <= rect_.top) ++i; - if (i > highI) break; - else if (path[i].y >= rect_.bottom) loc = Location::Bottom; - else if (path[i].x <= rect_.left) loc = Location::Left; - else if (path[i].x >= rect_.right) loc = Location::Right; - else loc = Location::Inside; - break; - - case Location::Right: - while (i <= highI && path[i].x >= rect_.right) ++i; - if (i > highI) break; - else if (path[i].x <= rect_.left) loc = Location::Left; - else if (path[i].y <= rect_.top) loc = Location::Top; - else if (path[i].y >= rect_.bottom) loc = Location::Bottom; - else loc = Location::Inside; - break; - - case Location::Bottom: - while (i <= highI && path[i].y >= rect_.bottom) ++i; - if (i > highI) break; - else if (path[i].y <= rect_.top) loc = Location::Top; - else if (path[i].x <= rect_.left) loc = Location::Left; - else if (path[i].x >= rect_.right) loc = Location::Right; - else loc = Location::Inside; - break; - - case Location::Inside: - while (i <= highI) - { - if (path[i].x < rect_.left) loc = Location::Left; - else if (path[i].x > rect_.right) loc = Location::Right; - else if (path[i].y > rect_.bottom) loc = Location::Bottom; - else if (path[i].y < rect_.top) loc = Location::Top; - else { result_.push_back(path[i]); ++i; continue; } - break; //inner loop - } - break; - } //switch - } - - Path64 RectClip::Execute(const Path64& path) - { - if (rect_.IsEmpty() || path.size() < 3) return Path64(); - - result_.clear(); - start_locs_.clear(); - int i = 0, highI = static_cast(path.size()) - 1; - Location prev = Location::Inside, loc; - Location crossing_loc = Location::Inside; - Location first_cross_ = Location::Inside; - if (!GetLocation(rect_, path[highI], loc)) - { - i = highI - 1; - while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i; - if (i < 0) return path; - if (prev == Location::Inside) loc = Location::Inside; - i = 0; - } - Location starting_loc = loc; - - /////////////////////////////////////////////////// - while (i <= highI) - { - prev = loc; - Location crossing_prev = crossing_loc; - - GetNextLocation(path, loc, i, highI); - - if (i > highI) break; - Point64 ip, ip2; - Point64 prev_pt = (i) ? path[static_cast(i - 1)] : path[highI]; - - crossing_loc = loc; - if (!GetIntersection(rectPath_, path[i], prev_pt, crossing_loc, ip)) - { - // ie remaining outside - - if (crossing_prev == Location::Inside) - { - bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], mp_); - do { - start_locs_.push_back(prev); - prev = GetAdjacentLocation(prev, isClockw); - } while (prev != loc); - crossing_loc = crossing_prev; // still not crossed - } - else if (prev != Location::Inside && prev != loc) - { - bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], mp_); - do { - AddCorner(prev, isClockw); - } while (prev != loc); - } - ++i; - continue; - } - - //////////////////////////////////////////////////// - // we must be crossing the rect boundary to get here - //////////////////////////////////////////////////// - - if (loc == Location::Inside) // path must be entering rect - { - if (first_cross_ == Location::Inside) - { - first_cross_ = crossing_loc; - start_locs_.push_back(prev); - } - else if (prev != crossing_loc) - { - bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], mp_); - do { - AddCorner(prev, isClockw); - } while (prev != crossing_loc); - } - } - else if (prev != Location::Inside) - { - // passing right through rect. 'ip' here will be the second - // intersect pt but we'll also need the first intersect pt (ip2) - loc = prev; - GetIntersection(rectPath_, prev_pt, path[i], loc, ip2); - if (crossing_prev != Location::Inside) - AddCorner(crossing_prev, loc); - - if (first_cross_ == Location::Inside) - { - first_cross_ = loc; - start_locs_.push_back(prev); - } - - loc = crossing_loc; - result_.push_back(ip2); - if (ip == ip2) - { - // it's very likely that path[i] is on rect - GetLocation(rect_, path[i], loc); - AddCorner(crossing_loc, loc); - crossing_loc = loc; - continue; - } - } - else // path must be exiting rect - { - loc = crossing_loc; - if (first_cross_ == Location::Inside) - first_cross_ = crossing_loc; - } - - result_.push_back(ip); - - } //while i <= highI - /////////////////////////////////////////////////// - - if (first_cross_ == Location::Inside) - { - if (starting_loc == Location::Inside) return path; - Rect64 tmp_rect = Bounds(path); - if (tmp_rect.Contains(rect_) && - Path1ContainsPath2(path, rectPath_) != - PointInPolygonResult::IsOutside) return rectPath_; - else - return Path64(); - } - - if (loc != Location::Inside && - (loc != first_cross_ || start_locs_.size() > 2)) - { - if (start_locs_.size() > 0) - { - prev = loc; - for (auto loc2 : start_locs_) - { - if (prev == loc2) continue; - AddCorner(prev, HeadingClockwise(prev, loc2)); - prev = loc2; - } - loc = prev; - } - if (loc != first_cross_) - AddCorner(loc, HeadingClockwise(loc, first_cross_)); - } - - if (result_.size() < 3) return Path64(); - - // tidy up duplicates and collinear segments - Path64 res; - res.reserve(result_.size()); - size_t k = 0; highI = static_cast(result_.size()) - 1; - Point64 prev_pt = result_[highI]; - res.push_back(result_[0]); - Path64::const_iterator cit; - for (cit = result_.cbegin() + 1; cit != result_.cend(); ++cit) - { - if (CrossProduct(prev_pt, res[k], *cit)) - { - prev_pt = res[k++]; - res.push_back(*cit); - } - else - res[k] = *cit; - } - - if (k < 2) return Path64(); - // and a final check for collinearity - else if (!CrossProduct(res[0], res[k - 1], res[k])) res.pop_back(); - return res; - } - - Paths64 RectClipLines::Execute(const Path64& path) - { - result_.clear(); - Paths64 result; - if (rect_.IsEmpty() || path.size() == 0) return result; - - int i = 1, highI = static_cast(path.size()) - 1; - - Location prev = Location::Inside, loc; - Location crossing_loc; - if (!GetLocation(rect_, path[0], loc)) - { - while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i; - if (i > highI) { - result.push_back(path); - return result; - } - if (prev == Location::Inside) loc = Location::Inside; - i = 1; - } - if (loc == Location::Inside) result_.push_back(path[0]); - - /////////////////////////////////////////////////// - while (i <= highI) - { - prev = loc; - GetNextLocation(path, loc, i, highI); - if (i > highI) break; - Point64 ip, ip2; - Point64 prev_pt = path[static_cast(i - 1)]; - - crossing_loc = loc; - if (!GetIntersection(rectPath_, path[i], prev_pt, crossing_loc, ip)) - { - // ie remaining outside - ++i; - continue; - } - - //////////////////////////////////////////////////// - // we must be crossing the rect boundary to get here - //////////////////////////////////////////////////// - - if (loc == Location::Inside) // path must be entering rect - { - result_.push_back(ip); - } - else if (prev != Location::Inside) - { - // passing right through rect. 'ip' here will be the second - // intersect pt but we'll also need the first intersect pt (ip2) - crossing_loc = prev; - GetIntersection(rectPath_, prev_pt, path[i], crossing_loc, ip2); - result_.push_back(ip2); - result_.push_back(ip); - result.push_back(result_); - result_.clear(); - } - else // path must be exiting rect - { - result_.push_back(ip); - result.push_back(result_); - result_.clear(); - } - } //while i <= highI - /////////////////////////////////////////////////// - - if (result_.size() > 1) - result.push_back(result_); - return result; - } - -} // namespace diff --git a/modules/clipper2/polypartition.cpp b/modules/clipper2/polypartition.cpp deleted file mode 100644 index f395994e7..000000000 --- a/modules/clipper2/polypartition.cpp +++ /dev/null @@ -1,1851 +0,0 @@ -/*************************************************************************/ -/* Copyright (c) 2011-2021 Ivan Fratric and contributors. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "polypartition.h" - -#include -#include -#include - -TPPLPoly::TPPLPoly() { - hole = false; - numpoints = 0; - points = NULL; -} - -TPPLPoly::~TPPLPoly() { - if (points) { - delete[] points; - } -} - -void TPPLPoly::Clear() { - if (points) { - delete[] points; - } - hole = false; - numpoints = 0; - points = NULL; -} - -void TPPLPoly::Init(long numpoints) { - Clear(); - this->numpoints = numpoints; - points = new TPPLPoint[numpoints]; -} - -void TPPLPoly::Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { - Init(3); - points[0] = p1; - points[1] = p2; - points[2] = p3; -} - -TPPLPoly::TPPLPoly(const TPPLPoly &src) : - TPPLPoly() { - hole = src.hole; - numpoints = src.numpoints; - - if (numpoints > 0) { - points = new TPPLPoint[numpoints]; - memcpy(points, src.points, numpoints * sizeof(TPPLPoint)); - } -} - -TPPLPoly &TPPLPoly::operator=(const TPPLPoly &src) { - Clear(); - hole = src.hole; - numpoints = src.numpoints; - - if (numpoints > 0) { - points = new TPPLPoint[numpoints]; - memcpy(points, src.points, numpoints * sizeof(TPPLPoint)); - } - - return *this; -} - -TPPLOrientation TPPLPoly::GetOrientation() const { - long i1, i2; - tppl_float area = 0; - for (i1 = 0; i1 < numpoints; i1++) { - i2 = i1 + 1; - if (i2 == numpoints) { - i2 = 0; - } - area += points[i1].x * points[i2].y - points[i1].y * points[i2].x; - } - if (area > 0) { - return TPPL_ORIENTATION_CCW; - } - if (area < 0) { - return TPPL_ORIENTATION_CW; - } - return TPPL_ORIENTATION_NONE; -} - -void TPPLPoly::SetOrientation(TPPLOrientation orientation) { - TPPLOrientation polyorientation = GetOrientation(); - if (polyorientation != TPPL_ORIENTATION_NONE && polyorientation != orientation) { - Invert(); - } -} - -void TPPLPoly::Invert() { - std::reverse(points, points + numpoints); -} - -TPPLPartition::PartitionVertex::PartitionVertex() : - previous(NULL), next(NULL) { -} - -TPPLPoint TPPLPartition::Normalize(const TPPLPoint &p) { - TPPLPoint r; - tppl_float n = sqrt(p.x * p.x + p.y * p.y); - if (n != 0) { - r = p / n; - } else { - r.x = 0; - r.y = 0; - } - return r; -} - -tppl_float TPPLPartition::Distance(const TPPLPoint &p1, const TPPLPoint &p2) { - tppl_float dx, dy; - dx = p2.x - p1.x; - dy = p2.y - p1.y; - return (sqrt(dx * dx + dy * dy)); -} - -// Checks if two lines intersect. -int TPPLPartition::Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22) { - if ((p11.x == p21.x) && (p11.y == p21.y)) { - return 0; - } - if ((p11.x == p22.x) && (p11.y == p22.y)) { - return 0; - } - if ((p12.x == p21.x) && (p12.y == p21.y)) { - return 0; - } - if ((p12.x == p22.x) && (p12.y == p22.y)) { - return 0; - } - - TPPLPoint v1ort, v2ort, v; - tppl_float dot11, dot12, dot21, dot22; - - v1ort.x = p12.y - p11.y; - v1ort.y = p11.x - p12.x; - - v2ort.x = p22.y - p21.y; - v2ort.y = p21.x - p22.x; - - v = p21 - p11; - dot21 = v.x * v1ort.x + v.y * v1ort.y; - v = p22 - p11; - dot22 = v.x * v1ort.x + v.y * v1ort.y; - - v = p11 - p21; - dot11 = v.x * v2ort.x + v.y * v2ort.y; - v = p12 - p21; - dot12 = v.x * v2ort.x + v.y * v2ort.y; - - if (dot11 * dot12 > 0) { - return 0; - } - if (dot21 * dot22 > 0) { - return 0; - } - - return 1; -} - -// Removes holes from inpolys by merging them with non-holes. -int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) { - TPPLPolyList polys; - TPPLPolyList::Element *holeiter, *polyiter, *iter, *iter2; - long i, i2, holepointindex, polypointindex; - TPPLPoint holepoint, polypoint, bestpolypoint; - TPPLPoint linep1, linep2; - TPPLPoint v1, v2; - TPPLPoly newpoly; - bool hasholes; - bool pointvisible; - bool pointfound; - - // Check for the trivial case of no holes. - hasholes = false; - for (iter = inpolys->front(); iter; iter = iter->next()) { - if (iter->get().IsHole()) { - hasholes = true; - break; - } - } - if (!hasholes) { - for (iter = inpolys->front(); iter; iter = iter->next()) { - outpolys->push_back(iter->get()); - } - return 1; - } - - polys = *inpolys; - - while (1) { - // Find the hole point with the largest x. - hasholes = false; - for (iter = polys.front(); iter; iter = iter->next()) { - if (!iter->get().IsHole()) { - continue; - } - - if (!hasholes) { - hasholes = true; - holeiter = iter; - holepointindex = 0; - } - - for (i = 0; i < iter->get().GetNumPoints(); i++) { - if (iter->get().GetPoint(i).x > holeiter->get().GetPoint(holepointindex).x) { - holeiter = iter; - holepointindex = i; - } - } - } - if (!hasholes) { - break; - } - holepoint = holeiter->get().GetPoint(holepointindex); - - pointfound = false; - for (iter = polys.front(); iter; iter = iter->next()) { - if (iter->get().IsHole()) { - continue; - } - for (i = 0; i < iter->get().GetNumPoints(); i++) { - if (iter->get().GetPoint(i).x <= holepoint.x) { - continue; - } - if (!InCone(iter->get().GetPoint((i + iter->get().GetNumPoints() - 1) % (iter->get().GetNumPoints())), - iter->get().GetPoint(i), - iter->get().GetPoint((i + 1) % (iter->get().GetNumPoints())), - holepoint)) { - continue; - } - polypoint = iter->get().GetPoint(i); - if (pointfound) { - v1 = Normalize(polypoint - holepoint); - v2 = Normalize(bestpolypoint - holepoint); - if (v2.x > v1.x) { - continue; - } - } - pointvisible = true; - for (iter2 = polys.front(); iter2; iter2 = iter2->next()) { - if (iter2->get().IsHole()) { - continue; - } - for (i2 = 0; i2 < iter2->get().GetNumPoints(); i2++) { - linep1 = iter2->get().GetPoint(i2); - linep2 = iter2->get().GetPoint((i2 + 1) % (iter2->get().GetNumPoints())); - if (Intersects(holepoint, polypoint, linep1, linep2)) { - pointvisible = false; - break; - } - } - if (!pointvisible) { - break; - } - } - if (pointvisible) { - pointfound = true; - bestpolypoint = polypoint; - polyiter = iter; - polypointindex = i; - } - } - } - - if (!pointfound) { - return 0; - } - - newpoly.Init(holeiter->get().GetNumPoints() + polyiter->get().GetNumPoints() + 2); - i2 = 0; - for (i = 0; i <= polypointindex; i++) { - newpoly[i2] = polyiter->get().GetPoint(i); - i2++; - } - for (i = 0; i <= holeiter->get().GetNumPoints(); i++) { - newpoly[i2] = holeiter->get().GetPoint((i + holepointindex) % holeiter->get().GetNumPoints()); - i2++; - } - for (i = polypointindex; i < polyiter->get().GetNumPoints(); i++) { - newpoly[i2] = polyiter->get().GetPoint(i); - i2++; - } - - polys.erase(holeiter); - polys.erase(polyiter); - polys.push_back(newpoly); - } - - for (iter = polys.front(); iter; iter = iter->next()) { - outpolys->push_back(iter->get()); - } - - return 1; -} - -bool TPPLPartition::IsConvex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { - tppl_float tmp; - tmp = (p3.y - p1.y) * (p2.x - p1.x) - (p3.x - p1.x) * (p2.y - p1.y); - if (tmp > 0) { - return 1; - } else { - return 0; - } -} - -bool TPPLPartition::IsReflex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { - tppl_float tmp; - tmp = (p3.y - p1.y) * (p2.x - p1.x) - (p3.x - p1.x) * (p2.y - p1.y); - if (tmp < 0) { - return 1; - } else { - return 0; - } -} - -bool TPPLPartition::IsInside(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p) { - if (IsConvex(p1, p, p2)) { - return false; - } - if (IsConvex(p2, p, p3)) { - return false; - } - if (IsConvex(p3, p, p1)) { - return false; - } - return true; -} - -bool TPPLPartition::InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p) { - bool convex; - - convex = IsConvex(p1, p2, p3); - - if (convex) { - if (!IsConvex(p1, p2, p)) { - return false; - } - if (!IsConvex(p2, p3, p)) { - return false; - } - return true; - } else { - if (IsConvex(p1, p2, p)) { - return true; - } - if (IsConvex(p2, p3, p)) { - return true; - } - return false; - } -} - -bool TPPLPartition::InCone(PartitionVertex *v, TPPLPoint &p) { - TPPLPoint p1, p2, p3; - - p1 = v->previous->p; - p2 = v->p; - p3 = v->next->p; - - return InCone(p1, p2, p3, p); -} - -void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) { - PartitionVertex *v1 = NULL, *v3 = NULL; - v1 = v->previous; - v3 = v->next; - v->isConvex = !IsReflex(v1->p, v->p, v3->p); -} - -void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) { - long i; - PartitionVertex *v1 = NULL, *v3 = NULL; - TPPLPoint vec1, vec3; - - v1 = v->previous; - v3 = v->next; - - v->isConvex = IsConvex(v1->p, v->p, v3->p); - - vec1 = Normalize(v1->p - v->p); - vec3 = Normalize(v3->p - v->p); - v->angle = vec1.x * vec3.x + vec1.y * vec3.y; - - if (v->isConvex) { - v->isEar = true; - for (i = 0; i < numvertices; i++) { - if ((vertices[i].p.x == v->p.x) && (vertices[i].p.y == v->p.y)) { - continue; - } - if ((vertices[i].p.x == v1->p.x) && (vertices[i].p.y == v1->p.y)) { - continue; - } - if ((vertices[i].p.x == v3->p.x) && (vertices[i].p.y == v3->p.y)) { - continue; - } - if (IsInside(v1->p, v->p, v3->p, vertices[i].p)) { - v->isEar = false; - break; - } - } - } else { - v->isEar = false; - } -} - -// Triangulation by ear removal. -int TPPLPartition::Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles) { - if (!poly->Valid()) { - return 0; - } - - long numvertices; - PartitionVertex *vertices = NULL; - PartitionVertex *ear = NULL; - TPPLPoly triangle; - long i, j; - bool earfound; - - if (poly->GetNumPoints() < 3) { - return 0; - } - if (poly->GetNumPoints() == 3) { - triangles->push_back(*poly); - return 1; - } - - numvertices = poly->GetNumPoints(); - - vertices = new PartitionVertex[numvertices]; - for (i = 0; i < numvertices; i++) { - vertices[i].isActive = true; - vertices[i].p = poly->GetPoint(i); - if (i == (numvertices - 1)) { - vertices[i].next = &(vertices[0]); - } else { - vertices[i].next = &(vertices[i + 1]); - } - if (i == 0) { - vertices[i].previous = &(vertices[numvertices - 1]); - } else { - vertices[i].previous = &(vertices[i - 1]); - } - } - for (i = 0; i < numvertices; i++) { - UpdateVertex(&vertices[i], vertices, numvertices); - } - - for (i = 0; i < numvertices - 3; i++) { - earfound = false; - // Find the most extruded ear. - for (j = 0; j < numvertices; j++) { - if (!vertices[j].isActive) { - continue; - } - if (!vertices[j].isEar) { - continue; - } - if (!earfound) { - earfound = true; - ear = &(vertices[j]); - } else { - if (vertices[j].angle > ear->angle) { - ear = &(vertices[j]); - } - } - } - if (!earfound) { - delete[] vertices; - return 0; - } - - triangle.Triangle(ear->previous->p, ear->p, ear->next->p); - triangles->push_back(triangle); - - ear->isActive = false; - ear->previous->next = ear->next; - ear->next->previous = ear->previous; - - if (i == numvertices - 4) { - break; - } - - UpdateVertex(ear->previous, vertices, numvertices); - UpdateVertex(ear->next, vertices, numvertices); - } - for (i = 0; i < numvertices; i++) { - if (vertices[i].isActive) { - triangle.Triangle(vertices[i].previous->p, vertices[i].p, vertices[i].next->p); - triangles->push_back(triangle); - break; - } - } - - delete[] vertices; - - return 1; -} - -int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) { - TPPLPolyList outpolys; - TPPLPolyList::Element *iter; - - if (!RemoveHoles(inpolys, &outpolys)) { - return 0; - } - for (iter = outpolys.front(); iter; iter = iter->next()) { - if (!Triangulate_EC(&(iter->get()), triangles)) { - return 0; - } - } - return 1; -} - -int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) { - if (!poly->Valid()) { - return 0; - } - - TPPLPolyList triangles; - TPPLPolyList::Element *iter1, *iter2; - TPPLPoly *poly1 = NULL, *poly2 = NULL; - TPPLPoly newpoly; - TPPLPoint d1, d2, p1, p2, p3; - long i11, i12, i21, i22, i13, i23, j, k; - bool isdiagonal; - long numreflex; - - // Check if the poly is already convex. - numreflex = 0; - for (i11 = 0; i11 < poly->GetNumPoints(); i11++) { - if (i11 == 0) { - i12 = poly->GetNumPoints() - 1; - } else { - i12 = i11 - 1; - } - if (i11 == (poly->GetNumPoints() - 1)) { - i13 = 0; - } else { - i13 = i11 + 1; - } - if (IsReflex(poly->GetPoint(i12), poly->GetPoint(i11), poly->GetPoint(i13))) { - numreflex = 1; - break; - } - } - if (numreflex == 0) { - parts->push_back(*poly); - return 1; - } - - if (!Triangulate_EC(poly, &triangles)) { - return 0; - } - - for (iter1 = triangles.front(); iter1; iter1 = iter1->next()) { - poly1 = &(iter1->get()); - for (i11 = 0; i11 < poly1->GetNumPoints(); i11++) { - d1 = poly1->GetPoint(i11); - i12 = (i11 + 1) % (poly1->GetNumPoints()); - d2 = poly1->GetPoint(i12); - - isdiagonal = false; - for (iter2 = iter1; iter2; iter2 = iter2->next()) { - if (iter1 == iter2) { - continue; - } - poly2 = &(iter2->get()); - - for (i21 = 0; i21 < poly2->GetNumPoints(); i21++) { - if ((d2.x != poly2->GetPoint(i21).x) || (d2.y != poly2->GetPoint(i21).y)) { - continue; - } - i22 = (i21 + 1) % (poly2->GetNumPoints()); - if ((d1.x != poly2->GetPoint(i22).x) || (d1.y != poly2->GetPoint(i22).y)) { - continue; - } - isdiagonal = true; - break; - } - if (isdiagonal) { - break; - } - } - - if (!isdiagonal) { - continue; - } - - p2 = poly1->GetPoint(i11); - if (i11 == 0) { - i13 = poly1->GetNumPoints() - 1; - } else { - i13 = i11 - 1; - } - p1 = poly1->GetPoint(i13); - if (i22 == (poly2->GetNumPoints() - 1)) { - i23 = 0; - } else { - i23 = i22 + 1; - } - p3 = poly2->GetPoint(i23); - - if (!IsConvex(p1, p2, p3)) { - continue; - } - - p2 = poly1->GetPoint(i12); - if (i12 == (poly1->GetNumPoints() - 1)) { - i13 = 0; - } else { - i13 = i12 + 1; - } - p3 = poly1->GetPoint(i13); - if (i21 == 0) { - i23 = poly2->GetNumPoints() - 1; - } else { - i23 = i21 - 1; - } - p1 = poly2->GetPoint(i23); - - if (!IsConvex(p1, p2, p3)) { - continue; - } - - newpoly.Init(poly1->GetNumPoints() + poly2->GetNumPoints() - 2); - k = 0; - for (j = i12; j != i11; j = (j + 1) % (poly1->GetNumPoints())) { - newpoly[k] = poly1->GetPoint(j); - k++; - } - for (j = i22; j != i21; j = (j + 1) % (poly2->GetNumPoints())) { - newpoly[k] = poly2->GetPoint(j); - k++; - } - - triangles.erase(iter2); - iter1->get() = newpoly; - poly1 = &(iter1->get()); - i11 = -1; - - continue; - } - } - - for (iter1 = triangles.front(); iter1; iter1 = iter1->next()) { - parts->push_back(iter1->get()); - } - - return 1; -} - -int TPPLPartition::ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts) { - TPPLPolyList outpolys; - TPPLPolyList::Element *iter; - - if (!RemoveHoles(inpolys, &outpolys)) { - return 0; - } - for (iter = outpolys.front(); iter; iter = iter->next()) { - if (!ConvexPartition_HM(&(iter->get()), parts)) { - return 0; - } - } - return 1; -} - -// Minimum-weight polygon triangulation by dynamic programming. -// Time complexity: O(n^3) -// Space complexity: O(n^2) -int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles) { - if (!poly->Valid()) { - return 0; - } - - long i, j, k, gap, n; - DPState **dpstates = NULL; - TPPLPoint p1, p2, p3, p4; - long bestvertex; - tppl_float weight, minweight, d1, d2; - Diagonal diagonal, newdiagonal; - DiagonalList diagonals; - TPPLPoly triangle; - int ret = 1; - - n = poly->GetNumPoints(); - dpstates = new DPState *[n]; - for (i = 1; i < n; i++) { - dpstates[i] = new DPState[i]; - } - - // Initialize states and visibility. - for (i = 0; i < (n - 1); i++) { - p1 = poly->GetPoint(i); - for (j = i + 1; j < n; j++) { - dpstates[j][i].visible = true; - dpstates[j][i].weight = 0; - dpstates[j][i].bestvertex = -1; - if (j != (i + 1)) { - p2 = poly->GetPoint(j); - - // Visibility check. - if (i == 0) { - p3 = poly->GetPoint(n - 1); - } else { - p3 = poly->GetPoint(i - 1); - } - if (i == (n - 1)) { - p4 = poly->GetPoint(0); - } else { - p4 = poly->GetPoint(i + 1); - } - if (!InCone(p3, p1, p4, p2)) { - dpstates[j][i].visible = false; - continue; - } - - if (j == 0) { - p3 = poly->GetPoint(n - 1); - } else { - p3 = poly->GetPoint(j - 1); - } - if (j == (n - 1)) { - p4 = poly->GetPoint(0); - } else { - p4 = poly->GetPoint(j + 1); - } - if (!InCone(p3, p2, p4, p1)) { - dpstates[j][i].visible = false; - continue; - } - - for (k = 0; k < n; k++) { - p3 = poly->GetPoint(k); - if (k == (n - 1)) { - p4 = poly->GetPoint(0); - } else { - p4 = poly->GetPoint(k + 1); - } - if (Intersects(p1, p2, p3, p4)) { - dpstates[j][i].visible = false; - break; - } - } - } - } - } - dpstates[n - 1][0].visible = true; - dpstates[n - 1][0].weight = 0; - dpstates[n - 1][0].bestvertex = -1; - - for (gap = 2; gap < n; gap++) { - for (i = 0; i < (n - gap); i++) { - j = i + gap; - if (!dpstates[j][i].visible) { - continue; - } - bestvertex = -1; - for (k = (i + 1); k < j; k++) { - if (!dpstates[k][i].visible) { - continue; - } - if (!dpstates[j][k].visible) { - continue; - } - - if (k <= (i + 1)) { - d1 = 0; - } else { - d1 = Distance(poly->GetPoint(i), poly->GetPoint(k)); - } - if (j <= (k + 1)) { - d2 = 0; - } else { - d2 = Distance(poly->GetPoint(k), poly->GetPoint(j)); - } - - weight = dpstates[k][i].weight + dpstates[j][k].weight + d1 + d2; - - if ((bestvertex == -1) || (weight < minweight)) { - bestvertex = k; - minweight = weight; - } - } - if (bestvertex == -1) { - for (i = 1; i < n; i++) { - delete[] dpstates[i]; - } - delete[] dpstates; - - return 0; - } - - dpstates[j][i].bestvertex = bestvertex; - dpstates[j][i].weight = minweight; - } - } - - newdiagonal.index1 = 0; - newdiagonal.index2 = n - 1; - diagonals.push_back(newdiagonal); - while (!diagonals.empty()) { - diagonal = diagonals.front()->get(); - diagonals.pop_front(); - bestvertex = dpstates[diagonal.index2][diagonal.index1].bestvertex; - if (bestvertex == -1) { - ret = 0; - break; - } - triangle.Triangle(poly->GetPoint(diagonal.index1), poly->GetPoint(bestvertex), poly->GetPoint(diagonal.index2)); - triangles->push_back(triangle); - if (bestvertex > (diagonal.index1 + 1)) { - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = bestvertex; - diagonals.push_back(newdiagonal); - } - if (diagonal.index2 > (bestvertex + 1)) { - newdiagonal.index1 = bestvertex; - newdiagonal.index2 = diagonal.index2; - diagonals.push_back(newdiagonal); - } - } - - for (i = 1; i < n; i++) { - delete[] dpstates[i]; - } - delete[] dpstates; - - return ret; -} - -void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates) { - Diagonal newdiagonal; - DiagonalList *pairs = NULL; - long w2; - - w2 = dpstates[a][b].weight; - if (w > w2) { - return; - } - - pairs = &(dpstates[a][b].pairs); - newdiagonal.index1 = i; - newdiagonal.index2 = j; - - if (w < w2) { - pairs->clear(); - pairs->push_front(newdiagonal); - dpstates[a][b].weight = w; - } else { - if ((!pairs->empty()) && (i <= pairs->front()->get().index1)) { - return; - } - while ((!pairs->empty()) && (pairs->front()->get().index2 >= j)) { - pairs->pop_front(); - } - pairs->push_front(newdiagonal); - } -} - -void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { - DiagonalList *pairs = NULL; - DiagonalList::Element *iter, *lastiter; - long top; - long w; - - if (!dpstates[i][j].visible) { - return; - } - top = j; - w = dpstates[i][j].weight; - if (k - j > 1) { - if (!dpstates[j][k].visible) { - return; - } - w += dpstates[j][k].weight + 1; - } - if (j - i > 1) { - pairs = &(dpstates[i][j].pairs); - iter = pairs->back(); - lastiter = pairs->back(); - while (iter != pairs->front()) { - iter--; - if (!IsReflex(vertices[iter->get().index2].p, vertices[j].p, vertices[k].p)) { - lastiter = iter; - } else { - break; - } - } - if (lastiter == pairs->back()) { - w++; - } else { - if (IsReflex(vertices[k].p, vertices[i].p, vertices[lastiter->get().index1].p)) { - w++; - } else { - top = lastiter->get().index1; - } - } - } - UpdateState(i, k, w, top, j, dpstates); -} - -void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { - DiagonalList *pairs = NULL; - DiagonalList::Element *iter, *lastiter; - long top; - long w; - - if (!dpstates[j][k].visible) { - return; - } - top = j; - w = dpstates[j][k].weight; - - if (j - i > 1) { - if (!dpstates[i][j].visible) { - return; - } - w += dpstates[i][j].weight + 1; - } - if (k - j > 1) { - pairs = &(dpstates[j][k].pairs); - - iter = pairs->front(); - if ((!pairs->empty()) && (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->get().index1].p))) { - lastiter = iter; - while (iter) { - if (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->get().index1].p)) { - lastiter = iter; - iter = iter->next(); - } else { - break; - } - } - if (IsReflex(vertices[lastiter->get().index2].p, vertices[k].p, vertices[i].p)) { - w++; - } else { - top = lastiter->get().index2; - } - } else { - w++; - } - } - UpdateState(i, k, w, j, top, dpstates); -} - -int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) { - if (!poly->Valid()) { - return 0; - } - - TPPLPoint p1, p2, p3, p4; - PartitionVertex *vertices = NULL; - DPState2 **dpstates = NULL; - long i, j, k, n, gap; - DiagonalList diagonals, diagonals2; - Diagonal diagonal, newdiagonal; - DiagonalList *pairs = NULL, *pairs2 = NULL; - DiagonalList::Element *iter, *iter2; - int ret; - TPPLPoly newpoly; - List indices; - List::Element *iiter; - bool ijreal, jkreal; - - n = poly->GetNumPoints(); - vertices = new PartitionVertex[n]; - - dpstates = new DPState2 *[n]; - for (i = 0; i < n; i++) { - dpstates[i] = new DPState2[n]; - } - - // Initialize vertex information. - for (i = 0; i < n; i++) { - vertices[i].p = poly->GetPoint(i); - vertices[i].isActive = true; - if (i == 0) { - vertices[i].previous = &(vertices[n - 1]); - } else { - vertices[i].previous = &(vertices[i - 1]); - } - if (i == (poly->GetNumPoints() - 1)) { - vertices[i].next = &(vertices[0]); - } else { - vertices[i].next = &(vertices[i + 1]); - } - } - for (i = 1; i < n; i++) { - UpdateVertexReflexity(&(vertices[i])); - } - - // Initialize states and visibility. - for (i = 0; i < (n - 1); i++) { - p1 = poly->GetPoint(i); - for (j = i + 1; j < n; j++) { - dpstates[i][j].visible = true; - if (j == i + 1) { - dpstates[i][j].weight = 0; - } else { - dpstates[i][j].weight = 2147483647; - } - if (j != (i + 1)) { - p2 = poly->GetPoint(j); - - // Visibility check. - if (!InCone(&vertices[i], p2)) { - dpstates[i][j].visible = false; - continue; - } - if (!InCone(&vertices[j], p1)) { - dpstates[i][j].visible = false; - continue; - } - - for (k = 0; k < n; k++) { - p3 = poly->GetPoint(k); - if (k == (n - 1)) { - p4 = poly->GetPoint(0); - } else { - p4 = poly->GetPoint(k + 1); - } - if (Intersects(p1, p2, p3, p4)) { - dpstates[i][j].visible = false; - break; - } - } - } - } - } - for (i = 0; i < (n - 2); i++) { - j = i + 2; - if (dpstates[i][j].visible) { - dpstates[i][j].weight = 0; - newdiagonal.index1 = i + 1; - newdiagonal.index2 = i + 1; - dpstates[i][j].pairs.push_back(newdiagonal); - } - } - - dpstates[0][n - 1].visible = true; - vertices[0].isConvex = false; // By convention. - - for (gap = 3; gap < n; gap++) { - for (i = 0; i < n - gap; i++) { - if (vertices[i].isConvex) { - continue; - } - k = i + gap; - if (dpstates[i][k].visible) { - if (!vertices[k].isConvex) { - for (j = i + 1; j < k; j++) { - TypeA(i, j, k, vertices, dpstates); - } - } else { - for (j = i + 1; j < (k - 1); j++) { - if (vertices[j].isConvex) { - continue; - } - TypeA(i, j, k, vertices, dpstates); - } - TypeA(i, k - 1, k, vertices, dpstates); - } - } - } - for (k = gap; k < n; k++) { - if (vertices[k].isConvex) { - continue; - } - i = k - gap; - if ((vertices[i].isConvex) && (dpstates[i][k].visible)) { - TypeB(i, i + 1, k, vertices, dpstates); - for (j = i + 2; j < k; j++) { - if (vertices[j].isConvex) { - continue; - } - TypeB(i, j, k, vertices, dpstates); - } - } - } - } - - // Recover solution. - ret = 1; - newdiagonal.index1 = 0; - newdiagonal.index2 = n - 1; - diagonals.push_front(newdiagonal); - while (!diagonals.empty()) { - diagonal = diagonals.front()->get(); - diagonals.pop_front(); - if ((diagonal.index2 - diagonal.index1) <= 1) { - continue; - } - pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs); - if (pairs->empty()) { - ret = 0; - break; - } - if (!vertices[diagonal.index1].isConvex) { - iter = pairs->back(); - iter--; - j = iter->get().index2; - newdiagonal.index1 = j; - newdiagonal.index2 = diagonal.index2; - diagonals.push_front(newdiagonal); - if ((j - diagonal.index1) > 1) { - if (iter->get().index1 != iter->get().index2) { - pairs2 = &(dpstates[diagonal.index1][j].pairs); - while (1) { - if (pairs2->empty()) { - ret = 0; - break; - } - iter2 = pairs2->back(); - iter2--; - if (iter->get().index1 != iter2->get().index1) { - pairs2->pop_back(); - } else { - break; - } - } - if (ret == 0) { - break; - } - } - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = j; - diagonals.push_front(newdiagonal); - } - } else { - iter = pairs->front(); - j = iter->get().index1; - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = j; - diagonals.push_front(newdiagonal); - if ((diagonal.index2 - j) > 1) { - if (iter->get().index1 != iter->get().index2) { - pairs2 = &(dpstates[j][diagonal.index2].pairs); - while (1) { - if (pairs2->empty()) { - ret = 0; - break; - } - iter2 = pairs2->front(); - if (iter->get().index2 != iter2->get().index2) { - pairs2->pop_front(); - } else { - break; - } - } - if (ret == 0) { - break; - } - } - newdiagonal.index1 = j; - newdiagonal.index2 = diagonal.index2; - diagonals.push_front(newdiagonal); - } - } - } - - if (ret == 0) { - for (i = 0; i < n; i++) { - delete[] dpstates[i]; - } - delete[] dpstates; - delete[] vertices; - - return ret; - } - - newdiagonal.index1 = 0; - newdiagonal.index2 = n - 1; - diagonals.push_front(newdiagonal); - while (!diagonals.empty()) { - diagonal = diagonals.front()->get(); - diagonals.pop_front(); - if ((diagonal.index2 - diagonal.index1) <= 1) { - continue; - } - - indices.clear(); - diagonals2.clear(); - indices.push_back(diagonal.index1); - indices.push_back(diagonal.index2); - diagonals2.push_front(diagonal); - - while (!diagonals2.empty()) { - diagonal = diagonals2.front()->get(); - diagonals2.pop_front(); - if ((diagonal.index2 - diagonal.index1) <= 1) { - continue; - } - ijreal = true; - jkreal = true; - pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs); - if (!vertices[diagonal.index1].isConvex) { - iter = pairs->back(); - iter--; - j = iter->get().index2; - if (iter->get().index1 != iter->get().index2) { - ijreal = false; - } - } else { - iter = pairs->front(); - j = iter->get().index1; - if (iter->get().index1 != iter->get().index2) { - jkreal = false; - } - } - - newdiagonal.index1 = diagonal.index1; - newdiagonal.index2 = j; - if (ijreal) { - diagonals.push_back(newdiagonal); - } else { - diagonals2.push_back(newdiagonal); - } - - newdiagonal.index1 = j; - newdiagonal.index2 = diagonal.index2; - if (jkreal) { - diagonals.push_back(newdiagonal); - } else { - diagonals2.push_back(newdiagonal); - } - - indices.push_back(j); - } - - //std::sort(indices.begin(), indices.end()); - indices.sort(); - newpoly.Init((long)indices.size()); - k = 0; - for (iiter = indices.front(); iiter != indices.back(); iiter = iiter->next()) { - newpoly[k] = vertices[iiter->get()].p; - k++; - } - parts->push_back(newpoly); - } - - for (i = 0; i < n; i++) { - delete[] dpstates[i]; - } - delete[] dpstates; - delete[] vertices; - - return ret; -} - -// Creates a monotone partition of a list of polygons that -// can contain holes. Triangulates a set of polygons by -// first partitioning them into monotone polygons. -// Time complexity: O(n*log(n)), n is the number of vertices. -// Space complexity: O(n) -// The algorithm used here is outlined in the book -// "Computational Geometry: Algorithms and Applications" -// by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars. -int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys) { - TPPLPolyList::Element *iter; - MonotoneVertex *vertices = NULL; - long i, numvertices, vindex, vindex2, newnumvertices, maxnumvertices; - long polystartindex, polyendindex; - TPPLPoly *poly = NULL; - MonotoneVertex *v = NULL, *v2 = NULL, *vprev = NULL, *vnext = NULL; - ScanLineEdge newedge; - bool error = false; - - numvertices = 0; - for (iter = inpolys->front(); iter; iter = iter->next()) { - numvertices += iter->get().GetNumPoints(); - } - - maxnumvertices = numvertices * 3; - vertices = new MonotoneVertex[maxnumvertices]; - newnumvertices = numvertices; - - polystartindex = 0; - for (iter = inpolys->front(); iter; iter = iter->next()) { - poly = &(iter->get()); - polyendindex = polystartindex + poly->GetNumPoints() - 1; - for (i = 0; i < poly->GetNumPoints(); i++) { - vertices[i + polystartindex].p = poly->GetPoint(i); - if (i == 0) { - vertices[i + polystartindex].previous = polyendindex; - } else { - vertices[i + polystartindex].previous = i + polystartindex - 1; - } - if (i == (poly->GetNumPoints() - 1)) { - vertices[i + polystartindex].next = polystartindex; - } else { - vertices[i + polystartindex].next = i + polystartindex + 1; - } - } - polystartindex = polyendindex + 1; - } - - // Construct the priority queue. - long *priority = new long[numvertices]; - for (i = 0; i < numvertices; i++) { - priority[i] = i; - } - std::sort(priority, &(priority[numvertices]), VertexSorter(vertices)); - - // Determine vertex types. - TPPLVertexType *vertextypes = new TPPLVertexType[maxnumvertices]; - for (i = 0; i < numvertices; i++) { - v = &(vertices[i]); - vprev = &(vertices[v->previous]); - vnext = &(vertices[v->next]); - - if (Below(vprev->p, v->p) && Below(vnext->p, v->p)) { - if (IsConvex(vnext->p, vprev->p, v->p)) { - vertextypes[i] = TPPL_VERTEXTYPE_START; - } else { - vertextypes[i] = TPPL_VERTEXTYPE_SPLIT; - } - } else if (Below(v->p, vprev->p) && Below(v->p, vnext->p)) { - if (IsConvex(vnext->p, vprev->p, v->p)) { - vertextypes[i] = TPPL_VERTEXTYPE_END; - } else { - vertextypes[i] = TPPL_VERTEXTYPE_MERGE; - } - } else { - vertextypes[i] = TPPL_VERTEXTYPE_REGULAR; - } - } - - // Helpers. - long *helpers = new long[maxnumvertices]; - - // Binary search tree that holds edges intersecting the scanline. - // Note that while set doesn't actually have to be implemented as - // a tree, complexity requirements for operations are the same as - // for the balanced binary search tree. - RBSet edgeTree; - // Store iterators to the edge tree elements. - // This makes deleting existing edges much faster. - RBSet::Element **edgeTreeIterators, *edgeIter; - edgeTreeIterators = new RBSet::Element *[maxnumvertices]; - //Pair::iterator, bool> edgeTreeRet; - for (i = 0; i < numvertices; i++) { - edgeTreeIterators[i] = nullptr; - } - - // For each vertex. - for (i = 0; i < numvertices; i++) { - vindex = priority[i]; - v = &(vertices[vindex]); - vindex2 = vindex; - v2 = v; - - // Depending on the vertex type, do the appropriate action. - // Comments in the following sections are copied from - // "Computational Geometry: Algorithms and Applications". - // Notation: e_i = e subscript i, v_i = v subscript i, etc. - switch (vertextypes[vindex]) { - case TPPL_VERTEXTYPE_START: - // Insert e_i in T and set helper(e_i) to v_i. - newedge.p1 = v->p; - newedge.p2 = vertices[v->next].p; - newedge.index = vindex; - //edgeTreeRet = edgeTree.insert(newedge); - //edgeTreeIterators[vindex] = edgeTreeRet.first; - edgeTreeIterators[vindex] = edgeTree.insert(newedge); - helpers[vindex] = vindex; - break; - - case TPPL_VERTEXTYPE_END: - if (edgeTreeIterators[v->previous] == edgeTree.back()) { - error = true; - break; - } - // If helper(e_i - 1) is a merge vertex - if (vertextypes[helpers[v->previous]] == TPPL_VERTEXTYPE_MERGE) { - // Insert the diagonal connecting vi to helper(e_i - 1) in D. - AddDiagonal(vertices, &newnumvertices, vindex, helpers[v->previous], - vertextypes, edgeTreeIterators, &edgeTree, helpers); - } - // Delete e_i - 1 from T - edgeTree.erase(edgeTreeIterators[v->previous]); - break; - - case TPPL_VERTEXTYPE_SPLIT: - // Search in T to find the edge e_j directly left of v_i. - newedge.p1 = v->p; - newedge.p2 = v->p; - edgeIter = edgeTree.lower_bound(newedge); - if (edgeIter == nullptr || edgeIter == edgeTree.front()) { - error = true; - break; - } - edgeIter--; - // Insert the diagonal connecting vi to helper(e_j) in D. - AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->get().index], - vertextypes, edgeTreeIterators, &edgeTree, helpers); - vindex2 = newnumvertices - 2; - v2 = &(vertices[vindex2]); - // helper(e_j) in v_i. - helpers[edgeIter->get().index] = vindex; - // Insert e_i in T and set helper(e_i) to v_i. - newedge.p1 = v2->p; - newedge.p2 = vertices[v2->next].p; - newedge.index = vindex2; - //edgeTreeRet = edgeTree.insert(newedge); - //edgeTreeIterators[vindex2] = edgeTreeRet.first; - edgeTreeIterators[vindex2] = edgeTree.insert(newedge); - helpers[vindex2] = vindex2; - break; - - case TPPL_VERTEXTYPE_MERGE: - if (edgeTreeIterators[v->previous] == edgeTree.back()) { - error = true; - break; - } - // if helper(e_i - 1) is a merge vertex - if (vertextypes[helpers[v->previous]] == TPPL_VERTEXTYPE_MERGE) { - // Insert the diagonal connecting vi to helper(e_i - 1) in D. - AddDiagonal(vertices, &newnumvertices, vindex, helpers[v->previous], - vertextypes, edgeTreeIterators, &edgeTree, helpers); - vindex2 = newnumvertices - 2; - v2 = &(vertices[vindex2]); - } - // Delete e_i - 1 from T. - edgeTree.erase(edgeTreeIterators[v->previous]); - // Search in T to find the edge e_j directly left of v_i. - newedge.p1 = v->p; - newedge.p2 = v->p; - edgeIter = edgeTree.lower_bound(newedge); - if (edgeIter == nullptr || edgeIter == edgeTree.front()) { - error = true; - break; - } - edgeIter--; - // If helper(e_j) is a merge vertex. - if (vertextypes[helpers[edgeIter->get().index]] == TPPL_VERTEXTYPE_MERGE) { - // Insert the diagonal connecting v_i to helper(e_j) in D. - AddDiagonal(vertices, &newnumvertices, vindex2, helpers[edgeIter->get().index], - vertextypes, edgeTreeIterators, &edgeTree, helpers); - } - // helper(e_j) <- v_i - helpers[edgeIter->get().index] = vindex2; - break; - - case TPPL_VERTEXTYPE_REGULAR: - // If the interior of P lies to the right of v_i. - if (Below(v->p, vertices[v->previous].p)) { - if (edgeTreeIterators[v->previous] == edgeTree.back()) { - error = true; - break; - } - // If helper(e_i - 1) is a merge vertex. - if (vertextypes[helpers[v->previous]] == TPPL_VERTEXTYPE_MERGE) { - // Insert the diagonal connecting v_i to helper(e_i - 1) in D. - AddDiagonal(vertices, &newnumvertices, vindex, helpers[v->previous], - vertextypes, edgeTreeIterators, &edgeTree, helpers); - vindex2 = newnumvertices - 2; - v2 = &(vertices[vindex2]); - } - // Delete e_i - 1 from T. - edgeTree.erase(edgeTreeIterators[v->previous]); - // Insert e_i in T and set helper(e_i) to v_i. - newedge.p1 = v2->p; - newedge.p2 = vertices[v2->next].p; - newedge.index = vindex2; - //edgeTreeRet = edgeTree.insert(newedge); - //edgeTreeIterators[vindex2] = edgeTreeRet.first; - edgeTreeIterators[vindex2] = edgeTree.insert(newedge); - helpers[vindex2] = vindex; - } else { - // Search in T to find the edge e_j directly left of v_i. - newedge.p1 = v->p; - newedge.p2 = v->p; - edgeIter = edgeTree.lower_bound(newedge); - if (edgeIter == nullptr || edgeIter == edgeTree.front()) { - error = true; - break; - } - edgeIter = edgeIter->prev(); - // If helper(e_j) is a merge vertex. - if (vertextypes[helpers[edgeIter->get().index]] == TPPL_VERTEXTYPE_MERGE) { - // Insert the diagonal connecting v_i to helper(e_j) in D. - AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->get().index], - vertextypes, edgeTreeIterators, &edgeTree, helpers); - } - // helper(e_j) <- v_i. - helpers[edgeIter->get().index] = vindex; - } - break; - } - - if (error) - break; - } - - char *used = new char[newnumvertices]; - memset(used, 0, newnumvertices * sizeof(char)); - - if (!error) { - // Return result. - long size; - TPPLPoly mpoly; - for (i = 0; i < newnumvertices; i++) { - if (used[i]) { - continue; - } - v = &(vertices[i]); - vnext = &(vertices[v->next]); - size = 1; - while (vnext != v) { - vnext = &(vertices[vnext->next]); - size++; - } - mpoly.Init(size); - v = &(vertices[i]); - mpoly[0] = v->p; - vnext = &(vertices[v->next]); - size = 1; - used[i] = 1; - used[v->next] = 1; - while (vnext != v) { - mpoly[size] = vnext->p; - used[vnext->next] = 1; - vnext = &(vertices[vnext->next]); - size++; - } - monotonePolys->push_back(mpoly); - } - } - - // Cleanup. - delete[] vertices; - delete[] priority; - delete[] vertextypes; - delete[] edgeTreeIterators; - delete[] helpers; - delete[] used; - - if (error) { - return 0; - } else { - return 1; - } -} - -// Adds a diagonal to the doubly-connected list of vertices. -void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2, - TPPLVertexType *vertextypes, RBSet::Element **edgeTreeIterators, - RBSet *edgeTree, long *helpers) { - long newindex1, newindex2; - - newindex1 = *numvertices; - (*numvertices)++; - newindex2 = *numvertices; - (*numvertices)++; - - vertices[newindex1].p = vertices[index1].p; - vertices[newindex2].p = vertices[index2].p; - - vertices[newindex2].next = vertices[index2].next; - vertices[newindex1].next = vertices[index1].next; - - vertices[vertices[index2].next].previous = newindex2; - vertices[vertices[index1].next].previous = newindex1; - - vertices[index1].next = newindex2; - vertices[newindex2].previous = index1; - - vertices[index2].next = newindex1; - vertices[newindex1].previous = index2; - - // Update all relevant structures. - vertextypes[newindex1] = vertextypes[index1]; - edgeTreeIterators[newindex1] = edgeTreeIterators[index1]; - helpers[newindex1] = helpers[index1]; - if (edgeTreeIterators[newindex1] != edgeTree->back()) { - edgeTreeIterators[newindex1]->get().index = newindex1; - } - vertextypes[newindex2] = vertextypes[index2]; - edgeTreeIterators[newindex2] = edgeTreeIterators[index2]; - helpers[newindex2] = helpers[index2]; - if (edgeTreeIterators[newindex2] != edgeTree->back()) { - edgeTreeIterators[newindex2]->get().index = newindex2; - } -} - -bool TPPLPartition::Below(TPPLPoint &p1, TPPLPoint &p2) { - if (p1.y < p2.y) { - return true; - } else if (p1.y == p2.y) { - if (p1.x < p2.x) { - return true; - } - } - return false; -} - -// Sorts in the falling order of y values, if y is equal, x is used instead. -bool TPPLPartition::VertexSorter::operator()(long index1, long index2) { - if (vertices[index1].p.y > vertices[index2].p.y) { - return true; - } else if (vertices[index1].p.y == vertices[index2].p.y) { - if (vertices[index1].p.x > vertices[index2].p.x) { - return true; - } - } - return false; -} - -bool TPPLPartition::ScanLineEdge::IsConvex(const TPPLPoint &p1, const TPPLPoint &p2, const TPPLPoint &p3) const { - tppl_float tmp; - tmp = (p3.y - p1.y) * (p2.x - p1.x) - (p3.x - p1.x) * (p2.y - p1.y); - if (tmp > 0) { - return 1; - } - - return 0; -} - -bool TPPLPartition::ScanLineEdge::operator<(const ScanLineEdge &other) const { - if (other.p1.y == other.p2.y) { - if (p1.y == p2.y) { - return (p1.y < other.p1.y); - } - return IsConvex(p1, p2, other.p1); - } else if (p1.y == p2.y) { - return !IsConvex(other.p1, other.p2, p1); - } else if (p1.y < other.p1.y) { - return !IsConvex(other.p1, other.p2, p1); - } else { - return IsConvex(p1, p2, other.p1); - } -} - -// Triangulates monotone polygon. -// Time complexity: O(n) -// Space complexity: O(n) -int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles) { - if (!inPoly->Valid()) { - return 0; - } - - long i, i2, j, topindex, bottomindex, leftindex, rightindex, vindex; - TPPLPoint *points = NULL; - long numpoints; - TPPLPoly triangle; - - numpoints = inPoly->GetNumPoints(); - points = inPoly->GetPoints(); - - // Trivial case. - if (numpoints == 3) { - triangles->push_back(*inPoly); - return 1; - } - - topindex = 0; - bottomindex = 0; - for (i = 1; i < numpoints; i++) { - if (Below(points[i], points[bottomindex])) { - bottomindex = i; - } - if (Below(points[topindex], points[i])) { - topindex = i; - } - } - - // Check if the poly is really monotone. - i = topindex; - while (i != bottomindex) { - i2 = i + 1; - if (i2 >= numpoints) { - i2 = 0; - } - if (!Below(points[i2], points[i])) { - return 0; - } - i = i2; - } - i = bottomindex; - while (i != topindex) { - i2 = i + 1; - if (i2 >= numpoints) { - i2 = 0; - } - if (!Below(points[i], points[i2])) { - return 0; - } - i = i2; - } - - char *vertextypes = new char[numpoints]; - long *priority = new long[numpoints]; - - // Merge left and right vertex chains. - priority[0] = topindex; - vertextypes[topindex] = 0; - leftindex = topindex + 1; - if (leftindex >= numpoints) { - leftindex = 0; - } - rightindex = topindex - 1; - if (rightindex < 0) { - rightindex = numpoints - 1; - } - for (i = 1; i < (numpoints - 1); i++) { - if (leftindex == bottomindex) { - priority[i] = rightindex; - rightindex--; - if (rightindex < 0) { - rightindex = numpoints - 1; - } - vertextypes[priority[i]] = -1; - } else if (rightindex == bottomindex) { - priority[i] = leftindex; - leftindex++; - if (leftindex >= numpoints) { - leftindex = 0; - } - vertextypes[priority[i]] = 1; - } else { - if (Below(points[leftindex], points[rightindex])) { - priority[i] = rightindex; - rightindex--; - if (rightindex < 0) { - rightindex = numpoints - 1; - } - vertextypes[priority[i]] = -1; - } else { - priority[i] = leftindex; - leftindex++; - if (leftindex >= numpoints) { - leftindex = 0; - } - vertextypes[priority[i]] = 1; - } - } - } - priority[i] = bottomindex; - vertextypes[bottomindex] = 0; - - long *stack = new long[numpoints]; - long stackptr = 0; - - stack[0] = priority[0]; - stack[1] = priority[1]; - stackptr = 2; - - // For each vertex from top to bottom trim as many triangles as possible. - for (i = 2; i < (numpoints - 1); i++) { - vindex = priority[i]; - if (vertextypes[vindex] != vertextypes[stack[stackptr - 1]]) { - for (j = 0; j < (stackptr - 1); j++) { - if (vertextypes[vindex] == 1) { - triangle.Triangle(points[stack[j + 1]], points[stack[j]], points[vindex]); - } else { - triangle.Triangle(points[stack[j]], points[stack[j + 1]], points[vindex]); - } - triangles->push_back(triangle); - } - stack[0] = priority[i - 1]; - stack[1] = priority[i]; - stackptr = 2; - } else { - stackptr--; - while (stackptr > 0) { - if (vertextypes[vindex] == 1) { - if (IsConvex(points[vindex], points[stack[stackptr - 1]], points[stack[stackptr]])) { - triangle.Triangle(points[vindex], points[stack[stackptr - 1]], points[stack[stackptr]]); - triangles->push_back(triangle); - stackptr--; - } else { - break; - } - } else { - if (IsConvex(points[vindex], points[stack[stackptr]], points[stack[stackptr - 1]])) { - triangle.Triangle(points[vindex], points[stack[stackptr]], points[stack[stackptr - 1]]); - triangles->push_back(triangle); - stackptr--; - } else { - break; - } - } - } - stackptr++; - stack[stackptr] = vindex; - stackptr++; - } - } - vindex = priority[i]; - for (j = 0; j < (stackptr - 1); j++) { - if (vertextypes[stack[j + 1]] == 1) { - triangle.Triangle(points[stack[j]], points[stack[j + 1]], points[vindex]); - } else { - triangle.Triangle(points[stack[j + 1]], points[stack[j]], points[vindex]); - } - triangles->push_back(triangle); - } - - delete[] priority; - delete[] vertextypes; - delete[] stack; - - return 1; -} - -int TPPLPartition::Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles) { - TPPLPolyList monotone; - TPPLPolyList::Element *iter; - - if (!MonotonePartition(inpolys, &monotone)) { - return 0; - } - for (iter = monotone.front(); iter; iter = iter->next()) { - if (!TriangulateMonotone(&(iter->get()), triangles)) { - return 0; - } - } - return 1; -} - -int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles) { - TPPLPolyList polys; - polys.push_back(*poly); - - return Triangulate_MONO(&polys, triangles); -} diff --git a/modules/clipper2/polypartition.h b/modules/clipper2/polypartition.h deleted file mode 100644 index 5d38cc7ee..000000000 --- a/modules/clipper2/polypartition.h +++ /dev/null @@ -1,378 +0,0 @@ -/*************************************************************************/ -/* Copyright (c) 2011-2021 Ivan Fratric and contributors. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef POLYPARTITION_H -#define POLYPARTITION_H - -#include "core/math/vector2.h" -#include "core/containers/list.h" -#include "core/containers/rb_set.h" - -typedef double tppl_float; - -enum TPPLOrientation { - TPPL_ORIENTATION_CW = -1, - TPPL_ORIENTATION_NONE = 0, - TPPL_ORIENTATION_CCW = 1, -}; - -enum TPPLVertexType { - TPPL_VERTEXTYPE_REGULAR = 0, - TPPL_VERTEXTYPE_START = 1, - TPPL_VERTEXTYPE_END = 2, - TPPL_VERTEXTYPE_SPLIT = 3, - TPPL_VERTEXTYPE_MERGE = 4, -}; - -// 2D point structure. -typedef Vector2 TPPLPoint; - -// Polygon implemented as an array of points with a "hole" flag. -class TPPLPoly { - protected: - TPPLPoint *points; - long numpoints; - bool hole; - - public: - // Constructors and destructors. - TPPLPoly(); - ~TPPLPoly(); - - TPPLPoly(const TPPLPoly &src); - TPPLPoly &operator=(const TPPLPoly &src); - - // Getters and setters. - long GetNumPoints() const { - return numpoints; - } - - bool IsHole() const { - return hole; - } - - void SetHole(bool p_hole) { - this->hole = p_hole; - } - - TPPLPoint &GetPoint(long i) { - return points[i]; - } - - const TPPLPoint &GetPoint(long i) const { - return points[i]; - } - - TPPLPoint *GetPoints() { - return points; - } - - TPPLPoint &operator[](int i) { - return points[i]; - } - - const TPPLPoint &operator[](int i) const { - return points[i]; - } - - // Clears the polygon points. - void Clear(); - - // Inits the polygon with numpoints vertices. - void Init(long numpoints); - - // Creates a triangle with points p1, p2, and p3. - void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3); - - // Inverts the orfer of vertices. - void Invert(); - - // Returns the orientation of the polygon. - // Possible values: - // TPPL_ORIENTATION_CCW: Polygon vertices are in counter-clockwise order. - // TPPL_ORIENTATION_CW: Polygon vertices are in clockwise order. - // TPPL_ORIENTATION_NONE: The polygon has no (measurable) area. - TPPLOrientation GetOrientation() const; - - // Sets the polygon orientation. - // Possible values: - // TPPL_ORIENTATION_CCW: Sets vertices in counter-clockwise order. - // TPPL_ORIENTATION_CW: Sets vertices in clockwise order. - // TPPL_ORIENTATION_NONE: Reverses the orientation of the vertices if there - // is one, otherwise does nothing (if orientation is already NONE). - void SetOrientation(TPPLOrientation orientation); - - // Checks whether a polygon is valid or not. - inline bool Valid() const { return this->numpoints >= 3; } -}; - -#ifdef TPPL_ALLOCATOR -typedef List TPPLPolyList; -#else -typedef List TPPLPolyList; -#endif - -class TPPLPartition { - protected: - struct PartitionVertex { - bool isActive; - bool isConvex; - bool isEar; - - TPPLPoint p; - tppl_float angle; - PartitionVertex *previous; - PartitionVertex *next; - - PartitionVertex(); - }; - - struct MonotoneVertex { - TPPLPoint p; - long previous; - long next; - }; - - class VertexSorter { - MonotoneVertex *vertices; - -public: - VertexSorter(MonotoneVertex *v) : - vertices(v) {} - bool operator()(long index1, long index2); - }; - - struct Diagonal { - long index1; - long index2; - }; - -#ifdef TPPL_ALLOCATOR - typedef List DiagonalList; -#else - typedef List DiagonalList; -#endif - - // Dynamic programming state for minimum-weight triangulation. - struct DPState { - bool visible; - tppl_float weight; - long bestvertex; - }; - - // Dynamic programming state for convex partitioning. - struct DPState2 { - bool visible; - long weight; - DiagonalList pairs; - }; - - // Edge that intersects the scanline. - struct ScanLineEdge { - mutable long index; - TPPLPoint p1; - TPPLPoint p2; - - // Determines if the edge is to the left of another edge. - bool operator<(const ScanLineEdge &other) const; - - bool IsConvex(const TPPLPoint &p1, const TPPLPoint &p2, const TPPLPoint &p3) const; - }; - - // Standard helper functions. - bool IsConvex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3); - bool IsReflex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3); - bool IsInside(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p); - - bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p); - bool InCone(PartitionVertex *v, TPPLPoint &p); - - int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22); - - TPPLPoint Normalize(const TPPLPoint &p); - tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2); - - // Helper functions for Triangulate_EC. - void UpdateVertexReflexity(PartitionVertex *v); - void UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices); - - // Helper functions for ConvexPartition_OPT. - void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates); - void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates); - void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates); - - // Helper functions for MonotonePartition. - bool Below(TPPLPoint &p1, TPPLPoint &p2); - void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2, - TPPLVertexType *vertextypes, RBSet::Element **edgeTreeIterators, - RBSet *edgeTree, long *helpers); - - // Triangulates a monotone polygon, used in Triangulate_MONO. - int TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles); - - public: - // Simple heuristic procedure for removing holes from a list of polygons. - // It works by creating a diagonal from the right-most hole vertex - // to some other visible vertex. - // Time complexity: O(h*(n^2)), h is the # of holes, n is the # of vertices. - // Space complexity: O(n) - // params: - // inpolys: - // A list of polygons that can contain holes. - // Vertices of all non-hole polys have to be in counter-clockwise order. - // Vertices of all hole polys have to be in clockwise order. - // outpolys: - // A list of polygons without holes. - // Returns 1 on success, 0 on failure. - int RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys); - - // Triangulates a polygon by ear clipping. - // Time complexity: O(n^2), n is the number of vertices. - // Space complexity: O(n) - // params: - // poly: - // An input polygon to be triangulated. - // Vertices have to be in counter-clockwise order. - // triangles: - // A list of triangles (result). - // Returns 1 on success, 0 on failure. - int Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles); - - // Triangulates a list of polygons that may contain holes by ear clipping - // algorithm. It first calls RemoveHoles to get rid of the holes, and then - // calls Triangulate_EC for each resulting polygon. - // Time complexity: O(h*(n^2)), h is the # of holes, n is the # of vertices. - // Space complexity: O(n) - // params: - // inpolys: - // A list of polygons to be triangulated (can contain holes). - // Vertices of all non-hole polys have to be in counter-clockwise order. - // Vertices of all hole polys have to be in clockwise order. - // triangles: - // A list of triangles (result). - // Returns 1 on success, 0 on failure. - int Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles); - - // Creates an optimal polygon triangulation in terms of minimal edge length. - // Time complexity: O(n^3), n is the number of vertices - // Space complexity: O(n^2) - // params: - // poly: - // An input polygon to be triangulated. - // Vertices have to be in counter-clockwise order. - // triangles: - // A list of triangles (result). - // Returns 1 on success, 0 on failure. - int Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles); - - // Triangulates a polygon by first partitioning it into monotone polygons. - // Time complexity: O(n*log(n)), n is the number of vertices. - // Space complexity: O(n) - // params: - // poly: - // An input polygon to be triangulated. - // Vertices have to be in counter-clockwise order. - // triangles: - // A list of triangles (result). - // Returns 1 on success, 0 on failure. - int Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles); - - // Triangulates a list of polygons by first - // partitioning them into monotone polygons. - // Time complexity: O(n*log(n)), n is the number of vertices. - // Space complexity: O(n) - // params: - // inpolys: - // A list of polygons to be triangulated (can contain holes). - // Vertices of all non-hole polys have to be in counter-clockwise order. - // Vertices of all hole polys have to be in clockwise order. - // triangles: - // A list of triangles (result). - // Returns 1 on success, 0 on failure. - int Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles); - - // Creates a monotone partition of a list of polygons that - // can contain holes. Triangulates a set of polygons by - // first partitioning them into monotone polygons. - // Time complexity: O(n*log(n)), n is the number of vertices. - // Space complexity: O(n) - // params: - // inpolys: - // A list of polygons to be triangulated (can contain holes). - // Vertices of all non-hole polys have to be in counter-clockwise order. - // Vertices of all hole polys have to be in clockwise order. - // monotonePolys: - // A list of monotone polygons (result). - // Returns 1 on success, 0 on failure. - int MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys); - - // Partitions a polygon into convex polygons by using the - // Hertel-Mehlhorn algorithm. The algorithm gives at most four times - // the number of parts as the optimal algorithm, however, in practice - // it works much better than that and often gives optimal partition. - // It uses triangulation obtained by ear clipping as intermediate result. - // Time complexity O(n^2), n is the number of vertices. - // Space complexity: O(n) - // params: - // poly: - // An input polygon to be partitioned. - // Vertices have to be in counter-clockwise order. - // parts: - // Resulting list of convex polygons. - // Returns 1 on success, 0 on failure. - int ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts); - - // Partitions a list of polygons into convex parts by using the - // Hertel-Mehlhorn algorithm. The algorithm gives at most four times - // the number of parts as the optimal algorithm, however, in practice - // it works much better than that and often gives optimal partition. - // It uses triangulation obtained by ear clipping as intermediate result. - // Time complexity O(n^2), n is the number of vertices. - // Space complexity: O(n) - // params: - // inpolys: - // An input list of polygons to be partitioned. Vertices of - // all non-hole polys have to be in counter-clockwise order. - // Vertices of all hole polys have to be in clockwise order. - // parts: - // Resulting list of convex polygons. - // Returns 1 on success, 0 on failure. - int ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts); - - // Optimal convex partitioning (in terms of number of resulting - // convex polygons) using the Keil-Snoeyink algorithm. - // For reference, see M. Keil, J. Snoeyink, "On the time bound for - // convex decomposition of simple polygons", 1998. - // Time complexity O(n^3), n is the number of vertices. - // Space complexity: O(n^3) - // params: - // poly: - // An input polygon to be partitioned. - // Vertices have to be in counter-clockwise order. - // parts: - // Resulting list of convex polygons. - // Returns 1 on success, 0 on failure. - int ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts); -}; - -#endif diff --git a/modules/clipper2/register_types.cpp b/modules/clipper2/register_types.cpp deleted file mode 100644 index 7e795db02..000000000 --- a/modules/clipper2/register_types.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "register_types.h" - -void register_clipper2_types(ModuleRegistrationLevel p_level) { -} - -void unregister_clipper2_types(ModuleRegistrationLevel p_level) { -} diff --git a/modules/clipper2/register_types.h b/modules/clipper2/register_types.h deleted file mode 100644 index f9c4104d0..000000000 --- a/modules/clipper2/register_types.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CLIPPER2_REGISTER_TYPES_H -#define CLIPPER2_REGISTER_TYPES_H - -#include "modules/register_module_types.h" - -void register_clipper2_types(ModuleRegistrationLevel p_level); -void unregister_clipper2_types(ModuleRegistrationLevel p_level); - -#endif \ No newline at end of file diff --git a/modules/navigation_geometry_parsers/SCsub b/modules/navigation_geometry_parsers/SCsub index 4e2f76402..53a8a35b9 100644 --- a/modules/navigation_geometry_parsers/SCsub +++ b/modules/navigation_geometry_parsers/SCsub @@ -14,9 +14,3 @@ env_navigation.add_source_files(module_obj, "geometry_parser_2d/*.cpp") env_navigation.add_source_files(module_obj, "geometry_parser_3d/*.cpp") env.modules_sources += module_obj - -# Temp TODO Remove -if env_navigation.msvc: - env_navigation.Append(CXXFLAGS=['/std:c++17']) -else: - env_navigation.Append(CXXFLAGS=['-std=c++17'])