File indexing completed on 2025-04-20 03:36:28
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