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

0001 /*******************************************************************************
0002 * Author    :  Angus Johnson                                                   *
0003 * Date      :  8 September 2023                                                *
0004 * Website   :  http://www.angusj.com                                           *
0005 * Copyright :  Angus Johnson 2010-2023                                         *
0006 * Purpose   :  FAST rectangular clipping                                       *
0007 * License   :  http://www.boost.org/LICENSE_1_0.txt                            *
0008 *******************************************************************************/
0009 
0010 #include <cmath>
0011 #include "clipper2/clipper.h"
0012 #include "clipper2/clipper.rectclip.h"
0013 
0014 namespace Clipper2Lib {
0015 
0016   //------------------------------------------------------------------------------
0017   // Miscellaneous methods
0018   //------------------------------------------------------------------------------
0019 
0020   inline bool Path1ContainsPath2(const Path64& path1, const Path64& path2)
0021   {
0022     int io_count = 0;
0023     // precondition: no (significant) overlap
0024     for (const Point64& pt : path2)
0025     {
0026       PointInPolygonResult pip = PointInPolygon(pt, path1);
0027       switch (pip)
0028       {
0029       case PointInPolygonResult::IsOutside: ++io_count; break;
0030       case PointInPolygonResult::IsInside: --io_count; break;
0031       default: continue;
0032       }
0033       if (std::abs(io_count) > 1) break;
0034     }
0035     return io_count <= 0;
0036   }
0037 
0038   inline bool GetLocation(const Rect64& rec,
0039     const Point64& pt, Location& loc)
0040   {
0041     if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom)
0042     {
0043       loc = Location::Left;
0044       return false;
0045     }
0046     else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom)
0047     {
0048       loc = Location::Right;
0049       return false;
0050     }
0051     else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right)
0052     {
0053       loc = Location::Top;
0054       return false;
0055     }
0056     else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right)
0057     {
0058       loc = Location::Bottom;
0059       return false;
0060     }
0061     else if (pt.x < rec.left) loc = Location::Left;
0062     else if (pt.x > rec.right) loc = Location::Right;
0063     else if (pt.y < rec.top) loc = Location::Top;
0064     else if (pt.y > rec.bottom) loc = Location::Bottom;
0065     else loc = Location::Inside;
0066     return true;
0067   }
0068 
0069   inline bool IsHorizontal(const Point64& pt1, const Point64& pt2)
0070   {
0071     return pt1.y == pt2.y;
0072   }
0073 
0074   inline bool GetSegmentIntersection(const Point64& p1,
0075     const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
0076   {
0077     double res1 = CrossProduct(p1, p3, p4);
0078     double res2 = CrossProduct(p2, p3, p4);
0079     if (res1 == 0)
0080     {
0081       ip = p1;
0082       if (res2 == 0) return false; // segments are collinear
0083       else if (p1 == p3 || p1 == p4) return true;
0084       //else if (p2 == p3 || p2 == p4) { ip = p2; return true; }
0085       else if (IsHorizontal(p3, p4)) return ((p1.x > p3.x) == (p1.x < p4.x));
0086       else return ((p1.y > p3.y) == (p1.y < p4.y));
0087     }
0088     else if (res2 == 0)
0089     {
0090       ip = p2;
0091       if (p2 == p3 || p2 == p4) return true;
0092       else if (IsHorizontal(p3, p4)) return ((p2.x > p3.x) == (p2.x < p4.x));
0093       else return ((p2.y > p3.y) == (p2.y < p4.y));
0094     }
0095     if ((res1 > 0) == (res2 > 0)) return false;
0096 
0097     double res3 = CrossProduct(p3, p1, p2);
0098     double res4 = CrossProduct(p4, p1, p2);
0099     if (res3 == 0)
0100     {
0101       ip = p3;
0102       if (p3 == p1 || p3 == p2) return true;
0103       else if (IsHorizontal(p1, p2)) return ((p3.x > p1.x) == (p3.x < p2.x));
0104       else return ((p3.y > p1.y) == (p3.y < p2.y));
0105     }
0106     else if (res4 == 0)
0107     {
0108       ip = p4;
0109       if (p4 == p1 || p4 == p2) return true;
0110       else if (IsHorizontal(p1, p2)) return ((p4.x > p1.x) == (p4.x < p2.x));
0111       else return ((p4.y > p1.y) == (p4.y < p2.y));
0112     }
0113     if ((res3 > 0) == (res4 > 0)) return false;
0114 
0115     // segments must intersect to get here
0116     return GetIntersectPoint(p1, p2, p3, p4, ip);
0117   }
0118 
0119   inline bool GetIntersection(const Path64& rectPath,
0120     const Point64& p, const Point64& p2, Location& loc, Point64& ip)
0121   {
0122     // gets the intersection closest to 'p'
0123     // when Result = false, loc will remain unchanged
0124     switch (loc)
0125     {
0126     case Location::Left:
0127       if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true;
0128       else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))        
0129       {
0130         loc = Location::Top;
0131         return true;
0132       }
0133       else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))
0134       {
0135         loc = Location::Bottom;
0136         return true;
0137       }
0138       else return false;
0139 
0140     case Location::Top:
0141       if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) return true;
0142       else if ((p.x < rectPath[0].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
0143       {
0144         loc = Location::Left;
0145         return true;
0146       }
0147       else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))
0148       {
0149         loc = Location::Right;
0150         return true;
0151       }
0152       else return false;
0153 
0154     case Location::Right:
0155       if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) return true;
0156       else if ((p.y < rectPath[1].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
0157       {
0158         loc = Location::Top;
0159         return true;
0160       }
0161       else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))
0162       {
0163         loc = Location::Bottom;
0164         return true;
0165       }
0166       else return false;
0167 
0168     case Location::Bottom:
0169       if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) return true;
0170       else if ((p.x < rectPath[3].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))
0171       {
0172         loc = Location::Left;
0173         return true;
0174       }
0175       else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))
0176       {
0177         loc = Location::Right;
0178         return true;
0179       }
0180       else return false;
0181 
0182     default: // loc == rInside
0183       if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) 
0184       {
0185         loc = Location::Left;
0186         return true;
0187       }
0188       else if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))
0189       {
0190         loc = Location::Top;
0191         return true;
0192       }
0193       else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))
0194       {
0195         loc = Location::Right;
0196         return true;
0197       }
0198       else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))
0199       {
0200         loc = Location::Bottom;
0201         return true;
0202       }
0203       else return false;
0204     }
0205   }
0206 
0207   inline Location GetAdjacentLocation(Location loc, bool isClockwise)
0208   {
0209     int delta = (isClockwise) ? 1 : 3;
0210     return static_cast<Location>((static_cast<int>(loc) + delta) % 4);
0211   }
0212 
0213   inline bool HeadingClockwise(Location prev, Location curr)
0214   {
0215     return (static_cast<int>(prev) + 1) % 4 == static_cast<int>(curr);
0216   }
0217 
0218   inline bool AreOpposites(Location prev, Location curr)
0219   {
0220     return abs(static_cast<int>(prev) - static_cast<int>(curr)) == 2;
0221   }
0222 
0223   inline bool IsClockwise(Location prev, Location curr,
0224     const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp)
0225   {
0226     if (AreOpposites(prev, curr))
0227       return CrossProduct(prev_pt, rect_mp, curr_pt) < 0;
0228     else
0229       return HeadingClockwise(prev, curr);
0230   }
0231 
0232   inline OutPt2* UnlinkOp(OutPt2* op)
0233   {
0234     if (op->next == op) return nullptr;
0235     op->prev->next = op->next;
0236     op->next->prev = op->prev;
0237     return op->next;
0238   }
0239 
0240   inline OutPt2* UnlinkOpBack(OutPt2* op)
0241   {
0242     if (op->next == op) return nullptr;
0243     op->prev->next = op->next;
0244     op->next->prev = op->prev;
0245     return op->prev;
0246   }
0247 
0248   inline uint32_t GetEdgesForPt(const Point64& pt, const Rect64& rec)
0249   {
0250     uint32_t result = 0;
0251     if (pt.x == rec.left) result = 1;
0252     else if (pt.x == rec.right) result = 4;
0253     if (pt.y == rec.top) result += 2;
0254     else if (pt.y == rec.bottom) result += 8;
0255     return result;
0256   }
0257 
0258   inline bool IsHeadingClockwise(const Point64& pt1, const Point64& pt2, int edgeIdx)
0259   {
0260     switch (edgeIdx)
0261     {
0262     case 0: return pt2.y < pt1.y;
0263     case 1: return pt2.x > pt1.x;
0264     case 2: return pt2.y > pt1.y;
0265     default: return pt2.x < pt1.x;
0266     }
0267   }
0268 
0269   inline bool HasHorzOverlap(const Point64& left1, const Point64& right1,
0270     const Point64& left2, const Point64& right2)
0271   {
0272     return (left1.x < right2.x) && (right1.x > left2.x);
0273   }
0274 
0275   inline bool HasVertOverlap(const Point64& top1, const Point64& bottom1,
0276     const Point64& top2, const Point64& bottom2)
0277   {
0278     return (top1.y < bottom2.y) && (bottom1.y > top2.y);
0279   }
0280 
0281   inline void AddToEdge(OutPt2List& edge, OutPt2* op)
0282   {
0283     if (op->edge) return;
0284     op->edge = &edge;
0285     edge.push_back(op);
0286   }
0287 
0288   inline void UncoupleEdge(OutPt2* op)
0289   {
0290     if (!op->edge) return;
0291     for (size_t i = 0; i < op->edge->size(); ++i)
0292     {
0293       OutPt2* op2 = (*op->edge)[i];
0294       if (op2 == op)
0295       {
0296         (*op->edge)[i] = nullptr;
0297         break;
0298       }
0299     }
0300     op->edge = nullptr;
0301   }
0302 
0303   inline void SetNewOwner(OutPt2* op, size_t new_idx)
0304   {
0305     op->owner_idx = new_idx;
0306     OutPt2* op2 = op->next;
0307     while (op2 != op)
0308     {
0309       op2->owner_idx = new_idx;
0310       op2 = op2->next;
0311     }
0312   }
0313 
0314   //----------------------------------------------------------------------------
0315   // RectClip64
0316   //----------------------------------------------------------------------------
0317 
0318   OutPt2* RectClip64::Add(Point64 pt, bool start_new)
0319   {
0320     // this method is only called by InternalExecute.
0321     // Later splitting & rejoining won't create additional op's,
0322     // though they will change the (non-storage) results_ count.
0323     int curr_idx = static_cast<int>(results_.size()) - 1;
0324     OutPt2* result;
0325     if (curr_idx < 0 || start_new)
0326     {
0327       result = &op_container_.emplace_back(OutPt2());
0328       result->pt = pt;
0329       result->next = result;
0330       result->prev = result;
0331       results_.push_back(result);
0332     }
0333     else
0334     {
0335       OutPt2* prevOp = results_[curr_idx];
0336       if (prevOp->pt == pt)  return prevOp;
0337       result = &op_container_.emplace_back(OutPt2());
0338       result->owner_idx = curr_idx;
0339       result->pt = pt;
0340       result->next = prevOp->next;
0341       prevOp->next->prev = result;
0342       prevOp->next = result;
0343       result->prev = prevOp;
0344       results_[curr_idx] = result;
0345     }
0346     return result;
0347   }
0348 
0349   void RectClip64::AddCorner(Location prev, Location curr)
0350   {
0351     if (HeadingClockwise(prev, curr))
0352       Add(rect_as_path_[static_cast<int>(prev)]);
0353     else
0354       Add(rect_as_path_[static_cast<int>(curr)]);
0355   }
0356 
0357   void RectClip64::AddCorner(Location& loc, bool isClockwise)
0358   {
0359     if (isClockwise)
0360     {
0361       Add(rect_as_path_[static_cast<int>(loc)]);
0362       loc = GetAdjacentLocation(loc, true);
0363     }
0364     else
0365     {
0366       loc = GetAdjacentLocation(loc, false);
0367       Add(rect_as_path_[static_cast<int>(loc)]);
0368     }
0369   }
0370 
0371   void RectClip64::GetNextLocation(const Path64& path,
0372     Location& loc, int& i, int highI)
0373   {
0374     switch (loc)
0375     {
0376     case Location::Left:
0377       while (i <= highI && path[i].x <= rect_.left) ++i;
0378       if (i > highI) break;
0379       else if (path[i].x >= rect_.right) loc = Location::Right;
0380       else if (path[i].y <= rect_.top) loc = Location::Top;
0381       else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
0382       else loc = Location::Inside;
0383       break;
0384 
0385     case Location::Top:
0386       while (i <= highI && path[i].y <= rect_.top) ++i;
0387       if (i > highI) break;
0388       else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
0389       else if (path[i].x <= rect_.left) loc = Location::Left;
0390       else if (path[i].x >= rect_.right) loc = Location::Right;
0391       else loc = Location::Inside;
0392       break;
0393 
0394     case Location::Right:
0395       while (i <= highI && path[i].x >= rect_.right) ++i;
0396       if (i > highI) break;
0397       else if (path[i].x <= rect_.left) loc = Location::Left;
0398       else if (path[i].y <= rect_.top) loc = Location::Top;
0399       else if (path[i].y >= rect_.bottom) loc = Location::Bottom;
0400       else loc = Location::Inside;
0401       break;
0402 
0403     case Location::Bottom:
0404       while (i <= highI && path[i].y >= rect_.bottom) ++i;
0405       if (i > highI) break;
0406       else if (path[i].y <= rect_.top) loc = Location::Top;
0407       else if (path[i].x <= rect_.left) loc = Location::Left;
0408       else if (path[i].x >= rect_.right) loc = Location::Right;
0409       else loc = Location::Inside;
0410       break;
0411 
0412     case Location::Inside:
0413       while (i <= highI)
0414       {
0415         if (path[i].x < rect_.left) loc = Location::Left;
0416         else if (path[i].x > rect_.right) loc = Location::Right;
0417         else if (path[i].y > rect_.bottom) loc = Location::Bottom;
0418         else if (path[i].y < rect_.top) loc = Location::Top;
0419         else { Add(path[i]); ++i; continue; }
0420         break; //inner loop
0421       }
0422       break;
0423     } //switch          
0424   }
0425 
0426   void RectClip64::ExecuteInternal(const Path64& path)
0427   {
0428     int i = 0, highI = static_cast<int>(path.size()) - 1;
0429     Location prev = Location::Inside, loc;
0430     Location crossing_loc = Location::Inside;
0431     Location first_cross_ = Location::Inside;
0432     if (!GetLocation(rect_, path[highI], loc))
0433     {
0434       i = highI - 1;
0435       while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i;
0436       if (i < 0) 
0437       {
0438         // all of path must be inside fRect
0439         for (const auto& pt : path) Add(pt);
0440         return;
0441       }
0442       if (prev == Location::Inside) loc = Location::Inside;
0443       i = 0;
0444     }
0445     Location startingLoc = loc;
0446 
0447     ///////////////////////////////////////////////////
0448     while (i <= highI)
0449     {
0450       prev = loc;
0451       Location crossing_prev = crossing_loc;
0452 
0453       GetNextLocation(path, loc, i, highI);
0454 
0455       if (i > highI) break;
0456       Point64 ip, ip2;
0457       Point64 prev_pt = (i) ? 
0458         path[static_cast<size_t>(i - 1)] : 
0459         path[highI];
0460 
0461       crossing_loc = loc;
0462       if (!GetIntersection(rect_as_path_, 
0463         path[i], prev_pt, crossing_loc, ip))
0464       {
0465         // ie remaining outside
0466         if (crossing_prev == Location::Inside)
0467         {
0468           bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
0469           do {
0470             start_locs_.push_back(prev);
0471             prev = GetAdjacentLocation(prev, isClockw);
0472           } while (prev != loc);
0473           crossing_loc = crossing_prev; // still not crossed 
0474         }
0475         else if (prev != Location::Inside && prev != loc)
0476         {
0477           bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);
0478           do {
0479             AddCorner(prev, isClockw);
0480           } while (prev != loc);
0481         }
0482         ++i;
0483         continue;
0484       }
0485 
0486       ////////////////////////////////////////////////////
0487       // we must be crossing the rect boundary to get here
0488       ////////////////////////////////////////////////////
0489 
0490       if (loc == Location::Inside) // path must be entering rect
0491       {
0492         if (first_cross_ == Location::Inside)
0493         {
0494           first_cross_ = crossing_loc;
0495           start_locs_.push_back(prev);
0496         }
0497         else if (prev != crossing_loc)
0498         {
0499           bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], rect_mp_);
0500           do {
0501             AddCorner(prev, isClockw);
0502           } while (prev != crossing_loc);
0503         }
0504       }
0505       else if (prev != Location::Inside)
0506       {
0507         // passing right through rect. 'ip' here will be the second 
0508         // intersect pt but we'll also need the first intersect pt (ip2)
0509         loc = prev;
0510         GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);
0511         if (crossing_prev != Location::Inside && crossing_prev != loc) //579
0512           AddCorner(crossing_prev, loc);
0513 
0514         if (first_cross_ == Location::Inside)
0515         {
0516           first_cross_ = loc;
0517           start_locs_.push_back(prev);
0518         }
0519 
0520         loc = crossing_loc;
0521         Add(ip2);
0522         if (ip == ip2)
0523         {
0524           // it's very likely that path[i] is on rect
0525           GetLocation(rect_, path[i], loc);
0526           AddCorner(crossing_loc, loc);
0527           crossing_loc = loc;
0528           continue;
0529         }
0530       }
0531       else // path must be exiting rect
0532       {
0533         loc = crossing_loc;
0534         if (first_cross_ == Location::Inside)
0535           first_cross_ = crossing_loc;
0536       }
0537 
0538       Add(ip);
0539 
0540     } //while i <= highI
0541     ///////////////////////////////////////////////////
0542 
0543     if (first_cross_ == Location::Inside)
0544     {
0545       // path never intersects
0546       if (startingLoc != Location::Inside)
0547       {
0548         // path is outside rect
0549         // but being outside, it still may not contain rect
0550         if (path_bounds_.Contains(rect_) &&
0551           Path1ContainsPath2(path, rect_as_path_))
0552         {
0553           // yep, the path does fully contain rect
0554           // so add rect to the solution
0555           for (size_t j = 0; j < 4; ++j)
0556           {
0557             Add(rect_as_path_[j]);
0558             // we may well need to do some splitting later, so
0559             AddToEdge(edges_[j * 2], results_[0]);
0560           }
0561         }
0562       }
0563     }
0564     else if (loc != Location::Inside &&
0565       (loc != first_cross_ || start_locs_.size() > 2))
0566     {
0567       if (start_locs_.size() > 0)
0568       {
0569         prev = loc;
0570         for (auto loc2 : start_locs_)
0571         {
0572           if (prev == loc2) continue;
0573           AddCorner(prev, HeadingClockwise(prev, loc2));
0574           prev = loc2;
0575         }
0576         loc = prev;
0577       }
0578       if (loc != first_cross_)
0579         AddCorner(loc, HeadingClockwise(loc, first_cross_));
0580     }
0581   }
0582 
0583   void RectClip64::CheckEdges()
0584   {
0585     for (size_t i = 0; i < results_.size(); ++i)
0586     {
0587       OutPt2* op = results_[i];
0588       if (!op) continue;
0589       OutPt2* op2 = op;
0590       do
0591       {
0592         if (!CrossProduct(op2->prev->pt,
0593           op2->pt, op2->next->pt))
0594         {
0595           if (op2 == op)
0596           {
0597             op2 = UnlinkOpBack(op2);
0598             if (!op2) break;
0599             op = op2->prev;
0600           }
0601           else
0602           {
0603             op2 = UnlinkOpBack(op2);
0604             if (!op2) break;
0605           }
0606         }
0607         else
0608           op2 = op2->next;
0609       } while (op2 != op);
0610 
0611       if (!op2)
0612       {
0613         results_[i] = nullptr;
0614         continue;
0615       }
0616       results_[i] = op; // safety first
0617 
0618       uint32_t edgeSet1 = GetEdgesForPt(op->prev->pt, rect_);
0619       op2 = op;
0620       do
0621       {
0622         uint32_t edgeSet2 = GetEdgesForPt(op2->pt, rect_);
0623         if (edgeSet2 && !op2->edge)
0624         {
0625           uint32_t combinedSet = (edgeSet1 & edgeSet2);
0626           for (int j = 0; j < 4; ++j)
0627           {
0628             if (combinedSet & (1 << j))
0629             {
0630               if (IsHeadingClockwise(op2->prev->pt, op2->pt, j))
0631                 AddToEdge(edges_[j * 2], op2);
0632               else
0633                 AddToEdge(edges_[j * 2 + 1], op2);
0634             }
0635           }
0636         }
0637         edgeSet1 = edgeSet2;
0638         op2 = op2->next;
0639       } while (op2 != op);
0640     }
0641   }
0642 
0643   void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw)
0644   {
0645     if (ccw.empty()) return;
0646     bool isHorz = ((idx == 1) || (idx == 3));
0647     bool cwIsTowardLarger = ((idx == 1) || (idx == 2));
0648     size_t i = 0, j = 0;
0649     OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;
0650 
0651     while (i < cw.size()) 
0652     {
0653       p1 = cw[i];
0654       if (!p1 || p1->next == p1->prev)
0655       {
0656         cw[i++] = nullptr;
0657         j = 0;
0658         continue;
0659       }
0660 
0661       size_t jLim = ccw.size();
0662       while (j < jLim &&
0663         (!ccw[j] || ccw[j]->next == ccw[j]->prev)) ++j;
0664 
0665       if (j == jLim)
0666       {
0667         ++i;
0668         j = 0;
0669         continue;
0670       }
0671 
0672       if (cwIsTowardLarger)
0673       {
0674         // p1 >>>> p1a;
0675         // p2 <<<< p2a;
0676         p1 = cw[i]->prev;
0677         p1a = cw[i];
0678         p2 = ccw[j];
0679         p2a = ccw[j]->prev;
0680       }
0681       else
0682       {
0683         // p1 <<<< p1a;
0684         // p2 >>>> p2a;
0685         p1 = cw[i];
0686         p1a = cw[i]->prev;
0687         p2 = ccw[j]->prev;
0688         p2a = ccw[j];
0689       }
0690 
0691       if ((isHorz && !HasHorzOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)) ||
0692         (!isHorz && !HasVertOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)))
0693       {
0694         ++j;
0695         continue;
0696       }
0697 
0698       // to get here we're either splitting or rejoining
0699       bool isRejoining = cw[i]->owner_idx != ccw[j]->owner_idx;
0700 
0701       if (isRejoining)
0702       {
0703         results_[p2->owner_idx] = nullptr;
0704         SetNewOwner(p2, p1->owner_idx);
0705       }
0706 
0707       // do the split or re-join
0708       if (cwIsTowardLarger)
0709       {
0710         // p1 >> | >> p1a;
0711         // p2 << | << p2a;
0712         p1->next = p2;
0713         p2->prev = p1;
0714         p1a->prev = p2a;
0715         p2a->next = p1a;
0716       }
0717       else
0718       {
0719         // p1 << | << p1a;
0720         // p2 >> | >> p2a;
0721         p1->prev = p2;
0722         p2->next = p1;
0723         p1a->next = p2a;
0724         p2a->prev = p1a;
0725       }
0726 
0727       if (!isRejoining)
0728       {
0729         size_t new_idx = results_.size();
0730         results_.push_back(p1a);
0731         SetNewOwner(p1a, new_idx);
0732       }
0733 
0734       if (cwIsTowardLarger)
0735       {
0736         op = p2;
0737         op2 = p1a;
0738       }
0739       else
0740       {
0741         op = p1;
0742         op2 = p2a;
0743       }
0744       results_[op->owner_idx] = op;
0745       results_[op2->owner_idx] = op2;
0746 
0747       // and now lots of work to get ready for the next loop
0748 
0749       bool opIsLarger, op2IsLarger;
0750       if (isHorz) // X
0751       {
0752         opIsLarger = op->pt.x > op->prev->pt.x;
0753         op2IsLarger = op2->pt.x > op2->prev->pt.x;
0754       }
0755       else       // Y
0756       {
0757         opIsLarger = op->pt.y > op->prev->pt.y;
0758         op2IsLarger = op2->pt.y > op2->prev->pt.y;
0759       }
0760 
0761       if ((op->next == op->prev) ||
0762         (op->pt == op->prev->pt))
0763       {
0764         if (op2IsLarger == cwIsTowardLarger)
0765         {
0766           cw[i] = op2;
0767           ccw[j++] = nullptr;
0768         }
0769         else
0770         {
0771           ccw[j] = op2;
0772           cw[i++] = nullptr;
0773         }
0774       }
0775       else if ((op2->next == op2->prev) ||
0776         (op2->pt == op2->prev->pt))
0777       {
0778         if (opIsLarger == cwIsTowardLarger)
0779         {
0780           cw[i] = op;
0781           ccw[j++] = nullptr;
0782         }
0783         else
0784         {
0785           ccw[j] = op;
0786           cw[i++] = nullptr;
0787         }
0788       }
0789       else if (opIsLarger == op2IsLarger)
0790       {
0791         if (opIsLarger == cwIsTowardLarger)
0792         {
0793           cw[i] = op;
0794           UncoupleEdge(op2);
0795           AddToEdge(cw, op2);
0796           ccw[j++] = nullptr;
0797         }
0798         else
0799         {
0800           cw[i++] = nullptr;
0801           ccw[j] = op2;
0802           UncoupleEdge(op);
0803           AddToEdge(ccw, op);
0804           j = 0;
0805         }
0806       }
0807       else
0808       {
0809         if (opIsLarger == cwIsTowardLarger)
0810           cw[i] = op;
0811         else
0812           ccw[j] = op;
0813         if (op2IsLarger == cwIsTowardLarger)
0814           cw[i] = op2;
0815         else
0816           ccw[j] = op2;
0817       }
0818     }
0819   }
0820 
0821   Path64 RectClip64::GetPath(OutPt2*& op)
0822   {
0823     if (!op || op->next == op->prev) return Path64();
0824 
0825     OutPt2* op2 = op->next;
0826     while (op2 && op2 != op)
0827     {
0828       if (CrossProduct(op2->prev->pt, 
0829         op2->pt, op2->next->pt) == 0)
0830       {
0831         op = op2->prev;
0832         op2 = UnlinkOp(op2);
0833       }
0834       else
0835         op2 = op2->next;
0836     }
0837     op = op2; // needed for op cleanup
0838     if (!op2) return Path64();
0839 
0840     Path64 result;
0841     result.push_back(op->pt);
0842     op2 = op->next;
0843     while (op2 != op)
0844     {
0845       result.push_back(op2->pt);
0846       op2 = op2->next;
0847     }
0848     return result;
0849   }
0850 
0851   Paths64 RectClip64::Execute(const Paths64& paths)
0852   {
0853     Paths64 result;
0854     if (rect_.IsEmpty()) return result;
0855 
0856     for (const Path64& path : paths)
0857     {      
0858       if (path.size() < 3) continue;
0859       path_bounds_ = GetBounds(path);
0860       if (!rect_.Intersects(path_bounds_))
0861         continue; // the path must be completely outside rect_
0862       else if (rect_.Contains(path_bounds_))
0863       {
0864         // the path must be completely inside rect_
0865         result.push_back(path);
0866         continue;
0867       }
0868 
0869       ExecuteInternal(path);
0870       CheckEdges();
0871       for (int i = 0; i < 4; ++i)
0872         TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);
0873   
0874       for (OutPt2*& op :  results_)
0875       {
0876         Path64 tmp = GetPath(op);
0877         if (!tmp.empty())
0878           result.emplace_back(tmp);
0879       }
0880 
0881       //clean up after every loop
0882       op_container_ = std::deque<OutPt2>();
0883       results_.clear();
0884       for (OutPt2List &edge : edges_) edge.clear();
0885       start_locs_.clear();
0886     }
0887     return result;
0888   }
0889 
0890   //------------------------------------------------------------------------------
0891   // RectClipLines64
0892   //------------------------------------------------------------------------------
0893 
0894   Paths64 RectClipLines64::Execute(const Paths64& paths)
0895   {
0896     Paths64 result;
0897     if (rect_.IsEmpty()) return result;
0898 
0899     for (const auto& path : paths)
0900     {
0901       Rect64 pathrec = GetBounds(path);
0902       if (!rect_.Intersects(pathrec)) continue;
0903 
0904       ExecuteInternal(path);
0905 
0906       for (OutPt2*& op : results_)
0907       {
0908         Path64 tmp = GetPath(op);
0909         if (!tmp.empty())
0910           result.emplace_back(tmp);
0911       }
0912       results_.clear();
0913 
0914       op_container_ = std::deque<OutPt2>();
0915       start_locs_.clear();
0916     }
0917     return result;
0918   }
0919 
0920   void RectClipLines64::ExecuteInternal(const Path64& path)
0921   {
0922     if (rect_.IsEmpty() || path.size() < 2) return;
0923 
0924     results_.clear();
0925     op_container_ = std::deque<OutPt2>();
0926     start_locs_.clear();
0927 
0928     int i = 1, highI = static_cast<int>(path.size()) - 1;
0929 
0930     Location prev = Location::Inside, loc;
0931     Location crossing_loc;
0932     if (!GetLocation(rect_, path[0], loc))
0933     {
0934       while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;
0935       if (i > highI) 
0936       {
0937         // all of path must be inside fRect
0938         for (const auto& pt : path) Add(pt);
0939         return;
0940       }
0941       if (prev == Location::Inside) loc = Location::Inside;
0942       i = 1;
0943     }
0944     if (loc == Location::Inside) Add(path[0]);
0945 
0946     ///////////////////////////////////////////////////
0947     while (i <= highI)
0948     {
0949       prev = loc;
0950       GetNextLocation(path, loc, i, highI);
0951       if (i > highI) break;
0952       Point64 ip, ip2;
0953       Point64 prev_pt = path[static_cast<size_t>(i - 1)];
0954 
0955       crossing_loc = loc;
0956       if (!GetIntersection(rect_as_path_, 
0957         path[i], prev_pt, crossing_loc, ip))
0958       {
0959         // ie remaining outside
0960         ++i;
0961         continue;
0962       }
0963 
0964       ////////////////////////////////////////////////////
0965       // we must be crossing the rect boundary to get here
0966       ////////////////////////////////////////////////////
0967 
0968       if (loc == Location::Inside) // path must be entering rect
0969       {
0970         Add(ip, true);
0971       }
0972       else if (prev != Location::Inside)
0973       {
0974         // passing right through rect. 'ip' here will be the second 
0975         // intersect pt but we'll also need the first intersect pt (ip2)
0976         crossing_loc = prev;
0977         GetIntersection(rect_as_path_, 
0978           prev_pt, path[i], crossing_loc, ip2);
0979         Add(ip2, true);
0980         Add(ip);
0981       }
0982       else // path must be exiting rect
0983       {
0984         Add(ip);
0985       }
0986     } //while i <= highI
0987     ///////////////////////////////////////////////////
0988   }
0989 
0990   Path64 RectClipLines64::GetPath(OutPt2*& op)
0991   {
0992     Path64 result;
0993     if (!op || op == op->next) return result;
0994     op = op->next; // starting at path beginning 
0995     result.push_back(op->pt);
0996     OutPt2 *op2 = op->next;
0997     while (op2 != op)
0998     {
0999       result.push_back(op2->pt);
1000       op2 = op2->next;
1001     }        
1002     return result;
1003   }
1004 
1005 } // namespace