File indexing completed on 2024-05-12 03:52:07

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