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