File indexing completed on 2025-04-20 03:36:28
0001 /******************************************************************************* 0002 * Author : Angus Johnson * 0003 * Date : 6 October 2023 * 0004 * Website : http://www.angusj.com * 0005 * Copyright : Angus Johnson 2010-2023 * 0006 * Purpose : This is the main polygon clipping module * 0007 * License : http://www.boost.org/LICENSE_1_0.txt * 0008 *******************************************************************************/ 0009 0010 #ifndef CLIPPER_ENGINE_H 0011 #define CLIPPER_ENGINE_H 0012 0013 constexpr auto CLIPPER2_VERSION = "1.2.3"; 0014 0015 #include <cstdlib> 0016 #include <stdint.h> //#541 0017 #include <iostream> 0018 #include <queue> 0019 #include <vector> 0020 #include <functional> 0021 #include <numeric> 0022 #include <memory> 0023 0024 #include "clipper.core.h" 0025 0026 namespace Clipper2Lib { 0027 0028 struct Scanline; 0029 struct IntersectNode; 0030 struct Active; 0031 struct Vertex; 0032 struct LocalMinima; 0033 struct OutRec; 0034 struct HorzSegment; 0035 0036 //Note: all clipping operations except for Difference are commutative. 0037 enum class ClipType { None, Intersection, Union, Difference, Xor }; 0038 0039 enum class PathType { Subject, Clip }; 0040 enum class JoinWith { None, Left, Right }; 0041 0042 enum class VertexFlags : uint32_t { 0043 None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 0044 }; 0045 0046 constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) 0047 { 0048 return (enum VertexFlags)(uint32_t(a) & uint32_t(b)); 0049 } 0050 0051 constexpr enum VertexFlags operator |(enum VertexFlags a, enum VertexFlags b) 0052 { 0053 return (enum VertexFlags)(uint32_t(a) | uint32_t(b)); 0054 } 0055 0056 struct Vertex { 0057 Point64 pt; 0058 Vertex* next = nullptr; 0059 Vertex* prev = nullptr; 0060 VertexFlags flags = VertexFlags::None; 0061 }; 0062 0063 struct OutPt { 0064 Point64 pt; 0065 OutPt* next = nullptr; 0066 OutPt* prev = nullptr; 0067 OutRec* outrec; 0068 HorzSegment* horz = nullptr; 0069 0070 OutPt(const Point64& pt_, OutRec* outrec_): pt(pt_), outrec(outrec_) { 0071 next = this; 0072 prev = this; 0073 } 0074 }; 0075 0076 class PolyPath; 0077 class PolyPath64; 0078 class PolyPathD; 0079 using PolyTree64 = PolyPath64; 0080 using PolyTreeD = PolyPathD; 0081 0082 struct OutRec; 0083 typedef std::vector<OutRec*> OutRecList; 0084 0085 //OutRec: contains a path in the clipping solution. Edges in the AEL will 0086 //have OutRec pointers assigned when they form part of the clipping solution. 0087 struct OutRec { 0088 size_t idx = 0; 0089 OutRec* owner = nullptr; 0090 Active* front_edge = nullptr; 0091 Active* back_edge = nullptr; 0092 OutPt* pts = nullptr; 0093 PolyPath* polypath = nullptr; 0094 OutRecList* splits = nullptr; 0095 OutRec* recursive_split = nullptr; 0096 Rect64 bounds = {}; 0097 Path64 path; 0098 bool is_open = false; 0099 0100 ~OutRec() { 0101 if (splits) delete splits; 0102 // nb: don't delete the split pointers 0103 // as these are owned by ClipperBase's outrec_list_ 0104 }; 0105 }; 0106 0107 /////////////////////////////////////////////////////////////////// 0108 //Important: UP and DOWN here are premised on Y-axis positive down 0109 //displays, which is the orientation used in Clipper's development. 0110 /////////////////////////////////////////////////////////////////// 0111 0112 struct Active { 0113 Point64 bot; 0114 Point64 top; 0115 int64_t curr_x = 0; //current (updated at every new scanline) 0116 double dx = 0.0; 0117 int wind_dx = 1; //1 or -1 depending on winding direction 0118 int wind_cnt = 0; 0119 int wind_cnt2 = 0; //winding count of the opposite polytype 0120 OutRec* outrec = nullptr; 0121 //AEL: 'active edge list' (Vatti's AET - active edge table) 0122 // a linked list of all edges (from left to right) that are present 0123 // (or 'active') within the current scanbeam (a horizontal 'beam' that 0124 // sweeps from bottom to top over the paths in the clipping operation). 0125 Active* prev_in_ael = nullptr; 0126 Active* next_in_ael = nullptr; 0127 //SEL: 'sorted edge list' (Vatti's ST - sorted table) 0128 // linked list used when sorting edges into their new positions at the 0129 // top of scanbeams, but also (re)used to process horizontals. 0130 Active* prev_in_sel = nullptr; 0131 Active* next_in_sel = nullptr; 0132 Active* jump = nullptr; 0133 Vertex* vertex_top = nullptr; 0134 LocalMinima* local_min = nullptr; // the bottom of an edge 'bound' (also Vatti) 0135 bool is_left_bound = false; 0136 JoinWith join_with = JoinWith::None; 0137 }; 0138 0139 struct LocalMinima { 0140 Vertex* vertex; 0141 PathType polytype; 0142 bool is_open; 0143 LocalMinima(Vertex* v, PathType pt, bool open) : 0144 vertex(v), polytype(pt), is_open(open){} 0145 }; 0146 0147 struct IntersectNode { 0148 Point64 pt; 0149 Active* edge1; 0150 Active* edge2; 0151 IntersectNode() : pt(Point64(0,0)), edge1(NULL), edge2(NULL) {} 0152 IntersectNode(Active* e1, Active* e2, Point64& pt_) : 0153 pt(pt_), edge1(e1), edge2(e2) {} 0154 }; 0155 0156 struct HorzSegment { 0157 OutPt* left_op; 0158 OutPt* right_op = nullptr; 0159 bool left_to_right = true; 0160 HorzSegment() : left_op(nullptr) { } 0161 explicit HorzSegment(OutPt* op) : left_op(op) { } 0162 }; 0163 0164 struct HorzJoin { 0165 OutPt* op1 = nullptr; 0166 OutPt* op2 = nullptr; 0167 HorzJoin() {}; 0168 explicit HorzJoin(OutPt* ltr, OutPt* rtl) : op1(ltr), op2(rtl) { } 0169 }; 0170 0171 #ifdef USINGZ 0172 typedef std::function<void(const Point64& e1bot, const Point64& e1top, 0173 const Point64& e2bot, const Point64& e2top, Point64& pt)> ZCallback64; 0174 0175 typedef std::function<void(const PointD& e1bot, const PointD& e1top, 0176 const PointD& e2bot, const PointD& e2top, PointD& pt)> ZCallbackD; 0177 #endif 0178 0179 typedef std::vector<HorzSegment> HorzSegmentList; 0180 typedef std::unique_ptr<LocalMinima> LocalMinima_ptr; 0181 typedef std::vector<LocalMinima_ptr> LocalMinimaList; 0182 typedef std::vector<IntersectNode> IntersectNodeList; 0183 0184 // ReuseableDataContainer64 ------------------------------------------------ 0185 0186 class ReuseableDataContainer64 { 0187 private: 0188 friend class ClipperBase; 0189 LocalMinimaList minima_list_; 0190 std::vector<Vertex*> vertex_lists_; 0191 void AddLocMin(Vertex& vert, PathType polytype, bool is_open); 0192 public: 0193 virtual ~ReuseableDataContainer64(); 0194 void Clear(); 0195 void AddPaths(const Paths64& paths, PathType polytype, bool is_open); 0196 }; 0197 0198 // ClipperBase ------------------------------------------------------------- 0199 0200 class ClipperBase { 0201 private: 0202 ClipType cliptype_ = ClipType::None; 0203 FillRule fillrule_ = FillRule::EvenOdd; 0204 FillRule fillpos = FillRule::Positive; 0205 int64_t bot_y_ = 0; 0206 bool minima_list_sorted_ = false; 0207 bool using_polytree_ = false; 0208 Active* actives_ = nullptr; 0209 Active *sel_ = nullptr; 0210 LocalMinimaList minima_list_; //pointers in case of memory reallocs 0211 LocalMinimaList::iterator current_locmin_iter_; 0212 std::vector<Vertex*> vertex_lists_; 0213 std::priority_queue<int64_t> scanline_list_; 0214 IntersectNodeList intersect_nodes_; 0215 HorzSegmentList horz_seg_list_; 0216 std::vector<HorzJoin> horz_join_list_; 0217 void Reset(); 0218 inline void InsertScanline(int64_t y); 0219 inline bool PopScanline(int64_t &y); 0220 inline bool PopLocalMinima(int64_t y, LocalMinima*& local_minima); 0221 void DisposeAllOutRecs(); 0222 void DisposeVerticesAndLocalMinima(); 0223 void DeleteEdges(Active*& e); 0224 inline void AddLocMin(Vertex &vert, PathType polytype, bool is_open); 0225 bool IsContributingClosed(const Active &e) const; 0226 inline bool IsContributingOpen(const Active &e) const; 0227 void SetWindCountForClosedPathEdge(Active &edge); 0228 void SetWindCountForOpenPathEdge(Active &e); 0229 void InsertLocalMinimaIntoAEL(int64_t bot_y); 0230 void InsertLeftEdge(Active &e); 0231 inline void PushHorz(Active &e); 0232 inline bool PopHorz(Active *&e); 0233 inline OutPt* StartOpenPath(Active &e, const Point64& pt); 0234 inline void UpdateEdgeIntoAEL(Active *e); 0235 OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt); 0236 inline void DeleteFromAEL(Active &e); 0237 inline void AdjustCurrXAndCopyToSEL(const int64_t top_y); 0238 void DoIntersections(const int64_t top_y); 0239 void AddNewIntersectNode(Active &e1, Active &e2, const int64_t top_y); 0240 bool BuildIntersectList(const int64_t top_y); 0241 void ProcessIntersectList(); 0242 void SwapPositionsInAEL(Active& edge1, Active& edge2); 0243 OutRec* NewOutRec(); 0244 OutPt* AddOutPt(const Active &e, const Point64& pt); 0245 OutPt* AddLocalMinPoly(Active &e1, Active &e2, 0246 const Point64& pt, bool is_new = false); 0247 OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt); 0248 void DoHorizontal(Active &horz); 0249 bool ResetHorzDirection(const Active &horz, const Vertex* max_vertex, 0250 int64_t &horz_left, int64_t &horz_right); 0251 void DoTopOfScanbeam(const int64_t top_y); 0252 Active *DoMaxima(Active &e); 0253 void JoinOutrecPaths(Active &e1, Active &e2); 0254 void FixSelfIntersects(OutRec* outrec); 0255 void DoSplitOp(OutRec* outRec, OutPt* splitOp); 0256 0257 inline void AddTrialHorzJoin(OutPt* op); 0258 void ConvertHorzSegsToJoins(); 0259 void ProcessHorzJoins(); 0260 0261 void Split(Active& e, const Point64& pt); 0262 inline void CheckJoinLeft(Active& e, 0263 const Point64& pt, bool check_curr_x = false); 0264 inline void CheckJoinRight(Active& e, 0265 const Point64& pt, bool check_curr_x = false); 0266 protected: 0267 int error_code_ = 0; 0268 bool has_open_paths_ = false; 0269 bool succeeded_ = true; 0270 OutRecList outrec_list_; //pointers in case list memory reallocated 0271 bool ExecuteInternal(ClipType ct, FillRule ft, bool use_polytrees); 0272 void CleanCollinear(OutRec* outrec); 0273 bool CheckBounds(OutRec* outrec); 0274 bool CheckSplitOwner(OutRec* outrec, OutRecList* splits); 0275 void RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath); 0276 #ifdef USINGZ 0277 ZCallback64 zCallback_ = nullptr; 0278 void SetZ(const Active& e1, const Active& e2, Point64& pt); 0279 #endif 0280 void CleanUp(); // unlike Clear, CleanUp preserves added paths 0281 void AddPath(const Path64& path, PathType polytype, bool is_open); 0282 void AddPaths(const Paths64& paths, PathType polytype, bool is_open); 0283 public: 0284 virtual ~ClipperBase(); 0285 int ErrorCode() { return error_code_; }; 0286 bool PreserveCollinear = true; 0287 bool ReverseSolution = false; 0288 void Clear(); 0289 void AddReuseableData(const ReuseableDataContainer64& reuseable_data); 0290 #ifdef USINGZ 0291 int64_t DefaultZ = 0; 0292 #endif 0293 }; 0294 0295 // PolyPath / PolyTree -------------------------------------------------------- 0296 0297 //PolyTree: is intended as a READ-ONLY data structure for CLOSED paths returned 0298 //by clipping operations. While this structure is more complex than the 0299 //alternative Paths structure, it does preserve path 'ownership' - ie those 0300 //paths that contain (or own) other paths. This will be useful to some users. 0301 0302 class PolyPath { 0303 protected: 0304 PolyPath* parent_; 0305 public: 0306 PolyPath(PolyPath* parent = nullptr): parent_(parent){} 0307 virtual ~PolyPath() {}; 0308 //https://en.cppreference.com/w/cpp/language/rule_of_three 0309 PolyPath(const PolyPath&) = delete; 0310 PolyPath& operator=(const PolyPath&) = delete; 0311 0312 unsigned Level() const 0313 { 0314 unsigned result = 0; 0315 const PolyPath* p = parent_; 0316 while (p) { ++result; p = p->parent_; } 0317 return result; 0318 } 0319 0320 virtual PolyPath* AddChild(const Path64& path) = 0; 0321 0322 virtual void Clear() = 0; 0323 virtual size_t Count() const { return 0; } 0324 0325 const PolyPath* Parent() const { return parent_; } 0326 0327 bool IsHole() const 0328 { 0329 unsigned lvl = Level(); 0330 //Even levels except level 0 0331 return lvl && !(lvl & 1); 0332 } 0333 }; 0334 0335 typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List; 0336 typedef typename std::vector<std::unique_ptr<PolyPathD>> PolyPathDList; 0337 0338 class PolyPath64 : public PolyPath { 0339 private: 0340 PolyPath64List childs_; 0341 Path64 polygon_; 0342 public: 0343 explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {} 0344 0345 ~PolyPath64() { 0346 childs_.resize(0); 0347 } 0348 0349 const PolyPath64* operator [] (size_t index) const 0350 { 0351 return childs_[index].get(); //std::unique_ptr 0352 } 0353 0354 const PolyPath64* Child(size_t index) const 0355 { 0356 return childs_[index].get(); 0357 } 0358 0359 PolyPath64List::const_iterator begin() const { return childs_.cbegin(); } 0360 PolyPath64List::const_iterator end() const { return childs_.cend(); } 0361 0362 PolyPath64* AddChild(const Path64& path) override 0363 { 0364 auto p = std::make_unique<PolyPath64>(this); 0365 auto* result = childs_.emplace_back(std::move(p)).get(); 0366 result->polygon_ = path; 0367 return result; 0368 } 0369 0370 void Clear() override 0371 { 0372 childs_.resize(0); 0373 } 0374 0375 size_t Count() const override 0376 { 0377 return childs_.size(); 0378 } 0379 0380 const Path64& Polygon() const { return polygon_; }; 0381 0382 double Area() const 0383 { 0384 return std::accumulate(childs_.cbegin(), childs_.cend(), 0385 Clipper2Lib::Area<int64_t>(polygon_), 0386 [](double a, const auto& child) {return a + child->Area(); }); 0387 } 0388 0389 }; 0390 0391 class PolyPathD : public PolyPath { 0392 private: 0393 PolyPathDList childs_; 0394 double scale_; 0395 PathD polygon_; 0396 public: 0397 explicit PolyPathD(PolyPathD* parent = nullptr) : PolyPath(parent) 0398 { 0399 scale_ = parent ? parent->scale_ : 1.0; 0400 } 0401 0402 ~PolyPathD() { 0403 childs_.resize(0); 0404 } 0405 0406 const PolyPathD* operator [] (size_t index) const 0407 { 0408 return childs_[index].get(); 0409 } 0410 0411 const PolyPathD* Child(size_t index) const 0412 { 0413 return childs_[index].get(); 0414 } 0415 0416 PolyPathDList::const_iterator begin() const { return childs_.cbegin(); } 0417 PolyPathDList::const_iterator end() const { return childs_.cend(); } 0418 0419 void SetScale(double value) { scale_ = value; } 0420 double Scale() { return scale_; } 0421 PolyPathD* AddChild(const Path64& path) override 0422 { 0423 int error_code = 0; 0424 auto p = std::make_unique<PolyPathD>(this); 0425 PolyPathD* result = childs_.emplace_back(std::move(p)).get(); 0426 result->polygon_ = ScalePath<double, int64_t>(path, scale_, error_code); 0427 return result; 0428 } 0429 0430 void Clear() override 0431 { 0432 childs_.resize(0); 0433 } 0434 0435 size_t Count() const override 0436 { 0437 return childs_.size(); 0438 } 0439 0440 const PathD& Polygon() const { return polygon_; }; 0441 0442 double Area() const 0443 { 0444 return std::accumulate(childs_.begin(), childs_.end(), 0445 Clipper2Lib::Area<double>(polygon_), 0446 [](double a, const auto& child) {return a + child->Area(); }); 0447 } 0448 }; 0449 0450 class Clipper64 : public ClipperBase 0451 { 0452 private: 0453 void BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen); 0454 void BuildTree64(PolyPath64& polytree, Paths64& open_paths); 0455 public: 0456 #ifdef USINGZ 0457 void SetZCallback(ZCallback64 cb) { zCallback_ = cb; } 0458 #endif 0459 0460 void AddSubject(const Paths64& subjects) 0461 { 0462 AddPaths(subjects, PathType::Subject, false); 0463 } 0464 void AddOpenSubject(const Paths64& open_subjects) 0465 { 0466 AddPaths(open_subjects, PathType::Subject, true); 0467 } 0468 void AddClip(const Paths64& clips) 0469 { 0470 AddPaths(clips, PathType::Clip, false); 0471 } 0472 0473 bool Execute(ClipType clip_type, 0474 FillRule fill_rule, Paths64& closed_paths) 0475 { 0476 Paths64 dummy; 0477 return Execute(clip_type, fill_rule, closed_paths, dummy); 0478 } 0479 0480 bool Execute(ClipType clip_type, FillRule fill_rule, 0481 Paths64& closed_paths, Paths64& open_paths) 0482 { 0483 closed_paths.clear(); 0484 open_paths.clear(); 0485 if (ExecuteInternal(clip_type, fill_rule, false)) 0486 BuildPaths64(closed_paths, &open_paths); 0487 CleanUp(); 0488 return succeeded_; 0489 } 0490 0491 bool Execute(ClipType clip_type, FillRule fill_rule, PolyTree64& polytree) 0492 { 0493 Paths64 dummy; 0494 return Execute(clip_type, fill_rule, polytree, dummy); 0495 } 0496 0497 bool Execute(ClipType clip_type, 0498 FillRule fill_rule, PolyTree64& polytree, Paths64& open_paths) 0499 { 0500 if (ExecuteInternal(clip_type, fill_rule, true)) 0501 { 0502 open_paths.clear(); 0503 polytree.Clear(); 0504 BuildTree64(polytree, open_paths); 0505 } 0506 CleanUp(); 0507 return succeeded_; 0508 } 0509 }; 0510 0511 class ClipperD : public ClipperBase { 0512 private: 0513 double scale_ = 1.0, invScale_ = 1.0; 0514 #ifdef USINGZ 0515 ZCallbackD zCallbackD_ = nullptr; 0516 #endif 0517 void BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen); 0518 void BuildTreeD(PolyPathD& polytree, PathsD& open_paths); 0519 public: 0520 explicit ClipperD(int precision = 2) : ClipperBase() 0521 { 0522 CheckPrecision(precision, error_code_); 0523 // to optimize scaling / descaling precision 0524 // set the scale to a power of double's radix (2) (#25) 0525 scale_ = std::pow(std::numeric_limits<double>::radix, 0526 std::ilogb(std::pow(10, precision)) + 1); 0527 invScale_ = 1 / scale_; 0528 } 0529 0530 #ifdef USINGZ 0531 void SetZCallback(ZCallbackD cb) { zCallbackD_ = cb; }; 0532 0533 void ZCB(const Point64& e1bot, const Point64& e1top, 0534 const Point64& e2bot, const Point64& e2top, Point64& pt) 0535 { 0536 // de-scale (x & y) 0537 // temporarily convert integers to their initial float values 0538 // this will slow clipping marginally but will make it much easier 0539 // to understand the coordinates passed to the callback function 0540 PointD tmp = PointD(pt) * invScale_; 0541 PointD e1b = PointD(e1bot) * invScale_; 0542 PointD e1t = PointD(e1top) * invScale_; 0543 PointD e2b = PointD(e2bot) * invScale_; 0544 PointD e2t = PointD(e2top) * invScale_; 0545 zCallbackD_(e1b,e1t, e2b, e2t, tmp); 0546 pt.z = tmp.z; // only update 'z' 0547 }; 0548 0549 void CheckCallback() 0550 { 0551 if(zCallbackD_) 0552 // if the user defined float point callback has been assigned 0553 // then assign the proxy callback function 0554 ClipperBase::zCallback_ = 0555 std::bind(&ClipperD::ZCB, this, std::placeholders::_1, 0556 std::placeholders::_2, std::placeholders::_3, 0557 std::placeholders::_4, std::placeholders::_5); 0558 else 0559 ClipperBase::zCallback_ = nullptr; 0560 } 0561 0562 #endif 0563 0564 void AddSubject(const PathsD& subjects) 0565 { 0566 AddPaths(ScalePaths<int64_t, double>(subjects, scale_, error_code_), PathType::Subject, false); 0567 } 0568 0569 void AddOpenSubject(const PathsD& open_subjects) 0570 { 0571 AddPaths(ScalePaths<int64_t, double>(open_subjects, scale_, error_code_), PathType::Subject, true); 0572 } 0573 0574 void AddClip(const PathsD& clips) 0575 { 0576 AddPaths(ScalePaths<int64_t, double>(clips, scale_, error_code_), PathType::Clip, false); 0577 } 0578 0579 bool Execute(ClipType clip_type, FillRule fill_rule, PathsD& closed_paths) 0580 { 0581 PathsD dummy; 0582 return Execute(clip_type, fill_rule, closed_paths, dummy); 0583 } 0584 0585 bool Execute(ClipType clip_type, 0586 FillRule fill_rule, PathsD& closed_paths, PathsD& open_paths) 0587 { 0588 #ifdef USINGZ 0589 CheckCallback(); 0590 #endif 0591 if (ExecuteInternal(clip_type, fill_rule, false)) 0592 { 0593 BuildPathsD(closed_paths, &open_paths); 0594 } 0595 CleanUp(); 0596 return succeeded_; 0597 } 0598 0599 bool Execute(ClipType clip_type, FillRule fill_rule, PolyTreeD& polytree) 0600 { 0601 PathsD dummy; 0602 return Execute(clip_type, fill_rule, polytree, dummy); 0603 } 0604 0605 bool Execute(ClipType clip_type, 0606 FillRule fill_rule, PolyTreeD& polytree, PathsD& open_paths) 0607 { 0608 #ifdef USINGZ 0609 CheckCallback(); 0610 #endif 0611 if (ExecuteInternal(clip_type, fill_rule, true)) 0612 { 0613 polytree.Clear(); 0614 polytree.SetScale(invScale_); 0615 open_paths.clear(); 0616 BuildTreeD(polytree, open_paths); 0617 } 0618 CleanUp(); 0619 return succeeded_; 0620 } 0621 0622 }; 0623 0624 } // namespace 0625 0626 #endif // CLIPPER_ENGINE_H