Path: blob/master/thirdparty/clipper2/src/clipper.rectclip.cpp
9902 views
/*******************************************************************************1* Author : Angus Johnson *2* Date : 5 July 2024 *3* Website : https://www.angusj.com *4* Copyright : Angus Johnson 2010-2024 *5* Purpose : FAST rectangular clipping *6* License : https://www.boost.org/LICENSE_1_0.txt *7*******************************************************************************/89#include "clipper2/clipper.h"10#include "clipper2/clipper.rectclip.h"1112namespace Clipper2Lib {1314//------------------------------------------------------------------------------15// Miscellaneous methods16//------------------------------------------------------------------------------1718inline bool Path1ContainsPath2(const Path64& path1, const Path64& path2)19{20int io_count = 0;21// precondition: no (significant) overlap22for (const Point64& pt : path2)23{24PointInPolygonResult pip = PointInPolygon(pt, path1);25switch (pip)26{27case PointInPolygonResult::IsOutside: ++io_count; break;28case PointInPolygonResult::IsInside: --io_count; break;29default: continue;30}31if (std::abs(io_count) > 1) break;32}33return io_count <= 0;34}3536inline bool GetLocation(const Rect64& rec,37const Point64& pt, Location& loc)38{39if (pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom)40{41loc = Location::Left;42return false;43}44else if (pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom)45{46loc = Location::Right;47return false;48}49else if (pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right)50{51loc = Location::Top;52return false;53}54else if (pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right)55{56loc = Location::Bottom;57return false;58}59else if (pt.x < rec.left) loc = Location::Left;60else if (pt.x > rec.right) loc = Location::Right;61else if (pt.y < rec.top) loc = Location::Top;62else if (pt.y > rec.bottom) loc = Location::Bottom;63else loc = Location::Inside;64return true;65}6667inline bool IsHorizontal(const Point64& pt1, const Point64& pt2)68{69return pt1.y == pt2.y;70}7172bool GetSegmentIntersection(const Point64& p1,73const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)74{75double res1 = CrossProduct(p1, p3, p4);76double res2 = CrossProduct(p2, p3, p4);77if (res1 == 0)78{79ip = p1;80if (res2 == 0) return false; // segments are collinear81else if (p1 == p3 || p1 == p4) return true;82//else if (p2 == p3 || p2 == p4) { ip = p2; return true; }83else if (IsHorizontal(p3, p4)) return ((p1.x > p3.x) == (p1.x < p4.x));84else return ((p1.y > p3.y) == (p1.y < p4.y));85}86else if (res2 == 0)87{88ip = p2;89if (p2 == p3 || p2 == p4) return true;90else if (IsHorizontal(p3, p4)) return ((p2.x > p3.x) == (p2.x < p4.x));91else return ((p2.y > p3.y) == (p2.y < p4.y));92}93if ((res1 > 0) == (res2 > 0)) return false;9495double res3 = CrossProduct(p3, p1, p2);96double res4 = CrossProduct(p4, p1, p2);97if (res3 == 0)98{99ip = p3;100if (p3 == p1 || p3 == p2) return true;101else if (IsHorizontal(p1, p2)) return ((p3.x > p1.x) == (p3.x < p2.x));102else return ((p3.y > p1.y) == (p3.y < p2.y));103}104else if (res4 == 0)105{106ip = p4;107if (p4 == p1 || p4 == p2) return true;108else if (IsHorizontal(p1, p2)) return ((p4.x > p1.x) == (p4.x < p2.x));109else return ((p4.y > p1.y) == (p4.y < p2.y));110}111if ((res3 > 0) == (res4 > 0)) return false;112113// segments must intersect to get here114return GetSegmentIntersectPt(p1, p2, p3, p4, ip);115}116117inline bool GetIntersection(const Path64& rectPath,118const Point64& p, const Point64& p2, Location& loc, Point64& ip)119{120// gets the intersection closest to 'p'121// when Result = false, loc will remain unchanged122switch (loc)123{124case Location::Left:125if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true;126else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))127{128loc = Location::Top;129return true;130}131else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))132{133loc = Location::Bottom;134return true;135}136else return false;137138case Location::Top:139if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) return true;140else if ((p.x < rectPath[0].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))141{142loc = Location::Left;143return true;144}145else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))146{147loc = Location::Right;148return true;149}150else return false;151152case Location::Right:153if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip)) return true;154else if ((p.y < rectPath[1].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))155{156loc = Location::Top;157return true;158}159else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))160{161loc = Location::Bottom;162return true;163}164else return false;165166case Location::Bottom:167if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip)) return true;168else if ((p.x < rectPath[3].x) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))169{170loc = Location::Left;171return true;172}173else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))174{175loc = Location::Right;176return true;177}178else return false;179180default: // loc == rInside181if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip))182{183loc = Location::Left;184return true;185}186else if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip))187{188loc = Location::Top;189return true;190}191else if (GetSegmentIntersection(p, p2, rectPath[1], rectPath[2], ip))192{193loc = Location::Right;194return true;195}196else if (GetSegmentIntersection(p, p2, rectPath[2], rectPath[3], ip))197{198loc = Location::Bottom;199return true;200}201else return false;202}203}204205inline Location GetAdjacentLocation(Location loc, bool isClockwise)206{207int delta = (isClockwise) ? 1 : 3;208return static_cast<Location>((static_cast<int>(loc) + delta) % 4);209}210211inline bool HeadingClockwise(Location prev, Location curr)212{213return (static_cast<int>(prev) + 1) % 4 == static_cast<int>(curr);214}215216inline bool AreOpposites(Location prev, Location curr)217{218return abs(static_cast<int>(prev) - static_cast<int>(curr)) == 2;219}220221inline bool IsClockwise(Location prev, Location curr,222const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp)223{224if (AreOpposites(prev, curr))225return CrossProduct(prev_pt, rect_mp, curr_pt) < 0;226else227return HeadingClockwise(prev, curr);228}229230inline OutPt2* UnlinkOp(OutPt2* op)231{232if (op->next == op) return nullptr;233op->prev->next = op->next;234op->next->prev = op->prev;235return op->next;236}237238inline OutPt2* UnlinkOpBack(OutPt2* op)239{240if (op->next == op) return nullptr;241op->prev->next = op->next;242op->next->prev = op->prev;243return op->prev;244}245246inline uint32_t GetEdgesForPt(const Point64& pt, const Rect64& rec)247{248uint32_t result = 0;249if (pt.x == rec.left) result = 1;250else if (pt.x == rec.right) result = 4;251if (pt.y == rec.top) result += 2;252else if (pt.y == rec.bottom) result += 8;253return result;254}255256inline bool IsHeadingClockwise(const Point64& pt1, const Point64& pt2, int edgeIdx)257{258switch (edgeIdx)259{260case 0: return pt2.y < pt1.y;261case 1: return pt2.x > pt1.x;262case 2: return pt2.y > pt1.y;263default: return pt2.x < pt1.x;264}265}266267inline bool HasHorzOverlap(const Point64& left1, const Point64& right1,268const Point64& left2, const Point64& right2)269{270return (left1.x < right2.x) && (right1.x > left2.x);271}272273inline bool HasVertOverlap(const Point64& top1, const Point64& bottom1,274const Point64& top2, const Point64& bottom2)275{276return (top1.y < bottom2.y) && (bottom1.y > top2.y);277}278279inline void AddToEdge(OutPt2List& edge, OutPt2* op)280{281if (op->edge) return;282op->edge = &edge;283edge.emplace_back(op);284}285286inline void UncoupleEdge(OutPt2* op)287{288if (!op->edge) return;289for (size_t i = 0; i < op->edge->size(); ++i)290{291OutPt2* op2 = (*op->edge)[i];292if (op2 == op)293{294(*op->edge)[i] = nullptr;295break;296}297}298op->edge = nullptr;299}300301inline void SetNewOwner(OutPt2* op, size_t new_idx)302{303op->owner_idx = new_idx;304OutPt2* op2 = op->next;305while (op2 != op)306{307op2->owner_idx = new_idx;308op2 = op2->next;309}310}311312//----------------------------------------------------------------------------313// RectClip64314//----------------------------------------------------------------------------315316OutPt2* RectClip64::Add(Point64 pt, bool start_new)317{318// this method is only called by InternalExecute.319// Later splitting & rejoining won't create additional op's,320// though they will change the (non-storage) results_ count.321size_t curr_idx = results_.size();322OutPt2* result;323if (curr_idx == 0 || start_new)324{325result = &op_container_.emplace_back(OutPt2());326result->pt = pt;327result->next = result;328result->prev = result;329results_.emplace_back(result);330}331else332{333--curr_idx;334OutPt2* prevOp = results_[curr_idx];335if (prevOp->pt == pt) return prevOp;336result = &op_container_.emplace_back(OutPt2());337result->owner_idx = curr_idx;338result->pt = pt;339result->next = prevOp->next;340prevOp->next->prev = result;341prevOp->next = result;342result->prev = prevOp;343results_[curr_idx] = result;344}345return result;346}347348void RectClip64::AddCorner(Location prev, Location curr)349{350if (HeadingClockwise(prev, curr))351Add(rect_as_path_[static_cast<size_t>(prev)]);352else353Add(rect_as_path_[static_cast<size_t>(curr)]);354}355356void RectClip64::AddCorner(Location& loc, bool isClockwise)357{358if (isClockwise)359{360Add(rect_as_path_[static_cast<size_t>(loc)]);361loc = GetAdjacentLocation(loc, true);362}363else364{365loc = GetAdjacentLocation(loc, false);366Add(rect_as_path_[static_cast<size_t>(loc)]);367}368}369370void RectClip64::GetNextLocation(const Path64& path,371Location& loc, size_t& i, size_t highI)372{373switch (loc)374{375case Location::Left:376while (i <= highI && path[i].x <= rect_.left) ++i;377if (i > highI) break;378else if (path[i].x >= rect_.right) loc = Location::Right;379else if (path[i].y <= rect_.top) loc = Location::Top;380else if (path[i].y >= rect_.bottom) loc = Location::Bottom;381else loc = Location::Inside;382break;383384case Location::Top:385while (i <= highI && path[i].y <= rect_.top) ++i;386if (i > highI) break;387else if (path[i].y >= rect_.bottom) loc = Location::Bottom;388else if (path[i].x <= rect_.left) loc = Location::Left;389else if (path[i].x >= rect_.right) loc = Location::Right;390else loc = Location::Inside;391break;392393case Location::Right:394while (i <= highI && path[i].x >= rect_.right) ++i;395if (i > highI) break;396else if (path[i].x <= rect_.left) loc = Location::Left;397else if (path[i].y <= rect_.top) loc = Location::Top;398else if (path[i].y >= rect_.bottom) loc = Location::Bottom;399else loc = Location::Inside;400break;401402case Location::Bottom:403while (i <= highI && path[i].y >= rect_.bottom) ++i;404if (i > highI) break;405else if (path[i].y <= rect_.top) loc = Location::Top;406else if (path[i].x <= rect_.left) loc = Location::Left;407else if (path[i].x >= rect_.right) loc = Location::Right;408else loc = Location::Inside;409break;410411case Location::Inside:412while (i <= highI)413{414if (path[i].x < rect_.left) loc = Location::Left;415else if (path[i].x > rect_.right) loc = Location::Right;416else if (path[i].y > rect_.bottom) loc = Location::Bottom;417else if (path[i].y < rect_.top) loc = Location::Top;418else { Add(path[i]); ++i; continue; }419break; //inner loop420}421break;422} //switch423}424425bool StartLocsAreClockwise(const std::vector<Location>& startlocs)426{427int result = 0;428for (size_t i = 1; i < startlocs.size(); ++i)429{430int d = static_cast<int>(startlocs[i]) - static_cast<int>(startlocs[i - 1]);431switch (d)432{433case -1: result -= 1; break;434case 1: result += 1; break;435case -3: result += 1; break;436case 3: result -= 1; break;437}438}439return result > 0;440}441442void RectClip64::ExecuteInternal(const Path64& path)443{444if (path.size() < 1)445return;446447size_t highI = path.size() - 1;448Location prev = Location::Inside, loc;449Location crossing_loc = Location::Inside;450Location first_cross_ = Location::Inside;451if (!GetLocation(rect_, path[highI], loc))452{453size_t i = highI;454while (i > 0 && !GetLocation(rect_, path[i - 1], prev))455--i;456if (i == 0)457{458// all of path must be inside fRect459for (const auto& pt : path) Add(pt);460return;461}462if (prev == Location::Inside) loc = Location::Inside;463}464Location starting_loc = loc;465466///////////////////////////////////////////////////467size_t i = 0;468while (i <= highI)469{470prev = loc;471Location crossing_prev = crossing_loc;472473GetNextLocation(path, loc, i, highI);474475if (i > highI) break;476Point64 ip, ip2;477Point64 prev_pt = (i) ?478path[static_cast<size_t>(i - 1)] :479path[highI];480481crossing_loc = loc;482if (!GetIntersection(rect_as_path_,483path[i], prev_pt, crossing_loc, ip))484{485// ie remaining outside486if (crossing_prev == Location::Inside)487{488bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);489do {490start_locs_.emplace_back(prev);491prev = GetAdjacentLocation(prev, isClockw);492} while (prev != loc);493crossing_loc = crossing_prev; // still not crossed494}495else if (prev != Location::Inside && prev != loc)496{497bool isClockw = IsClockwise(prev, loc, prev_pt, path[i], rect_mp_);498do {499AddCorner(prev, isClockw);500} while (prev != loc);501}502++i;503continue;504}505506////////////////////////////////////////////////////507// we must be crossing the rect boundary to get here508////////////////////////////////////////////////////509510if (loc == Location::Inside) // path must be entering rect511{512if (first_cross_ == Location::Inside)513{514first_cross_ = crossing_loc;515start_locs_.emplace_back(prev);516}517else if (prev != crossing_loc)518{519bool isClockw = IsClockwise(prev, crossing_loc, prev_pt, path[i], rect_mp_);520do {521AddCorner(prev, isClockw);522} while (prev != crossing_loc);523}524}525else if (prev != Location::Inside)526{527// passing right through rect. 'ip' here will be the second528// intersect pt but we'll also need the first intersect pt (ip2)529loc = prev;530GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2);531if (crossing_prev != Location::Inside && crossing_prev != loc) //579532AddCorner(crossing_prev, loc);533534if (first_cross_ == Location::Inside)535{536first_cross_ = loc;537start_locs_.emplace_back(prev);538}539540loc = crossing_loc;541Add(ip2);542if (ip == ip2)543{544// it's very likely that path[i] is on rect545GetLocation(rect_, path[i], loc);546AddCorner(crossing_loc, loc);547crossing_loc = loc;548continue;549}550}551else // path must be exiting rect552{553loc = crossing_loc;554if (first_cross_ == Location::Inside)555first_cross_ = crossing_loc;556}557558Add(ip);559560} //while i <= highI561///////////////////////////////////////////////////562563if (first_cross_ == Location::Inside)564{565// path never intersects566if (starting_loc != Location::Inside)567{568// path is outside rect569// but being outside, it still may not contain rect570if (path_bounds_.Contains(rect_) &&571Path1ContainsPath2(path, rect_as_path_))572{573// yep, the path does fully contain rect574// so add rect to the solution575bool is_clockwise_path = StartLocsAreClockwise(start_locs_);576for (size_t j = 0; j < 4; ++j)577{578size_t k = is_clockwise_path ? j : 3 - j; // reverses result path579Add(rect_as_path_[k]);580// we may well need to do some splitting later, so581AddToEdge(edges_[k * 2], results_[0]);582}583}584}585}586else if (loc != Location::Inside &&587(loc != first_cross_ || start_locs_.size() > 2))588{589if (start_locs_.size() > 0)590{591prev = loc;592for (auto loc2 : start_locs_)593{594if (prev == loc2) continue;595AddCorner(prev, HeadingClockwise(prev, loc2));596prev = loc2;597}598loc = prev;599}600if (loc != first_cross_)601AddCorner(loc, HeadingClockwise(loc, first_cross_));602}603}604605void RectClip64::CheckEdges()606{607for (size_t i = 0; i < results_.size(); ++i)608{609OutPt2* op = results_[i];610if (!op) continue;611OutPt2* op2 = op;612do613{614if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt))615{616if (op2 == op)617{618op2 = UnlinkOpBack(op2);619if (!op2) break;620op = op2->prev;621}622else623{624op2 = UnlinkOpBack(op2);625if (!op2) break;626}627}628else629op2 = op2->next;630} while (op2 != op);631632if (!op2)633{634results_[i] = nullptr;635continue;636}637results_[i] = op; // safety first638639uint32_t edgeSet1 = GetEdgesForPt(op->prev->pt, rect_);640op2 = op;641do642{643uint32_t edgeSet2 = GetEdgesForPt(op2->pt, rect_);644if (edgeSet2 && !op2->edge)645{646uint32_t combinedSet = (edgeSet1 & edgeSet2);647for (int j = 0; j < 4; ++j)648{649if (combinedSet & (1 << j))650{651if (IsHeadingClockwise(op2->prev->pt, op2->pt, j))652AddToEdge(edges_[j * 2], op2);653else654AddToEdge(edges_[j * 2 + 1], op2);655}656}657}658edgeSet1 = edgeSet2;659op2 = op2->next;660} while (op2 != op);661}662}663664void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw)665{666if (ccw.empty()) return;667bool isHorz = ((idx == 1) || (idx == 3));668bool cwIsTowardLarger = ((idx == 1) || (idx == 2));669size_t i = 0, j = 0;670OutPt2* p1, * p2, * p1a, * p2a, * op, * op2;671672while (i < cw.size())673{674p1 = cw[i];675if (!p1 || p1->next == p1->prev)676{677cw[i++] = nullptr;678j = 0;679continue;680}681682size_t jLim = ccw.size();683while (j < jLim &&684(!ccw[j] || ccw[j]->next == ccw[j]->prev)) ++j;685686if (j == jLim)687{688++i;689j = 0;690continue;691}692693if (cwIsTowardLarger)694{695// p1 >>>> p1a;696// p2 <<<< p2a;697p1 = cw[i]->prev;698p1a = cw[i];699p2 = ccw[j];700p2a = ccw[j]->prev;701}702else703{704// p1 <<<< p1a;705// p2 >>>> p2a;706p1 = cw[i];707p1a = cw[i]->prev;708p2 = ccw[j]->prev;709p2a = ccw[j];710}711712if ((isHorz && !HasHorzOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)) ||713(!isHorz && !HasVertOverlap(p1->pt, p1a->pt, p2->pt, p2a->pt)))714{715++j;716continue;717}718719// to get here we're either splitting or rejoining720bool isRejoining = cw[i]->owner_idx != ccw[j]->owner_idx;721722if (isRejoining)723{724results_[p2->owner_idx] = nullptr;725SetNewOwner(p2, p1->owner_idx);726}727728// do the split or re-join729if (cwIsTowardLarger)730{731// p1 >> | >> p1a;732// p2 << | << p2a;733p1->next = p2;734p2->prev = p1;735p1a->prev = p2a;736p2a->next = p1a;737}738else739{740// p1 << | << p1a;741// p2 >> | >> p2a;742p1->prev = p2;743p2->next = p1;744p1a->next = p2a;745p2a->prev = p1a;746}747748if (!isRejoining)749{750size_t new_idx = results_.size();751results_.emplace_back(p1a);752SetNewOwner(p1a, new_idx);753}754755if (cwIsTowardLarger)756{757op = p2;758op2 = p1a;759}760else761{762op = p1;763op2 = p2a;764}765results_[op->owner_idx] = op;766results_[op2->owner_idx] = op2;767768// and now lots of work to get ready for the next loop769770bool opIsLarger, op2IsLarger;771if (isHorz) // X772{773opIsLarger = op->pt.x > op->prev->pt.x;774op2IsLarger = op2->pt.x > op2->prev->pt.x;775}776else // Y777{778opIsLarger = op->pt.y > op->prev->pt.y;779op2IsLarger = op2->pt.y > op2->prev->pt.y;780}781782if ((op->next == op->prev) ||783(op->pt == op->prev->pt))784{785if (op2IsLarger == cwIsTowardLarger)786{787cw[i] = op2;788ccw[j++] = nullptr;789}790else791{792ccw[j] = op2;793cw[i++] = nullptr;794}795}796else if ((op2->next == op2->prev) ||797(op2->pt == op2->prev->pt))798{799if (opIsLarger == cwIsTowardLarger)800{801cw[i] = op;802ccw[j++] = nullptr;803}804else805{806ccw[j] = op;807cw[i++] = nullptr;808}809}810else if (opIsLarger == op2IsLarger)811{812if (opIsLarger == cwIsTowardLarger)813{814cw[i] = op;815UncoupleEdge(op2);816AddToEdge(cw, op2);817ccw[j++] = nullptr;818}819else820{821cw[i++] = nullptr;822ccw[j] = op2;823UncoupleEdge(op);824AddToEdge(ccw, op);825j = 0;826}827}828else829{830if (opIsLarger == cwIsTowardLarger)831cw[i] = op;832else833ccw[j] = op;834if (op2IsLarger == cwIsTowardLarger)835cw[i] = op2;836else837ccw[j] = op2;838}839}840}841842Path64 RectClip64::GetPath(OutPt2*& op)843{844if (!op || op->next == op->prev) return Path64();845846OutPt2* op2 = op->next;847while (op2 && op2 != op)848{849if (IsCollinear(op2->prev->pt,850op2->pt, op2->next->pt))851{852op = op2->prev;853op2 = UnlinkOp(op2);854}855else856op2 = op2->next;857}858op = op2; // needed for op cleanup859if (!op2) return Path64();860861Path64 result;862result.emplace_back(op->pt);863op2 = op->next;864while (op2 != op)865{866result.emplace_back(op2->pt);867op2 = op2->next;868}869return result;870}871872Paths64 RectClip64::Execute(const Paths64& paths)873{874Paths64 result;875if (rect_.IsEmpty()) return result;876877for (const Path64& path : paths)878{879if (path.size() < 3) continue;880path_bounds_ = GetBounds(path);881if (!rect_.Intersects(path_bounds_))882continue; // the path must be completely outside rect_883else if (rect_.Contains(path_bounds_))884{885// the path must be completely inside rect_886result.emplace_back(path);887continue;888}889890ExecuteInternal(path);891CheckEdges();892for (size_t i = 0; i < 4; ++i)893TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]);894895for (OutPt2*& op : results_)896{897Path64 tmp = GetPath(op);898if (!tmp.empty())899result.emplace_back(std::move(tmp));900}901902//clean up after every loop903op_container_ = std::deque<OutPt2>();904results_.clear();905for (OutPt2List &edge : edges_) edge.clear();906start_locs_.clear();907}908return result;909}910911//------------------------------------------------------------------------------912// RectClipLines64913//------------------------------------------------------------------------------914915Paths64 RectClipLines64::Execute(const Paths64& paths)916{917Paths64 result;918if (rect_.IsEmpty()) return result;919920for (const auto& path : paths)921{922Rect64 pathrec = GetBounds(path);923if (!rect_.Intersects(pathrec)) continue;924925ExecuteInternal(path);926927for (OutPt2*& op : results_)928{929Path64 tmp = GetPath(op);930if (!tmp.empty())931result.emplace_back(std::move(tmp));932}933results_.clear();934935op_container_ = std::deque<OutPt2>();936start_locs_.clear();937}938return result;939}940941void RectClipLines64::ExecuteInternal(const Path64& path)942{943if (rect_.IsEmpty() || path.size() < 2) return;944945results_.clear();946op_container_ = std::deque<OutPt2>();947start_locs_.clear();948949size_t i = 1, highI = path.size() - 1;950951Location prev = Location::Inside, loc;952Location crossing_loc;953if (!GetLocation(rect_, path[0], loc))954{955while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i;956if (i > highI)957{958// all of path must be inside fRect959for (const auto& pt : path) Add(pt);960return;961}962if (prev == Location::Inside) loc = Location::Inside;963i = 1;964}965if (loc == Location::Inside) Add(path[0]);966967///////////////////////////////////////////////////968while (i <= highI)969{970prev = loc;971GetNextLocation(path, loc, i, highI);972if (i > highI) break;973Point64 ip, ip2;974Point64 prev_pt = path[static_cast<size_t>(i - 1)];975976crossing_loc = loc;977if (!GetIntersection(rect_as_path_,978path[i], prev_pt, crossing_loc, ip))979{980// ie remaining outside981++i;982continue;983}984985////////////////////////////////////////////////////986// we must be crossing the rect boundary to get here987////////////////////////////////////////////////////988989if (loc == Location::Inside) // path must be entering rect990{991Add(ip, true);992}993else if (prev != Location::Inside)994{995// passing right through rect. 'ip' here will be the second996// intersect pt but we'll also need the first intersect pt (ip2)997crossing_loc = prev;998GetIntersection(rect_as_path_,999prev_pt, path[i], crossing_loc, ip2);1000Add(ip2, true);1001Add(ip);1002}1003else // path must be exiting rect1004{1005Add(ip);1006}1007} //while i <= highI1008///////////////////////////////////////////////////1009}10101011Path64 RectClipLines64::GetPath(OutPt2*& op)1012{1013Path64 result;1014if (!op || op == op->next) return result;1015op = op->next; // starting at path beginning1016result.emplace_back(op->pt);1017OutPt2 *op2 = op->next;1018while (op2 != op)1019{1020result.emplace_back(op2->pt);1021op2 = op2->next;1022}1023return result;1024}10251026} // namespace102710281029