$search
00001 /******************************************************************************* 00002 * * 00003 * Author : Angus Johnson * 00004 * Version : 4.8.8 * 00005 * Date : 30 August 2012 * 00006 * Website : http://www.angusj.com * 00007 * Copyright : Angus Johnson 2010-2012 * 00008 * * 00009 * License: * 00010 * Use, modification & distribution is subject to Boost Software License Ver 1. * 00011 * http://www.boost.org/LICENSE_1_0.txt * 00012 * * 00013 * Attributions: * 00014 * The code in this library is an extension of Bala Vatti's clipping algorithm: * 00015 * "A generic solution to polygon clipping" * 00016 * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 00017 * http://portal.acm.org/citation.cfm?id=129906 * 00018 * * 00019 * Computer graphics and geometric modeling: implementation and algorithms * 00020 * By Max K. Agoston * 00021 * Springer; 1 edition (January 4, 2005) * 00022 * http://books.google.com/books?q=vatti+clipping+agoston * 00023 * * 00024 * See also: * 00025 * "Polygon Offsetting by Computing Winding Numbers" * 00026 * Paper no. DETC2005-85513 pp. 565-575 * 00027 * ASME 2005 International Design Engineering Technical Conferences * 00028 * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 00029 * September 24�28, 2005 , Long Beach, California, USA * 00030 * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 00031 * * 00032 *******************************************************************************/ 00033 00034 /******************************************************************************* 00035 * * 00036 * This is a translation of the Delphi Clipper library and the naming style * 00037 * used has retained a Delphi flavour. * 00038 * * 00039 *******************************************************************************/ 00040 00041 #include <srs_env_model_percp/but_plane_detector/clipper.hpp> 00042 #include <cmath> 00043 #include <vector> 00044 #include <algorithm> 00045 #include <stdexcept> 00046 #include <cstring> 00047 #include <cstdlib> 00048 #include <ostream> 00049 00050 namespace ClipperLib { 00051 00052 static long64 const loRange = 0x3FFFFFFF; 00053 static long64 const hiRange = 0x3FFFFFFFFFFFFFFFLL; 00054 static double const pi = 3.141592653589793238; 00055 enum Direction { dRightToLeft, dLeftToRight }; 00056 00057 #define HORIZONTAL (-1.0E+40) 00058 #define TOLERANCE (1.0e-20) 00059 #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) 00060 #define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) 00061 00062 inline long64 Abs(long64 val) 00063 { 00064 return val < 0 ? -val : val; 00065 } 00066 //------------------------------------------------------------------------------ 00067 00068 //------------------------------------------------------------------------------ 00069 // Int128 class (enables safe math on signed 64bit integers) 00070 // eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 00071 // Int128 val2((long64)9223372036854775807); 00072 // Int128 val3 = val1 * val2; 00073 // val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) 00074 //------------------------------------------------------------------------------ 00075 00076 class Int128 00077 { 00078 public: 00079 00080 Int128(long64 _lo = 0) 00081 { 00082 lo = _lo; 00083 if (lo < 0) hi = -1; else hi = 0; 00084 } 00085 00086 Int128(const Int128 &val): hi(val.hi), lo(val.lo){} 00087 00088 long64 operator = (const long64 &val) 00089 { 00090 lo = val; 00091 if (lo < 0) hi = -1; else hi = 0; 00092 return val; 00093 } 00094 00095 bool operator == (const Int128 &val) const 00096 {return (hi == val.hi && lo == val.lo);} 00097 00098 bool operator != (const Int128 &val) const 00099 { return !(*this == val);} 00100 00101 bool operator > (const Int128 &val) const 00102 { 00103 if (hi != val.hi) 00104 return hi > val.hi; 00105 else 00106 return lo > val.lo; 00107 } 00108 00109 bool operator < (const Int128 &val) const 00110 { 00111 if (hi != val.hi) 00112 return hi < val.hi; 00113 else 00114 return lo < val.lo; 00115 } 00116 00117 bool operator >= (const Int128 &val) const 00118 { return !(*this < val);} 00119 00120 bool operator <= (const Int128 &val) const 00121 { return !(*this > val);} 00122 00123 Int128& operator += (const Int128 &rhs) 00124 { 00125 hi += rhs.hi; 00126 lo += rhs.lo; 00127 if (ulong64(lo) < ulong64(rhs.lo)) hi++; 00128 return *this; 00129 } 00130 00131 Int128 operator + (const Int128 &rhs) const 00132 { 00133 Int128 result(*this); 00134 result+= rhs; 00135 return result; 00136 } 00137 00138 Int128& operator -= (const Int128 &rhs) 00139 { 00140 Int128 tmp(rhs); 00141 Negate(tmp); 00142 *this += tmp; 00143 return *this; 00144 } 00145 00146 //Int128 operator -() const 00147 //{ 00148 // Int128 result(*this); 00149 // if (result.lo == 0) { 00150 // if (result.hi != 0) result.hi = -1; 00151 // } 00152 // else { 00153 // result.lo = -result.lo; 00154 // result.hi = ~result.hi; 00155 // } 00156 // return result; 00157 //} 00158 00159 Int128 operator - (const Int128 &rhs) const 00160 { 00161 Int128 result(*this); 00162 result -= rhs; 00163 return result; 00164 } 00165 00166 Int128 operator * (const Int128 &rhs) const 00167 { 00168 if ( !(hi == 0 || hi == -1) || !(rhs.hi == 0 || rhs.hi == -1)) 00169 throw "Int128 operator*: overflow error"; 00170 bool negate = (hi < 0) != (rhs.hi < 0); 00171 00172 Int128 tmp(*this); 00173 if (tmp.hi < 0) Negate(tmp); 00174 ulong64 int1Hi = ulong64(tmp.lo) >> 32; 00175 ulong64 int1Lo = ulong64(tmp.lo & 0xFFFFFFFF); 00176 00177 tmp = rhs; 00178 if (tmp.hi < 0) Negate(tmp); 00179 ulong64 int2Hi = ulong64(tmp.lo) >> 32; 00180 ulong64 int2Lo = ulong64(tmp.lo & 0xFFFFFFFF); 00181 00182 //nb: see comments in clipper.pas 00183 ulong64 a = int1Hi * int2Hi; 00184 ulong64 b = int1Lo * int2Lo; 00185 ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; 00186 00187 tmp.hi = long64(a + (c >> 32)); 00188 tmp.lo = long64(c << 32); 00189 tmp.lo += long64(b); 00190 if (ulong64(tmp.lo) < b) tmp.hi++; 00191 if (negate) Negate(tmp); 00192 return tmp; 00193 } 00194 00195 Int128 operator/ (const Int128 &rhs) const 00196 { 00197 if (rhs.lo == 0 && rhs.hi == 0) 00198 throw "Int128 operator/: divide by zero"; 00199 bool negate = (rhs.hi < 0) != (hi < 0); 00200 Int128 result(*this), denom(rhs); 00201 if (result.hi < 0) Negate(result); 00202 if (denom.hi < 0) Negate(denom); 00203 if (denom > result) return Int128(0); //result is only a fraction of 1 00204 Negate(denom); 00205 00206 Int128 p(0); 00207 for (int i = 0; i < 128; ++i) 00208 { 00209 p.hi = p.hi << 1; 00210 if (p.lo < 0) p.hi++; 00211 p.lo = long64(p.lo) << 1; 00212 if (result.hi < 0) p.lo++; 00213 result.hi = result.hi << 1; 00214 if (result.lo < 0) result.hi++; 00215 result.lo = long64(result.lo) << 1; 00216 Int128 p2(p); 00217 p += denom; 00218 if (p.hi < 0) p = p2; 00219 else result.lo++; 00220 } 00221 if (negate) Negate(result); 00222 return result; 00223 } 00224 00225 double AsDouble() const 00226 { 00227 const double shift64 = 18446744073709551616.0; //2^64 00228 const double bit64 = 9223372036854775808.0; 00229 if (hi < 0) 00230 { 00231 Int128 tmp(*this); 00232 Negate(tmp); 00233 if (tmp.lo < 0) 00234 return (double)tmp.lo - bit64 - tmp.hi * shift64; 00235 else 00236 return -(double)tmp.lo - tmp.hi * shift64; 00237 } 00238 else if (lo < 0) 00239 return -(double)lo + bit64 + hi * shift64; 00240 else 00241 return (double)lo + (double)hi * shift64; 00242 } 00243 00244 //for bug testing ... 00245 //std::string AsString() const 00246 //{ 00247 // std::string result; 00248 // unsigned char r = 0; 00249 // Int128 tmp(0), val(*this); 00250 // if (hi < 0) Negate(val); 00251 // result.resize(50); 00252 // std::string::size_type i = result.size() -1; 00253 // while (val.hi != 0 || val.lo != 0) 00254 // { 00255 // Div10(val, tmp, r); 00256 // result[i--] = char('0' + r); 00257 // val = tmp; 00258 // } 00259 // if (hi < 0) result[i--] = '-'; 00260 // result.erase(0,i+1); 00261 // if (result.size() == 0) result = "0"; 00262 // return result; 00263 //} 00264 00265 private: 00266 long64 hi; 00267 long64 lo; 00268 00269 static void Negate(Int128 &val) 00270 { 00271 if (val.lo == 0) { 00272 if (val.hi != 0) val.hi = -val.hi;; 00273 } 00274 else { 00275 val.lo = -val.lo; 00276 val.hi = ~val.hi; 00277 } 00278 } 00279 00280 //debugging only ... 00281 //void Div10(const Int128 val, Int128& result, unsigned char & remainder) const 00282 //{ 00283 // remainder = 0; 00284 // result = 0; 00285 // for (int i = 63; i >= 0; --i) 00286 // { 00287 // if ((val.hi & ((long64)1 << i)) != 0) 00288 // remainder = char((remainder * 2) + 1); else 00289 // remainder *= char(2); 00290 // if (remainder >= 10) 00291 // { 00292 // result.hi += ((long64)1 << i); 00293 // remainder -= char(10); 00294 // } 00295 // } 00296 // for (int i = 63; i >= 0; --i) 00297 // { 00298 // if ((val.lo & ((long64)1 << i)) != 0) 00299 // remainder = char((remainder * 2) + 1); else 00300 // remainder *= char(2); 00301 // if (remainder >= 10) 00302 // { 00303 // result.lo += ((long64)1 << i); 00304 // remainder -= char(10); 00305 // } 00306 // } 00307 //} 00308 }; 00309 00310 //------------------------------------------------------------------------------ 00311 //------------------------------------------------------------------------------ 00312 00313 bool FullRangeNeeded(const Polygon &pts) 00314 { 00315 bool result = false; 00316 for (Polygon::size_type i = 0; i < pts.size(); ++i) 00317 { 00318 if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange) 00319 throw "Coordinate exceeds range bounds."; 00320 else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange) 00321 result = true; 00322 } 00323 return result; 00324 } 00325 //------------------------------------------------------------------------------ 00326 00327 bool Orientation(const Polygon &poly) 00328 { 00329 int highI = (int)poly.size() -1; 00330 if (highI < 2) return false; 00331 00332 int j = 0, jplus, jminus; 00333 for (int i = 0; i <= highI; ++i) 00334 { 00335 if (poly[i].Y < poly[j].Y) continue; 00336 if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i; 00337 }; 00338 if (j == highI) jplus = 0; 00339 else jplus = j +1; 00340 if (j == 0) jminus = highI; 00341 else jminus = j -1; 00342 00343 IntPoint vec1, vec2; 00344 //get cross product of vectors of the edges adjacent to highest point ... 00345 vec1.X = poly[j].X - poly[jminus].X; 00346 vec1.Y = poly[j].Y - poly[jminus].Y; 00347 vec2.X = poly[jplus].X - poly[j].X; 00348 vec2.Y = poly[jplus].Y - poly[j].Y; 00349 00350 if (Abs(vec1.X) > loRange || Abs(vec1.Y) > loRange || 00351 Abs(vec2.X) > loRange || Abs(vec2.Y) > loRange) 00352 { 00353 if (Abs(vec1.X) > hiRange || Abs(vec1.Y) > hiRange || 00354 Abs(vec2.X) > hiRange || Abs(vec2.Y) > hiRange) 00355 throw "Coordinate exceeds range bounds."; 00356 Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - 00357 Int128(vec2.X) * Int128(vec1.Y); 00358 return cross >= 0; 00359 } 00360 else 00361 return (vec1.X * vec2.Y - vec2.X * vec1.Y) >= 0; 00362 } 00363 //------------------------------------------------------------------------------ 00364 00365 inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) 00366 { 00367 return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); 00368 } 00369 //------------------------------------------------------------------------------ 00370 00371 bool Orientation(OutRec *outRec, bool UseFullInt64Range) 00372 { 00373 //first make sure bottomPt is correctly assigned ... 00374 OutPt *opBottom = outRec->pts, *op = outRec->pts->next; 00375 while (op != outRec->pts) 00376 { 00377 if (op->pt.Y >= opBottom->pt.Y) 00378 { 00379 if (op->pt.Y > opBottom->pt.Y || op->pt.X < opBottom->pt.X) 00380 opBottom = op; 00381 } 00382 op = op->next; 00383 } 00384 outRec->bottomPt = opBottom; 00385 opBottom->idx = outRec->idx; 00386 00387 op = opBottom; 00388 //find vertices either side of bottomPt (skipping duplicate points) .... 00389 OutPt *opPrev = op->prev; 00390 OutPt *opNext = op->next; 00391 while (op != opPrev && PointsEqual(op->pt, opPrev->pt)) 00392 opPrev = opPrev->prev; 00393 while (op != opNext && PointsEqual(op->pt, opNext->pt)) 00394 opNext = opNext->next; 00395 00396 IntPoint ip1, ip2; 00397 ip1.X = op->pt.X - opPrev->pt.X; 00398 ip1.Y = op->pt.Y - opPrev->pt.Y; 00399 ip2.X = opNext->pt.X - op->pt.X; 00400 ip2.Y = opNext->pt.Y - op->pt.Y; 00401 00402 if (UseFullInt64Range) 00403 return Int128(ip1.X) * Int128(ip2.Y) - Int128(ip2.X) * Int128(ip1.Y) >= 0; 00404 else 00405 return (ip1.X * ip2.Y - ip2.X * ip1.Y) >= 0; 00406 } 00407 //------------------------------------------------------------------------------ 00408 00409 double Area(const Polygon &poly) 00410 { 00411 int highI = (int)poly.size() -1; 00412 if (highI < 2) return 0; 00413 00414 if (FullRangeNeeded(poly)) { 00415 Int128 a; 00416 a = (Int128(poly[highI].X) * Int128(poly[0].Y)) - 00417 Int128(poly[0].X) * Int128(poly[highI].Y); 00418 for (int i = 0; i < highI; ++i) 00419 a += Int128(poly[i].X) * Int128(poly[i+1].Y) - 00420 Int128(poly[i+1].X) * Int128(poly[i].Y); 00421 return a.AsDouble() / 2; 00422 } 00423 else 00424 { 00425 double a; 00426 a = (double)poly[highI].X * poly[0].Y - (double)poly[0].X * poly[highI].Y; 00427 for (int i = 0; i < highI; ++i) 00428 a += (double)poly[i].X * poly[i+1].Y - (double)poly[i+1].X * poly[i].Y; 00429 return a/2; 00430 } 00431 } 00432 //------------------------------------------------------------------------------ 00433 00434 double Area(const OutRec &outRec, bool UseFullInt64Range) 00435 { 00436 OutPt *op = outRec.pts; 00437 if (UseFullInt64Range) { 00438 Int128 a(0); 00439 do { 00440 a += (Int128(op->prev->pt.X) * Int128(op->pt.Y)) - 00441 Int128(op->pt.X) * Int128(op->prev->pt.Y); 00442 op = op->next; 00443 } while (op != outRec.pts); 00444 return a.AsDouble() / 2; 00445 } 00446 else 00447 { 00448 double a = 0; 00449 do { 00450 a += (op->prev->pt.X * op->pt.Y) - (op->pt.X * op->prev->pt.Y); 00451 op = op->next; 00452 } while (op != outRec.pts); 00453 return a/2; 00454 } 00455 } 00456 //------------------------------------------------------------------------------ 00457 00458 bool PointIsVertex(const IntPoint &pt, OutPt *pp) 00459 { 00460 OutPt *pp2 = pp; 00461 do 00462 { 00463 if (PointsEqual(pp2->pt, pt)) return true; 00464 pp2 = pp2->next; 00465 } 00466 while (pp2 != pp); 00467 return false; 00468 } 00469 //------------------------------------------------------------------------------ 00470 00471 bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range) 00472 { 00473 OutPt *pp2 = pp; 00474 bool result = false; 00475 if (UseFullInt64Range) { 00476 do 00477 { 00478 if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || 00479 ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && 00480 Int128(pt.X - pp2->pt.X) < (Int128(pp2->prev->pt.X - pp2->pt.X) * 00481 Int128(pt.Y - pp2->pt.Y)) / Int128(pp2->prev->pt.Y - pp2->pt.Y)) 00482 result = !result; 00483 pp2 = pp2->next; 00484 } 00485 while (pp2 != pp); 00486 } 00487 else 00488 { 00489 do 00490 { 00491 if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || 00492 ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && 00493 (pt.X < (pp2->prev->pt.X - pp2->pt.X) * (pt.Y - pp2->pt.Y) / 00494 (pp2->prev->pt.Y - pp2->pt.Y) + pp2->pt.X )) result = !result; 00495 pp2 = pp2->next; 00496 } 00497 while (pp2 != pp); 00498 } 00499 return result; 00500 } 00501 //------------------------------------------------------------------------------ 00502 00503 bool SlopesEqual(TEdge &e1, TEdge &e2, bool UseFullInt64Range) 00504 { 00505 if (UseFullInt64Range) 00506 return Int128(e1.ytop - e1.ybot) * Int128(e2.xtop - e2.xbot) == 00507 Int128(e1.xtop - e1.xbot) * Int128(e2.ytop - e2.ybot); 00508 else return (e1.ytop - e1.ybot)*(e2.xtop - e2.xbot) == 00509 (e1.xtop - e1.xbot)*(e2.ytop - e2.ybot); 00510 } 00511 //------------------------------------------------------------------------------ 00512 00513 bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, 00514 const IntPoint pt3, bool UseFullInt64Range) 00515 { 00516 if (UseFullInt64Range) 00517 return Int128(pt1.Y-pt2.Y) * Int128(pt2.X-pt3.X) == 00518 Int128(pt1.X-pt2.X) * Int128(pt2.Y-pt3.Y); 00519 else return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); 00520 } 00521 //------------------------------------------------------------------------------ 00522 00523 bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, 00524 const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) 00525 { 00526 if (UseFullInt64Range) 00527 return Int128(pt1.Y-pt2.Y) * Int128(pt3.X-pt4.X) == 00528 Int128(pt1.X-pt2.X) * Int128(pt3.Y-pt4.Y); 00529 else return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); 00530 } 00531 //------------------------------------------------------------------------------ 00532 00533 double GetDx(const IntPoint pt1, const IntPoint pt2) 00534 { 00535 return (pt1.Y == pt2.Y) ? 00536 HORIZONTAL : (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y); 00537 } 00538 //--------------------------------------------------------------------------- 00539 00540 void SetDx(TEdge &e) 00541 { 00542 if (e.ybot == e.ytop) e.dx = HORIZONTAL; 00543 else e.dx = (double)(e.xtop - e.xbot) / (double)(e.ytop - e.ybot); 00544 } 00545 //--------------------------------------------------------------------------- 00546 00547 void SwapSides(TEdge &edge1, TEdge &edge2) 00548 { 00549 EdgeSide side = edge1.side; 00550 edge1.side = edge2.side; 00551 edge2.side = side; 00552 } 00553 //------------------------------------------------------------------------------ 00554 00555 void SwapPolyIndexes(TEdge &edge1, TEdge &edge2) 00556 { 00557 int outIdx = edge1.outIdx; 00558 edge1.outIdx = edge2.outIdx; 00559 edge2.outIdx = outIdx; 00560 } 00561 //------------------------------------------------------------------------------ 00562 00563 inline long64 Round(double val) 00564 { 00565 return (val < 0) ? 00566 static_cast<long64>(val - 0.5) : static_cast<long64>(val + 0.5); 00567 } 00568 //------------------------------------------------------------------------------ 00569 00570 long64 TopX(TEdge &edge, const long64 currentY) 00571 { 00572 return ( currentY == edge.ytop ) ? 00573 edge.xtop : edge.xbot + Round(edge.dx *(currentY - edge.ybot)); 00574 } 00575 //------------------------------------------------------------------------------ 00576 00577 long64 TopX(const IntPoint pt1, const IntPoint pt2, const long64 currentY) 00578 { 00579 //preconditions: pt1.Y <> pt2.Y and pt1.Y > pt2.Y 00580 if (currentY >= pt1.Y) return pt1.X; 00581 else if (currentY == pt2.Y) return pt2.X; 00582 else if (pt1.X == pt2.X) return pt1.X; 00583 else 00584 { 00585 double q = (double)(pt1.X-pt2.X)/(double)(pt1.Y-pt2.Y); 00586 return Round(pt1.X + (currentY - pt1.Y) *q); 00587 } 00588 } 00589 //------------------------------------------------------------------------------ 00590 00591 bool IntersectPoint(TEdge &edge1, TEdge &edge2, 00592 IntPoint &ip, bool UseFullInt64Range) 00593 { 00594 double b1, b2; 00595 if (SlopesEqual(edge1, edge2, UseFullInt64Range)) return false; 00596 else if (NEAR_ZERO(edge1.dx)) 00597 { 00598 ip.X = edge1.xbot; 00599 if (NEAR_EQUAL(edge2.dx, HORIZONTAL)) 00600 { 00601 ip.Y = edge2.ybot; 00602 } else 00603 { 00604 b2 = edge2.ybot - (edge2.xbot/edge2.dx); 00605 ip.Y = Round(ip.X/edge2.dx + b2); 00606 } 00607 } 00608 else if (NEAR_ZERO(edge2.dx)) 00609 { 00610 ip.X = edge2.xbot; 00611 if (NEAR_EQUAL(edge1.dx, HORIZONTAL)) 00612 { 00613 ip.Y = edge1.ybot; 00614 } else 00615 { 00616 b1 = edge1.ybot - (edge1.xbot/edge1.dx); 00617 ip.Y = Round(ip.X/edge1.dx + b1); 00618 } 00619 } else 00620 { 00621 b1 = edge1.xbot - edge1.ybot * edge1.dx; 00622 b2 = edge2.xbot - edge2.ybot * edge2.dx; 00623 b2 = (b2-b1)/(edge1.dx - edge2.dx); 00624 ip.Y = Round(b2); 00625 ip.X = Round(edge1.dx * b2 + b1); 00626 } 00627 00628 return 00629 //can be *so close* to the top of one edge that the rounded Y equals one ytop ... 00630 (ip.Y == edge1.ytop && ip.Y >= edge2.ytop && edge1.tmpX > edge2.tmpX) || 00631 (ip.Y == edge2.ytop && ip.Y >= edge1.ytop && edge1.tmpX > edge2.tmpX) || 00632 (ip.Y > edge1.ytop && ip.Y > edge2.ytop); 00633 } 00634 //------------------------------------------------------------------------------ 00635 00636 void ReversePolyPtLinks(OutPt &pp) 00637 { 00638 OutPt *pp1, *pp2; 00639 pp1 = &pp; 00640 do { 00641 pp2 = pp1->next; 00642 pp1->next = pp1->prev; 00643 pp1->prev = pp2; 00644 pp1 = pp2; 00645 } while( pp1 != &pp ); 00646 } 00647 //------------------------------------------------------------------------------ 00648 00649 void DisposeOutPts(OutPt*& pp) 00650 { 00651 if (pp == 0) return; 00652 pp->prev->next = 0; 00653 while( pp ) 00654 { 00655 OutPt *tmpPp = pp; 00656 pp = pp->next; 00657 delete tmpPp ; 00658 } 00659 } 00660 //------------------------------------------------------------------------------ 00661 00662 void InitEdge(TEdge *e, TEdge *eNext, 00663 TEdge *ePrev, const IntPoint &pt, PolyType polyType) 00664 { 00665 std::memset( e, 0, sizeof( TEdge )); 00666 00667 e->next = eNext; 00668 e->prev = ePrev; 00669 e->xcurr = pt.X; 00670 e->ycurr = pt.Y; 00671 if (e->ycurr >= e->next->ycurr) 00672 { 00673 e->xbot = e->xcurr; 00674 e->ybot = e->ycurr; 00675 e->xtop = e->next->xcurr; 00676 e->ytop = e->next->ycurr; 00677 e->windDelta = 1; 00678 } else 00679 { 00680 e->xtop = e->xcurr; 00681 e->ytop = e->ycurr; 00682 e->xbot = e->next->xcurr; 00683 e->ybot = e->next->ycurr; 00684 e->windDelta = -1; 00685 } 00686 SetDx(*e); 00687 e->polyType = polyType; 00688 e->outIdx = -1; 00689 } 00690 //------------------------------------------------------------------------------ 00691 00692 inline void SwapX(TEdge &e) 00693 { 00694 //swap horizontal edges' top and bottom x's so they follow the natural 00695 //progression of the bounds - ie so their xbots will align with the 00696 //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] 00697 e.xcurr = e.xtop; 00698 e.xtop = e.xbot; 00699 e.xbot = e.xcurr; 00700 } 00701 //------------------------------------------------------------------------------ 00702 00703 void SwapPoints(IntPoint &pt1, IntPoint &pt2) 00704 { 00705 IntPoint tmp = pt1; 00706 pt1 = pt2; 00707 pt2 = tmp; 00708 } 00709 //------------------------------------------------------------------------------ 00710 00711 bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, 00712 IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) 00713 { 00714 //precondition: segments are colinear. 00715 if ( pt1a.Y == pt1b.Y || Abs((pt1a.X - pt1b.X)/(pt1a.Y - pt1b.Y)) > 1 ) 00716 { 00717 if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); 00718 if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); 00719 if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; 00720 if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; 00721 return pt1.X < pt2.X; 00722 } else 00723 { 00724 if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); 00725 if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); 00726 if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; 00727 if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; 00728 return pt1.Y > pt2.Y; 00729 } 00730 } 00731 //------------------------------------------------------------------------------ 00732 00733 bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) 00734 { 00735 OutPt *p = btmPt1->prev; 00736 while (PointsEqual(p->pt, btmPt1->pt) && (p != btmPt1)) p = p->prev; 00737 double dx1p = std::fabs(GetDx(btmPt1->pt, p->pt)); 00738 p = btmPt1->next; 00739 while (PointsEqual(p->pt, btmPt1->pt) && (p != btmPt1)) p = p->next; 00740 double dx1n = std::fabs(GetDx(btmPt1->pt, p->pt)); 00741 00742 p = btmPt2->prev; 00743 while (PointsEqual(p->pt, btmPt2->pt) && (p != btmPt2)) p = p->prev; 00744 double dx2p = std::fabs(GetDx(btmPt2->pt, p->pt)); 00745 p = btmPt2->next; 00746 while (PointsEqual(p->pt, btmPt2->pt) && (p != btmPt2)) p = p->next; 00747 double dx2n = std::fabs(GetDx(btmPt2->pt, p->pt)); 00748 return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); 00749 } 00750 //------------------------------------------------------------------------------ 00751 00752 OutPt* GetBottomPt(OutPt *pp) 00753 { 00754 OutPt* dups = 0; 00755 OutPt* p = pp->next; 00756 while (p != pp) 00757 { 00758 if (p->pt.Y > pp->pt.Y) 00759 { 00760 pp = p; 00761 dups = 0; 00762 } 00763 else if (p->pt.Y == pp->pt.Y && p->pt.X <= pp->pt.X) 00764 { 00765 if (p->pt.X < pp->pt.X) 00766 { 00767 dups = 0; 00768 pp = p; 00769 } else 00770 { 00771 if (p->next != pp && p->prev != pp) dups = p; 00772 } 00773 } 00774 p = p->next; 00775 } 00776 if (dups) 00777 { 00778 //there appears to be at least 2 vertices at bottomPt so ... 00779 while (dups != p) 00780 { 00781 if (!FirstIsBottomPt(p, dups)) pp = dups; 00782 dups = dups->next; 00783 while (!PointsEqual(dups->pt, pp->pt)) dups = dups->next; 00784 } 00785 } 00786 return pp; 00787 } 00788 //------------------------------------------------------------------------------ 00789 00790 bool FindSegment(OutPt* &pp, IntPoint &pt1, IntPoint &pt2) 00791 { 00792 //outPt1 & outPt2 => the overlap segment (if the function returns true) 00793 if (!pp) return false; 00794 OutPt* pp2 = pp; 00795 IntPoint pt1a = pt1, pt2a = pt2; 00796 do 00797 { 00798 if (SlopesEqual(pt1a, pt2a, pp->pt, pp->prev->pt, true) && 00799 SlopesEqual(pt1a, pt2a, pp->pt, true) && 00800 GetOverlapSegment(pt1a, pt2a, pp->pt, pp->prev->pt, pt1, pt2)) 00801 return true; 00802 pp = pp->next; 00803 } 00804 while (pp != pp2); 00805 return false; 00806 } 00807 //------------------------------------------------------------------------------ 00808 00809 bool Pt3IsBetweenPt1AndPt2(const IntPoint pt1, 00810 const IntPoint pt2, const IntPoint pt3) 00811 { 00812 if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true; 00813 else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); 00814 else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); 00815 } 00816 //------------------------------------------------------------------------------ 00817 00818 OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint pt) 00819 { 00820 if (p1 == p2) throw "JoinError"; 00821 OutPt* result = new OutPt; 00822 result->pt = pt; 00823 if (p2 == p1->next) 00824 { 00825 p1->next = result; 00826 p2->prev = result; 00827 result->next = p2; 00828 result->prev = p1; 00829 } else 00830 { 00831 p2->next = result; 00832 p1->prev = result; 00833 result->next = p1; 00834 result->prev = p2; 00835 } 00836 return result; 00837 } 00838 00839 //------------------------------------------------------------------------------ 00840 // ClipperBase class methods ... 00841 //------------------------------------------------------------------------------ 00842 00843 ClipperBase::ClipperBase() //constructor 00844 { 00845 m_MinimaList = 0; 00846 m_CurrentLM = 0; 00847 m_UseFullRange = true; 00848 } 00849 //------------------------------------------------------------------------------ 00850 00851 ClipperBase::~ClipperBase() //destructor 00852 { 00853 Clear(); 00854 } 00855 //------------------------------------------------------------------------------ 00856 00857 bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) 00858 { 00859 int len = (int)pg.size(); 00860 if (len < 3) return false; 00861 Polygon p(len); 00862 p[0] = pg[0]; 00863 int j = 0; 00864 00865 long64 maxVal; 00866 if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; 00867 00868 for (int i = 0; i < len; ++i) 00869 { 00870 if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) 00871 { 00872 if (Abs(pg[i].X) > hiRange || Abs(pg[i].Y) > hiRange) 00873 throw "Coordinate exceeds range bounds"; 00874 maxVal = hiRange; 00875 m_UseFullRange = true; 00876 } 00877 00878 if (i == 0 || PointsEqual(p[j], pg[i])) continue; 00879 else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) 00880 { 00881 if (PointsEqual(p[j-1], pg[i])) j--; 00882 } else j++; 00883 p[j] = pg[i]; 00884 } 00885 if (j < 2) return false; 00886 00887 len = j+1; 00888 while (len > 2) 00889 { 00890 //nb: test for point equality before testing slopes ... 00891 if (PointsEqual(p[j], p[0])) j--; 00892 else if (PointsEqual(p[0], p[1]) || 00893 SlopesEqual(p[j], p[0], p[1], m_UseFullRange)) 00894 p[0] = p[j--]; 00895 else if (SlopesEqual(p[j-1], p[j], p[0], m_UseFullRange)) j--; 00896 else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange)) 00897 { 00898 for (int i = 2; i <= j; ++i) p[i-1] = p[i]; 00899 j--; 00900 } 00901 else break; 00902 len--; 00903 } 00904 if (len < 3) return false; 00905 00906 //create a new edge array ... 00907 TEdge *edges = new TEdge [len]; 00908 m_edges.push_back(edges); 00909 00910 //convert vertices to a double-linked-list of edges and initialize ... 00911 edges[0].xcurr = p[0].X; 00912 edges[0].ycurr = p[0].Y; 00913 InitEdge(&edges[len-1], &edges[0], &edges[len-2], p[len-1], polyType); 00914 for (int i = len-2; i > 0; --i) 00915 InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i], polyType); 00916 InitEdge(&edges[0], &edges[1], &edges[len-1], p[0], polyType); 00917 00918 //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates 00919 //increase downward so the 'highest' edge will have the smallest ytop) ... 00920 TEdge *e = &edges[0]; 00921 TEdge *eHighest = e; 00922 do 00923 { 00924 e->xcurr = e->xbot; 00925 e->ycurr = e->ybot; 00926 if (e->ytop < eHighest->ytop) eHighest = e; 00927 e = e->next; 00928 } 00929 while ( e != &edges[0]); 00930 00931 //make sure eHighest is positioned so the following loop works safely ... 00932 if (eHighest->windDelta > 0) eHighest = eHighest->next; 00933 if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next; 00934 00935 //finally insert each local minima ... 00936 e = eHighest; 00937 do { 00938 e = AddBoundsToLML(e); 00939 } 00940 while( e != eHighest ); 00941 return true; 00942 } 00943 //------------------------------------------------------------------------------ 00944 00945 void ClipperBase::InsertLocalMinima(LocalMinima *newLm) 00946 { 00947 if( ! m_MinimaList ) 00948 { 00949 m_MinimaList = newLm; 00950 } 00951 else if( newLm->Y >= m_MinimaList->Y ) 00952 { 00953 newLm->next = m_MinimaList; 00954 m_MinimaList = newLm; 00955 } else 00956 { 00957 LocalMinima* tmpLm = m_MinimaList; 00958 while( tmpLm->next && ( newLm->Y < tmpLm->next->Y ) ) 00959 tmpLm = tmpLm->next; 00960 newLm->next = tmpLm->next; 00961 tmpLm->next = newLm; 00962 } 00963 } 00964 //------------------------------------------------------------------------------ 00965 00966 TEdge* ClipperBase::AddBoundsToLML(TEdge *e) 00967 { 00968 //Starting at the top of one bound we progress to the bottom where there's 00969 //a local minima. We then go to the top of the next bound. These two bounds 00970 //form the left and right (or right and left) bounds of the local minima. 00971 e->nextInLML = 0; 00972 e = e->next; 00973 for (;;) 00974 { 00975 if (NEAR_EQUAL(e->dx, HORIZONTAL)) 00976 { 00977 //nb: proceed through horizontals when approaching from their right, 00978 // but break on horizontal minima if approaching from their left. 00979 // This ensures 'local minima' are always on the left of horizontals. 00980 if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break; 00981 if (e->xtop != e->prev->xbot) SwapX(*e); 00982 e->nextInLML = e->prev; 00983 } 00984 else if (e->ycurr == e->prev->ycurr) break; 00985 else e->nextInLML = e->prev; 00986 e = e->next; 00987 } 00988 00989 //e and e.prev are now at a local minima ... 00990 LocalMinima* newLm = new LocalMinima; 00991 newLm->next = 0; 00992 newLm->Y = e->prev->ybot; 00993 00994 if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound 00995 { 00996 if (e->xbot != e->prev->xbot) SwapX(*e); 00997 newLm->leftBound = e->prev; 00998 newLm->rightBound = e; 00999 } else if (e->dx < e->prev->dx) 01000 { 01001 newLm->leftBound = e->prev; 01002 newLm->rightBound = e; 01003 } else 01004 { 01005 newLm->leftBound = e; 01006 newLm->rightBound = e->prev; 01007 } 01008 newLm->leftBound->side = esLeft; 01009 newLm->rightBound->side = esRight; 01010 InsertLocalMinima( newLm ); 01011 01012 for (;;) 01013 { 01014 if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break; 01015 e->nextInLML = e->next; 01016 e = e->next; 01017 if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e); 01018 } 01019 return e->next; 01020 } 01021 //------------------------------------------------------------------------------ 01022 01023 bool ClipperBase::AddPolygons(const Polygons &ppg, PolyType polyType) 01024 { 01025 bool result = false; 01026 for (Polygons::size_type i = 0; i < ppg.size(); ++i) 01027 if (AddPolygon(ppg[i], polyType)) result = true; 01028 return result; 01029 } 01030 //------------------------------------------------------------------------------ 01031 01032 void ClipperBase::Clear() 01033 { 01034 DisposeLocalMinimaList(); 01035 for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) delete [] m_edges[i]; 01036 m_edges.clear(); 01037 m_UseFullRange = false; 01038 } 01039 //------------------------------------------------------------------------------ 01040 01041 void ClipperBase::Reset() 01042 { 01043 m_CurrentLM = m_MinimaList; 01044 if( !m_CurrentLM ) return; //ie nothing to process 01045 01046 //reset all edges ... 01047 LocalMinima* lm = m_MinimaList; 01048 while( lm ) 01049 { 01050 TEdge* e = lm->leftBound; 01051 while( e ) 01052 { 01053 e->xcurr = e->xbot; 01054 e->ycurr = e->ybot; 01055 e->side = esLeft; 01056 e->outIdx = -1; 01057 e = e->nextInLML; 01058 } 01059 e = lm->rightBound; 01060 while( e ) 01061 { 01062 e->xcurr = e->xbot; 01063 e->ycurr = e->ybot; 01064 e->side = esRight; 01065 e->outIdx = -1; 01066 e = e->nextInLML; 01067 } 01068 lm = lm->next; 01069 } 01070 } 01071 //------------------------------------------------------------------------------ 01072 01073 void ClipperBase::DisposeLocalMinimaList() 01074 { 01075 while( m_MinimaList ) 01076 { 01077 LocalMinima* tmpLm = m_MinimaList->next; 01078 delete m_MinimaList; 01079 m_MinimaList = tmpLm; 01080 } 01081 m_CurrentLM = 0; 01082 } 01083 //------------------------------------------------------------------------------ 01084 01085 void ClipperBase::PopLocalMinima() 01086 { 01087 if( ! m_CurrentLM ) return; 01088 m_CurrentLM = m_CurrentLM->next; 01089 } 01090 //------------------------------------------------------------------------------ 01091 01092 IntRect ClipperBase::GetBounds() 01093 { 01094 IntRect result; 01095 LocalMinima* lm = m_MinimaList; 01096 if (!lm) 01097 { 01098 result.left = result.top = result.right = result.bottom = 0; 01099 return result; 01100 } 01101 result.left = lm->leftBound->xbot; 01102 result.top = lm->leftBound->ybot; 01103 result.right = lm->leftBound->xbot; 01104 result.bottom = lm->leftBound->ybot; 01105 while (lm) 01106 { 01107 if (lm->leftBound->ybot > result.bottom) 01108 result.bottom = lm->leftBound->ybot; 01109 TEdge* e = lm->leftBound; 01110 for (;;) { 01111 TEdge* bottomE = e; 01112 while (e->nextInLML) 01113 { 01114 if (e->xbot < result.left) result.left = e->xbot; 01115 if (e->xbot > result.right) result.right = e->xbot; 01116 e = e->nextInLML; 01117 } 01118 if (e->xbot < result.left) result.left = e->xbot; 01119 if (e->xbot > result.right) result.right = e->xbot; 01120 if (e->xtop < result.left) result.left = e->xtop; 01121 if (e->xtop > result.right) result.right = e->xtop; 01122 if (e->ytop < result.top) result.top = e->ytop; 01123 01124 if (bottomE == lm->leftBound) e = lm->rightBound; 01125 else break; 01126 } 01127 lm = lm->next; 01128 } 01129 return result; 01130 } 01131 01132 01133 //------------------------------------------------------------------------------ 01134 // TClipper methods ... 01135 //------------------------------------------------------------------------------ 01136 01137 Clipper::Clipper() : ClipperBase() //constructor 01138 { 01139 m_Scanbeam = 0; 01140 m_ActiveEdges = 0; 01141 m_SortedEdges = 0; 01142 m_IntersectNodes = 0; 01143 m_ExecuteLocked = false; 01144 m_UseFullRange = false; 01145 m_ReverseOutput = false; 01146 } 01147 //------------------------------------------------------------------------------ 01148 01149 Clipper::~Clipper() //destructor 01150 { 01151 Clear(); 01152 DisposeScanbeamList(); 01153 } 01154 //------------------------------------------------------------------------------ 01155 01156 void Clipper::Clear() 01157 { 01158 if (m_edges.size() == 0) return; //avoids problems with ClipperBase destructor 01159 DisposeAllPolyPts(); 01160 ClipperBase::Clear(); 01161 } 01162 //------------------------------------------------------------------------------ 01163 01164 void Clipper::DisposeScanbeamList() 01165 { 01166 while ( m_Scanbeam ) { 01167 Scanbeam* sb2 = m_Scanbeam->next; 01168 delete m_Scanbeam; 01169 m_Scanbeam = sb2; 01170 } 01171 } 01172 //------------------------------------------------------------------------------ 01173 01174 void Clipper::Reset() 01175 { 01176 ClipperBase::Reset(); 01177 m_Scanbeam = 0; 01178 m_ActiveEdges = 0; 01179 m_SortedEdges = 0; 01180 DisposeAllPolyPts(); 01181 LocalMinima* lm = m_MinimaList; 01182 while (lm) 01183 { 01184 InsertScanbeam(lm->Y); 01185 InsertScanbeam(lm->leftBound->ytop); 01186 lm = lm->next; 01187 } 01188 } 01189 //------------------------------------------------------------------------------ 01190 01191 bool Clipper::Execute(ClipType clipType, Polygons &solution, 01192 PolyFillType subjFillType, PolyFillType clipFillType) 01193 { 01194 if( m_ExecuteLocked ) return false; 01195 m_ExecuteLocked = true; 01196 solution.resize(0); 01197 m_SubjFillType = subjFillType; 01198 m_ClipFillType = clipFillType; 01199 m_ClipType = clipType; 01200 bool succeeded = ExecuteInternal(false); 01201 if (succeeded) BuildResult(solution); 01202 m_ExecuteLocked = false; 01203 return succeeded; 01204 } 01205 //------------------------------------------------------------------------------ 01206 01207 bool Clipper::Execute(ClipType clipType, ExPolygons &solution, 01208 PolyFillType subjFillType, PolyFillType clipFillType) 01209 { 01210 if( m_ExecuteLocked ) return false; 01211 m_ExecuteLocked = true; 01212 solution.resize(0); 01213 m_SubjFillType = subjFillType; 01214 m_ClipFillType = clipFillType; 01215 m_ClipType = clipType; 01216 bool succeeded = ExecuteInternal(true); 01217 if (succeeded) BuildResultEx(solution); 01218 m_ExecuteLocked = false; 01219 return succeeded; 01220 } 01221 //------------------------------------------------------------------------------ 01222 01223 bool PolySort(OutRec *or1, OutRec *or2) 01224 { 01225 if (or1 == or2) return false; 01226 if (!or1->pts || !or2->pts) 01227 { 01228 if (or1->pts != or2->pts) 01229 { 01230 return or1->pts ? true : false; 01231 } 01232 else return false; 01233 } 01234 int i1, i2; 01235 if (or1->isHole) 01236 i1 = or1->FirstLeft->idx; else 01237 i1 = or1->idx; 01238 if (or2->isHole) 01239 i2 = or2->FirstLeft->idx; else 01240 i2 = or2->idx; 01241 int result = i1 - i2; 01242 if (result == 0 && (or1->isHole != or2->isHole)) 01243 { 01244 return or1->isHole ? false : true; 01245 } 01246 else return result < 0; 01247 } 01248 //------------------------------------------------------------------------------ 01249 01250 OutRec* FindAppendLinkEnd(OutRec *outRec) 01251 { 01252 while (outRec->AppendLink) outRec = outRec->AppendLink; 01253 return outRec; 01254 } 01255 //------------------------------------------------------------------------------ 01256 01257 void Clipper::FixHoleLinkage(OutRec *outRec) 01258 { 01259 OutRec *tmp; 01260 if (outRec->bottomPt) 01261 tmp = m_PolyOuts[outRec->bottomPt->idx]->FirstLeft; 01262 else 01263 tmp = outRec->FirstLeft; 01264 if (outRec == tmp) throw clipperException("HoleLinkage error"); 01265 01266 if (tmp) 01267 { 01268 if (tmp->AppendLink) tmp = FindAppendLinkEnd(tmp); 01269 if (tmp == outRec) tmp = 0; 01270 else if (tmp->isHole) 01271 { 01272 FixHoleLinkage(tmp); 01273 tmp = tmp->FirstLeft; 01274 } 01275 } 01276 outRec->FirstLeft = tmp; 01277 if (!tmp) outRec->isHole = false; 01278 outRec->AppendLink = 0; 01279 } 01280 //------------------------------------------------------------------------------ 01281 01282 bool Clipper::ExecuteInternal(bool fixHoleLinkages) 01283 { 01284 bool succeeded; 01285 try { 01286 Reset(); 01287 if (!m_CurrentLM ) return true; 01288 long64 botY = PopScanbeam(); 01289 do { 01290 InsertLocalMinimaIntoAEL(botY); 01291 ClearHorzJoins(); 01292 ProcessHorizontals(); 01293 long64 topY = PopScanbeam(); 01294 succeeded = ProcessIntersections(botY, topY); 01295 if (!succeeded) break; 01296 ProcessEdgesAtTopOfScanbeam(topY); 01297 botY = topY; 01298 } while( m_Scanbeam ); 01299 } 01300 catch(...) { 01301 succeeded = false; 01302 } 01303 01304 if (succeeded) 01305 { 01306 //tidy up output polygons and fix orientations where necessary ... 01307 for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) 01308 { 01309 OutRec *outRec = m_PolyOuts[i]; 01310 if (!outRec->pts) continue; 01311 FixupOutPolygon(*outRec); 01312 if (!outRec->pts) continue; 01313 if (outRec->isHole && fixHoleLinkages) FixHoleLinkage(outRec); 01314 01315 if (outRec->bottomPt == outRec->bottomFlag && 01316 (Orientation(outRec, m_UseFullRange) != (Area(*outRec, m_UseFullRange) > 0))) 01317 DisposeBottomPt(*outRec); 01318 01319 if (outRec->isHole == 01320 (m_ReverseOutput ^ Orientation(outRec, m_UseFullRange))) 01321 ReversePolyPtLinks(*outRec->pts); 01322 } 01323 01324 JoinCommonEdges(fixHoleLinkages); 01325 if (fixHoleLinkages) 01326 std::sort(m_PolyOuts.begin(), m_PolyOuts.end(), PolySort); 01327 } 01328 01329 ClearJoins(); 01330 ClearHorzJoins(); 01331 return succeeded; 01332 } 01333 //------------------------------------------------------------------------------ 01334 01335 void Clipper::InsertScanbeam(const long64 Y) 01336 { 01337 if( !m_Scanbeam ) 01338 { 01339 m_Scanbeam = new Scanbeam; 01340 m_Scanbeam->next = 0; 01341 m_Scanbeam->Y = Y; 01342 } 01343 else if( Y > m_Scanbeam->Y ) 01344 { 01345 Scanbeam* newSb = new Scanbeam; 01346 newSb->Y = Y; 01347 newSb->next = m_Scanbeam; 01348 m_Scanbeam = newSb; 01349 } else 01350 { 01351 Scanbeam* sb2 = m_Scanbeam; 01352 while( sb2->next && ( Y <= sb2->next->Y ) ) sb2 = sb2->next; 01353 if( Y == sb2->Y ) return; //ie ignores duplicates 01354 Scanbeam* newSb = new Scanbeam; 01355 newSb->Y = Y; 01356 newSb->next = sb2->next; 01357 sb2->next = newSb; 01358 } 01359 } 01360 //------------------------------------------------------------------------------ 01361 01362 long64 Clipper::PopScanbeam() 01363 { 01364 long64 Y = m_Scanbeam->Y; 01365 Scanbeam* sb2 = m_Scanbeam; 01366 m_Scanbeam = m_Scanbeam->next; 01367 delete sb2; 01368 return Y; 01369 } 01370 //------------------------------------------------------------------------------ 01371 01372 void Clipper::DisposeAllPolyPts(){ 01373 for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) 01374 DisposeOutRec(i); 01375 m_PolyOuts.clear(); 01376 } 01377 //------------------------------------------------------------------------------ 01378 01379 void Clipper::DisposeOutRec(PolyOutList::size_type index) 01380 { 01381 OutRec *outRec = m_PolyOuts[index]; 01382 if (outRec->pts) DisposeOutPts(outRec->pts); 01383 delete outRec; 01384 m_PolyOuts[index] = 0; 01385 } 01386 //------------------------------------------------------------------------------ 01387 01388 void Clipper::SetWindingCount(TEdge &edge) 01389 { 01390 TEdge *e = edge.prevInAEL; 01391 //find the edge of the same polytype that immediately preceeds 'edge' in AEL 01392 while ( e && e->polyType != edge.polyType ) e = e->prevInAEL; 01393 if ( !e ) 01394 { 01395 edge.windCnt = edge.windDelta; 01396 edge.windCnt2 = 0; 01397 e = m_ActiveEdges; //ie get ready to calc windCnt2 01398 } else if ( IsEvenOddFillType(edge) ) 01399 { 01400 //EvenOdd filling ... 01401 edge.windCnt = 1; 01402 edge.windCnt2 = e->windCnt2; 01403 e = e->nextInAEL; //ie get ready to calc windCnt2 01404 } else 01405 { 01406 //nonZero, Positive or Negative filling ... 01407 if ( e->windCnt * e->windDelta < 0 ) 01408 { 01409 if (Abs(e->windCnt) > 1) 01410 { 01411 if (e->windDelta * edge.windDelta < 0) edge.windCnt = e->windCnt; 01412 else edge.windCnt = e->windCnt + edge.windDelta; 01413 } else 01414 edge.windCnt = e->windCnt + e->windDelta + edge.windDelta; 01415 } else 01416 { 01417 if ( Abs(e->windCnt) > 1 && e->windDelta * edge.windDelta < 0) 01418 edge.windCnt = e->windCnt; 01419 else if ( e->windCnt + edge.windDelta == 0 ) 01420 edge.windCnt = e->windCnt; 01421 else edge.windCnt = e->windCnt + edge.windDelta; 01422 } 01423 edge.windCnt2 = e->windCnt2; 01424 e = e->nextInAEL; //ie get ready to calc windCnt2 01425 } 01426 01427 //update windCnt2 ... 01428 if ( IsEvenOddAltFillType(edge) ) 01429 { 01430 //EvenOdd filling ... 01431 while ( e != &edge ) 01432 { 01433 edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; 01434 e = e->nextInAEL; 01435 } 01436 } else 01437 { 01438 //nonZero, Positive or Negative filling ... 01439 while ( e != &edge ) 01440 { 01441 edge.windCnt2 += e->windDelta; 01442 e = e->nextInAEL; 01443 } 01444 } 01445 } 01446 //------------------------------------------------------------------------------ 01447 01448 bool Clipper::IsEvenOddFillType(const TEdge& edge) const 01449 { 01450 if (edge.polyType == ptSubject) 01451 return m_SubjFillType == pftEvenOdd; else 01452 return m_ClipFillType == pftEvenOdd; 01453 } 01454 //------------------------------------------------------------------------------ 01455 01456 bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const 01457 { 01458 if (edge.polyType == ptSubject) 01459 return m_ClipFillType == pftEvenOdd; else 01460 return m_SubjFillType == pftEvenOdd; 01461 } 01462 //------------------------------------------------------------------------------ 01463 01464 bool Clipper::IsContributing(const TEdge& edge) const 01465 { 01466 PolyFillType pft, pft2; 01467 if (edge.polyType == ptSubject) 01468 { 01469 pft = m_SubjFillType; 01470 pft2 = m_ClipFillType; 01471 } else 01472 { 01473 pft = m_ClipFillType; 01474 pft2 = m_SubjFillType; 01475 } 01476 01477 switch(pft) 01478 { 01479 case pftEvenOdd: 01480 case pftNonZero: 01481 if (Abs(edge.windCnt) != 1) return false; 01482 break; 01483 case pftPositive: 01484 if (edge.windCnt != 1) return false; 01485 break; 01486 default: //pftNegative 01487 if (edge.windCnt != -1) return false; 01488 } 01489 01490 switch(m_ClipType) 01491 { 01492 case ctIntersection: 01493 switch(pft2) 01494 { 01495 case pftEvenOdd: 01496 case pftNonZero: 01497 return (edge.windCnt2 != 0); 01498 case pftPositive: 01499 return (edge.windCnt2 > 0); 01500 default: 01501 return (edge.windCnt2 < 0); 01502 } 01503 case ctUnion: 01504 switch(pft2) 01505 { 01506 case pftEvenOdd: 01507 case pftNonZero: 01508 return (edge.windCnt2 == 0); 01509 case pftPositive: 01510 return (edge.windCnt2 <= 0); 01511 default: 01512 return (edge.windCnt2 >= 0); 01513 } 01514 case ctDifference: 01515 if (edge.polyType == ptSubject) 01516 switch(pft2) 01517 { 01518 case pftEvenOdd: 01519 case pftNonZero: 01520 return (edge.windCnt2 == 0); 01521 case pftPositive: 01522 return (edge.windCnt2 <= 0); 01523 default: 01524 return (edge.windCnt2 >= 0); 01525 } 01526 else 01527 switch(pft2) 01528 { 01529 case pftEvenOdd: 01530 case pftNonZero: 01531 return (edge.windCnt2 != 0); 01532 case pftPositive: 01533 return (edge.windCnt2 > 0); 01534 default: 01535 return (edge.windCnt2 < 0); 01536 } 01537 default: 01538 return true; 01539 } 01540 } 01541 //------------------------------------------------------------------------------ 01542 01543 void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) 01544 { 01545 TEdge *e, *prevE; 01546 if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) ) 01547 { 01548 AddOutPt( e1, pt ); 01549 e2->outIdx = e1->outIdx; 01550 e1->side = esLeft; 01551 e2->side = esRight; 01552 e = e1; 01553 if (e->prevInAEL == e2) 01554 prevE = e2->prevInAEL; 01555 else 01556 prevE = e->prevInAEL; 01557 } else 01558 { 01559 AddOutPt( e2, pt ); 01560 e1->outIdx = e2->outIdx; 01561 e1->side = esRight; 01562 e2->side = esLeft; 01563 e = e2; 01564 if (e->prevInAEL == e1) 01565 prevE = e1->prevInAEL; 01566 else 01567 prevE = e->prevInAEL; 01568 } 01569 if (prevE && prevE->outIdx >= 0 && 01570 (TopX(*prevE, pt.Y) == TopX(*e, pt.Y)) && 01571 SlopesEqual(*e, *prevE, m_UseFullRange)) 01572 AddJoin(e, prevE, -1, -1); 01573 } 01574 //------------------------------------------------------------------------------ 01575 01576 void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) 01577 { 01578 AddOutPt( e1, pt ); 01579 if( e1->outIdx == e2->outIdx ) 01580 { 01581 e1->outIdx = -1; 01582 e2->outIdx = -1; 01583 } 01584 else if (e1->outIdx < e2->outIdx) 01585 AppendPolygon(e1, e2); 01586 else 01587 AppendPolygon(e2, e1); 01588 } 01589 //------------------------------------------------------------------------------ 01590 01591 void Clipper::AddEdgeToSEL(TEdge *edge) 01592 { 01593 //SEL pointers in PEdge are reused to build a list of horizontal edges. 01594 //However, we don't need to worry about order with horizontal edge processing. 01595 if( !m_SortedEdges ) 01596 { 01597 m_SortedEdges = edge; 01598 edge->prevInSEL = 0; 01599 edge->nextInSEL = 0; 01600 } 01601 else 01602 { 01603 edge->nextInSEL = m_SortedEdges; 01604 edge->prevInSEL = 0; 01605 m_SortedEdges->prevInSEL = edge; 01606 m_SortedEdges = edge; 01607 } 01608 } 01609 //------------------------------------------------------------------------------ 01610 01611 void Clipper::CopyAELToSEL() 01612 { 01613 TEdge* e = m_ActiveEdges; 01614 m_SortedEdges = e; 01615 if (!m_ActiveEdges) return; 01616 m_SortedEdges->prevInSEL = 0; 01617 e = e->nextInAEL; 01618 while ( e ) 01619 { 01620 e->prevInSEL = e->prevInAEL; 01621 e->prevInSEL->nextInSEL = e; 01622 e->nextInSEL = 0; 01623 e = e->nextInAEL; 01624 } 01625 } 01626 //------------------------------------------------------------------------------ 01627 01628 void Clipper::AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx, int e2OutIdx) 01629 { 01630 JoinRec* jr = new JoinRec; 01631 if (e1OutIdx >= 0) 01632 jr->poly1Idx = e1OutIdx; else 01633 jr->poly1Idx = e1->outIdx; 01634 jr->pt1a = IntPoint(e1->xcurr, e1->ycurr); 01635 jr->pt1b = IntPoint(e1->xtop, e1->ytop); 01636 if (e2OutIdx >= 0) 01637 jr->poly2Idx = e2OutIdx; else 01638 jr->poly2Idx = e2->outIdx; 01639 jr->pt2a = IntPoint(e2->xcurr, e2->ycurr); 01640 jr->pt2b = IntPoint(e2->xtop, e2->ytop); 01641 m_Joins.push_back(jr); 01642 } 01643 //------------------------------------------------------------------------------ 01644 01645 void Clipper::ClearJoins() 01646 { 01647 for (JoinList::size_type i = 0; i < m_Joins.size(); i++) 01648 delete m_Joins[i]; 01649 m_Joins.resize(0); 01650 } 01651 //------------------------------------------------------------------------------ 01652 01653 void Clipper::AddHorzJoin(TEdge *e, int idx) 01654 { 01655 HorzJoinRec* hj = new HorzJoinRec; 01656 hj->edge = e; 01657 hj->savedIdx = idx; 01658 m_HorizJoins.push_back(hj); 01659 } 01660 //------------------------------------------------------------------------------ 01661 01662 void Clipper::ClearHorzJoins() 01663 { 01664 for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) 01665 delete m_HorizJoins[i]; 01666 m_HorizJoins.resize(0); 01667 } 01668 //------------------------------------------------------------------------------ 01669 01670 void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) 01671 { 01672 while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) 01673 { 01674 TEdge* lb = m_CurrentLM->leftBound; 01675 TEdge* rb = m_CurrentLM->rightBound; 01676 01677 InsertEdgeIntoAEL( lb ); 01678 InsertScanbeam( lb->ytop ); 01679 InsertEdgeIntoAEL( rb ); 01680 01681 if (IsEvenOddFillType(*lb)) 01682 { 01683 lb->windDelta = 1; 01684 rb->windDelta = 1; 01685 } 01686 else 01687 { 01688 rb->windDelta = -lb->windDelta; 01689 } 01690 SetWindingCount( *lb ); 01691 rb->windCnt = lb->windCnt; 01692 rb->windCnt2 = lb->windCnt2; 01693 01694 if( NEAR_EQUAL(rb->dx, HORIZONTAL) ) 01695 { 01696 //nb: only rightbounds can have a horizontal bottom edge 01697 AddEdgeToSEL( rb ); 01698 InsertScanbeam( rb->nextInLML->ytop ); 01699 } 01700 else 01701 InsertScanbeam( rb->ytop ); 01702 01703 if( IsContributing(*lb) ) 01704 AddLocalMinPoly( lb, rb, IntPoint(lb->xcurr, m_CurrentLM->Y) ); 01705 01706 //if any output polygons share an edge, they'll need joining later ... 01707 if (rb->outIdx >= 0) 01708 { 01709 if (NEAR_EQUAL(rb->dx, HORIZONTAL)) 01710 { 01711 for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) 01712 { 01713 IntPoint pt, pt2; //returned by GetOverlapSegment() but unused here. 01714 HorzJoinRec* hj = m_HorizJoins[i]; 01715 //if horizontals rb and hj.edge overlap, flag for joining later ... 01716 if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), 01717 IntPoint(hj->edge->xtop, hj->edge->ytop), 01718 IntPoint(rb->xbot, rb->ybot), 01719 IntPoint(rb->xtop, rb->ytop), pt, pt2)) 01720 AddJoin(hj->edge, rb, hj->savedIdx); 01721 } 01722 } 01723 } 01724 01725 if( lb->nextInAEL != rb ) 01726 { 01727 if (rb->outIdx >= 0 && rb->prevInAEL->outIdx >= 0 && 01728 SlopesEqual(*rb->prevInAEL, *rb, m_UseFullRange)) 01729 AddJoin(rb, rb->prevInAEL); 01730 01731 TEdge* e = lb->nextInAEL; 01732 IntPoint pt = IntPoint(lb->xcurr, lb->ycurr); 01733 while( e != rb ) 01734 { 01735 if(!e) throw clipperException("InsertLocalMinimaIntoAEL: missing rightbound!"); 01736 //nb: For calculating winding counts etc, IntersectEdges() assumes 01737 //that param1 will be to the right of param2 ABOVE the intersection ... 01738 IntersectEdges( rb , e , pt , ipNone); //order important here 01739 e = e->nextInAEL; 01740 } 01741 } 01742 PopLocalMinima(); 01743 } 01744 } 01745 //------------------------------------------------------------------------------ 01746 01747 void Clipper::DeleteFromAEL(TEdge *e) 01748 { 01749 TEdge* AelPrev = e->prevInAEL; 01750 TEdge* AelNext = e->nextInAEL; 01751 if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted 01752 if( AelPrev ) AelPrev->nextInAEL = AelNext; 01753 else m_ActiveEdges = AelNext; 01754 if( AelNext ) AelNext->prevInAEL = AelPrev; 01755 e->nextInAEL = 0; 01756 e->prevInAEL = 0; 01757 } 01758 //------------------------------------------------------------------------------ 01759 01760 void Clipper::DeleteFromSEL(TEdge *e) 01761 { 01762 TEdge* SelPrev = e->prevInSEL; 01763 TEdge* SelNext = e->nextInSEL; 01764 if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted 01765 if( SelPrev ) SelPrev->nextInSEL = SelNext; 01766 else m_SortedEdges = SelNext; 01767 if( SelNext ) SelNext->prevInSEL = SelPrev; 01768 e->nextInSEL = 0; 01769 e->prevInSEL = 0; 01770 } 01771 //------------------------------------------------------------------------------ 01772 01773 void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, 01774 const IntPoint &pt, IntersectProtects protects) 01775 { 01776 //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before 01777 //e2 in AEL except when e1 is being inserted at the intersection point ... 01778 bool e1stops = !(ipLeft & protects) && !e1->nextInLML && 01779 e1->xtop == pt.X && e1->ytop == pt.Y; 01780 bool e2stops = !(ipRight & protects) && !e2->nextInLML && 01781 e2->xtop == pt.X && e2->ytop == pt.Y; 01782 bool e1Contributing = ( e1->outIdx >= 0 ); 01783 bool e2contributing = ( e2->outIdx >= 0 ); 01784 01785 //update winding counts... 01786 //assumes that e1 will be to the right of e2 ABOVE the intersection 01787 if ( e1->polyType == e2->polyType ) 01788 { 01789 if ( IsEvenOddFillType( *e1) ) 01790 { 01791 int oldE1WindCnt = e1->windCnt; 01792 e1->windCnt = e2->windCnt; 01793 e2->windCnt = oldE1WindCnt; 01794 } else 01795 { 01796 if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt; 01797 else e1->windCnt += e2->windDelta; 01798 if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt; 01799 else e2->windCnt -= e1->windDelta; 01800 } 01801 } else 01802 { 01803 if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta; 01804 else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0; 01805 if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta; 01806 else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0; 01807 } 01808 01809 PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; 01810 if (e1->polyType == ptSubject) 01811 { 01812 e1FillType = m_SubjFillType; 01813 e1FillType2 = m_ClipFillType; 01814 } else 01815 { 01816 e1FillType = m_ClipFillType; 01817 e1FillType2 = m_SubjFillType; 01818 } 01819 if (e2->polyType == ptSubject) 01820 { 01821 e2FillType = m_SubjFillType; 01822 e2FillType2 = m_ClipFillType; 01823 } else 01824 { 01825 e2FillType = m_ClipFillType; 01826 e2FillType2 = m_SubjFillType; 01827 } 01828 01829 long64 e1Wc, e2Wc; 01830 switch (e1FillType) 01831 { 01832 case pftPositive: e1Wc = e1->windCnt; break; 01833 case pftNegative: e1Wc = -e1->windCnt; break; 01834 default: e1Wc = Abs(e1->windCnt); 01835 } 01836 switch(e2FillType) 01837 { 01838 case pftPositive: e2Wc = e2->windCnt; break; 01839 case pftNegative: e2Wc = -e2->windCnt; break; 01840 default: e2Wc = Abs(e2->windCnt); 01841 } 01842 01843 if ( e1Contributing && e2contributing ) 01844 { 01845 if ( e1stops || e2stops || 01846 (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || 01847 (e1->polyType != e2->polyType && m_ClipType != ctXor) ) 01848 AddLocalMaxPoly(e1, e2, pt); 01849 else 01850 DoBothEdges( e1, e2, pt ); 01851 } 01852 else if ( e1Contributing ) 01853 { 01854 if ((e2Wc == 0 || e2Wc == 1) && 01855 (m_ClipType != ctIntersection || 01856 e2->polyType == ptSubject || (e2->windCnt2 != 0))) 01857 DoEdge1(e1, e2, pt); 01858 } 01859 else if ( e2contributing ) 01860 { 01861 if ((e1Wc == 0 || e1Wc == 1) && 01862 (m_ClipType != ctIntersection || 01863 e1->polyType == ptSubject || (e1->windCnt2 != 0))) 01864 DoEdge2(e1, e2, pt); 01865 } 01866 else if ( (e1Wc == 0 || e1Wc == 1) && 01867 (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) 01868 { 01869 //neither edge is currently contributing ... 01870 01871 long64 e1Wc2, e2Wc2; 01872 switch (e1FillType2) 01873 { 01874 case pftPositive: e1Wc2 = e1->windCnt2; break; 01875 case pftNegative : e1Wc2 = -e1->windCnt2; break; 01876 default: e1Wc2 = Abs(e1->windCnt2); 01877 } 01878 switch (e2FillType2) 01879 { 01880 case pftPositive: e2Wc2 = e2->windCnt2; break; 01881 case pftNegative: e2Wc2 = -e2->windCnt2; break; 01882 default: e2Wc2 = Abs(e2->windCnt2); 01883 } 01884 01885 if (e1->polyType != e2->polyType) 01886 AddLocalMinPoly(e1, e2, pt); 01887 else if (e1Wc == 1 && e2Wc == 1) 01888 switch( m_ClipType ) { 01889 case ctIntersection: 01890 if (e1Wc2 > 0 && e2Wc2 > 0) 01891 AddLocalMinPoly(e1, e2, pt); 01892 break; 01893 case ctUnion: 01894 if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) 01895 AddLocalMinPoly(e1, e2, pt); 01896 break; 01897 case ctDifference: 01898 if (((e1->polyType == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || 01899 ((e1->polyType == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) 01900 AddLocalMinPoly(e1, e2, pt); 01901 break; 01902 case ctXor: 01903 AddLocalMinPoly(e1, e2, pt); 01904 } 01905 else 01906 SwapSides( *e1, *e2 ); 01907 } 01908 01909 if( (e1stops != e2stops) && 01910 ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) ) 01911 { 01912 SwapSides( *e1, *e2 ); 01913 SwapPolyIndexes( *e1, *e2 ); 01914 } 01915 01916 //finally, delete any non-contributing maxima edges ... 01917 if( e1stops ) DeleteFromAEL( e1 ); 01918 if( e2stops ) DeleteFromAEL( e2 ); 01919 } 01920 //------------------------------------------------------------------------------ 01921 01922 void Clipper::SetHoleState(TEdge *e, OutRec *outRec) 01923 { 01924 bool isHole = false; 01925 TEdge *e2 = e->prevInAEL; 01926 while (e2) 01927 { 01928 if (e2->outIdx >= 0) 01929 { 01930 isHole = !isHole; 01931 if (! outRec->FirstLeft) 01932 outRec->FirstLeft = m_PolyOuts[e2->outIdx]; 01933 } 01934 e2 = e2->prevInAEL; 01935 } 01936 if (isHole) outRec->isHole = true; 01937 } 01938 //------------------------------------------------------------------------------ 01939 01940 OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) 01941 { 01942 //work out which polygon fragment has the correct hole state ... 01943 OutPt *outPt1 = outRec1->bottomPt; 01944 OutPt *outPt2 = outRec2->bottomPt; 01945 if (outPt1->pt.Y > outPt2->pt.Y) return outRec1; 01946 else if (outPt1->pt.Y < outPt2->pt.Y) return outRec2; 01947 else if (outPt1->pt.X < outPt2->pt.X) return outRec1; 01948 else if (outPt1->pt.X > outPt2->pt.X) return outRec2; 01949 else if (outPt1->next == outPt1) return outRec2; 01950 else if (outPt2->next == outPt2) return outRec1; 01951 else if (FirstIsBottomPt(outPt1, outPt2)) return outRec1; 01952 else return outRec2; 01953 } 01954 //------------------------------------------------------------------------------ 01955 01956 bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) 01957 { 01958 do 01959 { 01960 outRec1 = outRec1->FirstLeft; 01961 if (outRec1 == outRec2) return true; 01962 } while (outRec1); 01963 return false; 01964 } 01965 //------------------------------------------------------------------------------ 01966 01967 void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) 01968 { 01969 //get the start and ends of both output polygons ... 01970 OutRec *outRec1 = m_PolyOuts[e1->outIdx]; 01971 OutRec *outRec2 = m_PolyOuts[e2->outIdx]; 01972 01973 OutRec *holeStateRec; 01974 if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; 01975 else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; 01976 else holeStateRec = GetLowermostRec(outRec1, outRec2); 01977 01978 OutPt* p1_lft = outRec1->pts; 01979 OutPt* p1_rt = p1_lft->prev; 01980 OutPt* p2_lft = outRec2->pts; 01981 OutPt* p2_rt = p2_lft->prev; 01982 01983 EdgeSide side; 01984 //join e2 poly onto e1 poly and delete pointers to e2 ... 01985 if( e1->side == esLeft ) 01986 { 01987 if( e2->side == esLeft ) 01988 { 01989 //z y x a b c 01990 ReversePolyPtLinks(*p2_lft); 01991 p2_lft->next = p1_lft; 01992 p1_lft->prev = p2_lft; 01993 p1_rt->next = p2_rt; 01994 p2_rt->prev = p1_rt; 01995 outRec1->pts = p2_rt; 01996 } else 01997 { 01998 //x y z a b c 01999 p2_rt->next = p1_lft; 02000 p1_lft->prev = p2_rt; 02001 p2_lft->prev = p1_rt; 02002 p1_rt->next = p2_lft; 02003 outRec1->pts = p2_lft; 02004 } 02005 side = esLeft; 02006 } else 02007 { 02008 if( e2->side == esRight ) 02009 { 02010 //a b c z y x 02011 ReversePolyPtLinks( *p2_lft ); 02012 p1_rt->next = p2_rt; 02013 p2_rt->prev = p1_rt; 02014 p2_lft->next = p1_lft; 02015 p1_lft->prev = p2_lft; 02016 } else 02017 { 02018 //a b c x y z 02019 p1_rt->next = p2_lft; 02020 p2_lft->prev = p1_rt; 02021 p1_lft->prev = p2_rt; 02022 p2_rt->next = p1_lft; 02023 } 02024 side = esRight; 02025 } 02026 02027 if (holeStateRec == outRec2) 02028 { 02029 outRec1->bottomPt = outRec2->bottomPt; 02030 outRec1->bottomPt->idx = outRec1->idx; 02031 if (outRec2->FirstLeft != outRec1) 02032 outRec1->FirstLeft = outRec2->FirstLeft; 02033 outRec1->isHole = outRec2->isHole; 02034 } 02035 outRec2->pts = 0; 02036 outRec2->bottomPt = 0; 02037 outRec2->AppendLink = outRec1; 02038 int OKIdx = e1->outIdx; 02039 int ObsoleteIdx = e2->outIdx; 02040 02041 e1->outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly 02042 e2->outIdx = -1; 02043 02044 TEdge* e = m_ActiveEdges; 02045 while( e ) 02046 { 02047 if( e->outIdx == ObsoleteIdx ) 02048 { 02049 e->outIdx = OKIdx; 02050 e->side = side; 02051 break; 02052 } 02053 e = e->nextInAEL; 02054 } 02055 02056 for (JoinList::size_type i = 0; i < m_Joins.size(); ++i) 02057 { 02058 if (m_Joins[i]->poly1Idx == ObsoleteIdx) m_Joins[i]->poly1Idx = OKIdx; 02059 if (m_Joins[i]->poly2Idx == ObsoleteIdx) m_Joins[i]->poly2Idx = OKIdx; 02060 } 02061 02062 for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) 02063 { 02064 if (m_HorizJoins[i]->savedIdx == ObsoleteIdx) 02065 m_HorizJoins[i]->savedIdx = OKIdx; 02066 } 02067 02068 } 02069 //------------------------------------------------------------------------------ 02070 02071 OutRec* Clipper::CreateOutRec() 02072 { 02073 OutRec* result = new OutRec; 02074 result->isHole = false; 02075 result->FirstLeft = 0; 02076 result->AppendLink = 0; 02077 result->pts = 0; 02078 result->bottomPt = 0; 02079 result->sides = esNeither; 02080 result->bottomFlag = 0; 02081 02082 return result; 02083 } 02084 //------------------------------------------------------------------------------ 02085 02086 void Clipper::DisposeBottomPt(OutRec &outRec) 02087 { 02088 OutPt* next = outRec.bottomPt->next; 02089 OutPt* prev = outRec.bottomPt->prev; 02090 if (outRec.pts == outRec.bottomPt) outRec.pts = next; 02091 delete outRec.bottomPt; 02092 next->prev = prev; 02093 prev->next = next; 02094 outRec.bottomPt = next; 02095 FixupOutPolygon(outRec); 02096 } 02097 //------------------------------------------------------------------------------ 02098 02099 void Clipper::AddOutPt(TEdge *e, const IntPoint &pt) 02100 { 02101 bool ToFront = (e->side == esLeft); 02102 if( e->outIdx < 0 ) 02103 { 02104 OutRec *outRec = CreateOutRec(); 02105 m_PolyOuts.push_back(outRec); 02106 outRec->idx = (int)m_PolyOuts.size()-1; 02107 e->outIdx = outRec->idx; 02108 OutPt* op = new OutPt; 02109 outRec->pts = op; 02110 outRec->bottomPt = op; 02111 op->pt = pt; 02112 op->idx = outRec->idx; 02113 op->next = op; 02114 op->prev = op; 02115 SetHoleState(e, outRec); 02116 } else 02117 { 02118 OutRec *outRec = m_PolyOuts[e->outIdx]; 02119 OutPt* op = outRec->pts; 02120 if ((ToFront && PointsEqual(pt, op->pt)) || 02121 (!ToFront && PointsEqual(pt, op->prev->pt))) return; 02122 02123 if ((e->side | outRec->sides) != outRec->sides) 02124 { 02125 //check for 'rounding' artefacts ... 02126 if (outRec->sides == esNeither && pt.Y == op->pt.Y) 02127 if (ToFront) 02128 { 02129 if (pt.X == op->pt.X +1) return; //ie wrong side of bottomPt 02130 } 02131 else if (pt.X == op->pt.X -1) return; //ie wrong side of bottomPt 02132 02133 outRec->sides = (EdgeSide)(outRec->sides | e->side); 02134 if (outRec->sides == esBoth) 02135 { 02136 //A vertex from each side has now been added. 02137 //Vertices of one side of an output polygon are quite commonly close to 02138 //or even 'touching' edges of the other side of the output polygon. 02139 //Very occasionally vertices from one side can 'cross' an edge on the 02140 //the other side. The distance 'crossed' is always less that a unit 02141 //and is purely an artefact of coordinate rounding. Nevertheless, this 02142 //results in very tiny self-intersections. Because of the way 02143 //orientation is calculated, even tiny self-intersections can cause 02144 //the Orientation function to return the wrong result. Therefore, it's 02145 //important to ensure that any self-intersections close to BottomPt are 02146 //detected and removed before orientation is assigned. 02147 02148 OutPt *opBot, *op2; 02149 if (ToFront) 02150 { 02151 opBot = outRec->pts; 02152 op2 = opBot->next; //op2 == right side 02153 if (opBot->pt.Y != op2->pt.Y && opBot->pt.Y != pt.Y && 02154 ((opBot->pt.X - pt.X)/(opBot->pt.Y - pt.Y) < 02155 (opBot->pt.X - op2->pt.X)/(opBot->pt.Y - op2->pt.Y))) 02156 outRec->bottomFlag = opBot; 02157 } else 02158 { 02159 opBot = outRec->pts->prev; 02160 op2 = opBot->prev; //op2 == left side 02161 if (opBot->pt.Y != op2->pt.Y && opBot->pt.Y != pt.Y && 02162 ((opBot->pt.X - pt.X)/(opBot->pt.Y - pt.Y) > 02163 (opBot->pt.X - op2->pt.X)/(opBot->pt.Y - op2->pt.Y))) 02164 outRec->bottomFlag = opBot; 02165 } 02166 } 02167 } 02168 02169 OutPt* op2 = new OutPt; 02170 op2->pt = pt; 02171 op2->idx = outRec->idx; 02172 if (op2->pt.Y == outRec->bottomPt->pt.Y && 02173 op2->pt.X < outRec->bottomPt->pt.X) 02174 outRec->bottomPt = op2; 02175 op2->next = op; 02176 op2->prev = op->prev; 02177 op2->prev->next = op2; 02178 op->prev = op2; 02179 if (ToFront) outRec->pts = op2; 02180 } 02181 } 02182 //------------------------------------------------------------------------------ 02183 02184 void Clipper::ProcessHorizontals() 02185 { 02186 TEdge* horzEdge = m_SortedEdges; 02187 while( horzEdge ) 02188 { 02189 DeleteFromSEL( horzEdge ); 02190 ProcessHorizontal( horzEdge ); 02191 horzEdge = m_SortedEdges; 02192 } 02193 } 02194 //------------------------------------------------------------------------------ 02195 02196 bool Clipper::IsTopHorz(const long64 XPos) 02197 { 02198 TEdge* e = m_SortedEdges; 02199 while( e ) 02200 { 02201 if( ( XPos >= std::min(e->xcurr, e->xtop) ) && 02202 ( XPos <= std::max(e->xcurr, e->xtop) ) ) return false; 02203 e = e->nextInSEL; 02204 } 02205 return true; 02206 } 02207 //------------------------------------------------------------------------------ 02208 02209 bool IsMinima(TEdge *e) 02210 { 02211 return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e); 02212 } 02213 //------------------------------------------------------------------------------ 02214 02215 bool IsMaxima(TEdge *e, const long64 Y) 02216 { 02217 return e && e->ytop == Y && !e->nextInLML; 02218 } 02219 //------------------------------------------------------------------------------ 02220 02221 bool IsIntermediate(TEdge *e, const long64 Y) 02222 { 02223 return e->ytop == Y && e->nextInLML; 02224 } 02225 //------------------------------------------------------------------------------ 02226 02227 TEdge *GetMaximaPair(TEdge *e) 02228 { 02229 if( !IsMaxima(e->next, e->ytop) || e->next->xtop != e->xtop ) 02230 return e->prev; else 02231 return e->next; 02232 } 02233 //------------------------------------------------------------------------------ 02234 02235 void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2) 02236 { 02237 if( !edge1->nextInAEL && !edge1->prevInAEL ) return; 02238 if( !edge2->nextInAEL && !edge2->prevInAEL ) return; 02239 02240 if( edge1->nextInAEL == edge2 ) 02241 { 02242 TEdge* next = edge2->nextInAEL; 02243 if( next ) next->prevInAEL = edge1; 02244 TEdge* prev = edge1->prevInAEL; 02245 if( prev ) prev->nextInAEL = edge2; 02246 edge2->prevInAEL = prev; 02247 edge2->nextInAEL = edge1; 02248 edge1->prevInAEL = edge2; 02249 edge1->nextInAEL = next; 02250 } 02251 else if( edge2->nextInAEL == edge1 ) 02252 { 02253 TEdge* next = edge1->nextInAEL; 02254 if( next ) next->prevInAEL = edge2; 02255 TEdge* prev = edge2->prevInAEL; 02256 if( prev ) prev->nextInAEL = edge1; 02257 edge1->prevInAEL = prev; 02258 edge1->nextInAEL = edge2; 02259 edge2->prevInAEL = edge1; 02260 edge2->nextInAEL = next; 02261 } 02262 else 02263 { 02264 TEdge* next = edge1->nextInAEL; 02265 TEdge* prev = edge1->prevInAEL; 02266 edge1->nextInAEL = edge2->nextInAEL; 02267 if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1; 02268 edge1->prevInAEL = edge2->prevInAEL; 02269 if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1; 02270 edge2->nextInAEL = next; 02271 if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2; 02272 edge2->prevInAEL = prev; 02273 if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2; 02274 } 02275 02276 if( !edge1->prevInAEL ) m_ActiveEdges = edge1; 02277 else if( !edge2->prevInAEL ) m_ActiveEdges = edge2; 02278 } 02279 //------------------------------------------------------------------------------ 02280 02281 void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2) 02282 { 02283 if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return; 02284 if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return; 02285 02286 if( edge1->nextInSEL == edge2 ) 02287 { 02288 TEdge* next = edge2->nextInSEL; 02289 if( next ) next->prevInSEL = edge1; 02290 TEdge* prev = edge1->prevInSEL; 02291 if( prev ) prev->nextInSEL = edge2; 02292 edge2->prevInSEL = prev; 02293 edge2->nextInSEL = edge1; 02294 edge1->prevInSEL = edge2; 02295 edge1->nextInSEL = next; 02296 } 02297 else if( edge2->nextInSEL == edge1 ) 02298 { 02299 TEdge* next = edge1->nextInSEL; 02300 if( next ) next->prevInSEL = edge2; 02301 TEdge* prev = edge2->prevInSEL; 02302 if( prev ) prev->nextInSEL = edge1; 02303 edge1->prevInSEL = prev; 02304 edge1->nextInSEL = edge2; 02305 edge2->prevInSEL = edge1; 02306 edge2->nextInSEL = next; 02307 } 02308 else 02309 { 02310 TEdge* next = edge1->nextInSEL; 02311 TEdge* prev = edge1->prevInSEL; 02312 edge1->nextInSEL = edge2->nextInSEL; 02313 if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1; 02314 edge1->prevInSEL = edge2->prevInSEL; 02315 if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1; 02316 edge2->nextInSEL = next; 02317 if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2; 02318 edge2->prevInSEL = prev; 02319 if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2; 02320 } 02321 02322 if( !edge1->prevInSEL ) m_SortedEdges = edge1; 02323 else if( !edge2->prevInSEL ) m_SortedEdges = edge2; 02324 } 02325 //------------------------------------------------------------------------------ 02326 02327 TEdge* GetNextInAEL(TEdge *e, Direction dir) 02328 { 02329 return dir == dLeftToRight ? e->nextInAEL : e->prevInAEL; 02330 } 02331 //------------------------------------------------------------------------------ 02332 02333 void Clipper::ProcessHorizontal(TEdge *horzEdge) 02334 { 02335 Direction dir; 02336 long64 horzLeft, horzRight; 02337 02338 if( horzEdge->xcurr < horzEdge->xtop ) 02339 { 02340 horzLeft = horzEdge->xcurr; 02341 horzRight = horzEdge->xtop; 02342 dir = dLeftToRight; 02343 } else 02344 { 02345 horzLeft = horzEdge->xtop; 02346 horzRight = horzEdge->xcurr; 02347 dir = dRightToLeft; 02348 } 02349 02350 TEdge* eMaxPair; 02351 if( horzEdge->nextInLML ) eMaxPair = 0; 02352 else eMaxPair = GetMaximaPair(horzEdge); 02353 02354 TEdge* e = GetNextInAEL( horzEdge , dir ); 02355 while( e ) 02356 { 02357 TEdge* eNext = GetNextInAEL( e, dir ); 02358 02359 if (eMaxPair || 02360 ((dir == dLeftToRight) && (e->xcurr <= horzRight)) || 02361 ((dir == dRightToLeft) && (e->xcurr >= horzLeft))) 02362 { 02363 //ok, so far it looks like we're still in range of the horizontal edge 02364 if ( e->xcurr == horzEdge->xtop && !eMaxPair ) 02365 { 02366 if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange)) 02367 { 02368 //if output polygons share an edge, they'll need joining later ... 02369 if (horzEdge->outIdx >= 0 && e->outIdx >= 0) 02370 AddJoin(horzEdge->nextInLML, e, horzEdge->outIdx); 02371 break; //we've reached the end of the horizontal line 02372 } 02373 else if (e->dx < horzEdge->nextInLML->dx) 02374 //we really have got to the end of the intermediate horz edge so quit. 02375 //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal. 02376 break; 02377 } 02378 02379 if( e == eMaxPair ) 02380 { 02381 //horzEdge is evidently a maxima horizontal and we've arrived at its end. 02382 if (dir == dLeftToRight) 02383 IntersectEdges(horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); 02384 else 02385 IntersectEdges(e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); 02386 if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); 02387 return; 02388 } 02389 else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) ) 02390 { 02391 //An overlapping horizontal edge. Overlapping horizontal edges are 02392 //processed as if layered with the current horizontal edge (horizEdge) 02393 //being infinitesimally lower that the next (e). Therfore, we 02394 //intersect with e only if e.xcurr is within the bounds of horzEdge ... 02395 if( dir == dLeftToRight ) 02396 IntersectEdges( horzEdge , e, IntPoint(e->xcurr, horzEdge->ycurr), 02397 (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); 02398 else 02399 IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), 02400 (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); 02401 } 02402 else if( dir == dLeftToRight ) 02403 { 02404 IntersectEdges( horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), 02405 (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); 02406 } 02407 else 02408 { 02409 IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), 02410 (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); 02411 } 02412 SwapPositionsInAEL( horzEdge, e ); 02413 } 02414 else if( (dir == dLeftToRight && e->xcurr > horzRight && m_SortedEdges) || 02415 (dir == dRightToLeft && e->xcurr < horzLeft && m_SortedEdges) ) break; 02416 e = eNext; 02417 } //end while 02418 02419 if( horzEdge->nextInLML ) 02420 { 02421 if( horzEdge->outIdx >= 0 ) 02422 AddOutPt( horzEdge, IntPoint(horzEdge->xtop, horzEdge->ytop)); 02423 UpdateEdgeIntoAEL( horzEdge ); 02424 } 02425 else 02426 { 02427 if ( horzEdge->outIdx >= 0 ) 02428 IntersectEdges( horzEdge, eMaxPair, 02429 IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth); 02430 if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); 02431 DeleteFromAEL(eMaxPair); 02432 DeleteFromAEL(horzEdge); 02433 } 02434 } 02435 //------------------------------------------------------------------------------ 02436 02437 void Clipper::UpdateEdgeIntoAEL(TEdge *&e) 02438 { 02439 if( !e->nextInLML ) throw 02440 clipperException("UpdateEdgeIntoAEL: invalid call"); 02441 TEdge* AelPrev = e->prevInAEL; 02442 TEdge* AelNext = e->nextInAEL; 02443 e->nextInLML->outIdx = e->outIdx; 02444 if( AelPrev ) AelPrev->nextInAEL = e->nextInLML; 02445 else m_ActiveEdges = e->nextInLML; 02446 if( AelNext ) AelNext->prevInAEL = e->nextInLML; 02447 e->nextInLML->side = e->side; 02448 e->nextInLML->windDelta = e->windDelta; 02449 e->nextInLML->windCnt = e->windCnt; 02450 e->nextInLML->windCnt2 = e->windCnt2; 02451 e = e->nextInLML; 02452 e->prevInAEL = AelPrev; 02453 e->nextInAEL = AelNext; 02454 if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop ); 02455 } 02456 //------------------------------------------------------------------------------ 02457 02458 bool Clipper::ProcessIntersections(const long64 botY, const long64 topY) 02459 { 02460 if( !m_ActiveEdges ) return true; 02461 try { 02462 BuildIntersectList(botY, topY); 02463 if ( !m_IntersectNodes) return true; 02464 if ( FixupIntersections() ) ProcessIntersectList(); 02465 else return false; 02466 } 02467 catch(...) { 02468 m_SortedEdges = 0; 02469 DisposeIntersectNodes(); 02470 throw clipperException("ProcessIntersections error"); 02471 } 02472 return true; 02473 } 02474 //------------------------------------------------------------------------------ 02475 02476 void Clipper::DisposeIntersectNodes() 02477 { 02478 while ( m_IntersectNodes ) 02479 { 02480 IntersectNode* iNode = m_IntersectNodes->next; 02481 delete m_IntersectNodes; 02482 m_IntersectNodes = iNode; 02483 } 02484 } 02485 //------------------------------------------------------------------------------ 02486 02487 void Clipper::BuildIntersectList(const long64 botY, const long64 topY) 02488 { 02489 if ( !m_ActiveEdges ) return; 02490 02491 //prepare for sorting ... 02492 TEdge* e = m_ActiveEdges; 02493 e->tmpX = TopX( *e, topY ); 02494 m_SortedEdges = e; 02495 m_SortedEdges->prevInSEL = 0; 02496 e = e->nextInAEL; 02497 while( e ) 02498 { 02499 e->prevInSEL = e->prevInAEL; 02500 e->prevInSEL->nextInSEL = e; 02501 e->nextInSEL = 0; 02502 e->tmpX = TopX( *e, topY ); 02503 e = e->nextInAEL; 02504 } 02505 02506 //bubblesort ... 02507 bool isModified = true; 02508 while( isModified && m_SortedEdges ) 02509 { 02510 isModified = false; 02511 e = m_SortedEdges; 02512 while( e->nextInSEL ) 02513 { 02514 TEdge *eNext = e->nextInSEL; 02515 IntPoint pt; 02516 if(e->tmpX > eNext->tmpX && 02517 IntersectPoint(*e, *eNext, pt, m_UseFullRange)) 02518 { 02519 if (pt.Y > botY) 02520 { 02521 pt.Y = botY; 02522 pt.X = TopX(*e, pt.Y); 02523 } 02524 AddIntersectNode( e, eNext, pt ); 02525 SwapPositionsInSEL(e, eNext); 02526 isModified = true; 02527 } 02528 else 02529 e = eNext; 02530 } 02531 if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; 02532 else break; 02533 } 02534 m_SortedEdges = 0; 02535 } 02536 //------------------------------------------------------------------------------ 02537 02538 bool ProcessParam1BeforeParam2(IntersectNode &node1, IntersectNode &node2) 02539 { 02540 bool result; 02541 if (node1.pt.Y == node2.pt.Y) 02542 { 02543 if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) 02544 { 02545 result = node2.pt.X > node1.pt.X; 02546 return node2.edge1->dx > 0 ? !result : result; 02547 } 02548 else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) 02549 { 02550 result = node2.pt.X > node1.pt.X; 02551 return node2.edge2->dx > 0 ? !result : result; 02552 } 02553 else return node2.pt.X > node1.pt.X; 02554 } 02555 else return node1.pt.Y > node2.pt.Y; 02556 } 02557 //------------------------------------------------------------------------------ 02558 02559 void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) 02560 { 02561 IntersectNode* newNode = new IntersectNode; 02562 newNode->edge1 = e1; 02563 newNode->edge2 = e2; 02564 newNode->pt = pt; 02565 newNode->next = 0; 02566 if( !m_IntersectNodes ) m_IntersectNodes = newNode; 02567 else if( ProcessParam1BeforeParam2(*newNode, *m_IntersectNodes) ) 02568 { 02569 newNode->next = m_IntersectNodes; 02570 m_IntersectNodes = newNode; 02571 } 02572 else 02573 { 02574 IntersectNode* iNode = m_IntersectNodes; 02575 while( iNode->next && ProcessParam1BeforeParam2(*iNode->next, *newNode) ) 02576 iNode = iNode->next; 02577 newNode->next = iNode->next; 02578 iNode->next = newNode; 02579 } 02580 } 02581 //------------------------------------------------------------------------------ 02582 02583 void Clipper::ProcessIntersectList() 02584 { 02585 while( m_IntersectNodes ) 02586 { 02587 IntersectNode* iNode = m_IntersectNodes->next; 02588 { 02589 IntersectEdges( m_IntersectNodes->edge1 , 02590 m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth ); 02591 SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 ); 02592 } 02593 delete m_IntersectNodes; 02594 m_IntersectNodes = iNode; 02595 } 02596 } 02597 //------------------------------------------------------------------------------ 02598 02599 void Clipper::DoMaxima(TEdge *e, long64 topY) 02600 { 02601 TEdge* eMaxPair = GetMaximaPair(e); 02602 long64 X = e->xtop; 02603 TEdge* eNext = e->nextInAEL; 02604 while( eNext != eMaxPair ) 02605 { 02606 if (!eNext) throw clipperException("DoMaxima error"); 02607 IntersectEdges( e, eNext, IntPoint(X, topY), ipBoth ); 02608 eNext = eNext->nextInAEL; 02609 } 02610 if( e->outIdx < 0 && eMaxPair->outIdx < 0 ) 02611 { 02612 DeleteFromAEL( e ); 02613 DeleteFromAEL( eMaxPair ); 02614 } 02615 else if( e->outIdx >= 0 && eMaxPair->outIdx >= 0 ) 02616 { 02617 IntersectEdges( e, eMaxPair, IntPoint(X, topY), ipNone ); 02618 } 02619 else throw clipperException("DoMaxima error"); 02620 } 02621 //------------------------------------------------------------------------------ 02622 02623 void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) 02624 { 02625 TEdge* e = m_ActiveEdges; 02626 while( e ) 02627 { 02628 //1. process maxima, treating them as if they're 'bent' horizontal edges, 02629 // but exclude maxima with horizontal edges. nb: e can't be a horizontal. 02630 if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) ) 02631 { 02632 //'e' might be removed from AEL, as may any following edges so ... 02633 TEdge* ePrior = e->prevInAEL; 02634 DoMaxima(e, topY); 02635 if( !ePrior ) e = m_ActiveEdges; 02636 else e = ePrior->nextInAEL; 02637 } 02638 else 02639 { 02640 //2. promote horizontal edges, otherwise update xcurr and ycurr ... 02641 if( IsIntermediate(e, topY) && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) ) 02642 { 02643 if (e->outIdx >= 0) 02644 { 02645 AddOutPt(e, IntPoint(e->xtop, e->ytop)); 02646 02647 for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) 02648 { 02649 IntPoint pt, pt2; 02650 HorzJoinRec* hj = m_HorizJoins[i]; 02651 if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), 02652 IntPoint(hj->edge->xtop, hj->edge->ytop), 02653 IntPoint(e->nextInLML->xbot, e->nextInLML->ybot), 02654 IntPoint(e->nextInLML->xtop, e->nextInLML->ytop), pt, pt2)) 02655 AddJoin(hj->edge, e->nextInLML, hj->savedIdx, e->outIdx); 02656 } 02657 02658 AddHorzJoin(e->nextInLML, e->outIdx); 02659 } 02660 UpdateEdgeIntoAEL(e); 02661 AddEdgeToSEL(e); 02662 } else 02663 { 02664 //this just simplifies horizontal processing ... 02665 e->xcurr = TopX( *e, topY ); 02666 e->ycurr = topY; 02667 } 02668 e = e->nextInAEL; 02669 } 02670 } 02671 02672 //3. Process horizontals at the top of the scanbeam ... 02673 ProcessHorizontals(); 02674 02675 //4. Promote intermediate vertices ... 02676 e = m_ActiveEdges; 02677 while( e ) 02678 { 02679 if( IsIntermediate( e, topY ) ) 02680 { 02681 if( e->outIdx >= 0 ) AddOutPt(e, IntPoint(e->xtop,e->ytop)); 02682 UpdateEdgeIntoAEL(e); 02683 02684 //if output polygons share an edge, they'll need joining later ... 02685 if (e->outIdx >= 0 && e->prevInAEL && e->prevInAEL->outIdx >= 0 && 02686 e->prevInAEL->xcurr == e->xbot && e->prevInAEL->ycurr == e->ybot && 02687 SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), 02688 IntPoint(e->xbot,e->ybot), 02689 IntPoint(e->prevInAEL->xtop, e->prevInAEL->ytop), m_UseFullRange)) 02690 { 02691 AddOutPt(e->prevInAEL, IntPoint(e->xbot, e->ybot)); 02692 AddJoin(e, e->prevInAEL); 02693 } 02694 else if (e->outIdx >= 0 && e->nextInAEL && e->nextInAEL->outIdx >= 0 && 02695 e->nextInAEL->ycurr > e->nextInAEL->ytop && 02696 e->nextInAEL->ycurr <= e->nextInAEL->ybot && 02697 e->nextInAEL->xcurr == e->xbot && e->nextInAEL->ycurr == e->ybot && 02698 SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), 02699 IntPoint(e->xbot,e->ybot), 02700 IntPoint(e->nextInAEL->xtop, e->nextInAEL->ytop), m_UseFullRange)) 02701 { 02702 AddOutPt(e->nextInAEL, IntPoint(e->xbot, e->ybot)); 02703 AddJoin(e, e->nextInAEL); 02704 } 02705 } 02706 e = e->nextInAEL; 02707 } 02708 } 02709 //------------------------------------------------------------------------------ 02710 02711 void Clipper::FixupOutPolygon(OutRec &outRec) 02712 { 02713 //FixupOutPolygon() - removes duplicate points and simplifies consecutive 02714 //parallel edges by removing the middle vertex. 02715 OutPt *lastOK = 0; 02716 outRec.pts = outRec.bottomPt; 02717 OutPt *pp = outRec.bottomPt; 02718 02719 for (;;) 02720 { 02721 if (pp->prev == pp || pp->prev == pp->next ) 02722 { 02723 DisposeOutPts(pp); 02724 outRec.pts = 0; 02725 outRec.bottomPt = 0; 02726 return; 02727 } 02728 //test for duplicate points and for same slope (cross-product) ... 02729 if ( PointsEqual(pp->pt, pp->next->pt) || 02730 SlopesEqual(pp->prev->pt, pp->pt, pp->next->pt, m_UseFullRange) ) 02731 { 02732 lastOK = 0; 02733 OutPt *tmp = pp; 02734 if (pp == outRec.bottomPt) 02735 outRec.bottomPt = 0; //flags need for updating 02736 pp->prev->next = pp->next; 02737 pp->next->prev = pp->prev; 02738 pp = pp->prev; 02739 delete tmp; 02740 } 02741 else if (pp == lastOK) break; 02742 else 02743 { 02744 if (!lastOK) lastOK = pp; 02745 pp = pp->next; 02746 } 02747 } 02748 if (!outRec.bottomPt) { 02749 outRec.bottomPt = GetBottomPt(pp); 02750 outRec.bottomPt->idx = outRec.idx; 02751 outRec.pts = outRec.bottomPt; 02752 } 02753 } 02754 //------------------------------------------------------------------------------ 02755 02756 void Clipper::BuildResult(Polygons &polys) 02757 { 02758 int k = 0; 02759 polys.resize(m_PolyOuts.size()); 02760 for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) 02761 { 02762 if (m_PolyOuts[i]->pts) 02763 { 02764 Polygon* pg = &polys[k]; 02765 pg->clear(); 02766 OutPt* p = m_PolyOuts[i]->pts; 02767 do 02768 { 02769 pg->push_back(p->pt); 02770 p = p->next; 02771 } while (p != m_PolyOuts[i]->pts); 02772 //make sure each polygon has at least 3 vertices ... 02773 if (pg->size() < 3) pg->clear(); else k++; 02774 } 02775 } 02776 polys.resize(k); 02777 } 02778 //------------------------------------------------------------------------------ 02779 02780 void Clipper::BuildResultEx(ExPolygons &polys) 02781 { 02782 PolyOutList::size_type i = 0; 02783 int k = 0; 02784 polys.resize(0); 02785 polys.reserve(m_PolyOuts.size()); 02786 while (i < m_PolyOuts.size() && m_PolyOuts[i]->pts) 02787 { 02788 ExPolygon epg; 02789 OutPt* p = m_PolyOuts[i]->pts; 02790 do { 02791 epg.outer.push_back(p->pt); 02792 p = p->next; 02793 } while (p != m_PolyOuts[i]->pts); 02794 i++; 02795 //make sure polygons have at least 3 vertices ... 02796 if (epg.outer.size() < 3) continue; 02797 while (i < m_PolyOuts.size() 02798 && m_PolyOuts[i]->pts && m_PolyOuts[i]->isHole) 02799 { 02800 Polygon pg; 02801 p = m_PolyOuts[i]->pts; 02802 do { 02803 pg.push_back(p->pt); 02804 p = p->next; 02805 } while (p != m_PolyOuts[i]->pts); 02806 epg.holes.push_back(pg); 02807 i++; 02808 } 02809 polys.push_back(epg); 02810 k++; 02811 } 02812 polys.resize(k); 02813 } 02814 //------------------------------------------------------------------------------ 02815 02816 void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) 02817 { 02818 TEdge *e1 = int1.edge1; 02819 TEdge *e2 = int1.edge2; 02820 IntPoint p = int1.pt; 02821 02822 int1.edge1 = int2.edge1; 02823 int1.edge2 = int2.edge2; 02824 int1.pt = int2.pt; 02825 02826 int2.edge1 = e1; 02827 int2.edge2 = e2; 02828 int2.pt = p; 02829 } 02830 //------------------------------------------------------------------------------ 02831 02832 bool Clipper::FixupIntersections() 02833 { 02834 if ( !m_IntersectNodes->next ) return true; 02835 02836 CopyAELToSEL(); 02837 IntersectNode *int1 = m_IntersectNodes; 02838 IntersectNode *int2 = m_IntersectNodes->next; 02839 while (int2) 02840 { 02841 TEdge *e1 = int1->edge1; 02842 TEdge *e2; 02843 if (e1->prevInSEL == int1->edge2) e2 = e1->prevInSEL; 02844 else if (e1->nextInSEL == int1->edge2) e2 = e1->nextInSEL; 02845 else 02846 { 02847 //The current intersection is out of order, so try and swap it with 02848 //a subsequent intersection ... 02849 while (int2) 02850 { 02851 if (int2->edge1->nextInSEL == int2->edge2 || 02852 int2->edge1->prevInSEL == int2->edge2) break; 02853 else int2 = int2->next; 02854 } 02855 if ( !int2 ) return false; //oops!!! 02856 02857 //found an intersect node that can be swapped ... 02858 SwapIntersectNodes(*int1, *int2); 02859 e1 = int1->edge1; 02860 e2 = int1->edge2; 02861 } 02862 SwapPositionsInSEL(e1, e2); 02863 int1 = int1->next; 02864 int2 = int1->next; 02865 } 02866 02867 m_SortedEdges = 0; 02868 02869 //finally, check the last intersection too ... 02870 return (int1->edge1->prevInSEL == int1->edge2 || 02871 int1->edge1->nextInSEL == int1->edge2); 02872 } 02873 //------------------------------------------------------------------------------ 02874 02875 bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) 02876 { 02877 return e2.xcurr == e1.xcurr ? e2.dx > e1.dx : e2.xcurr < e1.xcurr; 02878 } 02879 //------------------------------------------------------------------------------ 02880 02881 void Clipper::InsertEdgeIntoAEL(TEdge *edge) 02882 { 02883 edge->prevInAEL = 0; 02884 edge->nextInAEL = 0; 02885 if( !m_ActiveEdges ) 02886 { 02887 m_ActiveEdges = edge; 02888 } 02889 else if( E2InsertsBeforeE1(*m_ActiveEdges, *edge) ) 02890 { 02891 edge->nextInAEL = m_ActiveEdges; 02892 m_ActiveEdges->prevInAEL = edge; 02893 m_ActiveEdges = edge; 02894 } else 02895 { 02896 TEdge* e = m_ActiveEdges; 02897 while( e->nextInAEL && !E2InsertsBeforeE1(*e->nextInAEL , *edge) ) 02898 e = e->nextInAEL; 02899 edge->nextInAEL = e->nextInAEL; 02900 if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge; 02901 edge->prevInAEL = e; 02902 e->nextInAEL = edge; 02903 } 02904 } 02905 //---------------------------------------------------------------------- 02906 02907 void Clipper::DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt) 02908 { 02909 AddOutPt(edge1, pt); 02910 SwapSides(*edge1, *edge2); 02911 SwapPolyIndexes(*edge1, *edge2); 02912 } 02913 //---------------------------------------------------------------------- 02914 02915 void Clipper::DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt) 02916 { 02917 AddOutPt(edge2, pt); 02918 SwapSides(*edge1, *edge2); 02919 SwapPolyIndexes(*edge1, *edge2); 02920 } 02921 //---------------------------------------------------------------------- 02922 02923 void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) 02924 { 02925 AddOutPt(edge1, pt); 02926 AddOutPt(edge2, pt); 02927 SwapSides( *edge1 , *edge2 ); 02928 SwapPolyIndexes( *edge1 , *edge2 ); 02929 } 02930 //---------------------------------------------------------------------- 02931 02932 void Clipper::CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2) 02933 { 02934 //when a polygon is split into 2 polygons, make sure any holes the original 02935 //polygon contained link to the correct polygon ... 02936 for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) 02937 { 02938 OutRec *orec = m_PolyOuts[i]; 02939 if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec1 && 02940 !PointInPolygon(orec->bottomPt->pt, outRec1->pts, m_UseFullRange)) 02941 orec->FirstLeft = outRec2; 02942 } 02943 } 02944 //---------------------------------------------------------------------- 02945 02946 void Clipper::CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2) 02947 { 02948 //if a hole is owned by outRec2 then make it owned by outRec1 ... 02949 for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) 02950 if (m_PolyOuts[i]->isHole && m_PolyOuts[i]->bottomPt && 02951 m_PolyOuts[i]->FirstLeft == outRec2) 02952 m_PolyOuts[i]->FirstLeft = outRec1; 02953 } 02954 //---------------------------------------------------------------------- 02955 02956 void Clipper::JoinCommonEdges(bool fixHoleLinkages) 02957 { 02958 for (JoinList::size_type i = 0; i < m_Joins.size(); i++) 02959 { 02960 JoinRec* j = m_Joins[i]; 02961 OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; 02962 OutPt *pp1a = outRec1->pts; 02963 OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; 02964 OutPt *pp2a = outRec2->pts; 02965 IntPoint pt1 = j->pt2a, pt2 = j->pt2b; 02966 IntPoint pt3 = j->pt1a, pt4 = j->pt1b; 02967 if (!FindSegment(pp1a, pt1, pt2)) continue; 02968 if (j->poly1Idx == j->poly2Idx) 02969 { 02970 //we're searching the same polygon for overlapping segments so 02971 //segment 2 mustn't be the same as segment 1 ... 02972 pp2a = pp1a->next; 02973 if (!FindSegment(pp2a, pt3, pt4) || (pp2a == pp1a)) continue; 02974 } 02975 else if (!FindSegment(pp2a, pt3, pt4)) continue; 02976 02977 if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) continue; 02978 02979 OutPt *p1, *p2, *p3, *p4; 02980 OutPt *prev = pp1a->prev; 02981 //get p1 & p2 polypts - the overlap start & endpoints on poly1 02982 if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; 02983 else if (PointsEqual(prev->pt, pt1)) p1 = prev; 02984 else p1 = InsertPolyPtBetween(pp1a, prev, pt1); 02985 02986 if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; 02987 else if (PointsEqual(prev->pt, pt2)) p2 = prev; 02988 else if ((p1 == pp1a) || (p1 == prev)) 02989 p2 = InsertPolyPtBetween(pp1a, prev, pt2); 02990 else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) 02991 p2 = InsertPolyPtBetween(pp1a, p1, pt2); else 02992 p2 = InsertPolyPtBetween(p1, prev, pt2); 02993 02994 //get p3 & p4 polypts - the overlap start & endpoints on poly2 02995 prev = pp2a->prev; 02996 if (PointsEqual(pp2a->pt, pt1)) p3 = pp2a; 02997 else if (PointsEqual(prev->pt, pt1)) p3 = prev; 02998 else p3 = InsertPolyPtBetween(pp2a, prev, pt1); 02999 03000 if (PointsEqual(pp2a->pt, pt2)) p4 = pp2a; 03001 else if (PointsEqual(prev->pt, pt2)) p4 = prev; 03002 else if ((p3 == pp2a) || (p3 == prev)) 03003 p4 = InsertPolyPtBetween(pp2a, prev, pt2); 03004 else if (Pt3IsBetweenPt1AndPt2(pp2a->pt, p3->pt, pt2)) 03005 p4 = InsertPolyPtBetween(pp2a, p3, pt2); else 03006 p4 = InsertPolyPtBetween(p3, prev, pt2); 03007 03008 //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... 03009 if (p1->next == p2 && p3->prev == p4) 03010 { 03011 p1->next = p3; 03012 p3->prev = p1; 03013 p2->prev = p4; 03014 p4->next = p2; 03015 } 03016 else if (p1->prev == p2 && p3->next == p4) 03017 { 03018 p1->prev = p3; 03019 p3->next = p1; 03020 p2->next = p4; 03021 p4->prev = p2; 03022 } 03023 else 03024 continue; //an orientation is probably wrong 03025 03026 if (j->poly2Idx == j->poly1Idx) 03027 { 03028 //instead of joining two polygons, we've just created a new one by 03029 //splitting one polygon into two. 03030 outRec1->pts = GetBottomPt(p1); 03031 outRec1->bottomPt = outRec1->pts; 03032 outRec1->bottomPt->idx = outRec1->idx; 03033 outRec2 = CreateOutRec(); 03034 m_PolyOuts.push_back(outRec2); 03035 outRec2->idx = (int)m_PolyOuts.size()-1; 03036 j->poly2Idx = outRec2->idx; 03037 outRec2->pts = GetBottomPt(p2); 03038 outRec2->bottomPt = outRec2->pts; 03039 outRec2->bottomPt->idx = outRec2->idx; 03040 03041 if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) 03042 { 03043 //outRec2 is contained by outRec1 ... 03044 outRec2->isHole = !outRec1->isHole; 03045 outRec2->FirstLeft = outRec1; 03046 if (outRec2->isHole == 03047 (m_ReverseOutput ^ Orientation(outRec2, m_UseFullRange))) 03048 ReversePolyPtLinks(*outRec2->pts); 03049 } else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRange)) 03050 { 03051 //outRec1 is contained by outRec2 ... 03052 outRec2->isHole = outRec1->isHole; 03053 outRec1->isHole = !outRec2->isHole; 03054 outRec2->FirstLeft = outRec1->FirstLeft; 03055 outRec1->FirstLeft = outRec2; 03056 if (outRec1->isHole == 03057 (m_ReverseOutput ^ Orientation(outRec1, m_UseFullRange))) 03058 ReversePolyPtLinks(*outRec1->pts); 03059 //make sure any contained holes now link to the correct polygon ... 03060 if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); 03061 } else 03062 { 03063 outRec2->isHole = outRec1->isHole; 03064 outRec2->FirstLeft = outRec1->FirstLeft; 03065 //make sure any contained holes now link to the correct polygon ... 03066 if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); 03067 } 03068 03069 //now fixup any subsequent joins that match this polygon 03070 for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) 03071 { 03072 JoinRec* j2 = m_Joins[k]; 03073 if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, p2)) 03074 j2->poly1Idx = j->poly2Idx; 03075 if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, p2)) 03076 j2->poly2Idx = j->poly2Idx; 03077 } 03078 03079 //now cleanup redundant edges too ... 03080 FixupOutPolygon(*outRec1); 03081 FixupOutPolygon(*outRec2); 03082 03083 if (Orientation(outRec1, m_UseFullRange) != (Area(*outRec1, m_UseFullRange) > 0)) 03084 DisposeBottomPt(*outRec1); 03085 if (Orientation(outRec2, m_UseFullRange) != (Area(*outRec2, m_UseFullRange) > 0)) 03086 DisposeBottomPt(*outRec2); 03087 03088 } else 03089 { 03090 //joined 2 polygons together ... 03091 03092 //make sure any holes contained by outRec2 now link to outRec1 ... 03093 if (fixHoleLinkages) CheckHoleLinkages2(outRec1, outRec2); 03094 03095 //now cleanup redundant edges too ... 03096 FixupOutPolygon(*outRec1); 03097 03098 if (outRec1->pts) 03099 { 03100 outRec1->isHole = !Orientation(outRec1, m_UseFullRange); 03101 if (outRec1->isHole && !outRec1->FirstLeft) 03102 outRec1->FirstLeft = outRec2->FirstLeft; 03103 } 03104 03105 //delete the obsolete pointer ... 03106 int OKIdx = outRec1->idx; 03107 int ObsoleteIdx = outRec2->idx; 03108 outRec2->pts = 0; 03109 outRec2->bottomPt = 0; 03110 outRec2->AppendLink = outRec1; 03111 03112 //now fixup any subsequent Joins that match this polygon 03113 for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) 03114 { 03115 JoinRec* j2 = m_Joins[k]; 03116 if (j2->poly1Idx == ObsoleteIdx) j2->poly1Idx = OKIdx; 03117 if (j2->poly2Idx == ObsoleteIdx) j2->poly2Idx = OKIdx; 03118 } 03119 } 03120 } 03121 } 03122 //------------------------------------------------------------------------------ 03123 03124 void ReversePolygon(Polygon& p) 03125 { 03126 std::reverse(p.begin(), p.end()); 03127 } 03128 //------------------------------------------------------------------------------ 03129 03130 void ReversePolygons(Polygons& p) 03131 { 03132 for (Polygons::size_type i = 0; i < p.size(); ++i) 03133 ReversePolygon(p[i]); 03134 } 03135 03136 //------------------------------------------------------------------------------ 03137 // OffsetPolygon functions ... 03138 //------------------------------------------------------------------------------ 03139 03140 struct DoublePoint 03141 { 03142 double X; 03143 double Y; 03144 DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 03145 }; 03146 //------------------------------------------------------------------------------ 03147 03148 Polygon BuildArc(const IntPoint &pt, 03149 const double a1, const double a2, const double r) 03150 { 03151 long64 steps = std::max(6, int(std::sqrt(std::fabs(r)) * std::fabs(a2 - a1))); 03152 if (steps > 0x100000) steps = 0x100000; 03153 int n = (unsigned)steps; 03154 Polygon result(n); 03155 double da = (a2 - a1) / (n -1); 03156 double a = a1; 03157 for (int i = 0; i < n; ++i) 03158 { 03159 result[i].X = pt.X + Round(std::cos(a)*r); 03160 result[i].Y = pt.Y + Round(std::sin(a)*r); 03161 a += da; 03162 } 03163 return result; 03164 } 03165 //------------------------------------------------------------------------------ 03166 03167 DoublePoint GetUnitNormal( const IntPoint &pt1, const IntPoint &pt2) 03168 { 03169 if(pt2.X == pt1.X && pt2.Y == pt1.Y) 03170 return DoublePoint(0, 0); 03171 03172 double dx = (double)(pt2.X - pt1.X); 03173 double dy = (double)(pt2.Y - pt1.Y); 03174 double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); 03175 dx *= f; 03176 dy *= f; 03177 return DoublePoint(dy, -dx); 03178 } 03179 03180 //------------------------------------------------------------------------------ 03181 //------------------------------------------------------------------------------ 03182 03183 class PolyOffsetBuilder 03184 { 03185 private: 03186 Polygons m_p; 03187 Polygon* m_curr_poly; 03188 std::vector<DoublePoint> normals; 03189 double m_delta, m_RMin, m_R; 03190 size_t m_i, m_j, m_k; 03191 static const int buffLength = 128; 03192 JoinType m_jointype; 03193 03194 public: 03195 03196 PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, 03197 double delta, JoinType jointype, double MiterLimit) 03198 { 03199 //nb precondition - out_polys != ptsin_polys 03200 if (NEAR_ZERO(delta)) 03201 { 03202 out_polys = in_polys; 03203 return; 03204 } 03205 03206 this->m_p = in_polys; 03207 this->m_delta = delta; 03208 this->m_jointype = jointype; 03209 if (MiterLimit <= 1) MiterLimit = 1; 03210 m_RMin = 2/(MiterLimit*MiterLimit); 03211 03212 double deltaSq = delta*delta; 03213 out_polys.clear(); 03214 out_polys.resize(in_polys.size()); 03215 for (m_i = 0; m_i < in_polys.size(); m_i++) 03216 { 03217 m_curr_poly = &out_polys[m_i]; 03218 size_t len = in_polys[m_i].size(); 03219 if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && 03220 m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; 03221 03222 //when 'shrinking' polygons - to minimize artefacts 03223 //strip those polygons that have an area < pi * delta^2 ... 03224 double a1 = Area(in_polys[m_i]); 03225 if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } 03226 else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area 03227 03228 if (len == 0 || (len < 3 && delta <= 0)) 03229 continue; 03230 else if (len == 1) 03231 { 03232 Polygon arc; 03233 arc = BuildArc(in_polys[m_i][len-1], 0, 2 * pi, delta); 03234 out_polys[m_i] = arc; 03235 continue; 03236 } 03237 03238 //build normals ... 03239 normals.clear(); 03240 normals.resize(len); 03241 normals[len-1] = GetUnitNormal(in_polys[m_i][len-1], in_polys[m_i][0]); 03242 for (m_j = 0; m_j < len -1; ++m_j) 03243 normals[m_j] = GetUnitNormal(in_polys[m_i][m_j], in_polys[m_i][m_j+1]); 03244 03245 m_k = len -1; 03246 for (m_j = 0; m_j < len; ++m_j) 03247 { 03248 switch (jointype) 03249 { 03250 case jtMiter: 03251 { 03252 m_R = 1 + (normals[m_j].X*normals[m_k].X + 03253 normals[m_j].Y*normals[m_k].Y); 03254 if (m_R >= m_RMin) DoMiter(); else DoSquare(MiterLimit); 03255 break; 03256 } 03257 case jtSquare: DoSquare(); break; 03258 case jtRound: DoRound(); break; 03259 } 03260 m_k = m_j; 03261 } 03262 } 03263 03264 //finally, clean up untidy corners using Clipper ... 03265 Clipper clpr; 03266 clpr.AddPolygons(out_polys, ptSubject); 03267 if (delta > 0) 03268 { 03269 if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive)) 03270 out_polys.clear(); 03271 } 03272 else 03273 { 03274 IntRect r = clpr.GetBounds(); 03275 Polygon outer(4); 03276 outer[0] = IntPoint(r.left - 10, r.bottom + 10); 03277 outer[1] = IntPoint(r.right + 10, r.bottom + 10); 03278 outer[2] = IntPoint(r.right + 10, r.top - 10); 03279 outer[3] = IntPoint(r.left - 10, r.top - 10); 03280 03281 clpr.AddPolygon(outer, ptSubject); 03282 if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) 03283 { 03284 out_polys.erase(out_polys.begin()); 03285 ReversePolygons(out_polys); 03286 03287 } else 03288 out_polys.clear(); 03289 } 03290 } 03291 //------------------------------------------------------------------------------ 03292 03293 private: 03294 03295 void AddPoint(const IntPoint& pt) 03296 { 03297 Polygon::size_type len = m_curr_poly->size(); 03298 if (len == m_curr_poly->capacity()) 03299 m_curr_poly->reserve(len + buffLength); 03300 m_curr_poly->push_back(pt); 03301 } 03302 //------------------------------------------------------------------------------ 03303 03304 void DoSquare(double mul = 1.0) 03305 { 03306 IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), 03307 (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); 03308 IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), 03309 (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); 03310 if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) 03311 { 03312 double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); 03313 double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); 03314 a1 = std::fabs(a2 - a1); 03315 if (a1 > pi) a1 = pi * 2 - a1; 03316 double dx = std::tan((pi - a1)/4) * std::fabs(m_delta * mul); 03317 pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), 03318 (long64)(pt1.Y + normals[m_k].X * dx)); 03319 AddPoint(pt1); 03320 pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), 03321 (long64)(pt2.Y -normals[m_j].X * dx)); 03322 AddPoint(pt2); 03323 } 03324 else 03325 { 03326 AddPoint(pt1); 03327 AddPoint(m_p[m_i][m_j]); 03328 AddPoint(pt2); 03329 } 03330 } 03331 //------------------------------------------------------------------------------ 03332 03333 void DoMiter() 03334 { 03335 if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) 03336 { 03337 double q = m_delta / m_R; 03338 AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X + 03339 (normals[m_k].X + normals[m_j].X) * q), 03340 (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); 03341 } 03342 else 03343 { 03344 IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * 03345 m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); 03346 IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * 03347 m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); 03348 AddPoint(pt1); 03349 AddPoint(m_p[m_i][m_j]); 03350 AddPoint(pt2); 03351 } 03352 } 03353 //------------------------------------------------------------------------------ 03354 03355 void DoRound() 03356 { 03357 IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), 03358 (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); 03359 IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), 03360 (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); 03361 AddPoint(pt1); 03362 //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg). 03363 if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) 03364 { 03365 if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985) 03366 { 03367 double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); 03368 double a2 = std::atan2(normals[m_j].Y, normals[m_j].X); 03369 if (m_delta > 0 && a2 < a1) a2 += pi *2; 03370 else if (m_delta < 0 && a2 > a1) a2 -= pi *2; 03371 Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta); 03372 for (Polygon::size_type m = 0; m < arc.size(); m++) 03373 AddPoint(arc[m]); 03374 } 03375 } 03376 else 03377 AddPoint(m_p[m_i][m_j]); 03378 AddPoint(pt2); 03379 } 03380 //-------------------------------------------------------------------------- 03381 03382 }; //end PolyOffsetBuilder 03383 03384 //------------------------------------------------------------------------------ 03385 //------------------------------------------------------------------------------ 03386 03387 void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, 03388 double delta, JoinType jointype, double MiterLimit) 03389 { 03390 if (&out_polys == &in_polys) 03391 { 03392 Polygons poly2(in_polys); 03393 PolyOffsetBuilder(poly2, out_polys, delta, jointype, MiterLimit); 03394 } 03395 else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, MiterLimit); 03396 } 03397 //------------------------------------------------------------------------------ 03398 03399 void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType) 03400 { 03401 Clipper c; 03402 c.AddPolygon(in_poly, ptSubject); 03403 c.Execute(ctUnion, out_polys, fillType, fillType); 03404 } 03405 //------------------------------------------------------------------------------ 03406 03407 void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType) 03408 { 03409 Clipper c; 03410 c.AddPolygons(in_polys, ptSubject); 03411 c.Execute(ctUnion, out_polys, fillType, fillType); 03412 } 03413 //------------------------------------------------------------------------------ 03414 03415 void SimplifyPolygons(Polygons &polys, PolyFillType fillType) 03416 { 03417 SimplifyPolygons(polys, polys, fillType); 03418 } 03419 //------------------------------------------------------------------------------ 03420 03421 std::ostream& operator <<(std::ostream &s, IntPoint& p) 03422 { 03423 s << p.X << ' ' << p.Y << "\n"; 03424 return s; 03425 } 03426 //------------------------------------------------------------------------------ 03427 03428 std::ostream& operator <<(std::ostream &s, Polygon &p) 03429 { 03430 for (Polygon::size_type i = 0; i < p.size(); i++) 03431 s << p[i]; 03432 s << "\n"; 03433 return s; 03434 } 03435 //------------------------------------------------------------------------------ 03436 03437 std::ostream& operator <<(std::ostream &s, Polygons &p) 03438 { 03439 for (Polygons::size_type i = 0; i < p.size(); i++) 03440 s << p[i]; 03441 s << "\n"; 03442 return s; 03443 } 03444 //------------------------------------------------------------------------------ 03445 03446 } //ClipperLib namespace