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

0001 /*******************************************************************************
0002 * Author    :  Angus Johnson                                                   *
0003 * Date      :  27 August 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 #include <cstdlib>
0011 #include <cmath>
0012 #include <stdexcept>
0013 #include <vector>
0014 #include <numeric>
0015 #include <algorithm>
0016 
0017 #include "clipper2/clipper.engine.h"
0018 
0019 // https://github.com/AngusJohnson/Clipper2/discussions/334
0020 // #discussioncomment-4248602
0021 #if defined(_MSC_VER) && ( defined(_M_AMD64) || defined(_M_X64) )
0022 #include <xmmintrin.h>
0023 #include <emmintrin.h>
0024 #define fmin(a,b) _mm_cvtsd_f64(_mm_min_sd(_mm_set_sd(a),_mm_set_sd(b)))
0025 #define fmax(a,b) _mm_cvtsd_f64(_mm_max_sd(_mm_set_sd(a),_mm_set_sd(b)))
0026 #define nearbyint(a) _mm_cvtsd_si64(_mm_set_sd(a)) /* Note: expression type is (int64_t) */
0027 #endif
0028 
0029 namespace Clipper2Lib {
0030 
0031   static const Rect64 invalid_rect = Rect64(false);
0032 
0033   // Every closed path (or polygon) is made up of a series of vertices forming
0034   // edges that alternate between going up (relative to the Y-axis) and going
0035   // down. Edges consecutively going up or consecutively going down are called
0036   // 'bounds' (ie sides if they're simple polygons). 'Local Minima' refer to
0037   // vertices where descending bounds become ascending ones.
0038 
0039   struct Scanline {
0040     int64_t y = 0;
0041     Scanline* next = nullptr;
0042 
0043     explicit Scanline(int64_t y_) : y(y_) {}
0044   };
0045 
0046   struct HorzSegSorter {
0047     inline bool operator()(const HorzSegment& hs1, const HorzSegment& hs2)
0048     {
0049       if (!hs1.right_op || !hs2.right_op) return (hs1.right_op);
0050       return  hs2.left_op->pt.x > hs1.left_op->pt.x;
0051     }
0052   };
0053 
0054   struct LocMinSorter {
0055     inline bool operator()(const LocalMinima_ptr& locMin1,
0056       const LocalMinima_ptr& locMin2)
0057     {
0058       if (locMin2->vertex->pt.y != locMin1->vertex->pt.y)
0059         return locMin2->vertex->pt.y < locMin1->vertex->pt.y;
0060       else
0061         return locMin2->vertex->pt.x > locMin1->vertex->pt.x;
0062     }
0063   };
0064 
0065   inline bool IsOdd(int val)
0066   {
0067     return (val & 1) ? true : false;
0068   }
0069 
0070 
0071   inline bool IsHotEdge(const Active& e)
0072   {
0073     return (e.outrec);
0074   }
0075 
0076 
0077   inline bool IsOpen(const Active& e)
0078   {
0079     return (e.local_min->is_open);
0080   }
0081 
0082 
0083   inline bool IsOpenEnd(const Vertex& v)
0084   {
0085     return (v.flags & (VertexFlags::OpenStart | VertexFlags::OpenEnd)) !=
0086       VertexFlags::None;
0087   }
0088 
0089 
0090   inline bool IsOpenEnd(const Active& ae)
0091   {
0092     return IsOpenEnd(*ae.vertex_top);
0093   }
0094 
0095 
0096   inline Active* GetPrevHotEdge(const Active& e)
0097   {
0098     Active* prev = e.prev_in_ael;
0099     while (prev && (IsOpen(*prev) || !IsHotEdge(*prev)))
0100       prev = prev->prev_in_ael;
0101     return prev;
0102   }
0103 
0104   inline bool IsFront(const Active& e)
0105   {
0106     return (&e == e.outrec->front_edge);
0107   }
0108 
0109   inline bool IsInvalidPath(OutPt* op)
0110   {
0111     return (!op || op->next == op);
0112   }
0113 
0114   /*******************************************************************************
0115     *  Dx:                             0(90deg)                                    *
0116     *                                  |                                           *
0117     *               +inf (180deg) <--- o ---> -inf (0deg)                          *
0118     *******************************************************************************/
0119 
0120   inline double GetDx(const Point64& pt1, const Point64& pt2)
0121   {
0122     double dy = double(pt2.y - pt1.y);
0123     if (dy != 0)
0124       return double(pt2.x - pt1.x) / dy;
0125     else if (pt2.x > pt1.x)
0126       return -std::numeric_limits<double>::max();
0127     else
0128       return std::numeric_limits<double>::max();
0129   }
0130 
0131   inline int64_t TopX(const Active& ae, const int64_t currentY)
0132   {
0133     if ((currentY == ae.top.y) || (ae.top.x == ae.bot.x)) return ae.top.x;
0134     else if (currentY == ae.bot.y) return ae.bot.x;
0135     else return ae.bot.x + static_cast<int64_t>(nearbyint(ae.dx * (currentY - ae.bot.y)));
0136     // nb: std::nearbyint (or std::round) substantially *improves* performance here
0137     // as it greatly improves the likelihood of edge adjacency in ProcessIntersectList().
0138   }
0139 
0140 
0141   inline bool IsHorizontal(const Active& e)
0142   {
0143     return (e.top.y == e.bot.y);
0144   }
0145 
0146 
0147   inline bool IsHeadingRightHorz(const Active& e)
0148   {
0149     return e.dx == -std::numeric_limits<double>::max();
0150   }
0151 
0152 
0153   inline bool IsHeadingLeftHorz(const Active& e)
0154   {
0155     return e.dx == std::numeric_limits<double>::max();
0156   }
0157 
0158 
0159   inline void SwapActives(Active*& e1, Active*& e2)
0160   {
0161     Active* e = e1;
0162     e1 = e2;
0163     e2 = e;
0164   }
0165 
0166   inline PathType GetPolyType(const Active& e)
0167   {
0168     return e.local_min->polytype;
0169   }
0170 
0171   inline bool IsSamePolyType(const Active& e1, const Active& e2)
0172   {
0173     return e1.local_min->polytype == e2.local_min->polytype;
0174   }
0175 
0176   inline void SetDx(Active& e)
0177   {
0178     e.dx = GetDx(e.bot, e.top);
0179   }
0180 
0181   inline Vertex* NextVertex(const Active& e)
0182   {
0183     if (e.wind_dx > 0)
0184       return e.vertex_top->next;
0185     else
0186       return e.vertex_top->prev;
0187   }
0188 
0189   //PrevPrevVertex: useful to get the (inverted Y-axis) top of the
0190   //alternate edge (ie left or right bound) during edge insertion.  
0191   inline Vertex* PrevPrevVertex(const Active& ae)
0192   {
0193     if (ae.wind_dx > 0)
0194       return ae.vertex_top->prev->prev;
0195     else
0196       return ae.vertex_top->next->next;
0197   }
0198 
0199 
0200   inline Active* ExtractFromSEL(Active* ae)
0201   {
0202     Active* res = ae->next_in_sel;
0203     if (res)
0204       res->prev_in_sel = ae->prev_in_sel;
0205     ae->prev_in_sel->next_in_sel = res;
0206     return res;
0207   }
0208 
0209 
0210   inline void Insert1Before2InSEL(Active* ae1, Active* ae2)
0211   {
0212     ae1->prev_in_sel = ae2->prev_in_sel;
0213     if (ae1->prev_in_sel)
0214       ae1->prev_in_sel->next_in_sel = ae1;
0215     ae1->next_in_sel = ae2;
0216     ae2->prev_in_sel = ae1;
0217   }
0218 
0219   inline bool IsMaxima(const Vertex& v)
0220   {
0221     return ((v.flags & VertexFlags::LocalMax) != VertexFlags::None);
0222   }
0223 
0224 
0225   inline bool IsMaxima(const Active& e)
0226   {
0227     return IsMaxima(*e.vertex_top);
0228   }
0229 
0230   inline Vertex* GetCurrYMaximaVertex_Open(const Active& e)
0231   {
0232     Vertex* result = e.vertex_top;
0233     if (e.wind_dx > 0)
0234       while ((result->next->pt.y == result->pt.y) &&
0235         ((result->flags & (VertexFlags::OpenEnd | 
0236           VertexFlags::LocalMax)) == VertexFlags::None))
0237             result = result->next;
0238     else
0239       while (result->prev->pt.y == result->pt.y &&
0240         ((result->flags & (VertexFlags::OpenEnd | 
0241           VertexFlags::LocalMax)) == VertexFlags::None))
0242           result = result->prev;
0243     if (!IsMaxima(*result)) result = nullptr; // not a maxima   
0244     return result;
0245   }
0246 
0247     inline Vertex* GetCurrYMaximaVertex(const Active& e)
0248   {
0249     Vertex* result = e.vertex_top;
0250     if (e.wind_dx > 0)
0251       while (result->next->pt.y == result->pt.y) result = result->next;
0252     else
0253       while (result->prev->pt.y == result->pt.y) result = result->prev;
0254     if (!IsMaxima(*result)) result = nullptr; // not a maxima   
0255     return result;
0256   }
0257 
0258   Active* GetMaximaPair(const Active& e)
0259   {
0260     Active* e2;
0261     e2 = e.next_in_ael;
0262     while (e2)
0263     {
0264       if (e2->vertex_top == e.vertex_top) return e2;  // Found!
0265       e2 = e2->next_in_ael;
0266     }
0267     return nullptr;
0268   }
0269 
0270   inline int PointCount(OutPt* op)
0271   {
0272     OutPt* op2 = op;
0273     int cnt = 0;
0274     do
0275     {
0276       op2 = op2->next;
0277       ++cnt;
0278     } while (op2 != op);
0279     return cnt;
0280   }
0281 
0282   inline OutPt* DuplicateOp(OutPt* op, bool insert_after)
0283   {
0284     OutPt* result = new OutPt(op->pt, op->outrec);
0285     if (insert_after)
0286     {
0287       result->next = op->next;
0288       result->next->prev = result;
0289       result->prev = op;
0290       op->next = result;
0291     }
0292     else
0293     {
0294       result->prev = op->prev;
0295       result->prev->next = result;
0296       result->next = op;
0297       op->prev = result;
0298     }
0299     return result;
0300   }
0301 
0302   inline OutPt* DisposeOutPt(OutPt* op)
0303   {
0304     OutPt* result = op->next;
0305     op->prev->next = op->next;
0306     op->next->prev = op->prev;
0307     delete op;
0308     return result;
0309   }
0310 
0311 
0312   inline void DisposeOutPts(OutRec* outrec)
0313   {
0314     OutPt* op = outrec->pts;
0315     op->prev->next = nullptr;
0316     while (op)
0317     {
0318       OutPt* tmp = op;
0319       op = op->next;
0320       delete tmp;
0321     };
0322     outrec->pts = nullptr;
0323   }
0324 
0325 
0326   bool IntersectListSort(const IntersectNode& a, const IntersectNode& b)
0327   {
0328     //note different inequality tests ...
0329     return (a.pt.y == b.pt.y) ? (a.pt.x < b.pt.x) : (a.pt.y > b.pt.y);
0330   }
0331 
0332 
0333   inline void SetSides(OutRec& outrec, Active& start_edge, Active& end_edge)
0334   {
0335     outrec.front_edge = &start_edge;
0336     outrec.back_edge = &end_edge;
0337   }
0338 
0339 
0340   void SwapOutrecs(Active& e1, Active& e2)
0341   {
0342     OutRec* or1 = e1.outrec;
0343     OutRec* or2 = e2.outrec;
0344     if (or1 == or2)
0345     {
0346       Active* e = or1->front_edge;
0347       or1->front_edge = or1->back_edge;
0348       or1->back_edge = e;
0349       return;
0350     }
0351     if (or1)
0352     {
0353       if (&e1 == or1->front_edge)
0354         or1->front_edge = &e2;
0355       else
0356         or1->back_edge = &e2;
0357     }
0358     if (or2)
0359     {
0360       if (&e2 == or2->front_edge)
0361         or2->front_edge = &e1;
0362       else
0363         or2->back_edge = &e1;
0364     }
0365     e1.outrec = or2;
0366     e2.outrec = or1;
0367   }
0368 
0369 
0370   double Area(OutPt* op)
0371   {
0372     //https://en.wikipedia.org/wiki/Shoelace_formula
0373     double result = 0.0;
0374     OutPt* op2 = op;
0375     do
0376     {
0377       result += static_cast<double>(op2->prev->pt.y + op2->pt.y) *
0378         static_cast<double>(op2->prev->pt.x - op2->pt.x);
0379       op2 = op2->next;
0380     } while (op2 != op);
0381     return result * 0.5;
0382   }
0383 
0384   inline double AreaTriangle(const Point64& pt1,
0385     const Point64& pt2, const Point64& pt3)
0386   {
0387     return (static_cast<double>(pt3.y + pt1.y) * static_cast<double>(pt3.x - pt1.x) +
0388       static_cast<double>(pt1.y + pt2.y) * static_cast<double>(pt1.x - pt2.x) +
0389       static_cast<double>(pt2.y + pt3.y) * static_cast<double>(pt2.x - pt3.x));
0390   }
0391 
0392   void ReverseOutPts(OutPt* op)
0393   {
0394     if (!op) return;
0395 
0396     OutPt* op1 = op;
0397     OutPt* op2;
0398 
0399     do
0400     {
0401       op2 = op1->next;
0402       op1->next = op1->prev;
0403       op1->prev = op2;
0404       op1 = op2;
0405     } while (op1 != op);
0406   }
0407 
0408   inline void SwapSides(OutRec& outrec)
0409   {
0410     Active* e2 = outrec.front_edge;
0411     outrec.front_edge = outrec.back_edge;
0412     outrec.back_edge = e2;
0413     outrec.pts = outrec.pts->next;
0414   }
0415 
0416   inline OutRec* GetRealOutRec(OutRec* outrec)
0417   {
0418     while (outrec && !outrec->pts) outrec = outrec->owner;
0419     return outrec;
0420   }
0421 
0422   inline bool IsValidOwner(OutRec* outrec, OutRec* testOwner)
0423   {
0424     // prevent outrec owning itself either directly or indirectly
0425     while (testOwner && testOwner != outrec) testOwner = testOwner->owner;
0426     return !testOwner;
0427   }
0428 
0429   inline void UncoupleOutRec(Active ae)
0430   {
0431     OutRec* outrec = ae.outrec;
0432     if (!outrec) return;
0433     outrec->front_edge->outrec = nullptr;
0434     outrec->back_edge->outrec = nullptr;
0435     outrec->front_edge = nullptr;
0436     outrec->back_edge = nullptr;
0437   }
0438 
0439 
0440   inline bool PtsReallyClose(const Point64& pt1, const Point64& pt2)
0441   {
0442     return (std::llabs(pt1.x - pt2.x) < 2) && (std::llabs(pt1.y - pt2.y) < 2);
0443   }
0444 
0445   inline bool IsVerySmallTriangle(const OutPt& op)
0446   {
0447     return op.next->next == op.prev &&
0448       (PtsReallyClose(op.prev->pt, op.next->pt) ||
0449         PtsReallyClose(op.pt, op.next->pt) ||
0450         PtsReallyClose(op.pt, op.prev->pt));
0451   }
0452 
0453   inline bool IsValidClosedPath(const OutPt* op)
0454   {
0455     return op && (op->next != op) && (op->next != op->prev) &&
0456       !IsVerySmallTriangle(*op);
0457   }
0458 
0459   inline bool OutrecIsAscending(const Active* hotEdge)
0460   {
0461     return (hotEdge == hotEdge->outrec->front_edge);
0462   }
0463 
0464   inline void SwapFrontBackSides(OutRec& outrec)
0465   {
0466     Active* tmp = outrec.front_edge;
0467     outrec.front_edge = outrec.back_edge;
0468     outrec.back_edge = tmp;
0469     outrec.pts = outrec.pts->next;
0470   }
0471 
0472   inline bool EdgesAdjacentInAEL(const IntersectNode& inode)
0473   {
0474     return (inode.edge1->next_in_ael == inode.edge2) || (inode.edge1->prev_in_ael == inode.edge2);
0475   }
0476 
0477   inline bool IsJoined(const Active& e)
0478   {
0479     return e.join_with != JoinWith::None;
0480   }
0481 
0482   inline void SetOwner(OutRec* outrec, OutRec* new_owner)
0483   {
0484     //precondition1: new_owner is never null
0485     while (new_owner->owner && !new_owner->owner->pts)
0486       new_owner->owner = new_owner->owner->owner;
0487     OutRec* tmp = new_owner;
0488     while (tmp && tmp != outrec) tmp = tmp->owner;
0489     if (tmp) new_owner->owner = outrec->owner;
0490     outrec->owner = new_owner;
0491   }
0492 
0493   static PointInPolygonResult PointInOpPolygon(const Point64& pt, OutPt* op)
0494   {
0495     if (op == op->next || op->prev == op->next)
0496       return PointInPolygonResult::IsOutside;
0497 
0498     OutPt* op2 = op;
0499     do
0500     {
0501       if (op->pt.y != pt.y) break;
0502       op = op->next;
0503     } while (op != op2);
0504     if (op->pt.y == pt.y) // not a proper polygon
0505       return PointInPolygonResult::IsOutside;
0506 
0507     bool is_above = op->pt.y < pt.y, starting_above = is_above;
0508     int val = 0;
0509     op2 = op->next;
0510     while (op2 != op)
0511     {
0512       if (is_above)
0513         while (op2 != op && op2->pt.y < pt.y) op2 = op2->next;
0514       else
0515         while (op2 != op && op2->pt.y > pt.y) op2 = op2->next;
0516       if (op2 == op) break;
0517 
0518       // must have touched or crossed the pt.Y horizonal
0519       // and this must happen an even number of times
0520 
0521       if (op2->pt.y == pt.y) // touching the horizontal
0522       {
0523         if (op2->pt.x == pt.x || (op2->pt.y == op2->prev->pt.y &&
0524           (pt.x < op2->prev->pt.x) != (pt.x < op2->pt.x)))
0525           return PointInPolygonResult::IsOn;
0526 
0527         op2 = op2->next;
0528         if (op2 == op) break;
0529         continue;
0530       }
0531 
0532       if (pt.x < op2->pt.x && pt.x < op2->prev->pt.x);
0533       // do nothing because
0534       // we're only interested in edges crossing on the left
0535       else if ((pt.x > op2->prev->pt.x && pt.x > op2->pt.x))
0536         val = 1 - val; // toggle val
0537       else
0538       {
0539         double d = CrossProduct(op2->prev->pt, op2->pt, pt);
0540         if (d == 0) return PointInPolygonResult::IsOn;
0541         if ((d < 0) == is_above) val = 1 - val;
0542       }
0543       is_above = !is_above;
0544       op2 = op2->next;
0545     }
0546 
0547     if (is_above != starting_above)
0548     {
0549       double d = CrossProduct(op2->prev->pt, op2->pt, pt);
0550       if (d == 0) return PointInPolygonResult::IsOn;
0551       if ((d < 0) == is_above) val = 1 - val;
0552     }
0553 
0554     if (val == 0) return PointInPolygonResult::IsOutside;
0555     else return PointInPolygonResult::IsInside;
0556   }
0557 
0558   inline Path64 GetCleanPath(OutPt* op)
0559   {
0560     Path64 result;
0561     OutPt* op2 = op;
0562     while (op2->next != op &&
0563       ((op2->pt.x == op2->next->pt.x && op2->pt.x == op2->prev->pt.x) ||
0564         (op2->pt.y == op2->next->pt.y && op2->pt.y == op2->prev->pt.y))) op2 = op2->next;
0565     result.push_back(op2->pt);
0566     OutPt* prevOp = op2;
0567     op2 = op2->next;
0568     while (op2 != op)
0569     {
0570       if ((op2->pt.x != op2->next->pt.x || op2->pt.x != prevOp->pt.x) &&
0571         (op2->pt.y != op2->next->pt.y || op2->pt.y != prevOp->pt.y))
0572       {
0573         result.push_back(op2->pt);
0574         prevOp = op2;
0575       }
0576       op2 = op2->next;
0577     }
0578     return result;
0579   }
0580 
0581   inline bool Path1InsidePath2(OutPt* op1, OutPt* op2)
0582   {
0583     // we need to make some accommodation for rounding errors
0584     // so we won't jump if the first vertex is found outside
0585     PointInPolygonResult result;
0586     int outside_cnt = 0;
0587     OutPt* op = op1;
0588     do
0589     {
0590       result = PointInOpPolygon(op->pt, op2);
0591       if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
0592       else if (result == PointInPolygonResult::IsInside) --outside_cnt;
0593       op = op->next;
0594     } while (op != op1 && std::abs(outside_cnt) < 2);
0595     if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
0596     // since path1's location is still equivocal, check its midpoint
0597     Point64 mp = GetBounds(GetCleanPath(op1)).MidPoint();
0598     Path64 path2 = GetCleanPath(op2);
0599     return PointInPolygon(mp, path2) != PointInPolygonResult::IsOutside;
0600   }
0601 
0602   //------------------------------------------------------------------------------
0603   //------------------------------------------------------------------------------
0604 
0605   void AddLocMin(LocalMinimaList& list,
0606     Vertex& vert, PathType polytype, bool is_open)
0607   {
0608     //make sure the vertex is added only once ...
0609     if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return;
0610 
0611     vert.flags = (vert.flags | VertexFlags::LocalMin);
0612     list.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
0613   }
0614 
0615   void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, 
0616     std::vector<Vertex*>& vertexLists, LocalMinimaList& locMinList)
0617   {
0618     const auto total_vertex_count =
0619       std::accumulate(paths.begin(), paths.end(), 0,
0620         [](const auto& a, const Path64& path)
0621         {return a + static_cast<unsigned>(path.size()); });
0622     if (total_vertex_count == 0) return;
0623 
0624     Vertex* vertices = new Vertex[total_vertex_count], * v = vertices;
0625     for (const Path64& path : paths)
0626     {
0627       //for each path create a circular double linked list of vertices
0628       Vertex* v0 = v, * curr_v = v, * prev_v = nullptr;
0629 
0630       if (path.empty())
0631         continue;
0632 
0633       v->prev = nullptr;
0634       int cnt = 0;
0635       for (const Point64& pt : path)
0636       {
0637         if (prev_v)
0638         {
0639           if (prev_v->pt == pt) continue; // ie skips duplicates
0640           prev_v->next = curr_v;
0641         }
0642         curr_v->prev = prev_v;
0643         curr_v->pt = pt;
0644         curr_v->flags = VertexFlags::None;
0645         prev_v = curr_v++;
0646         cnt++;
0647       }
0648       if (!prev_v || !prev_v->prev) continue;
0649       if (!is_open && prev_v->pt == v0->pt)
0650         prev_v = prev_v->prev;
0651       prev_v->next = v0;
0652       v0->prev = prev_v;
0653       v = curr_v; // ie get ready for next path
0654       if (cnt < 2 || (cnt == 2 && !is_open)) continue;
0655 
0656       //now find and assign local minima
0657       bool going_up, going_up0;
0658       if (is_open)
0659       {
0660         curr_v = v0->next;
0661         while (curr_v != v0 && curr_v->pt.y == v0->pt.y)
0662           curr_v = curr_v->next;
0663         going_up = curr_v->pt.y <= v0->pt.y;
0664         if (going_up)
0665         {
0666           v0->flags = VertexFlags::OpenStart;
0667           AddLocMin(locMinList , *v0, polytype, true);
0668         }
0669         else
0670           v0->flags = VertexFlags::OpenStart | VertexFlags::LocalMax;
0671       }
0672       else // closed path
0673       {
0674         prev_v = v0->prev;
0675         while (prev_v != v0 && prev_v->pt.y == v0->pt.y)
0676           prev_v = prev_v->prev;
0677         if (prev_v == v0)
0678           continue; // only open paths can be completely flat
0679         going_up = prev_v->pt.y > v0->pt.y;
0680       }
0681 
0682       going_up0 = going_up;
0683       prev_v = v0;
0684       curr_v = v0->next;
0685       while (curr_v != v0)
0686       {
0687         if (curr_v->pt.y > prev_v->pt.y && going_up)
0688         {
0689           prev_v->flags = (prev_v->flags | VertexFlags::LocalMax);
0690           going_up = false;
0691         }
0692         else if (curr_v->pt.y < prev_v->pt.y && !going_up)
0693         {
0694           going_up = true;
0695           AddLocMin(locMinList, *prev_v, polytype, is_open);
0696         }
0697         prev_v = curr_v;
0698         curr_v = curr_v->next;
0699       }
0700 
0701       if (is_open)
0702       {
0703         prev_v->flags = prev_v->flags | VertexFlags::OpenEnd;
0704         if (going_up)
0705           prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
0706         else
0707           AddLocMin(locMinList, *prev_v, polytype, is_open);
0708       }
0709       else if (going_up != going_up0)
0710       {
0711         if (going_up0) AddLocMin(locMinList, *prev_v, polytype, false);
0712         else prev_v->flags = prev_v->flags | VertexFlags::LocalMax;
0713       }
0714     } // end processing current path
0715 
0716     vertexLists.emplace_back(vertices);
0717   }
0718 
0719   //------------------------------------------------------------------------------
0720   // ReuseableDataContainer64 methods ...
0721   //------------------------------------------------------------------------------
0722 
0723   void ReuseableDataContainer64::AddLocMin(Vertex& vert, PathType polytype, bool is_open)
0724   {
0725     //make sure the vertex is added only once ...
0726     if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return;
0727 
0728     vert.flags = (vert.flags | VertexFlags::LocalMin);
0729     minima_list_.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
0730   }
0731 
0732   void ReuseableDataContainer64::AddPaths(const Paths64& paths,
0733     PathType polytype, bool is_open)
0734   {
0735     AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
0736   }
0737 
0738   ReuseableDataContainer64::~ReuseableDataContainer64()
0739   {
0740     Clear();
0741   }
0742 
0743   void ReuseableDataContainer64::Clear()
0744   {
0745     minima_list_.clear();
0746     for (auto v : vertex_lists_) delete[] v;
0747     vertex_lists_.clear();
0748   }
0749 
0750   //------------------------------------------------------------------------------
0751   // ClipperBase methods ...
0752   //------------------------------------------------------------------------------
0753 
0754   ClipperBase::~ClipperBase()
0755   {
0756     Clear();
0757   }
0758 
0759   void ClipperBase::DeleteEdges(Active*& e)
0760   {
0761     while (e)
0762     {
0763       Active* e2 = e;
0764       e = e->next_in_ael;
0765       delete e2;
0766     }
0767   }
0768 
0769   void ClipperBase::CleanUp()
0770   {
0771     DeleteEdges(actives_);
0772     scanline_list_ = std::priority_queue<int64_t>();
0773     intersect_nodes_.clear();
0774     DisposeAllOutRecs();
0775     horz_seg_list_.clear();
0776     horz_join_list_.clear();
0777   }
0778 
0779 
0780   void ClipperBase::Clear()
0781   {
0782     CleanUp();
0783     DisposeVerticesAndLocalMinima();
0784     current_locmin_iter_ = minima_list_.begin();
0785     minima_list_sorted_ = false;
0786     has_open_paths_ = false;
0787   }
0788 
0789 
0790   void ClipperBase::Reset()
0791   {
0792     if (!minima_list_sorted_)
0793     {
0794       std::stable_sort(minima_list_.begin(), minima_list_.end(), LocMinSorter()); //#594
0795       minima_list_sorted_ = true;
0796     }
0797     LocalMinimaList::const_reverse_iterator i;
0798     for (i = minima_list_.rbegin(); i != minima_list_.rend(); ++i)
0799       InsertScanline((*i)->vertex->pt.y);
0800 
0801     current_locmin_iter_ = minima_list_.begin();
0802     actives_ = nullptr;
0803     sel_ = nullptr;
0804     succeeded_ = true;
0805   }
0806 
0807 
0808 #ifdef USINGZ
0809   void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip)
0810   {
0811     if (!zCallback_) return;
0812     // prioritize subject over clip vertices by passing 
0813     // subject vertices before clip vertices in the callback
0814     if (GetPolyType(e1) == PathType::Subject)
0815     {
0816       if (ip == e1.bot) ip.z = e1.bot.z;
0817       else if (ip == e1.top) ip.z = e1.top.z;
0818       else if (ip == e2.bot) ip.z = e2.bot.z;
0819       else if (ip == e2.top) ip.z = e2.top.z;
0820       else ip.z = DefaultZ;
0821       zCallback_(e1.bot, e1.top, e2.bot, e2.top, ip);
0822     }
0823     else
0824     {
0825       if (ip == e2.bot) ip.z = e2.bot.z;
0826       else if (ip == e2.top) ip.z = e2.top.z;
0827       else if (ip == e1.bot) ip.z = e1.bot.z;
0828       else if (ip == e1.top) ip.z = e1.top.z;
0829       else ip.z = DefaultZ;
0830       zCallback_(e2.bot, e2.top, e1.bot, e1.top, ip);
0831     }
0832   }
0833 #endif
0834 
0835   void ClipperBase::AddPath(const Path64& path, PathType polytype, bool is_open)
0836   {
0837     Paths64 tmp;
0838     tmp.push_back(path);
0839     AddPaths(tmp, polytype, is_open);
0840   }
0841 
0842   void ClipperBase::AddPaths(const Paths64& paths, PathType polytype, bool is_open)
0843   {
0844     if (is_open) has_open_paths_ = true;
0845     minima_list_sorted_ = false;
0846     AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_);
0847   } 
0848 
0849   void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data) 
0850   {
0851     // nb: reuseable_data will continue to own the vertices 
0852     // and remains responsible for their clean up.
0853     succeeded_ = false;
0854     minima_list_sorted_ = false;
0855     LocalMinimaList::const_iterator i;
0856     for (i = reuseable_data.minima_list_.cbegin(); i != reuseable_data.minima_list_.cend(); ++i)
0857     {
0858       minima_list_.push_back(std::make_unique <LocalMinima>((*i)->vertex, (*i)->polytype, (*i)->is_open));
0859       if ((*i)->is_open) has_open_paths_ = true;
0860     }
0861   }
0862 
0863   void ClipperBase::InsertScanline(int64_t y)
0864   {
0865     scanline_list_.push(y);
0866   }
0867 
0868 
0869   bool ClipperBase::PopScanline(int64_t& y)
0870   {
0871     if (scanline_list_.empty()) return false;
0872     y = scanline_list_.top();
0873     scanline_list_.pop();
0874     while (!scanline_list_.empty() && y == scanline_list_.top())
0875       scanline_list_.pop();  // Pop duplicates.
0876     return true;
0877   }
0878 
0879 
0880   bool ClipperBase::PopLocalMinima(int64_t y, LocalMinima*& local_minima)
0881   {
0882     if (current_locmin_iter_ == minima_list_.end() || (*current_locmin_iter_)->vertex->pt.y != y) return false;
0883     local_minima = (current_locmin_iter_++)->get();
0884     return true;
0885   }
0886 
0887   void ClipperBase::DisposeAllOutRecs()
0888   {
0889     for (auto outrec : outrec_list_)
0890     {
0891       if (outrec->pts) DisposeOutPts(outrec);
0892       delete outrec;
0893     }
0894     outrec_list_.resize(0);
0895   }
0896 
0897   void ClipperBase::DisposeVerticesAndLocalMinima()
0898   {
0899     minima_list_.clear();
0900     for (auto v : vertex_lists_) delete[] v;
0901     vertex_lists_.clear();
0902   }
0903 
0904 
0905   void ClipperBase::AddLocMin(Vertex& vert, PathType polytype, bool is_open)
0906   {
0907     //make sure the vertex is added only once ...
0908     if ((VertexFlags::LocalMin & vert.flags) != VertexFlags::None) return;
0909 
0910     vert.flags = (vert.flags | VertexFlags::LocalMin);
0911     minima_list_.push_back(std::make_unique <LocalMinima>(&vert, polytype, is_open));
0912   }
0913 
0914   bool ClipperBase::IsContributingClosed(const Active& e) const
0915   {
0916     switch (fillrule_)
0917     {
0918     case FillRule::EvenOdd:
0919       break;
0920     case FillRule::NonZero:
0921       if (abs(e.wind_cnt) != 1) return false;
0922       break;
0923     case FillRule::Positive:
0924       if (e.wind_cnt != 1) return false;
0925       break;
0926     case FillRule::Negative:
0927       if (e.wind_cnt != -1) return false;
0928       break;
0929     }
0930 
0931     switch (cliptype_)
0932     {
0933     case ClipType::None:
0934       return false;
0935     case ClipType::Intersection:
0936       switch (fillrule_)
0937       {
0938       case FillRule::Positive:
0939         return (e.wind_cnt2 > 0);
0940       case FillRule::Negative:
0941         return (e.wind_cnt2 < 0);
0942       default:
0943         return (e.wind_cnt2 != 0);
0944       }
0945       break;
0946 
0947     case ClipType::Union:
0948       switch (fillrule_)
0949       {
0950       case FillRule::Positive:
0951         return (e.wind_cnt2 <= 0);
0952       case FillRule::Negative:
0953         return (e.wind_cnt2 >= 0);
0954       default:
0955         return (e.wind_cnt2 == 0);
0956       }
0957       break;
0958 
0959     case ClipType::Difference:
0960       bool result;
0961       switch (fillrule_)
0962       {
0963       case FillRule::Positive:
0964         result = (e.wind_cnt2 <= 0);
0965         break;
0966       case FillRule::Negative:
0967         result = (e.wind_cnt2 >= 0);
0968         break;
0969       default:
0970         result = (e.wind_cnt2 == 0);
0971       }
0972       if (GetPolyType(e) == PathType::Subject)
0973         return result;
0974       else
0975         return !result;
0976       break;
0977 
0978     case ClipType::Xor: return true;  break;
0979     }
0980     return false;  // we should never get here
0981   }
0982 
0983 
0984   inline bool ClipperBase::IsContributingOpen(const Active& e) const
0985   {
0986     bool is_in_clip, is_in_subj;
0987     switch (fillrule_)
0988     {
0989     case FillRule::Positive:
0990       is_in_clip = e.wind_cnt2 > 0;
0991       is_in_subj = e.wind_cnt > 0;
0992       break;
0993     case FillRule::Negative:
0994       is_in_clip = e.wind_cnt2 < 0;
0995       is_in_subj = e.wind_cnt < 0;
0996       break;
0997     default:
0998       is_in_clip = e.wind_cnt2 != 0;
0999       is_in_subj = e.wind_cnt != 0;
1000     }
1001 
1002     switch (cliptype_)
1003     {
1004     case ClipType::Intersection: return is_in_clip;
1005     case ClipType::Union: return (!is_in_subj && !is_in_clip);
1006     default: return !is_in_clip;
1007     }
1008   }
1009 
1010 
1011   void ClipperBase::SetWindCountForClosedPathEdge(Active& e)
1012   {
1013     //Wind counts refer to polygon regions not edges, so here an edge's WindCnt
1014     //indicates the higher of the wind counts for the two regions touching the
1015     //edge. (NB Adjacent regions can only ever have their wind counts differ by
1016     //one. Also, open paths have no meaningful wind directions or counts.)
1017 
1018     Active* e2 = e.prev_in_ael;
1019     //find the nearest closed path edge of the same PolyType in AEL (heading left)
1020     PathType pt = GetPolyType(e);
1021     while (e2 && (GetPolyType(*e2) != pt || IsOpen(*e2))) e2 = e2->prev_in_ael;
1022 
1023     if (!e2)
1024     {
1025       e.wind_cnt = e.wind_dx;
1026       e2 = actives_;
1027     }
1028     else if (fillrule_ == FillRule::EvenOdd)
1029     {
1030       e.wind_cnt = e.wind_dx;
1031       e.wind_cnt2 = e2->wind_cnt2;
1032       e2 = e2->next_in_ael;
1033     }
1034     else
1035     {
1036       //NonZero, positive, or negative filling here ...
1037       //if e's WindCnt is in the SAME direction as its WindDx, then polygon
1038       //filling will be on the right of 'e'.
1039       //NB neither e2.WindCnt nor e2.WindDx should ever be 0.
1040       if (e2->wind_cnt * e2->wind_dx < 0)
1041       {
1042         //opposite directions so 'e' is outside 'e2' ...
1043         if (abs(e2->wind_cnt) > 1)
1044         {
1045           //outside prev poly but still inside another.
1046           if (e2->wind_dx * e.wind_dx < 0)
1047             //reversing direction so use the same WC
1048             e.wind_cnt = e2->wind_cnt;
1049           else
1050             //otherwise keep 'reducing' the WC by 1 (ie towards 0) ...
1051             e.wind_cnt = e2->wind_cnt + e.wind_dx;
1052         }
1053         else
1054           //now outside all polys of same polytype so set own WC ...
1055           e.wind_cnt = (IsOpen(e) ? 1 : e.wind_dx);
1056       }
1057       else
1058       {
1059         //'e' must be inside 'e2'
1060         if (e2->wind_dx * e.wind_dx < 0)
1061           //reversing direction so use the same WC
1062           e.wind_cnt = e2->wind_cnt;
1063         else
1064           //otherwise keep 'increasing' the WC by 1 (ie away from 0) ...
1065           e.wind_cnt = e2->wind_cnt + e.wind_dx;
1066       }
1067       e.wind_cnt2 = e2->wind_cnt2;
1068       e2 = e2->next_in_ael;  // ie get ready to calc WindCnt2
1069     }
1070 
1071     //update wind_cnt2 ...
1072     if (fillrule_ == FillRule::EvenOdd)
1073       while (e2 != &e)
1074       {
1075         if (GetPolyType(*e2) != pt && !IsOpen(*e2))
1076           e.wind_cnt2 = (e.wind_cnt2 == 0 ? 1 : 0);
1077         e2 = e2->next_in_ael;
1078       }
1079     else
1080       while (e2 != &e)
1081       {
1082         if (GetPolyType(*e2) != pt && !IsOpen(*e2))
1083           e.wind_cnt2 += e2->wind_dx;
1084         e2 = e2->next_in_ael;
1085       }
1086   }
1087 
1088 
1089   void ClipperBase::SetWindCountForOpenPathEdge(Active& e)
1090   {
1091     Active* e2 = actives_;
1092     if (fillrule_ == FillRule::EvenOdd)
1093     {
1094       int cnt1 = 0, cnt2 = 0;
1095       while (e2 != &e)
1096       {
1097         if (GetPolyType(*e2) == PathType::Clip)
1098           cnt2++;
1099         else if (!IsOpen(*e2))
1100           cnt1++;
1101         e2 = e2->next_in_ael;
1102       }
1103       e.wind_cnt = (IsOdd(cnt1) ? 1 : 0);
1104       e.wind_cnt2 = (IsOdd(cnt2) ? 1 : 0);
1105     }
1106     else
1107     {
1108       while (e2 != &e)
1109       {
1110         if (GetPolyType(*e2) == PathType::Clip)
1111           e.wind_cnt2 += e2->wind_dx;
1112         else if (!IsOpen(*e2))
1113           e.wind_cnt += e2->wind_dx;
1114         e2 = e2->next_in_ael;
1115       }
1116     }
1117   }
1118 
1119 
1120   bool IsValidAelOrder(const Active& resident, const Active& newcomer)
1121   {
1122     if (newcomer.curr_x != resident.curr_x)
1123         return newcomer.curr_x > resident.curr_x;
1124 
1125     //get the turning direction  a1.top, a2.bot, a2.top
1126     double d = CrossProduct(resident.top, newcomer.bot, newcomer.top);
1127     if (d != 0) return d < 0;
1128 
1129     //edges must be collinear to get here
1130     //for starting open paths, place them according to
1131     //the direction they're about to turn
1132     if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y))
1133     {
1134       return CrossProduct(newcomer.bot,
1135         resident.top, NextVertex(resident)->pt) <= 0;
1136     }
1137     else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y))
1138     {
1139       return CrossProduct(newcomer.bot,
1140         newcomer.top, NextVertex(newcomer)->pt) >= 0;
1141     }
1142 
1143     int64_t y = newcomer.bot.y;
1144     bool newcomerIsLeft = newcomer.is_left_bound;
1145 
1146     if (resident.bot.y != y || resident.local_min->vertex->pt.y != y)
1147       return newcomer.is_left_bound;
1148     //resident must also have just been inserted
1149     else if (resident.is_left_bound != newcomerIsLeft)
1150       return newcomerIsLeft;
1151     else if (CrossProduct(PrevPrevVertex(resident)->pt,
1152       resident.bot, resident.top) == 0) return true;
1153     else
1154       //compare turning direction of the alternate bound
1155       return (CrossProduct(PrevPrevVertex(resident)->pt,
1156         newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
1157   }
1158 
1159 
1160   void ClipperBase::InsertLeftEdge(Active& e)
1161   {
1162     Active* e2;
1163     if (!actives_)
1164     {
1165       e.prev_in_ael = nullptr;
1166       e.next_in_ael = nullptr;
1167       actives_ = &e;
1168     }
1169     else if (!IsValidAelOrder(*actives_, e))
1170     {
1171       e.prev_in_ael = nullptr;
1172       e.next_in_ael = actives_;
1173       actives_->prev_in_ael = &e;
1174       actives_ = &e;
1175     }
1176     else
1177     {
1178       e2 = actives_;
1179       while (e2->next_in_ael && IsValidAelOrder(*e2->next_in_ael, e))
1180         e2 = e2->next_in_ael;
1181       if (e2->join_with == JoinWith::Right)
1182         e2 = e2->next_in_ael;
1183       if (!e2) return; // should never happen and stops compiler warning :)
1184       e.next_in_ael = e2->next_in_ael;
1185       if (e2->next_in_ael) e2->next_in_ael->prev_in_ael = &e;
1186       e.prev_in_ael = e2;
1187       e2->next_in_ael = &e;
1188     }
1189   }
1190 
1191 
1192   void InsertRightEdge(Active& e, Active& e2)
1193   {
1194     e2.next_in_ael = e.next_in_ael;
1195     if (e.next_in_ael) e.next_in_ael->prev_in_ael = &e2;
1196     e2.prev_in_ael = &e;
1197     e.next_in_ael = &e2;
1198   }
1199 
1200 
1201   void ClipperBase::InsertLocalMinimaIntoAEL(int64_t bot_y)
1202   {
1203     LocalMinima* local_minima;
1204     Active* left_bound, * right_bound;
1205     //Add any local minima (if any) at BotY ...
1206     //nb: horizontal local minima edges should contain locMin.vertex.prev
1207 
1208     while (PopLocalMinima(bot_y, local_minima))
1209     {
1210       if ((local_minima->vertex->flags & VertexFlags::OpenStart) != VertexFlags::None)
1211       {
1212         left_bound = nullptr;
1213       }
1214       else
1215       {
1216         left_bound = new Active();
1217         left_bound->bot = local_minima->vertex->pt;
1218         left_bound->curr_x = left_bound->bot.x;
1219         left_bound->wind_dx = -1;
1220         left_bound->vertex_top = local_minima->vertex->prev;  // ie descending
1221         left_bound->top = left_bound->vertex_top->pt;
1222         left_bound->local_min = local_minima;
1223         SetDx(*left_bound);
1224       }
1225 
1226       if ((local_minima->vertex->flags & VertexFlags::OpenEnd) != VertexFlags::None)
1227       {
1228         right_bound = nullptr;
1229       }
1230       else
1231       {
1232         right_bound = new Active();
1233         right_bound->bot = local_minima->vertex->pt;
1234         right_bound->curr_x = right_bound->bot.x;
1235         right_bound->wind_dx = 1;
1236         right_bound->vertex_top = local_minima->vertex->next;  // ie ascending
1237         right_bound->top = right_bound->vertex_top->pt;
1238         right_bound->local_min = local_minima;
1239         SetDx(*right_bound);
1240       }
1241 
1242       //Currently LeftB is just the descending bound and RightB is the ascending.
1243       //Now if the LeftB isn't on the left of RightB then we need swap them.
1244       if (left_bound && right_bound)
1245       {
1246         if (IsHorizontal(*left_bound))
1247         {
1248           if (IsHeadingRightHorz(*left_bound)) SwapActives(left_bound, right_bound);
1249         }
1250         else if (IsHorizontal(*right_bound))
1251         {
1252           if (IsHeadingLeftHorz(*right_bound)) SwapActives(left_bound, right_bound);
1253         }
1254         else if (left_bound->dx < right_bound->dx)
1255           SwapActives(left_bound, right_bound);
1256       }
1257       else if (!left_bound)
1258       {
1259         left_bound = right_bound;
1260         right_bound = nullptr;
1261       }
1262 
1263       bool contributing;
1264       left_bound->is_left_bound = true;
1265       InsertLeftEdge(*left_bound);
1266 
1267       if (IsOpen(*left_bound))
1268       {
1269         SetWindCountForOpenPathEdge(*left_bound);
1270         contributing = IsContributingOpen(*left_bound);
1271       }
1272       else
1273       {
1274         SetWindCountForClosedPathEdge(*left_bound);
1275         contributing = IsContributingClosed(*left_bound);
1276       }
1277 
1278       if (right_bound)
1279       {
1280         right_bound->is_left_bound = false;
1281         right_bound->wind_cnt = left_bound->wind_cnt;
1282         right_bound->wind_cnt2 = left_bound->wind_cnt2;
1283         InsertRightEdge(*left_bound, *right_bound);  ///////
1284         if (contributing)
1285         {
1286           AddLocalMinPoly(*left_bound, *right_bound, left_bound->bot, true);
1287           if (!IsHorizontal(*left_bound))
1288             CheckJoinLeft(*left_bound, left_bound->bot);
1289         }
1290 
1291         while (right_bound->next_in_ael &&
1292           IsValidAelOrder(*right_bound->next_in_ael, *right_bound))
1293         {
1294           IntersectEdges(*right_bound, *right_bound->next_in_ael, right_bound->bot);
1295           SwapPositionsInAEL(*right_bound, *right_bound->next_in_ael);
1296         }
1297 
1298         if (IsHorizontal(*right_bound))
1299           PushHorz(*right_bound);
1300         else
1301         {
1302           CheckJoinRight(*right_bound, right_bound->bot);
1303           InsertScanline(right_bound->top.y);
1304         }
1305       }
1306       else if (contributing)
1307       {
1308         StartOpenPath(*left_bound, left_bound->bot);
1309       }
1310 
1311       if (IsHorizontal(*left_bound))
1312         PushHorz(*left_bound);
1313       else
1314         InsertScanline(left_bound->top.y);
1315     }  // while (PopLocalMinima())
1316   }
1317 
1318 
1319   inline void ClipperBase::PushHorz(Active& e)
1320   {
1321     e.next_in_sel = (sel_ ? sel_ : nullptr);
1322     sel_ = &e;
1323   }
1324 
1325 
1326   inline bool ClipperBase::PopHorz(Active*& e)
1327   {
1328     e = sel_;
1329     if (!e) return false;
1330     sel_ = sel_->next_in_sel;
1331     return true;
1332   }
1333 
1334 
1335   OutPt* ClipperBase::AddLocalMinPoly(Active& e1, Active& e2,
1336     const Point64& pt, bool is_new)
1337   {
1338     OutRec* outrec = NewOutRec();
1339     e1.outrec = outrec;
1340     e2.outrec = outrec;
1341 
1342     if (IsOpen(e1))
1343     {
1344       outrec->owner = nullptr;
1345       outrec->is_open = true;
1346       if (e1.wind_dx > 0)
1347         SetSides(*outrec, e1, e2);
1348       else
1349         SetSides(*outrec, e2, e1);
1350     }
1351     else
1352     {
1353       Active* prevHotEdge = GetPrevHotEdge(e1);
1354       //e.windDx is the winding direction of the **input** paths
1355       //and unrelated to the winding direction of output polygons.
1356       //Output orientation is determined by e.outrec.frontE which is
1357       //the ascending edge (see AddLocalMinPoly).
1358       if (prevHotEdge)
1359       {
1360         if (using_polytree_)
1361           SetOwner(outrec, prevHotEdge->outrec);
1362         if (OutrecIsAscending(prevHotEdge) == is_new)
1363           SetSides(*outrec, e2, e1);
1364         else
1365           SetSides(*outrec, e1, e2);
1366       }
1367       else
1368       {
1369         outrec->owner = nullptr;
1370         if (is_new)
1371           SetSides(*outrec, e1, e2);
1372         else
1373           SetSides(*outrec, e2, e1);
1374       }
1375     }
1376 
1377     OutPt* op = new OutPt(pt, outrec);
1378     outrec->pts = op;
1379     return op;
1380   }
1381 
1382 
1383   OutPt* ClipperBase::AddLocalMaxPoly(Active& e1, Active& e2, const Point64& pt)
1384   {
1385     if (IsJoined(e1)) Split(e1, pt);
1386     if (IsJoined(e2)) Split(e2, pt);
1387     
1388     if (IsFront(e1) == IsFront(e2))
1389     {
1390       if (IsOpenEnd(e1))
1391         SwapFrontBackSides(*e1.outrec);
1392       else if (IsOpenEnd(e2))
1393         SwapFrontBackSides(*e2.outrec);
1394       else
1395       {
1396         succeeded_ = false;
1397         return nullptr;
1398       }
1399     }
1400 
1401     OutPt* result = AddOutPt(e1, pt);
1402     if (e1.outrec == e2.outrec)
1403     {
1404       OutRec& outrec = *e1.outrec;
1405       outrec.pts = result;
1406 
1407       if (using_polytree_)
1408       {
1409         Active* e = GetPrevHotEdge(e1);
1410         if (!e)
1411           outrec.owner = nullptr; 
1412         else
1413           SetOwner(&outrec, e->outrec);
1414         // nb: outRec.owner here is likely NOT the real
1415         // owner but this will be checked in RecursiveCheckOwners()
1416       }
1417 
1418       UncoupleOutRec(e1);
1419       result = outrec.pts;
1420       if (outrec.owner && !outrec.owner->front_edge)
1421         outrec.owner = GetRealOutRec(outrec.owner);
1422     }
1423     //and to preserve the winding orientation of outrec ...
1424     else if (IsOpen(e1))
1425     {
1426       if (e1.wind_dx < 0)
1427         JoinOutrecPaths(e1, e2);
1428       else
1429         JoinOutrecPaths(e2, e1);
1430     }
1431     else if (e1.outrec->idx < e2.outrec->idx)
1432       JoinOutrecPaths(e1, e2);
1433     else
1434       JoinOutrecPaths(e2, e1);
1435     return result;
1436   }
1437 
1438   void ClipperBase::JoinOutrecPaths(Active& e1, Active& e2)
1439   {
1440     //join e2 outrec path onto e1 outrec path and then delete e2 outrec path
1441     //pointers. (NB Only very rarely do the joining ends share the same coords.)
1442     OutPt* p1_st = e1.outrec->pts;
1443     OutPt* p2_st = e2.outrec->pts;
1444     OutPt* p1_end = p1_st->next;
1445     OutPt* p2_end = p2_st->next;
1446     if (IsFront(e1))
1447     {
1448       p2_end->prev = p1_st;
1449       p1_st->next = p2_end;
1450       p2_st->next = p1_end;
1451       p1_end->prev = p2_st;
1452       e1.outrec->pts = p2_st;
1453       e1.outrec->front_edge = e2.outrec->front_edge;
1454       if (e1.outrec->front_edge)
1455         e1.outrec->front_edge->outrec = e1.outrec;
1456     }
1457     else
1458     {
1459       p1_end->prev = p2_st;
1460       p2_st->next = p1_end;
1461       p1_st->next = p2_end;
1462       p2_end->prev = p1_st;
1463       e1.outrec->back_edge = e2.outrec->back_edge;
1464       if (e1.outrec->back_edge)
1465         e1.outrec->back_edge->outrec = e1.outrec;
1466     }
1467 
1468     //after joining, the e2.OutRec must contains no vertices ...
1469     e2.outrec->front_edge = nullptr;
1470     e2.outrec->back_edge = nullptr;
1471     e2.outrec->pts = nullptr;
1472     SetOwner(e2.outrec, e1.outrec);
1473 
1474     if (IsOpenEnd(e1))
1475     {
1476       e2.outrec->pts = e1.outrec->pts;
1477       e1.outrec->pts = nullptr;
1478     }
1479 
1480     //and e1 and e2 are maxima and are about to be dropped from the Actives list.
1481     e1.outrec = nullptr;
1482     e2.outrec = nullptr;
1483   }
1484 
1485   OutRec* ClipperBase::NewOutRec()
1486   {
1487     OutRec* result = new OutRec();
1488     result->idx = outrec_list_.size();
1489     outrec_list_.push_back(result);
1490     result->pts = nullptr;
1491     result->owner = nullptr;
1492     result->polypath = nullptr;
1493     result->is_open = false;
1494     result->splits = nullptr;
1495     return result;
1496   }
1497 
1498 
1499   OutPt* ClipperBase::AddOutPt(const Active& e, const Point64& pt)
1500   {
1501     OutPt* new_op = nullptr;
1502 
1503     //Outrec.OutPts: a circular doubly-linked-list of POutPt where ...
1504     //op_front[.Prev]* ~~~> op_back & op_back == op_front.Next
1505     OutRec* outrec = e.outrec;
1506     bool to_front = IsFront(e);
1507     OutPt* op_front = outrec->pts;
1508     OutPt* op_back = op_front->next;
1509 
1510     if (to_front)
1511     {
1512       if (pt == op_front->pt)
1513         return op_front;
1514     }
1515     else if (pt == op_back->pt)
1516       return op_back;
1517 
1518     new_op = new OutPt(pt, outrec);
1519     op_back->prev = new_op;
1520     new_op->prev = op_front;
1521     new_op->next = op_back;
1522     op_front->next = new_op;
1523     if (to_front) outrec->pts = new_op;
1524     return new_op;
1525   }
1526 
1527 
1528   void ClipperBase::CleanCollinear(OutRec* outrec)
1529   {
1530     outrec = GetRealOutRec(outrec);
1531     if (!outrec || outrec->is_open) return;
1532     if (!IsValidClosedPath(outrec->pts))
1533     {
1534       DisposeOutPts(outrec);
1535       return;
1536     }
1537 
1538     OutPt* startOp = outrec->pts, * op2 = startOp;
1539     for (; ; )
1540     {
1541       //NB if preserveCollinear == true, then only remove 180 deg. spikes
1542       if ((CrossProduct(op2->prev->pt, op2->pt, op2->next->pt) == 0) &&
1543         (op2->pt == op2->prev->pt ||
1544           op2->pt == op2->next->pt || !PreserveCollinear ||
1545           DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0))
1546       {
1547 
1548         if (op2 == outrec->pts) outrec->pts = op2->prev;
1549 
1550         op2 = DisposeOutPt(op2);
1551         if (!IsValidClosedPath(op2))
1552         {
1553           DisposeOutPts(outrec);
1554           return;
1555         }
1556         startOp = op2;
1557         continue;
1558       }
1559       op2 = op2->next;
1560       if (op2 == startOp) break;
1561     }
1562     FixSelfIntersects(outrec);
1563   }
1564 
1565   void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
1566   {
1567     // splitOp.prev -> splitOp && 
1568     // splitOp.next -> splitOp.next.next are intersecting
1569     OutPt* prevOp = splitOp->prev;
1570     OutPt* nextNextOp = splitOp->next->next;
1571     outrec->pts = prevOp;
1572 
1573     Point64 ip;
1574     GetIntersectPoint(prevOp->pt, splitOp->pt,
1575       splitOp->next->pt, nextNextOp->pt, ip);
1576 
1577 #ifdef USINGZ
1578     if (zCallback_) zCallback_(prevOp->pt, splitOp->pt,
1579       splitOp->next->pt, nextNextOp->pt, ip);
1580 #endif
1581     double area1 = Area(outrec->pts);
1582     double absArea1 = std::fabs(area1);
1583     if (absArea1 < 2)
1584     {
1585       DisposeOutPts(outrec);
1586       return;
1587     }
1588 
1589     double area2 = AreaTriangle(ip, splitOp->pt, splitOp->next->pt);
1590     double absArea2 = std::fabs(area2);
1591 
1592     // de-link splitOp and splitOp.next from the path
1593     // while inserting the intersection point
1594     if (ip == prevOp->pt || ip == nextNextOp->pt)
1595     {
1596       nextNextOp->prev = prevOp;
1597       prevOp->next = nextNextOp;
1598     }
1599     else
1600     {
1601       OutPt* newOp2 = new OutPt(ip, prevOp->outrec);
1602       newOp2->prev = prevOp;
1603       newOp2->next = nextNextOp;
1604       nextNextOp->prev = newOp2;
1605       prevOp->next = newOp2;
1606     }
1607 
1608     // area1 is the path's area *before* splitting, whereas area2 is
1609     // the area of the triangle containing splitOp & splitOp.next.
1610     // So the only way for these areas to have the same sign is if
1611     // the split triangle is larger than the path containing prevOp or
1612     // if there's more than one self-intersection.
1613     if (absArea2 >= 1 &&
1614       (absArea2 > absArea1 || (area2 > 0) == (area1 > 0)))
1615     {
1616       OutRec* newOr = NewOutRec();
1617       newOr->owner = outrec->owner;
1618       
1619       splitOp->outrec = newOr;
1620       splitOp->next->outrec = newOr;
1621       OutPt* newOp = new OutPt(ip, newOr);
1622       newOp->prev = splitOp->next;
1623       newOp->next = splitOp;
1624       newOr->pts = newOp;
1625       splitOp->prev = newOp;
1626       splitOp->next->next = newOp;
1627 
1628       if (using_polytree_)
1629       {
1630         if (Path1InsidePath2(prevOp, newOp))
1631         {
1632           newOr->splits = new OutRecList();
1633           newOr->splits->push_back(outrec);
1634         }
1635         else
1636         {
1637           if (!outrec->splits) outrec->splits = new OutRecList();
1638           outrec->splits->push_back(newOr);
1639         }
1640       }
1641     }
1642     else
1643     {
1644       delete splitOp->next;
1645       delete splitOp;
1646     }
1647   }
1648 
1649   void ClipperBase::FixSelfIntersects(OutRec* outrec)
1650   {
1651     OutPt* op2 = outrec->pts;
1652     for (; ; )
1653     {
1654       // triangles can't self-intersect
1655       if (op2->prev == op2->next->next) break;
1656       if (SegmentsIntersect(op2->prev->pt,
1657         op2->pt, op2->next->pt, op2->next->next->pt))
1658       {
1659         if (op2 == outrec->pts || op2->next == outrec->pts)
1660           outrec->pts = outrec->pts->prev;
1661         DoSplitOp(outrec, op2);
1662         if (!outrec->pts) break;
1663         op2 = outrec->pts;
1664         continue;
1665       }
1666       else
1667         op2 = op2->next;
1668 
1669       if (op2 == outrec->pts) break;
1670     }
1671   }
1672 
1673 
1674   inline void UpdateOutrecOwner(OutRec* outrec)
1675   {
1676     OutPt* opCurr = outrec->pts;
1677     for (; ; )
1678     {
1679       opCurr->outrec = outrec;
1680       opCurr = opCurr->next;
1681       if (opCurr == outrec->pts) return;
1682     }
1683   }
1684 
1685 
1686   OutPt* ClipperBase::StartOpenPath(Active& e, const Point64& pt)
1687   {
1688     OutRec* outrec = NewOutRec();
1689     outrec->is_open = true;
1690 
1691     if (e.wind_dx > 0)
1692     {
1693       outrec->front_edge = &e;
1694       outrec->back_edge = nullptr;
1695     }
1696     else
1697     {
1698       outrec->front_edge = nullptr;
1699       outrec->back_edge = &e;
1700     }
1701 
1702     e.outrec = outrec;
1703 
1704     OutPt* op = new OutPt(pt, outrec);
1705     outrec->pts = op;
1706     return op;
1707   }
1708 
1709 
1710   inline void ClipperBase::UpdateEdgeIntoAEL(Active* e)
1711   {
1712     e->bot = e->top;
1713     e->vertex_top = NextVertex(*e);
1714     e->top = e->vertex_top->pt;
1715     e->curr_x = e->bot.x;
1716     SetDx(*e);
1717 
1718     if (IsJoined(*e)) Split(*e, e->bot);
1719 
1720     if (IsHorizontal(*e)) return;
1721     InsertScanline(e->top.y);
1722 
1723     CheckJoinLeft(*e, e->bot);
1724     CheckJoinRight(*e, e->bot, true); // (#500)
1725   }
1726 
1727   Active* FindEdgeWithMatchingLocMin(Active* e)
1728   {
1729     Active* result = e->next_in_ael;
1730     while (result)
1731     {
1732       if (result->local_min == e->local_min) return result;
1733       else if (!IsHorizontal(*result) && e->bot != result->bot) result = nullptr;
1734       else result = result->next_in_ael;
1735     }
1736     result = e->prev_in_ael;
1737     while (result)
1738     {
1739       if (result->local_min == e->local_min) return result;
1740       else if (!IsHorizontal(*result) && e->bot != result->bot) return nullptr;
1741       else result = result->prev_in_ael;
1742     }
1743     return result;
1744   }
1745 
1746 
1747   OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt)
1748   {
1749     //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ...
1750     if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2)))
1751     {
1752       if (IsOpen(e1) && IsOpen(e2)) return nullptr;
1753       Active* edge_o, * edge_c;
1754       if (IsOpen(e1))
1755       {
1756         edge_o = &e1;
1757         edge_c = &e2;
1758       }
1759       else
1760       {
1761         edge_o = &e2;
1762         edge_c = &e1;
1763       }
1764       if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety
1765 
1766       if (abs(edge_c->wind_cnt) != 1) return nullptr;
1767       switch (cliptype_)
1768       {
1769       case ClipType::Union:
1770         if (!IsHotEdge(*edge_c)) return nullptr;
1771         break;
1772       default:
1773         if (edge_c->local_min->polytype == PathType::Subject)
1774           return nullptr;
1775       }
1776 
1777       switch (fillrule_)
1778       {
1779       case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break;
1780       case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break;
1781       default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break;
1782       }
1783 
1784       OutPt* resultOp;
1785       //toggle contribution ...
1786       if (IsHotEdge(*edge_o))
1787       {
1788         resultOp = AddOutPt(*edge_o, pt);
1789         if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr;
1790         else edge_o->outrec->back_edge = nullptr;
1791         edge_o->outrec = nullptr;
1792       }
1793 
1794       //horizontal edges can pass under open paths at a LocMins
1795       else if (pt == edge_o->local_min->vertex->pt &&
1796         !IsOpenEnd(*edge_o->local_min->vertex))
1797       {
1798         //find the other side of the LocMin and
1799         //if it's 'hot' join up with it ...
1800         Active* e3 = FindEdgeWithMatchingLocMin(edge_o);
1801         if (e3 && IsHotEdge(*e3))
1802         {
1803           edge_o->outrec = e3->outrec;
1804           if (edge_o->wind_dx > 0)
1805             SetSides(*e3->outrec, *edge_o, *e3);
1806           else
1807             SetSides(*e3->outrec, *e3, *edge_o);
1808           return e3->outrec->pts;
1809         }
1810         else
1811           resultOp = StartOpenPath(*edge_o, pt);
1812       }
1813       else
1814         resultOp = StartOpenPath(*edge_o, pt);
1815 
1816 #ifdef USINGZ
1817       if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt);
1818 #endif
1819       return resultOp;
1820     } // end of an open path intersection
1821 
1822     //MANAGING CLOSED PATHS FROM HERE ON
1823 
1824     if (IsJoined(e1)) Split(e1, pt);
1825     if (IsJoined(e2)) Split(e2, pt);
1826 
1827     //UPDATE WINDING COUNTS...
1828 
1829     int old_e1_windcnt, old_e2_windcnt;
1830     if (e1.local_min->polytype == e2.local_min->polytype)
1831     {
1832       if (fillrule_ == FillRule::EvenOdd)
1833       {
1834         old_e1_windcnt = e1.wind_cnt;
1835         e1.wind_cnt = e2.wind_cnt;
1836         e2.wind_cnt = old_e1_windcnt;
1837       }
1838       else
1839       {
1840         if (e1.wind_cnt + e2.wind_dx == 0)
1841           e1.wind_cnt = -e1.wind_cnt;
1842         else
1843           e1.wind_cnt += e2.wind_dx;
1844         if (e2.wind_cnt - e1.wind_dx == 0)
1845           e2.wind_cnt = -e2.wind_cnt;
1846         else
1847           e2.wind_cnt -= e1.wind_dx;
1848       }
1849     }
1850     else
1851     {
1852       if (fillrule_ != FillRule::EvenOdd)
1853       {
1854         e1.wind_cnt2 += e2.wind_dx;
1855         e2.wind_cnt2 -= e1.wind_dx;
1856       }
1857       else
1858       {
1859         e1.wind_cnt2 = (e1.wind_cnt2 == 0 ? 1 : 0);
1860         e2.wind_cnt2 = (e2.wind_cnt2 == 0 ? 1 : 0);
1861       }
1862     }
1863 
1864     switch (fillrule_)
1865     {
1866     case FillRule::EvenOdd:
1867     case FillRule::NonZero:
1868       old_e1_windcnt = abs(e1.wind_cnt);
1869       old_e2_windcnt = abs(e2.wind_cnt);
1870       break;
1871     default:
1872       if (fillrule_ == fillpos)
1873       {
1874         old_e1_windcnt = e1.wind_cnt;
1875         old_e2_windcnt = e2.wind_cnt;
1876       }
1877       else
1878       {
1879         old_e1_windcnt = -e1.wind_cnt;
1880         old_e2_windcnt = -e2.wind_cnt;
1881       }
1882       break;
1883     }
1884 
1885     const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
1886     const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
1887 
1888     if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01))
1889     {
1890       return nullptr;
1891     }
1892 
1893     //NOW PROCESS THE INTERSECTION ...
1894     OutPt* resultOp = nullptr;
1895     //if both edges are 'hot' ...
1896     if (IsHotEdge(e1) && IsHotEdge(e2))
1897     {
1898       if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) ||
1899         (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor))
1900       {
1901         resultOp = AddLocalMaxPoly(e1, e2, pt);
1902 #ifdef USINGZ
1903         if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
1904 #endif
1905       }
1906       else if (IsFront(e1) || (e1.outrec == e2.outrec))
1907       {
1908         //this 'else if' condition isn't strictly needed but
1909         //it's sensible to split polygons that ony touch at
1910         //a common vertex (not at common edges).
1911 
1912         resultOp = AddLocalMaxPoly(e1, e2, pt);
1913 #ifdef USINGZ
1914         OutPt* op2 = AddLocalMinPoly(e1, e2, pt);
1915         if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt);
1916         if (zCallback_) SetZ(e1, e2, op2->pt);
1917 #else
1918         AddLocalMinPoly(e1, e2, pt);
1919 #endif
1920       }
1921       else
1922       {
1923         resultOp = AddOutPt(e1, pt);
1924 #ifdef USINGZ
1925         OutPt* op2 = AddOutPt(e2, pt);
1926         if (zCallback_)
1927         {
1928           SetZ(e1, e2, resultOp->pt);
1929           SetZ(e1, e2, op2->pt);
1930         }
1931 #else
1932         AddOutPt(e2, pt);
1933 #endif
1934         SwapOutrecs(e1, e2);
1935       }
1936     }
1937     else if (IsHotEdge(e1))
1938     {
1939       resultOp = AddOutPt(e1, pt);
1940 #ifdef USINGZ
1941       if (zCallback_) SetZ(e1, e2, resultOp->pt);
1942 #endif
1943       SwapOutrecs(e1, e2);
1944     }
1945     else if (IsHotEdge(e2))
1946     {
1947       resultOp = AddOutPt(e2, pt);
1948 #ifdef USINGZ
1949       if (zCallback_) SetZ(e1, e2, resultOp->pt);
1950 #endif
1951       SwapOutrecs(e1, e2);
1952     }
1953     else
1954     {
1955       int64_t e1Wc2, e2Wc2;
1956       switch (fillrule_)
1957       {
1958       case FillRule::EvenOdd:
1959       case FillRule::NonZero:
1960         e1Wc2 = abs(e1.wind_cnt2);
1961         e2Wc2 = abs(e2.wind_cnt2);
1962         break;
1963       default:
1964         if (fillrule_ == fillpos)
1965         {
1966           e1Wc2 = e1.wind_cnt2;
1967           e2Wc2 = e2.wind_cnt2;
1968         }
1969         else
1970         {
1971           e1Wc2 = -e1.wind_cnt2;
1972           e2Wc2 = -e2.wind_cnt2;
1973         }
1974         break;
1975       }
1976 
1977       if (!IsSamePolyType(e1, e2))
1978       {
1979         resultOp = AddLocalMinPoly(e1, e2, pt, false);
1980 #ifdef USINGZ
1981         if (zCallback_) SetZ(e1, e2, resultOp->pt);
1982 #endif
1983       }
1984       else if (old_e1_windcnt == 1 && old_e2_windcnt == 1)
1985       {
1986         resultOp = nullptr;
1987         switch (cliptype_)
1988         {
1989         case ClipType::Union:
1990           if (e1Wc2 <= 0 && e2Wc2 <= 0)
1991             resultOp = AddLocalMinPoly(e1, e2, pt, false);
1992           break;
1993         case ClipType::Difference:
1994           if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
1995             ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
1996           {
1997             resultOp = AddLocalMinPoly(e1, e2, pt, false);
1998           }
1999           break;
2000         case ClipType::Xor:
2001           resultOp = AddLocalMinPoly(e1, e2, pt, false);
2002           break;
2003         default:
2004           if (e1Wc2 > 0 && e2Wc2 > 0)
2005             resultOp = AddLocalMinPoly(e1, e2, pt, false);
2006           break;
2007         }
2008 #ifdef USINGZ
2009         if (resultOp && zCallback_) SetZ(e1, e2, resultOp->pt);
2010 #endif
2011       }
2012     }
2013     return resultOp;
2014   }
2015 
2016   inline void ClipperBase::DeleteFromAEL(Active& e)
2017   {
2018     Active* prev = e.prev_in_ael;
2019     Active* next = e.next_in_ael;
2020     if (!prev && !next && (&e != actives_)) return;  // already deleted
2021     if (prev)
2022       prev->next_in_ael = next;
2023     else
2024       actives_ = next;
2025     if (next) next->prev_in_ael = prev;
2026     delete& e;
2027   }
2028 
2029 
2030   inline void ClipperBase::AdjustCurrXAndCopyToSEL(const int64_t top_y)
2031   {
2032     Active* e = actives_;
2033     sel_ = e;
2034     while (e)
2035     {
2036       e->prev_in_sel = e->prev_in_ael;
2037       e->next_in_sel = e->next_in_ael;
2038       e->jump = e->next_in_sel;
2039       if (e->join_with == JoinWith::Left)
2040         e->curr_x = e->prev_in_ael->curr_x; // also avoids complications      
2041       else
2042         e->curr_x = TopX(*e, top_y);
2043       e = e->next_in_ael;
2044     }
2045   }
2046 
2047   bool ClipperBase::ExecuteInternal(ClipType ct, FillRule fillrule, bool use_polytrees)
2048   {
2049     cliptype_ = ct;
2050     fillrule_ = fillrule;
2051     using_polytree_ = use_polytrees;
2052     Reset();
2053     int64_t y;
2054     if (ct == ClipType::None || !PopScanline(y)) return true;
2055 
2056     while (succeeded_)
2057     {
2058       InsertLocalMinimaIntoAEL(y);
2059       Active* e;
2060       while (PopHorz(e)) DoHorizontal(*e);
2061       if (horz_seg_list_.size() > 0)
2062       {
2063         ConvertHorzSegsToJoins();
2064         horz_seg_list_.clear();
2065       }
2066       bot_y_ = y;  // bot_y_ == bottom of scanbeam
2067       if (!PopScanline(y)) break;  // y new top of scanbeam
2068       DoIntersections(y);
2069       DoTopOfScanbeam(y);
2070       while (PopHorz(e)) DoHorizontal(*e);
2071     }
2072     if (succeeded_) ProcessHorzJoins();
2073     return succeeded_;
2074   }
2075 
2076   inline void FixOutRecPts(OutRec* outrec)
2077   {
2078     OutPt* op = outrec->pts;
2079     do {
2080       op->outrec = outrec;
2081       op = op->next;
2082     } while (op != outrec->pts);
2083   }
2084 
2085   inline bool SetHorzSegHeadingForward(HorzSegment& hs, OutPt* opP, OutPt* opN)
2086   {
2087     if (opP->pt.x == opN->pt.x) return false;
2088     if (opP->pt.x < opN->pt.x)
2089     {
2090       hs.left_op = opP;
2091       hs.right_op = opN;
2092       hs.left_to_right = true;
2093     }
2094     else
2095     {
2096       hs.left_op = opN;
2097       hs.right_op = opP;
2098       hs.left_to_right = false;
2099     }
2100     return true;
2101   }
2102 
2103   inline bool UpdateHorzSegment(HorzSegment& hs)
2104   {
2105     OutPt* op = hs.left_op;
2106     OutRec* outrec = GetRealOutRec(op->outrec);
2107     bool outrecHasEdges = outrec->front_edge;
2108     int64_t curr_y = op->pt.y;
2109     OutPt* opP = op, * opN = op;
2110     if (outrecHasEdges)
2111     {
2112       OutPt* opA = outrec->pts, * opZ = opA->next;
2113       while (opP != opZ && opP->prev->pt.y == curr_y) 
2114         opP = opP->prev;
2115       while (opN != opA && opN->next->pt.y == curr_y)
2116         opN = opN->next;
2117     }
2118     else
2119     {
2120       while (opP->prev != opN && opP->prev->pt.y == curr_y)
2121         opP = opP->prev;
2122       while (opN->next != opP && opN->next->pt.y == curr_y)
2123         opN = opN->next;
2124     }
2125     bool result = 
2126       SetHorzSegHeadingForward(hs, opP, opN) &&
2127       !hs.left_op->horz;
2128 
2129     if (result)
2130       hs.left_op->horz = &hs;
2131     else
2132       hs.right_op = nullptr; // (for sorting)
2133     return result;
2134   }
2135   
2136   void ClipperBase::ConvertHorzSegsToJoins()
2137   {
2138     auto j = std::count_if(horz_seg_list_.begin(), 
2139       horz_seg_list_.end(),
2140       [](HorzSegment& hs) { return UpdateHorzSegment(hs); });
2141     if (j < 2) return;
2142     std::sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter());
2143 
2144     HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2;
2145     HorzSegmentList::iterator hs_end = hs1 +j;
2146     HorzSegmentList::iterator hs_end1 = hs_end - 1;
2147 
2148     for (; hs1 != hs_end1; ++hs1)
2149     {
2150       for (hs2 = hs1 + 1; hs2 != hs_end; ++hs2)
2151       {
2152         if ((hs2->left_op->pt.x >= hs1->right_op->pt.x) ||
2153           (hs2->left_to_right == hs1->left_to_right) ||
2154           (hs2->right_op->pt.x <= hs1->left_op->pt.x)) continue;
2155         int64_t curr_y = hs1->left_op->pt.y;
2156         if (hs1->left_to_right)
2157         {
2158           while (hs1->left_op->next->pt.y == curr_y &&
2159             hs1->left_op->next->pt.x <= hs2->left_op->pt.x)
2160             hs1->left_op = hs1->left_op->next;
2161           while (hs2->left_op->prev->pt.y == curr_y &&
2162             hs2->left_op->prev->pt.x <= hs1->left_op->pt.x)
2163             hs2->left_op = hs2->left_op->prev;
2164           HorzJoin join = HorzJoin(
2165             DuplicateOp(hs1->left_op, true),
2166             DuplicateOp(hs2->left_op, false));
2167           horz_join_list_.push_back(join);
2168         }
2169         else
2170         {
2171           while (hs1->left_op->prev->pt.y == curr_y &&
2172             hs1->left_op->prev->pt.x <= hs2->left_op->pt.x)
2173             hs1->left_op = hs1->left_op->prev;
2174           while (hs2->left_op->next->pt.y == curr_y &&
2175             hs2->left_op->next->pt.x <= hs1->left_op->pt.x)
2176             hs2->left_op = hs2->left_op->next;
2177           HorzJoin join = HorzJoin(
2178             DuplicateOp(hs2->left_op, true),
2179             DuplicateOp(hs1->left_op, false));
2180           horz_join_list_.push_back(join);
2181         }
2182       } 
2183     } 
2184   }
2185 
2186   void MoveSplits(OutRec* fromOr, OutRec* toOr)
2187   {
2188     if (!fromOr->splits) return;
2189     if (!toOr->splits) toOr->splits = new OutRecList();
2190     OutRecList::iterator orIter = fromOr->splits->begin();
2191     for (; orIter != fromOr->splits->end(); ++orIter)
2192       toOr->splits->push_back(*orIter);
2193     fromOr->splits->clear();
2194   }
2195 
2196 
2197   void ClipperBase::ProcessHorzJoins()
2198   {
2199     for (const HorzJoin& j : horz_join_list_)
2200     {
2201       OutRec* or1 = GetRealOutRec(j.op1->outrec);
2202       OutRec* or2 = GetRealOutRec(j.op2->outrec);
2203 
2204       OutPt* op1b = j.op1->next;
2205       OutPt* op2b = j.op2->prev;
2206       j.op1->next = j.op2;
2207       j.op2->prev = j.op1;
2208       op1b->prev = op2b;
2209       op2b->next = op1b;
2210 
2211       if (or1 == or2) // 'join' is really a split
2212       {
2213         or2 = NewOutRec();
2214         or2->pts = op1b;
2215         FixOutRecPts(or2);
2216 
2217         //if or1->pts has moved to or2 then update or1->pts!!
2218         if (or1->pts->outrec == or2)
2219         {
2220           or1->pts = j.op1;
2221           or1->pts->outrec = or1;
2222         }
2223 
2224         if (using_polytree_) //#498, #520, #584, D#576, #618
2225         {
2226           if (Path1InsidePath2(or1->pts, or2->pts))
2227           {
2228             //swap or1's & or2's pts
2229             OutPt* tmp = or1->pts;
2230             or1->pts = or2->pts;
2231             or2->pts = tmp;
2232             FixOutRecPts(or1);
2233             FixOutRecPts(or2);
2234             //or2 is now inside or1
2235             or2->owner = or1;
2236           }
2237           else if (Path1InsidePath2(or2->pts, or1->pts))
2238           {
2239             or2->owner = or1;
2240           }
2241           else
2242             or2->owner = or1->owner;
2243 
2244           if (!or1->splits) or1->splits = new OutRecList();
2245           or1->splits->push_back(or2);
2246         }
2247         else
2248           or2->owner = or1;
2249       }
2250       else
2251       {
2252         or2->pts = nullptr;
2253         if (using_polytree_)
2254         {
2255           SetOwner(or2, or1);
2256           MoveSplits(or2, or1); //#618
2257         }
2258         else
2259           or2->owner = or1;
2260       }
2261     }
2262   }
2263 
2264   void ClipperBase::DoIntersections(const int64_t top_y)
2265   {
2266     if (BuildIntersectList(top_y))
2267     {
2268       ProcessIntersectList();
2269       intersect_nodes_.clear();
2270     }
2271   }
2272 
2273   void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
2274   {
2275     Point64 ip;
2276     if (!GetIntersectPoint(e1.bot, e1.top, e2.bot, e2.top, ip))
2277       ip = Point64(e1.curr_x, top_y); //parallel edges
2278 
2279     //rounding errors can occasionally place the calculated intersection
2280     //point either below or above the scanbeam, so check and correct ...
2281     if (ip.y > bot_y_ || ip.y < top_y)
2282     {
2283       double abs_dx1 = std::fabs(e1.dx);
2284       double abs_dx2 = std::fabs(e2.dx);
2285       if (abs_dx1 > 100 && abs_dx2 > 100)
2286       {
2287         if (abs_dx1 > abs_dx2)
2288           ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
2289         else
2290           ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
2291       }
2292       else if (abs_dx1 > 100)
2293         ip = GetClosestPointOnSegment(ip, e1.bot, e1.top);
2294       else if (abs_dx2 > 100)
2295         ip = GetClosestPointOnSegment(ip, e2.bot, e2.top);
2296       else 
2297       {
2298         if (ip.y < top_y) ip.y = top_y;
2299         else ip.y = bot_y_;
2300         if (abs_dx1 < abs_dx2) ip.x = TopX(e1, ip.y);
2301         else ip.x = TopX(e2, ip.y);
2302       }
2303     }
2304     intersect_nodes_.push_back(IntersectNode(&e1, &e2, ip));
2305   }
2306 
2307   bool ClipperBase::BuildIntersectList(const int64_t top_y)
2308   {
2309     if (!actives_ || !actives_->next_in_ael) return false;
2310 
2311     //Calculate edge positions at the top of the current scanbeam, and from this
2312     //we will determine the intersections required to reach these new positions.
2313     AdjustCurrXAndCopyToSEL(top_y);
2314     //Find all edge intersections in the current scanbeam using a stable merge
2315     //sort that ensures only adjacent edges are intersecting. Intersect info is
2316     //stored in FIntersectList ready to be processed in ProcessIntersectList.
2317     //Re merge sorts see https://stackoverflow.com/a/46319131/359538
2318 
2319     Active* left = sel_, * right, * l_end, * r_end, * curr_base, * tmp;
2320 
2321     while (left && left->jump)
2322     {
2323       Active* prev_base = nullptr;
2324       while (left && left->jump)
2325       {
2326         curr_base = left;
2327         right = left->jump;
2328         l_end = right;
2329         r_end = right->jump;
2330         left->jump = r_end;
2331         while (left != l_end && right != r_end)
2332         {
2333           if (right->curr_x < left->curr_x)
2334           {
2335             tmp = right->prev_in_sel;
2336             for (; ; )
2337             {
2338               AddNewIntersectNode(*tmp, *right, top_y);
2339               if (tmp == left) break;
2340               tmp = tmp->prev_in_sel;
2341             }
2342 
2343             tmp = right;
2344             right = ExtractFromSEL(tmp);
2345             l_end = right;
2346             Insert1Before2InSEL(tmp, left);
2347             if (left == curr_base)
2348             {
2349               curr_base = tmp;
2350               curr_base->jump = r_end;
2351               if (!prev_base) sel_ = curr_base;
2352               else prev_base->jump = curr_base;
2353             }
2354           }
2355           else left = left->next_in_sel;
2356         }
2357         prev_base = curr_base;
2358         left = r_end;
2359       }
2360       left = sel_;
2361     }
2362     return intersect_nodes_.size() > 0;
2363   }
2364 
2365   void ClipperBase::ProcessIntersectList()
2366   {
2367     //We now have a list of intersections required so that edges will be
2368     //correctly positioned at the top of the scanbeam. However, it's important
2369     //that edge intersections are processed from the bottom up, but it's also
2370     //crucial that intersections only occur between adjacent edges.
2371 
2372     //First we do a quicksort so intersections proceed in a bottom up order ...
2373     std::sort(intersect_nodes_.begin(), intersect_nodes_.end(), IntersectListSort);
2374     //Now as we process these intersections, we must sometimes adjust the order
2375     //to ensure that intersecting edges are always adjacent ...
2376 
2377     IntersectNodeList::iterator node_iter, node_iter2;
2378     for (node_iter = intersect_nodes_.begin();
2379       node_iter != intersect_nodes_.end();  ++node_iter)
2380     {
2381       if (!EdgesAdjacentInAEL(*node_iter))
2382       {
2383         node_iter2 = node_iter + 1;
2384         while (!EdgesAdjacentInAEL(*node_iter2)) ++node_iter2;
2385         std::swap(*node_iter, *node_iter2);
2386       }
2387 
2388       IntersectNode& node = *node_iter;
2389       IntersectEdges(*node.edge1, *node.edge2, node.pt);
2390       SwapPositionsInAEL(*node.edge1, *node.edge2);
2391 
2392       node.edge1->curr_x = node.pt.x;
2393       node.edge2->curr_x = node.pt.x;
2394       CheckJoinLeft(*node.edge2, node.pt, true);
2395       CheckJoinRight(*node.edge1, node.pt, true);
2396     }
2397   }
2398 
2399   void ClipperBase::SwapPositionsInAEL(Active& e1, Active& e2)
2400   {
2401     //preconditon: e1 must be immediately to the left of e2
2402     Active* next = e2.next_in_ael;
2403     if (next) next->prev_in_ael = &e1;
2404     Active* prev = e1.prev_in_ael;
2405     if (prev) prev->next_in_ael = &e2;
2406     e2.prev_in_ael = prev;
2407     e2.next_in_ael = &e1;
2408     e1.prev_in_ael = &e2;
2409     e1.next_in_ael = next;
2410     if (!e2.prev_in_ael) actives_ = &e2;
2411   }
2412 
2413   inline OutPt* GetLastOp(const Active& hot_edge)
2414   {
2415     OutRec* outrec = hot_edge.outrec;
2416     OutPt* result = outrec->pts;
2417     if (&hot_edge != outrec->front_edge)
2418       result = result->next;
2419     return result;
2420   }
2421 
2422   void ClipperBase::AddTrialHorzJoin(OutPt* op)
2423   {
2424     if (op->outrec->is_open) return;
2425     horz_seg_list_.push_back(HorzSegment(op));
2426   }
2427 
2428   bool ClipperBase::ResetHorzDirection(const Active& horz, 
2429     const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right)
2430   {
2431     if (horz.bot.x == horz.top.x)
2432     {
2433       //the horizontal edge is going nowhere ...
2434       horz_left = horz.curr_x;
2435       horz_right = horz.curr_x;
2436       Active* e = horz.next_in_ael;
2437       while (e && e->vertex_top != max_vertex) e = e->next_in_ael;
2438       return e != nullptr;
2439     }
2440     else if (horz.curr_x < horz.top.x)
2441     {
2442       horz_left = horz.curr_x;
2443       horz_right = horz.top.x;
2444       return true;
2445     }
2446     else
2447     {
2448       horz_left = horz.top.x;
2449       horz_right = horz.curr_x;
2450       return false;  // right to left
2451     }
2452   }
2453 
2454   inline bool HorzIsSpike(const Active& horzEdge)
2455   {
2456     Point64 nextPt = NextVertex(horzEdge)->pt;
2457     return (nextPt.y == horzEdge.bot.y) &&
2458       (horzEdge.bot.x < horzEdge.top.x) != (horzEdge.top.x < nextPt.x);
2459   }
2460 
2461   inline void TrimHorz(Active& horzEdge, bool preserveCollinear)
2462   {
2463     bool wasTrimmed = false;
2464     Point64 pt = NextVertex(horzEdge)->pt;
2465     while (pt.y == horzEdge.top.y)
2466     {
2467       //always trim 180 deg. spikes (in closed paths)
2468       //but otherwise break if preserveCollinear = true
2469       if (preserveCollinear &&
2470         ((pt.x < horzEdge.top.x) != (horzEdge.bot.x < horzEdge.top.x)))
2471         break;
2472 
2473       horzEdge.vertex_top = NextVertex(horzEdge);
2474       horzEdge.top = pt;
2475       wasTrimmed = true;
2476       if (IsMaxima(horzEdge)) break;
2477       pt = NextVertex(horzEdge)->pt;
2478     }
2479 
2480     if (wasTrimmed) SetDx(horzEdge); // +/-infinity
2481   }
2482 
2483   void ClipperBase::DoHorizontal(Active& horz)
2484     /*******************************************************************************
2485         * Notes: Horizontal edges (HEs) at scanline intersections (ie at the top or    *
2486         * bottom of a scanbeam) are processed as if layered.The order in which HEs     *
2487         * are processed doesn't matter. HEs intersect with the bottom vertices of      *
2488         * other HEs[#] and with non-horizontal edges [*]. Once these intersections     *
2489         * are completed, intermediate HEs are 'promoted' to the next edge in their     *
2490         * bounds, and they in turn may be intersected[%] by other HEs.                 *
2491         *                                                                              *
2492         * eg: 3 horizontals at a scanline:    /   |                     /           /  *
2493         *              |                     /    |     (HE3)o ========%========== o   *
2494         *              o ======= o(HE2)     /     |         /         /                *
2495         *          o ============#=========*======*========#=========o (HE1)           *
2496         *         /              |        /       |       /                            *
2497         *******************************************************************************/
2498   {
2499     Point64 pt;
2500     bool horzIsOpen = IsOpen(horz);
2501     int64_t y = horz.bot.y;
2502     Vertex* vertex_max;
2503     if (horzIsOpen)
2504       vertex_max = GetCurrYMaximaVertex_Open(horz);
2505     else
2506       vertex_max = GetCurrYMaximaVertex(horz);
2507 
2508     // remove 180 deg.spikes and also simplify
2509     // consecutive horizontals when PreserveCollinear = true
2510     if (vertex_max && !horzIsOpen && vertex_max != horz.vertex_top)
2511       TrimHorz(horz, PreserveCollinear);
2512 
2513     int64_t horz_left, horz_right;
2514     bool is_left_to_right =
2515       ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
2516 
2517     if (IsHotEdge(horz))
2518     {
2519 #ifdef USINGZ
2520       OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y, horz.bot.z));
2521 #else
2522       OutPt* op = AddOutPt(horz, Point64(horz.curr_x, y));
2523 #endif
2524       AddTrialHorzJoin(op);
2525     }
2526 
2527     while (true) // loop through consec. horizontal edges
2528     {
2529       Active* e;
2530       if (is_left_to_right) e = horz.next_in_ael;
2531       else e = horz.prev_in_ael;
2532 
2533       while (e)
2534       {
2535         if (e->vertex_top == vertex_max)
2536         {
2537           if (IsHotEdge(horz) && IsJoined(*e))
2538             Split(*e, e->top);
2539 
2540           if (IsHotEdge(horz))
2541           {
2542             while (horz.vertex_top != vertex_max)
2543             {
2544               AddOutPt(horz, horz.top);
2545               UpdateEdgeIntoAEL(&horz);
2546             }
2547             if (is_left_to_right)
2548               AddLocalMaxPoly(horz, *e, horz.top);
2549             else
2550               AddLocalMaxPoly(*e, horz, horz.top);
2551           }
2552           DeleteFromAEL(*e);
2553           DeleteFromAEL(horz);
2554           return;
2555         }
2556 
2557         //if horzEdge is a maxima, keep going until we reach
2558         //its maxima pair, otherwise check for break conditions
2559         if (vertex_max != horz.vertex_top || IsOpenEnd(horz))
2560         {
2561           //otherwise stop when 'ae' is beyond the end of the horizontal line
2562           if ((is_left_to_right && e->curr_x > horz_right) ||
2563             (!is_left_to_right && e->curr_x < horz_left)) break;
2564 
2565           if (e->curr_x == horz.top.x && !IsHorizontal(*e))
2566           {
2567             pt = NextVertex(horz)->pt;
2568             if (is_left_to_right)
2569             {
2570               //with open paths we'll only break once past horz's end
2571               if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e))
2572               {
2573                 if (TopX(*e, pt.y) > pt.x) break;
2574               }
2575               //otherwise we'll only break when horz's outslope is greater than e's
2576               else if (TopX(*e, pt.y) >= pt.x) break;
2577             }
2578             else
2579             {
2580               if (IsOpen(*e) && !IsSamePolyType(*e, horz) && !IsHotEdge(*e))
2581               {
2582                 if (TopX(*e, pt.y) < pt.x) break;
2583               }
2584               else if (TopX(*e, pt.y) <= pt.x) break;
2585             }
2586           }
2587         }
2588 
2589         pt = Point64(e->curr_x, horz.bot.y);
2590         if (is_left_to_right)
2591         {
2592           IntersectEdges(horz, *e, pt);
2593           SwapPositionsInAEL(horz, *e);
2594           horz.curr_x = e->curr_x;
2595           e = horz.next_in_ael;
2596         }
2597         else
2598         {
2599           IntersectEdges(*e, horz, pt);
2600           SwapPositionsInAEL(*e, horz);
2601           horz.curr_x = e->curr_x;
2602           e = horz.prev_in_ael;
2603         }
2604 
2605         if (horz.outrec)
2606         {
2607           //nb: The outrec containining the op returned by IntersectEdges
2608           //above may no longer be associated with horzEdge.
2609           AddTrialHorzJoin(GetLastOp(horz));
2610         }
2611       }
2612 
2613       //check if we've finished with (consecutive) horizontals ...
2614       if (horzIsOpen && IsOpenEnd(horz)) // ie open at top
2615       {
2616         if (IsHotEdge(horz))
2617         {
2618           AddOutPt(horz, horz.top);
2619           if (IsFront(horz))
2620             horz.outrec->front_edge = nullptr;
2621           else
2622             horz.outrec->back_edge = nullptr;
2623           horz.outrec = nullptr;
2624         }
2625         DeleteFromAEL(horz);
2626         return;
2627       }
2628       else if (NextVertex(horz)->pt.y != horz.top.y)
2629         break;
2630 
2631       //still more horizontals in bound to process ...
2632       if (IsHotEdge(horz))
2633         AddOutPt(horz, horz.top);
2634       UpdateEdgeIntoAEL(&horz);
2635 
2636       if (PreserveCollinear && !horzIsOpen && HorzIsSpike(horz))
2637         TrimHorz(horz, true);
2638 
2639       is_left_to_right =
2640         ResetHorzDirection(horz, vertex_max, horz_left, horz_right);
2641     }
2642 
2643     if (IsHotEdge(horz)) 
2644     {
2645       OutPt* op = AddOutPt(horz, horz.top);
2646       AddTrialHorzJoin(op);
2647     }
2648 
2649     UpdateEdgeIntoAEL(&horz); // end of an intermediate horiz.
2650   }
2651 
2652   void ClipperBase::DoTopOfScanbeam(const int64_t y)
2653   {
2654     sel_ = nullptr;  // sel_ is reused to flag horizontals (see PushHorz below)
2655     Active* e = actives_;
2656     while (e)
2657     {
2658       //nb: 'e' will never be horizontal here
2659       if (e->top.y == y)
2660       {
2661         e->curr_x = e->top.x;
2662         if (IsMaxima(*e))
2663         {
2664           e = DoMaxima(*e);  // TOP OF BOUND (MAXIMA)
2665           continue;
2666         }
2667         else
2668         {
2669           //INTERMEDIATE VERTEX ...
2670           if (IsHotEdge(*e)) AddOutPt(*e, e->top);
2671           UpdateEdgeIntoAEL(e);
2672           if (IsHorizontal(*e))
2673             PushHorz(*e);  // horizontals are processed later
2674         }
2675       }
2676       else // i.e. not the top of the edge
2677         e->curr_x = TopX(*e, y);
2678 
2679       e = e->next_in_ael;
2680     }
2681   }
2682 
2683 
2684   Active* ClipperBase::DoMaxima(Active& e)
2685   {
2686     Active* next_e, * prev_e, * max_pair;
2687     prev_e = e.prev_in_ael;
2688     next_e = e.next_in_ael;
2689     if (IsOpenEnd(e))
2690     {
2691       if (IsHotEdge(e)) AddOutPt(e, e.top);
2692       if (!IsHorizontal(e))
2693       {
2694         if (IsHotEdge(e))
2695         {
2696           if (IsFront(e))
2697             e.outrec->front_edge = nullptr;
2698           else
2699             e.outrec->back_edge = nullptr;
2700           e.outrec = nullptr;
2701         }
2702         DeleteFromAEL(e);
2703       }
2704       return next_e;
2705     }
2706 
2707     max_pair = GetMaximaPair(e);
2708     if (!max_pair) return next_e;  // eMaxPair is horizontal
2709 
2710     if (IsJoined(e)) Split(e, e.top);
2711     if (IsJoined(*max_pair)) Split(*max_pair, max_pair->top);
2712 
2713     //only non-horizontal maxima here.
2714     //process any edges between maxima pair ...
2715     while (next_e != max_pair)
2716     {
2717       IntersectEdges(e, *next_e, e.top);
2718       SwapPositionsInAEL(e, *next_e);
2719       next_e = e.next_in_ael;
2720     }
2721 
2722     if (IsOpen(e))
2723     {
2724       if (IsHotEdge(e))
2725         AddLocalMaxPoly(e, *max_pair, e.top);
2726       DeleteFromAEL(*max_pair);
2727       DeleteFromAEL(e);
2728       return (prev_e ? prev_e->next_in_ael : actives_);
2729     }
2730 
2731     // e.next_in_ael== max_pair ...
2732     if (IsHotEdge(e))
2733       AddLocalMaxPoly(e, *max_pair, e.top);
2734 
2735     DeleteFromAEL(e);
2736     DeleteFromAEL(*max_pair);
2737     return (prev_e ? prev_e->next_in_ael : actives_);
2738   }
2739 
2740   void ClipperBase::Split(Active& e, const Point64& pt)
2741   {
2742     if (e.join_with == JoinWith::Right)
2743     {
2744       e.join_with = JoinWith::None;
2745       e.next_in_ael->join_with = JoinWith::None;
2746       AddLocalMinPoly(e, *e.next_in_ael, pt, true);
2747     }
2748     else
2749     {
2750       e.join_with = JoinWith::None;
2751       e.prev_in_ael->join_with = JoinWith::None;
2752       AddLocalMinPoly(*e.prev_in_ael, e, pt, true);
2753     }
2754   }
2755 
2756   void ClipperBase::CheckJoinLeft(Active& e, 
2757     const Point64& pt, bool check_curr_x)
2758   {
2759     Active* prev = e.prev_in_ael;
2760     if (IsOpen(e) || !IsHotEdge(e) || !prev ||
2761       IsOpen(*prev) || !IsHotEdge(*prev)) return;
2762     if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) &&
2763       ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins              
2764 
2765     if (check_curr_x)
2766     {
2767       if (DistanceFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return;
2768     }
2769     else if (e.curr_x != prev->curr_x) return;
2770     if (CrossProduct(e.top, pt, prev->top)) return;
2771 
2772     if (e.outrec->idx == prev->outrec->idx)
2773       AddLocalMaxPoly(*prev, e, pt);
2774     else if (e.outrec->idx < prev->outrec->idx)
2775       JoinOutrecPaths(e, *prev);
2776     else
2777       JoinOutrecPaths(*prev, e);
2778     prev->join_with = JoinWith::Right;
2779     e.join_with = JoinWith::Left;
2780   }
2781 
2782   void ClipperBase::CheckJoinRight(Active& e, 
2783     const Point64& pt, bool check_curr_x)
2784   {
2785     Active* next = e.next_in_ael;
2786     if (IsOpen(e) || !IsHotEdge(e) ||
2787       !next || IsOpen(*next) || !IsHotEdge(*next)) return;
2788     if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) &&
2789       ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins              
2790 
2791     if (check_curr_x)
2792     {
2793       if (DistanceFromLineSqrd(pt, next->bot, next->top) > 0.35) return;
2794     }
2795     else if (e.curr_x != next->curr_x) return;
2796     if (CrossProduct(e.top, pt, next->top)) return;
2797       
2798     if (e.outrec->idx == next->outrec->idx)
2799       AddLocalMaxPoly(e, *next, pt);
2800     else if (e.outrec->idx < next->outrec->idx)
2801       JoinOutrecPaths(e, *next);
2802     else
2803       JoinOutrecPaths(*next, e);
2804 
2805     e.join_with = JoinWith::Right;
2806     next->join_with = JoinWith::Left;
2807   }
2808 
2809   inline bool GetHorzExtendedHorzSeg(OutPt*& op, OutPt*& op2)
2810   {
2811     OutRec* outrec = GetRealOutRec(op->outrec);
2812     op2 = op;
2813     if (outrec->front_edge)
2814     {
2815       while (op->prev != outrec->pts &&
2816         op->prev->pt.y == op->pt.y) op = op->prev;
2817       while (op2 != outrec->pts &&
2818         op2->next->pt.y == op2->pt.y) op2 = op2->next;
2819       return op2 != op;
2820     }
2821     else
2822     {
2823       while (op->prev != op2 && op->prev->pt.y == op->pt.y)
2824         op = op->prev;
2825       while (op2->next != op && op2->next->pt.y == op2->pt.y)
2826         op2 = op2->next;
2827       return op2 != op && op2->next != op;
2828     }
2829   }
2830 
2831   bool BuildPath64(OutPt* op, bool reverse, bool isOpen, Path64& path)
2832   {
2833     if (!op || op->next == op || (!isOpen && op->next == op->prev))
2834       return false;
2835 
2836     path.resize(0);
2837     Point64 lastPt;
2838     OutPt* op2;
2839     if (reverse)
2840     {
2841       lastPt = op->pt;
2842       op2 = op->prev;
2843     }
2844     else
2845     {
2846       op = op->next;
2847       lastPt = op->pt;
2848       op2 = op->next;
2849     }
2850     path.push_back(lastPt);
2851 
2852     while (op2 != op)
2853     {
2854       if (op2->pt != lastPt)
2855       {
2856         lastPt = op2->pt;
2857         path.push_back(lastPt);
2858       }
2859       if (reverse)
2860         op2 = op2->prev;
2861       else
2862         op2 = op2->next;
2863     }
2864 
2865     if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
2866     else return true;
2867   }
2868 
2869   bool ClipperBase::CheckBounds(OutRec* outrec)
2870   {
2871     if (!outrec->pts) return false;
2872     if (!outrec->bounds.IsEmpty()) return true;
2873     CleanCollinear(outrec);
2874     if (!outrec->pts || 
2875       !BuildPath64(outrec->pts, ReverseSolution, false, outrec->path)){ 
2876         return false;}
2877     outrec->bounds = GetBounds(outrec->path);
2878     return true;
2879   }
2880 
2881   bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
2882   {
2883     for (auto split : *splits)
2884     {
2885       split = GetRealOutRec(split);
2886       if(!split || split == outrec || split->recursive_split == outrec) continue;
2887       split->recursive_split = outrec; // prevent infinite loops
2888 
2889       if (split->splits && CheckSplitOwner(outrec, split->splits)) 
2890           return true;
2891       else if (CheckBounds(split) && 
2892         IsValidOwner(outrec, split) && 
2893         split->bounds.Contains(outrec->bounds) &&
2894         Path1InsidePath2(outrec->pts, split->pts))
2895       {
2896         outrec->owner = split; //found in split
2897         return true;
2898       }
2899     }
2900     return false;
2901   }
2902 
2903   void ClipperBase::RecursiveCheckOwners(OutRec* outrec, PolyPath* polypath)
2904   {
2905     // pre-condition: outrec will have valid bounds
2906     // post-condition: if a valid path, outrec will have a polypath
2907 
2908     if (outrec->polypath || outrec->bounds.IsEmpty()) return;
2909 
2910     while (outrec->owner)
2911     {
2912       if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
2913       if (outrec->owner->pts && CheckBounds(outrec->owner) &&
2914         outrec->owner->bounds.Contains(outrec->bounds) &&
2915         Path1InsidePath2(outrec->pts, outrec->owner->pts)) break;
2916       outrec->owner = outrec->owner->owner;
2917     }
2918 
2919     if (outrec->owner)
2920     {
2921       if (!outrec->owner->polypath) 
2922         RecursiveCheckOwners(outrec->owner, polypath);
2923       outrec->polypath = outrec->owner->polypath->AddChild(outrec->path);
2924     }
2925     else
2926       outrec->polypath = polypath->AddChild(outrec->path);
2927   }
2928 
2929   void Clipper64::BuildPaths64(Paths64& solutionClosed, Paths64* solutionOpen)
2930   {
2931     solutionClosed.resize(0);
2932     solutionClosed.reserve(outrec_list_.size());
2933     if (solutionOpen)
2934     {
2935       solutionOpen->resize(0);
2936       solutionOpen->reserve(outrec_list_.size());
2937     }
2938 
2939     // nb: outrec_list_.size() may change in the following
2940     // while loop because polygons may be split during
2941     // calls to CleanCollinear which calls FixSelfIntersects
2942     for (size_t i = 0; i < outrec_list_.size(); ++i)
2943     {
2944       OutRec* outrec = outrec_list_[i];
2945       if (outrec->pts == nullptr) continue;
2946 
2947       Path64 path;
2948       if (solutionOpen && outrec->is_open)
2949       {
2950         if (BuildPath64(outrec->pts, ReverseSolution, true, path))
2951           solutionOpen->emplace_back(std::move(path));
2952       }
2953       else
2954       {
2955         // nb: CleanCollinear can add to outrec_list_
2956         CleanCollinear(outrec);
2957         //closed paths should always return a Positive orientation
2958         if (BuildPath64(outrec->pts, ReverseSolution, false, path))
2959           solutionClosed.emplace_back(std::move(path));
2960       }
2961     }
2962   }
2963 
2964   void Clipper64::BuildTree64(PolyPath64& polytree, Paths64& open_paths)
2965   {
2966     polytree.Clear();
2967     open_paths.resize(0);
2968     if (has_open_paths_)
2969       open_paths.reserve(outrec_list_.size());
2970     
2971     // outrec_list_.size() is not static here because
2972     // CheckBounds below can indirectly add additional
2973     // OutRec (via FixOutRecPts & CleanCollinear)
2974     for (size_t i = 0; i < outrec_list_.size(); ++i)
2975     {
2976       OutRec* outrec = outrec_list_[i];
2977       if (!outrec || !outrec->pts) continue;
2978       if (outrec->is_open)
2979       {
2980         Path64 path;
2981         if (BuildPath64(outrec->pts, ReverseSolution, true, path))
2982           open_paths.push_back(path);
2983         continue;
2984       }
2985 
2986       if (CheckBounds(outrec))
2987         RecursiveCheckOwners(outrec, &polytree);
2988     }
2989   }
2990 
2991   bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale)
2992   {
2993     if (!op || op->next == op || (!isOpen && op->next == op->prev)) 
2994       return false;
2995 
2996     path.resize(0);
2997     Point64 lastPt;
2998     OutPt* op2;
2999     if (reverse)
3000     {
3001       lastPt = op->pt;
3002       op2 = op->prev;
3003     }
3004     else
3005     {
3006       op = op->next;
3007       lastPt = op->pt;
3008       op2 = op->next;
3009     }
3010 #ifdef USINGZ
3011     path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z));
3012 #else
3013     path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
3014 #endif
3015 
3016     while (op2 != op)
3017     {
3018       if (op2->pt != lastPt)
3019       {
3020         lastPt = op2->pt;
3021 #ifdef USINGZ
3022         path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale, lastPt.z));
3023 #else
3024         path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale));
3025 #endif
3026         
3027       }
3028       if (reverse)
3029         op2 = op2->prev;
3030       else
3031         op2 = op2->next;
3032     }
3033     if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false;
3034     return true;
3035   }
3036 
3037   void ClipperD::BuildPathsD(PathsD& solutionClosed, PathsD* solutionOpen)
3038   {
3039     solutionClosed.resize(0);
3040     solutionClosed.reserve(outrec_list_.size());
3041     if (solutionOpen)
3042     {
3043       solutionOpen->resize(0);
3044       solutionOpen->reserve(outrec_list_.size());
3045     }
3046 
3047     // outrec_list_.size() is not static here because
3048     // CleanCollinear below can indirectly add additional
3049     // OutRec (via FixOutRecPts)
3050     for (std::size_t i = 0; i < outrec_list_.size(); ++i)
3051     {
3052       OutRec* outrec = outrec_list_[i];
3053       if (outrec->pts == nullptr) continue;
3054 
3055       PathD path;
3056       if (solutionOpen && outrec->is_open)
3057       {
3058         if (BuildPathD(outrec->pts, ReverseSolution, true, path, invScale_))
3059           solutionOpen->emplace_back(std::move(path));
3060       }
3061       else
3062       {
3063         CleanCollinear(outrec);
3064         //closed paths should always return a Positive orientation
3065         if (BuildPathD(outrec->pts, ReverseSolution, false, path, invScale_))
3066           solutionClosed.emplace_back(std::move(path));
3067       }
3068     }
3069   }
3070 
3071   void ClipperD::BuildTreeD(PolyPathD& polytree, PathsD& open_paths)
3072   {
3073     polytree.Clear();
3074     open_paths.resize(0);
3075     if (has_open_paths_)
3076       open_paths.reserve(outrec_list_.size());
3077 
3078     // outrec_list_.size() is not static here because
3079     // BuildPathD below can indirectly add additional OutRec //#607
3080     for (size_t i = 0; i < outrec_list_.size(); ++i)
3081     {
3082       OutRec* outrec = outrec_list_[i];
3083       if (!outrec || !outrec->pts) continue;
3084       if (outrec->is_open)
3085       {
3086         PathD path;
3087         if (BuildPathD(outrec->pts, ReverseSolution, true, path, invScale_))
3088           open_paths.push_back(path);
3089         continue;
3090       }
3091 
3092       if (CheckBounds(outrec))
3093         RecursiveCheckOwners(outrec, &polytree);
3094     }
3095   }
3096 
3097 }  // namespace clipper2lib