/src/MapServer/src/renderers/agg/src/clipper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /******************************************************************************* |
2 | | * * |
3 | | * Author : Angus Johnson * |
4 | | * Version : 4.6.3 * |
5 | | * Date : 11 November 2011 * |
6 | | * Website : http://www.angusj.com * |
7 | | * Copyright : Angus Johnson 2010-2011 * |
8 | | * * |
9 | | * License: * |
10 | | * Use, modification & distribution is subject to Boost Software License Ver 1. * |
11 | | * http://www.boost.org/LICENSE_1_0.txt * |
12 | | * * |
13 | | * Attributions: * |
14 | | * The code in this library is an extension of Bala Vatti's clipping algorithm: * |
15 | | * "A generic solution to polygon clipping" * |
16 | | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * |
17 | | * http://portal.acm.org/citation.cfm?id=129906 * |
18 | | * * |
19 | | * Computer graphics and geometric modeling: implementation and algorithms * |
20 | | * By Max K. Agoston * |
21 | | * Springer; 1 edition (January 4, 2005) * |
22 | | * http://books.google.com/books?q=vatti+clipping+agoston * |
23 | | * * |
24 | | * See also: * |
25 | | * "Polygon Offsetting by Computing Winding Numbers" * |
26 | | * Paper no. DETC2005-85513 pp. 565-575 * |
27 | | * ASME 2005 International Design Engineering Technical Conferences * |
28 | | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * |
29 | | * September 24-28, 2005 , Long Beach, California, USA * |
30 | | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * |
31 | | * * |
32 | | *******************************************************************************/ |
33 | | |
34 | | /******************************************************************************* |
35 | | * * |
36 | | * This is a translation of the Delphi Clipper library and the naming style * |
37 | | * used has retained a Delphi flavor. * |
38 | | * * |
39 | | *******************************************************************************/ |
40 | | |
41 | | #include "../include/clipper.hpp" |
42 | | #include <cassert> |
43 | | #include <cmath> |
44 | | #include <vector> |
45 | | #include <algorithm> |
46 | | #include <stdexcept> |
47 | | #include <cstring> |
48 | | #include <cstdlib> |
49 | | #include <ostream> |
50 | | |
51 | | namespace ClipperLib { |
52 | | |
53 | | static long64 const loRange = 1518500249; //sqrt(2^63 -1)/2 |
54 | | static long64 const hiRange = 6521908912666391106LL; //sqrt(2^127 -1)/2 |
55 | | static double const pi = 3.141592653589793238; |
56 | | enum Direction { dRightToLeft, dLeftToRight }; |
57 | | enum RangeTest { rtLo, rtHi, rtError }; |
58 | | |
59 | 0 | #define HORIZONTAL (-1.0E+40) |
60 | 0 | #define TOLERANCE (1.0e-20) |
61 | 0 | #define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) |
62 | 0 | #define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b)) |
63 | | |
64 | | inline long64 Abs(long64 val) |
65 | 0 | { |
66 | 0 | if (val < 0) return -val; else return val; |
67 | 0 | } |
68 | | //------------------------------------------------------------------------------ |
69 | | |
70 | | //------------------------------------------------------------------------------ |
71 | | // Int128 class (enables safe math on signed 64bit integers) |
72 | | // eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 |
73 | | // Int128 val2((long64)9223372036854775807); |
74 | | // Int128 val3 = val1 * val2; |
75 | | // val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) |
76 | | //------------------------------------------------------------------------------ |
77 | | |
78 | | class Int128 |
79 | | { |
80 | | public: |
81 | | |
82 | | Int128(long64 _lo = 0) |
83 | 0 | { |
84 | 0 | hi = 0; |
85 | 0 | if (_lo < 0) { |
86 | 0 | lo = -_lo; |
87 | 0 | Negate(*this); |
88 | 0 | } else |
89 | 0 | lo = _lo; |
90 | 0 | } |
91 | | |
92 | 0 | Int128(const Int128 &val): hi(val.hi), lo(val.lo){} |
93 | | |
94 | | Int128& operator=( const Int128& ) = default; |
95 | | |
96 | | long64 operator = (const long64 &val) |
97 | 0 | { |
98 | 0 | hi = 0; |
99 | 0 | lo = Abs(val); |
100 | 0 | if (val < 0) Negate(*this); |
101 | 0 | return val; |
102 | 0 | } |
103 | | |
104 | | bool operator == (const Int128 &val) const |
105 | 0 | {return (hi == val.hi && lo == val.lo);} |
106 | | |
107 | 0 | bool operator != (const Int128 &val) const { return !(*this == val);} |
108 | | |
109 | | bool operator > (const Int128 &val) const |
110 | 0 | { |
111 | 0 | if (hi > val.hi) return true; |
112 | 0 | else if (hi < val.hi) return false; |
113 | 0 | else return ulong64(lo) > ulong64(val.lo); |
114 | 0 | } |
115 | | |
116 | | bool operator < (const Int128 &val) const |
117 | 0 | { |
118 | 0 | if (hi < val.hi) return true; |
119 | 0 | else if (hi > val.hi) return false; |
120 | 0 | else return ulong64(lo) < ulong64(val.lo); |
121 | 0 | } |
122 | | |
123 | | Int128& operator += (const Int128 &rhs) |
124 | 0 | { |
125 | 0 | hi += rhs.hi; |
126 | 0 | lo += rhs.lo; |
127 | 0 | if (ulong64(lo) < ulong64(rhs.lo)) hi++; |
128 | 0 | return *this; |
129 | 0 | } |
130 | | |
131 | | Int128 operator + (const Int128 &rhs) const |
132 | 0 | { |
133 | 0 | Int128 result(*this); |
134 | 0 | result+= rhs; |
135 | 0 | return result; |
136 | 0 | } |
137 | | |
138 | | Int128& operator -= (const Int128 &rhs) |
139 | 0 | { |
140 | 0 | Int128 tmp(rhs); |
141 | 0 | Negate(tmp); |
142 | 0 | *this += tmp; |
143 | 0 | return *this; |
144 | 0 | } |
145 | | |
146 | | Int128 operator - (const Int128 &rhs) const |
147 | 0 | { |
148 | 0 | Int128 result(*this); |
149 | 0 | result-= rhs; |
150 | 0 | return result; |
151 | 0 | } |
152 | | |
153 | 0 | Int128 operator * (const Int128 &rhs) const { |
154 | 0 | if ( !(hi == 0 || hi == -1) || !(rhs.hi == 0 || rhs.hi == -1)) |
155 | 0 | throw "Int128 operator*: overflow error"; |
156 | 0 | bool negate = (hi < 0) != (rhs.hi < 0); |
157 | |
|
158 | 0 | Int128 tmp(*this); |
159 | 0 | if (tmp.hi < 0) Negate(tmp); |
160 | 0 | ulong64 int1Hi = ulong64(tmp.lo) >> 32; |
161 | 0 | ulong64 int1Lo = ulong64(tmp.lo & 0xFFFFFFFF); |
162 | |
|
163 | 0 | tmp = rhs; |
164 | 0 | if (tmp.hi < 0) Negate(tmp); |
165 | 0 | ulong64 int2Hi = ulong64(tmp.lo) >> 32; |
166 | 0 | ulong64 int2Lo = ulong64(tmp.lo & 0xFFFFFFFF); |
167 | | |
168 | | //nb: see comments in clipper.pas |
169 | 0 | ulong64 a = int1Hi * int2Hi; |
170 | 0 | ulong64 b = int1Lo * int2Lo; |
171 | 0 | ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; |
172 | |
|
173 | 0 | tmp.hi = long64(a + (c >> 32)); |
174 | 0 | tmp.lo = long64(c << 32); |
175 | 0 | tmp.lo += long64(b); |
176 | 0 | if (ulong64(tmp.lo) < b) tmp.hi++; |
177 | 0 | if (negate) Negate(tmp); |
178 | 0 | return tmp; |
179 | 0 | } |
180 | | |
181 | | Int128 operator/ (const Int128 &rhs) const |
182 | 0 | { |
183 | 0 | if (rhs.lo == 0 && rhs.hi == 0) |
184 | 0 | throw "Int128 operator/: divide by zero"; |
185 | 0 | bool negate = (rhs.hi < 0) != (hi < 0); |
186 | 0 | Int128 result(*this), denom(rhs); |
187 | 0 | if (result.hi < 0) Negate(result); |
188 | 0 | if (denom.hi < 0) Negate(denom); |
189 | 0 | if (denom > result) return Int128(0); //result is only a fraction of 1 |
190 | 0 | Negate(denom); |
191 | |
|
192 | 0 | Int128 p(0); |
193 | 0 | for (int i = 0; i < 128; ++i) |
194 | 0 | { |
195 | 0 | p.hi = p.hi << 1; |
196 | 0 | if (p.lo < 0) p.hi++; |
197 | 0 | p.lo = long64(p.lo) << 1; |
198 | 0 | if (result.hi < 0) p.lo++; |
199 | 0 | result.hi = result.hi << 1; |
200 | 0 | if (result.lo < 0) result.hi++; |
201 | 0 | result.lo = long64(result.lo) << 1; |
202 | 0 | Int128 p2(p); |
203 | 0 | p += denom; |
204 | 0 | if (p.hi < 0) p = p2; |
205 | 0 | else result.lo++; |
206 | 0 | } |
207 | 0 | if (negate) Negate(result); |
208 | 0 | return result; |
209 | 0 | } |
210 | | |
211 | | double AsDouble() const |
212 | 0 | { |
213 | 0 | const double shift64 = 18446744073709551616.0; //2^64 |
214 | 0 | const double bit64 = 9223372036854775808.0; |
215 | 0 | if (hi < 0) |
216 | 0 | { |
217 | 0 | Int128 tmp(*this); |
218 | 0 | Negate(tmp); |
219 | 0 | if (tmp.lo < 0) |
220 | 0 | return (double)tmp.lo - bit64 - tmp.hi * shift64; |
221 | 0 | else |
222 | 0 | return -(double)tmp.lo - tmp.hi * shift64; |
223 | 0 | } |
224 | 0 | else if (lo < 0) |
225 | 0 | return -(double)lo + bit64 + hi * shift64; |
226 | 0 | else |
227 | 0 | return (double)lo + (double)hi * shift64; |
228 | 0 | } |
229 | | |
230 | | //for bug testing ... |
231 | | std::string AsString() const |
232 | 0 | { |
233 | 0 | std::string result; |
234 | 0 | unsigned char r = 0; |
235 | 0 | Int128 tmp(0), val(*this); |
236 | 0 | if (hi < 0) Negate(val); |
237 | 0 | result.resize(50); |
238 | 0 | std::string::size_type i = result.size() -1; |
239 | 0 | while (val.hi != 0 || val.lo != 0) |
240 | 0 | { |
241 | 0 | Div10(val, tmp, r); |
242 | 0 | result[i--] = char('0' + r); |
243 | 0 | val = tmp; |
244 | 0 | } |
245 | 0 | if (hi < 0) result[i--] = '-'; |
246 | 0 | result.erase(0,i+1); |
247 | 0 | if (result.size() == 0) result = "0"; |
248 | 0 | return result; |
249 | 0 | } |
250 | | |
251 | | private: |
252 | | long64 hi; |
253 | | long64 lo; |
254 | | |
255 | | static void Negate(Int128 &val) |
256 | 0 | { |
257 | 0 | if (val.lo == 0) |
258 | 0 | { |
259 | 0 | if( val.hi == 0) return; |
260 | 0 | val.lo = ~val.lo; |
261 | 0 | val.hi = ~val.hi +1; |
262 | 0 | } |
263 | 0 | else |
264 | 0 | { |
265 | 0 | val.lo = ~val.lo +1; |
266 | 0 | val.hi = ~val.hi; |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | //debugging only ... |
271 | | void Div10(const Int128 val, Int128& result, unsigned char & remainder) const |
272 | 0 | { |
273 | 0 | remainder = 0; |
274 | 0 | result = 0; |
275 | 0 | for (int i = 63; i >= 0; --i) |
276 | 0 | { |
277 | 0 | if ((val.hi & ((long64)1 << i)) != 0) |
278 | 0 | remainder = char((remainder * 2) + 1); else |
279 | 0 | remainder *= char(2); |
280 | 0 | if (remainder >= 10) |
281 | 0 | { |
282 | 0 | result.hi += ((long64)1 << i); |
283 | 0 | remainder -= char(10); |
284 | 0 | } |
285 | 0 | } |
286 | 0 | for (int i = 63; i >= 0; --i) |
287 | 0 | { |
288 | 0 | if ((val.lo & ((long64)1 << i)) != 0) |
289 | 0 | remainder = char((remainder * 2) + 1); else |
290 | 0 | remainder *= char(2); |
291 | 0 | if (remainder >= 10) |
292 | 0 | { |
293 | 0 | result.lo += ((long64)1 << i); |
294 | 0 | remainder -= char(10); |
295 | 0 | } |
296 | 0 | } |
297 | 0 | } |
298 | | }; |
299 | | |
300 | | //------------------------------------------------------------------------------ |
301 | | //------------------------------------------------------------------------------ |
302 | | |
303 | | RangeTest TestRange(const Polygon &pts) |
304 | 0 | { |
305 | 0 | RangeTest result = rtLo; |
306 | 0 | for (Polygon::size_type i = 0; i < pts.size(); ++i) |
307 | 0 | { |
308 | 0 | if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange) |
309 | 0 | return rtError; |
310 | 0 | else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange) |
311 | 0 | result = rtHi; |
312 | 0 | } |
313 | 0 | return result; |
314 | 0 | } |
315 | | //------------------------------------------------------------------------------ |
316 | | |
317 | | bool Orientation(const Polygon &poly) |
318 | 0 | { |
319 | 0 | int highI = (int)poly.size() -1; |
320 | 0 | if (highI < 2) return false; |
321 | 0 | bool UseFullInt64Range = false; |
322 | |
|
323 | 0 | int j = 0, jplus, jminus; |
324 | 0 | for (int i = 0; i <= highI; ++i) |
325 | 0 | { |
326 | 0 | if (Abs(poly[i].X) > hiRange || Abs(poly[i].Y) > hiRange) |
327 | 0 | throw "Coordinate exceeds range bounds."; |
328 | 0 | if (Abs(poly[i].X) > loRange || Abs(poly[i].Y) > loRange) |
329 | 0 | UseFullInt64Range = true; |
330 | 0 | if (poly[i].Y < poly[j].Y) continue; |
331 | 0 | if ((poly[i].Y > poly[j].Y || poly[i].X < poly[j].X)) j = i; |
332 | 0 | }; |
333 | 0 | if (j == highI) jplus = 0; |
334 | 0 | else jplus = j +1; |
335 | 0 | if (j == 0) jminus = highI; |
336 | 0 | else jminus = j -1; |
337 | |
|
338 | 0 | IntPoint vec1, vec2; |
339 | | //get cross product of vectors of the edges adjacent to highest point ... |
340 | 0 | vec1.X = poly[j].X - poly[jminus].X; |
341 | 0 | vec1.Y = poly[j].Y - poly[jminus].Y; |
342 | 0 | vec2.X = poly[jplus].X - poly[j].X; |
343 | 0 | vec2.Y = poly[jplus].Y - poly[j].Y; |
344 | |
|
345 | 0 | if (UseFullInt64Range) |
346 | 0 | { |
347 | 0 | Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - |
348 | 0 | Int128(vec2.X) * Int128(vec1.Y); |
349 | 0 | return cross > 0; |
350 | 0 | } |
351 | 0 | else |
352 | 0 | { |
353 | 0 | return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; |
354 | 0 | } |
355 | 0 | } |
356 | | //------------------------------------------------------------------------------ |
357 | | |
358 | | bool Orientation(OutRec *outRec, bool UseFullInt64Range) |
359 | 0 | { |
360 | 0 | OutPt *opBottom = outRec->pts, *op = outRec->pts->next; |
361 | 0 | while (op != outRec->pts) |
362 | 0 | { |
363 | 0 | if (op->pt.Y >= opBottom->pt.Y) |
364 | 0 | { |
365 | 0 | if (op->pt.Y > opBottom->pt.Y || op->pt.X < opBottom->pt.X) |
366 | 0 | opBottom = op; |
367 | 0 | } |
368 | 0 | op = op->next; |
369 | 0 | } |
370 | |
|
371 | 0 | IntPoint vec1, vec2; |
372 | 0 | vec1.X = op->pt.X - op->prev->pt.X; |
373 | 0 | vec1.Y = op->pt.Y - op->prev->pt.Y; |
374 | 0 | vec2.X = op->next->pt.X - op->pt.X; |
375 | 0 | vec2.Y = op->next->pt.Y - op->pt.Y; |
376 | |
|
377 | 0 | if (UseFullInt64Range) |
378 | 0 | { |
379 | 0 | Int128 cross = Int128(vec1.X) * Int128(vec2.Y) - Int128(vec2.X) * Int128(vec1.Y); |
380 | 0 | return cross > 0; |
381 | 0 | } |
382 | 0 | else |
383 | 0 | { |
384 | 0 | return (vec1.X * vec2.Y - vec2.X * vec1.Y) > 0; |
385 | 0 | } |
386 | 0 | } |
387 | | //------------------------------------------------------------------------------ |
388 | | |
389 | | inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2) |
390 | 0 | { |
391 | 0 | return ( pt1.X == pt2.X && pt1.Y == pt2.Y ); |
392 | 0 | } |
393 | | //------------------------------------------------------------------------------ |
394 | | |
395 | | double Area(const Polygon &poly) |
396 | 0 | { |
397 | 0 | int highI = (int)poly.size() -1; |
398 | 0 | if (highI < 2) return 0; |
399 | 0 | bool UseFullInt64Range; |
400 | 0 | RangeTest rt = TestRange(poly); |
401 | 0 | switch (rt) { |
402 | 0 | case rtLo: |
403 | 0 | UseFullInt64Range = false; |
404 | 0 | break; |
405 | 0 | case rtHi: |
406 | 0 | UseFullInt64Range = true; |
407 | 0 | break; |
408 | 0 | default: |
409 | 0 | throw "Coordinate exceeds range bounds."; |
410 | 0 | } |
411 | | |
412 | 0 | if (UseFullInt64Range) { |
413 | 0 | Int128 a(0); |
414 | 0 | a = (Int128(poly[highI].X) * Int128(poly[0].Y)) - |
415 | 0 | Int128(poly[0].X) * Int128(poly[highI].Y); |
416 | 0 | for (int i = 0; i < highI; ++i) |
417 | 0 | a += Int128(poly[i].X) * Int128(poly[i+1].Y) - |
418 | 0 | Int128(poly[i+1].X) * Int128(poly[i].Y); |
419 | 0 | return a.AsDouble() / 2; |
420 | 0 | } |
421 | 0 | else |
422 | 0 | { |
423 | 0 | double a; |
424 | 0 | a = (double)poly[highI].X * poly[0].Y - (double)poly[0].X * poly[highI].Y; |
425 | 0 | for (int i = 0; i < highI; ++i) |
426 | 0 | a += (double)poly[i].X * poly[i+1].Y - (double)poly[i+1].X * poly[i].Y; |
427 | 0 | return a/2; |
428 | 0 | } |
429 | 0 | } |
430 | | //------------------------------------------------------------------------------ |
431 | | |
432 | | bool PointIsVertex(const IntPoint &pt, OutPt *pp) |
433 | 0 | { |
434 | 0 | OutPt *pp2 = pp; |
435 | 0 | do |
436 | 0 | { |
437 | 0 | if (PointsEqual(pp2->pt, pt)) return true; |
438 | 0 | pp2 = pp2->next; |
439 | 0 | } |
440 | 0 | while (pp2 != pp); |
441 | 0 | return false; |
442 | 0 | } |
443 | | //------------------------------------------------------------------------------ |
444 | | |
445 | | bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range) |
446 | 0 | { |
447 | 0 | OutPt *pp2 = pp; |
448 | 0 | bool result = false; |
449 | 0 | if (UseFullInt64Range) { |
450 | 0 | do |
451 | 0 | { |
452 | 0 | if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || |
453 | 0 | ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && |
454 | 0 | Int128(pt.X - pp2->pt.X) < (Int128(pp2->prev->pt.X - pp2->pt.X) * |
455 | 0 | Int128(pt.Y - pp2->pt.Y)) / Int128(pp2->prev->pt.Y - pp2->pt.Y)) |
456 | 0 | result = !result; |
457 | 0 | pp2 = pp2->next; |
458 | 0 | } |
459 | 0 | while (pp2 != pp); |
460 | 0 | } |
461 | 0 | else |
462 | 0 | { |
463 | 0 | do |
464 | 0 | { |
465 | 0 | if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) || |
466 | 0 | ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) && |
467 | 0 | (pt.X < (pp2->prev->pt.X - pp2->pt.X) * (pt.Y - pp2->pt.Y) / |
468 | 0 | (pp2->prev->pt.Y - pp2->pt.Y) + pp2->pt.X )) result = !result; |
469 | 0 | pp2 = pp2->next; |
470 | 0 | } |
471 | 0 | while (pp2 != pp); |
472 | 0 | } |
473 | 0 | return result; |
474 | 0 | } |
475 | | //------------------------------------------------------------------------------ |
476 | | |
477 | | bool SlopesEqual(TEdge &e1, TEdge &e2, bool UseFullInt64Range) |
478 | 0 | { |
479 | 0 | if (e1.ybot == e1.ytop) return (e2.ybot == e2.ytop); |
480 | 0 | else if (e1.xbot == e1.xtop) return (e2.xbot == e2.xtop); |
481 | 0 | else if (UseFullInt64Range) |
482 | 0 | return Int128(e1.ytop - e1.ybot) * Int128(e2.xtop - e2.xbot) == |
483 | 0 | Int128(e1.xtop - e1.xbot) * Int128(e2.ytop - e2.ybot); |
484 | 0 | else return (e1.ytop - e1.ybot)*(e2.xtop - e2.xbot) == |
485 | 0 | (e1.xtop - e1.xbot)*(e2.ytop - e2.ybot); |
486 | 0 | } |
487 | | //------------------------------------------------------------------------------ |
488 | | |
489 | | bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, |
490 | | const IntPoint pt3, bool UseFullInt64Range) |
491 | 0 | { |
492 | 0 | if (pt1.Y == pt2.Y) return (pt2.Y == pt3.Y); |
493 | 0 | else if (pt1.X == pt2.X) return (pt2.X == pt3.X); |
494 | 0 | else if (UseFullInt64Range) |
495 | 0 | return Int128(pt1.Y-pt2.Y) * Int128(pt2.X-pt3.X) == |
496 | 0 | Int128(pt1.X-pt2.X) * Int128(pt2.Y-pt3.Y); |
497 | 0 | else return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); |
498 | 0 | } |
499 | | //------------------------------------------------------------------------------ |
500 | | |
501 | | bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, |
502 | | const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) |
503 | 0 | { |
504 | 0 | if (pt1.Y == pt2.Y) return (pt3.Y == pt4.Y); |
505 | 0 | else if (pt1.X == pt2.X) return (pt3.X == pt4.X); |
506 | 0 | else if (UseFullInt64Range) |
507 | 0 | return Int128(pt1.Y-pt2.Y) * Int128(pt3.X-pt4.X) == |
508 | 0 | Int128(pt1.X-pt2.X) * Int128(pt3.Y-pt4.Y); |
509 | 0 | else return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); |
510 | 0 | } |
511 | | //------------------------------------------------------------------------------ |
512 | | |
513 | | double GetDx(const IntPoint pt1, const IntPoint pt2) |
514 | 0 | { |
515 | 0 | if (pt1.Y == pt2.Y) return HORIZONTAL; |
516 | 0 | else return |
517 | 0 | (double)(pt2.X - pt1.X) / (double)(pt2.Y - pt1.Y); |
518 | 0 | } |
519 | | //--------------------------------------------------------------------------- |
520 | | |
521 | | void SetDx(TEdge &e) |
522 | 0 | { |
523 | 0 | if (e.ybot == e.ytop) e.dx = HORIZONTAL; |
524 | 0 | else e.dx = |
525 | 0 | (double)(e.xtop - e.xbot) / (double)(e.ytop - e.ybot); |
526 | 0 | } |
527 | | //--------------------------------------------------------------------------- |
528 | | |
529 | | void SwapSides(TEdge &edge1, TEdge &edge2) |
530 | 0 | { |
531 | 0 | EdgeSide side = edge1.side; |
532 | 0 | edge1.side = edge2.side; |
533 | 0 | edge2.side = side; |
534 | 0 | } |
535 | | //------------------------------------------------------------------------------ |
536 | | |
537 | | void SwapPolyIndexes(TEdge &edge1, TEdge &edge2) |
538 | 0 | { |
539 | 0 | int outIdx = edge1.outIdx; |
540 | 0 | edge1.outIdx = edge2.outIdx; |
541 | 0 | edge2.outIdx = outIdx; |
542 | 0 | } |
543 | | //------------------------------------------------------------------------------ |
544 | | |
545 | | inline long64 Round(double val) |
546 | 0 | { |
547 | 0 | if ((val < 0)) return static_cast<long64>(val - 0.5); |
548 | 0 | else return static_cast<long64>(val + 0.5); |
549 | 0 | } |
550 | | //------------------------------------------------------------------------------ |
551 | | |
552 | | long64 TopX(TEdge &edge, const long64 currentY) |
553 | 0 | { |
554 | 0 | if( currentY == edge.ytop ) return edge.xtop; |
555 | 0 | return edge.xbot + Round(edge.dx *(currentY - edge.ybot)); |
556 | 0 | } |
557 | | //------------------------------------------------------------------------------ |
558 | | |
559 | | long64 TopX(const IntPoint pt1, const IntPoint pt2, const long64 currentY) |
560 | 0 | { |
561 | | //preconditions: pt1.Y <> pt2.Y and pt1.Y > pt2.Y |
562 | 0 | if (currentY >= pt1.Y) return pt1.X; |
563 | 0 | else if (currentY == pt2.Y) return pt2.X; |
564 | 0 | else if (pt1.X == pt2.X) return pt1.X; |
565 | 0 | else |
566 | 0 | { |
567 | 0 | double q = (double)(pt1.X-pt2.X)/(double)(pt1.Y-pt2.Y); |
568 | 0 | return Round(pt1.X + (currentY - pt1.Y) *q); |
569 | 0 | } |
570 | 0 | } |
571 | | //------------------------------------------------------------------------------ |
572 | | |
573 | | bool IntersectPoint(TEdge &edge1, TEdge &edge2, |
574 | | IntPoint &ip, bool UseFullInt64Range) |
575 | 0 | { |
576 | 0 | double b1, b2; |
577 | 0 | if (SlopesEqual(edge1, edge2, UseFullInt64Range)) return false; |
578 | 0 | else if (NEAR_ZERO(edge1.dx)) |
579 | 0 | { |
580 | 0 | ip.X = edge1.xbot; |
581 | 0 | if (NEAR_EQUAL(edge2.dx, HORIZONTAL)) |
582 | 0 | { |
583 | 0 | ip.Y = edge2.ybot; |
584 | 0 | } else |
585 | 0 | { |
586 | 0 | b2 = edge2.ybot - (edge2.xbot/edge2.dx); |
587 | 0 | ip.Y = Round(ip.X/edge2.dx + b2); |
588 | 0 | } |
589 | 0 | } |
590 | 0 | else if (NEAR_ZERO(edge2.dx)) |
591 | 0 | { |
592 | 0 | ip.X = edge2.xbot; |
593 | 0 | if (NEAR_EQUAL(edge1.dx, HORIZONTAL)) |
594 | 0 | { |
595 | 0 | ip.Y = edge1.ybot; |
596 | 0 | } else |
597 | 0 | { |
598 | 0 | b1 = edge1.ybot - (edge1.xbot/edge1.dx); |
599 | 0 | ip.Y = Round(ip.X/edge1.dx + b1); |
600 | 0 | } |
601 | 0 | } else |
602 | 0 | { |
603 | 0 | b1 = edge1.xbot - edge1.ybot * edge1.dx; |
604 | 0 | b2 = edge2.xbot - edge2.ybot * edge2.dx; |
605 | 0 | b2 = (b2-b1)/(edge1.dx - edge2.dx); |
606 | 0 | ip.Y = Round(b2); |
607 | 0 | ip.X = Round(edge1.dx * b2 + b1); |
608 | 0 | } |
609 | | |
610 | 0 | return |
611 | | //can be *so close* to the top of one edge that the rounded Y equals one ytop ... |
612 | 0 | (ip.Y == edge1.ytop && ip.Y >= edge2.ytop && edge1.tmpX > edge2.tmpX) || |
613 | 0 | (ip.Y == edge2.ytop && ip.Y >= edge1.ytop && edge1.tmpX > edge2.tmpX) || |
614 | 0 | (ip.Y > edge1.ytop && ip.Y > edge2.ytop); |
615 | 0 | } |
616 | | //------------------------------------------------------------------------------ |
617 | | |
618 | | void ReversePolyPtLinks(OutPt &pp) |
619 | 0 | { |
620 | 0 | OutPt *pp1, *pp2; |
621 | 0 | pp1 = &pp; |
622 | 0 | do { |
623 | 0 | pp2 = pp1->next; |
624 | 0 | pp1->next = pp1->prev; |
625 | 0 | pp1->prev = pp2; |
626 | 0 | pp1 = pp2; |
627 | 0 | } while( pp1 != &pp ); |
628 | 0 | } |
629 | | //------------------------------------------------------------------------------ |
630 | | |
631 | | void DisposeOutPts(OutPt*& pp) |
632 | 0 | { |
633 | 0 | if (pp == 0) return; |
634 | 0 | pp->prev->next = 0; |
635 | 0 | while( pp ) |
636 | 0 | { |
637 | 0 | OutPt *tmpPp = pp; |
638 | 0 | pp = pp->next; |
639 | 0 | delete tmpPp ; |
640 | 0 | } |
641 | 0 | } |
642 | | //------------------------------------------------------------------------------ |
643 | | |
644 | | void InitEdge(TEdge *e, TEdge *eNext, |
645 | | TEdge *ePrev, const IntPoint &pt, PolyType polyType) |
646 | 0 | { |
647 | 0 | std::memset( e, 0, sizeof( TEdge )); |
648 | |
|
649 | 0 | e->next = eNext; |
650 | 0 | e->prev = ePrev; |
651 | 0 | e->xcurr = pt.X; |
652 | 0 | e->ycurr = pt.Y; |
653 | 0 | if (e->ycurr >= e->next->ycurr) |
654 | 0 | { |
655 | 0 | e->xbot = e->xcurr; |
656 | 0 | e->ybot = e->ycurr; |
657 | 0 | e->xtop = e->next->xcurr; |
658 | 0 | e->ytop = e->next->ycurr; |
659 | 0 | e->windDelta = 1; |
660 | 0 | } else |
661 | 0 | { |
662 | 0 | e->xtop = e->xcurr; |
663 | 0 | e->ytop = e->ycurr; |
664 | 0 | e->xbot = e->next->xcurr; |
665 | 0 | e->ybot = e->next->ycurr; |
666 | 0 | e->windDelta = -1; |
667 | 0 | } |
668 | 0 | SetDx(*e); |
669 | 0 | e->polyType = polyType; |
670 | 0 | e->outIdx = -1; |
671 | 0 | } |
672 | | //------------------------------------------------------------------------------ |
673 | | |
674 | | inline void SwapX(TEdge &e) |
675 | 0 | { |
676 | | //swap horizontal edges' top and bottom x's so they follow the natural |
677 | | //progression of the bounds - ie so their xbots will align with the |
678 | | //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] |
679 | 0 | e.xcurr = e.xtop; |
680 | 0 | e.xtop = e.xbot; |
681 | 0 | e.xbot = e.xcurr; |
682 | 0 | } |
683 | | //------------------------------------------------------------------------------ |
684 | | |
685 | | void SwapPoints(IntPoint &pt1, IntPoint &pt2) |
686 | 0 | { |
687 | 0 | IntPoint tmp = pt1; |
688 | 0 | pt1 = pt2; |
689 | 0 | pt2 = tmp; |
690 | 0 | } |
691 | | //------------------------------------------------------------------------------ |
692 | | |
693 | | bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, |
694 | | IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) |
695 | 0 | { |
696 | | //precondition: segments are colinear. |
697 | 0 | if ( pt1a.Y == pt1b.Y || Abs((pt1a.X - pt1b.X)/(pt1a.Y - pt1b.Y)) > 1 ) |
698 | 0 | { |
699 | 0 | if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); |
700 | 0 | if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); |
701 | 0 | if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; |
702 | 0 | if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; |
703 | 0 | return pt1.X < pt2.X; |
704 | 0 | } else |
705 | 0 | { |
706 | 0 | if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); |
707 | 0 | if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); |
708 | 0 | if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; |
709 | 0 | if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; |
710 | 0 | return pt1.Y > pt2.Y; |
711 | 0 | } |
712 | 0 | } |
713 | | //------------------------------------------------------------------------------ |
714 | | |
715 | | OutPt* PolygonBottom(OutPt* pp) |
716 | 0 | { |
717 | 0 | OutPt* p = pp->next; |
718 | 0 | OutPt* result = pp; |
719 | 0 | while (p != pp) |
720 | 0 | { |
721 | 0 | if (p->pt.Y > result->pt.Y) result = p; |
722 | 0 | else if (p->pt.Y == result->pt.Y && p->pt.X < result->pt.X) result = p; |
723 | 0 | p = p->next; |
724 | 0 | } |
725 | 0 | return result; |
726 | 0 | } |
727 | | //------------------------------------------------------------------------------ |
728 | | |
729 | | bool FindSegment(OutPt* &pp, IntPoint &pt1, IntPoint &pt2) |
730 | 0 | { |
731 | | //outPt1 & outPt2 => the overlap segment (if the function returns true) |
732 | 0 | if (!pp) return false; |
733 | 0 | OutPt* pp2 = pp; |
734 | 0 | IntPoint pt1a = pt1, pt2a = pt2; |
735 | 0 | do |
736 | 0 | { |
737 | 0 | if (SlopesEqual(pt1a, pt2a, pp->pt, pp->prev->pt, true) && |
738 | 0 | SlopesEqual(pt1a, pt2a, pp->pt, true) && |
739 | 0 | GetOverlapSegment(pt1a, pt2a, pp->pt, pp->prev->pt, pt1, pt2)) |
740 | 0 | return true; |
741 | 0 | pp = pp->next; |
742 | 0 | } |
743 | 0 | while (pp != pp2); |
744 | 0 | return false; |
745 | 0 | } |
746 | | //------------------------------------------------------------------------------ |
747 | | |
748 | | bool Pt3IsBetweenPt1AndPt2(const IntPoint pt1, |
749 | | const IntPoint pt2, const IntPoint pt3) |
750 | 0 | { |
751 | 0 | if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true; |
752 | 0 | else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X); |
753 | 0 | else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y); |
754 | 0 | } |
755 | | //------------------------------------------------------------------------------ |
756 | | |
757 | | OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint pt) |
758 | 0 | { |
759 | 0 | if (p1 == p2) throw "JoinError"; |
760 | 0 | OutPt* result = new OutPt; |
761 | 0 | result->pt = pt; |
762 | 0 | if (p2 == p1->next) |
763 | 0 | { |
764 | 0 | p1->next = result; |
765 | 0 | p2->prev = result; |
766 | 0 | result->next = p2; |
767 | 0 | result->prev = p1; |
768 | 0 | } else |
769 | 0 | { |
770 | 0 | p2->next = result; |
771 | 0 | p1->prev = result; |
772 | 0 | result->next = p1; |
773 | 0 | result->prev = p2; |
774 | 0 | } |
775 | 0 | return result; |
776 | 0 | } |
777 | | |
778 | | //------------------------------------------------------------------------------ |
779 | | // ClipperBase class methods ... |
780 | | //------------------------------------------------------------------------------ |
781 | | |
782 | | ClipperBase::ClipperBase() //constructor |
783 | 0 | { |
784 | 0 | m_MinimaList = 0; |
785 | 0 | m_CurrentLM = 0; |
786 | 0 | m_UseFullRange = true; |
787 | 0 | } |
788 | | //------------------------------------------------------------------------------ |
789 | | |
790 | | ClipperBase::~ClipperBase() //destructor |
791 | 0 | { |
792 | 0 | Clear(); |
793 | 0 | } |
794 | | //------------------------------------------------------------------------------ |
795 | | |
796 | | bool ClipperBase::AddPolygon( const Polygon &pg, PolyType polyType) |
797 | 0 | { |
798 | 0 | int len = (int)pg.size(); |
799 | 0 | if (len < 3) return false; |
800 | 0 | Polygon p(len); |
801 | 0 | p[0] = pg[0]; |
802 | 0 | int j = 0; |
803 | |
|
804 | 0 | long64 maxVal; |
805 | 0 | if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange; |
806 | |
|
807 | 0 | for (int i = 0; i < len; ++i) |
808 | 0 | { |
809 | 0 | if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) |
810 | 0 | { |
811 | 0 | if (m_UseFullRange) |
812 | 0 | throw "Coordinate exceeds range bounds"; |
813 | 0 | maxVal = hiRange; |
814 | 0 | if (Abs(pg[i].X) > maxVal || Abs(pg[i].Y) > maxVal) |
815 | 0 | throw "Coordinate exceeds range bounds"; |
816 | 0 | m_UseFullRange = true; |
817 | 0 | } |
818 | | |
819 | 0 | if (i == 0 || PointsEqual(p[j], pg[i])) continue; |
820 | 0 | else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange)) |
821 | 0 | { |
822 | 0 | if (PointsEqual(p[j-1], pg[i])) j--; |
823 | 0 | } else j++; |
824 | 0 | p[j] = pg[i]; |
825 | 0 | } |
826 | 0 | if (j < 2) return false; |
827 | | |
828 | 0 | len = j+1; |
829 | 0 | for (;;) |
830 | 0 | { |
831 | | //nb: test for point equality before testing slopes ... |
832 | 0 | if (PointsEqual(p[j], p[0])) j--; |
833 | 0 | else if (PointsEqual(p[0], p[1]) || |
834 | 0 | SlopesEqual(p[j], p[0], p[1], m_UseFullRange)) |
835 | 0 | p[0] = p[j--]; |
836 | 0 | else if (SlopesEqual(p[j-1], p[j], p[0], m_UseFullRange)) j--; |
837 | 0 | else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange)) |
838 | 0 | { |
839 | 0 | for (int i = 2; i <= j; ++i) p[i-1] = p[i]; |
840 | 0 | j--; |
841 | 0 | } |
842 | | //exit loop if nothing is changed or there are too few vertices ... |
843 | 0 | if (j == len-1 || j < 2) break; |
844 | 0 | len = j +1; |
845 | 0 | } |
846 | | // if (len < 3) return false; |
847 | | |
848 | | //create a new edge array ... |
849 | 0 | TEdge *edges = new TEdge [len]; |
850 | 0 | m_edges.push_back(edges); |
851 | | |
852 | | //convert vertices to a double-linked-list of edges and initialize ... |
853 | 0 | edges[0].xcurr = p[0].X; |
854 | 0 | edges[0].ycurr = p[0].Y; |
855 | 0 | InitEdge(&edges[len-1], &edges[0], &edges[len-2], p[len-1], polyType); |
856 | 0 | for (int i = len-2; i > 0; --i) |
857 | 0 | InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i], polyType); |
858 | 0 | InitEdge(&edges[0], &edges[1], &edges[len-1], p[0], polyType); |
859 | | |
860 | | //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates |
861 | | //increase downward so the 'highest' edge will have the smallest ytop) ... |
862 | 0 | TEdge *e = &edges[0]; |
863 | 0 | TEdge *eHighest = e; |
864 | 0 | do |
865 | 0 | { |
866 | 0 | e->xcurr = e->xbot; |
867 | 0 | e->ycurr = e->ybot; |
868 | 0 | if (e->ytop < eHighest->ytop) eHighest = e; |
869 | 0 | e = e->next; |
870 | 0 | } |
871 | 0 | while ( e != &edges[0]); |
872 | | |
873 | | //make sure eHighest is positioned so the following loop works safely ... |
874 | 0 | if (eHighest->windDelta > 0) eHighest = eHighest->next; |
875 | 0 | if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next; |
876 | | |
877 | | //finally insert each local minima ... |
878 | 0 | e = eHighest; |
879 | 0 | do { |
880 | 0 | e = AddBoundsToLML(e); |
881 | 0 | } |
882 | 0 | while( e != eHighest ); |
883 | 0 | return true; |
884 | 0 | } |
885 | | //------------------------------------------------------------------------------ |
886 | | |
887 | | void ClipperBase::InsertLocalMinima(LocalMinima *newLm) |
888 | 0 | { |
889 | 0 | if( ! m_MinimaList ) |
890 | 0 | { |
891 | 0 | m_MinimaList = newLm; |
892 | 0 | } |
893 | 0 | else if( newLm->Y >= m_MinimaList->Y ) |
894 | 0 | { |
895 | 0 | newLm->next = m_MinimaList; |
896 | 0 | m_MinimaList = newLm; |
897 | 0 | } else |
898 | 0 | { |
899 | 0 | LocalMinima* tmpLm = m_MinimaList; |
900 | 0 | while( tmpLm->next && ( newLm->Y < tmpLm->next->Y ) ) |
901 | 0 | tmpLm = tmpLm->next; |
902 | 0 | newLm->next = tmpLm->next; |
903 | 0 | tmpLm->next = newLm; |
904 | 0 | } |
905 | 0 | } |
906 | | //------------------------------------------------------------------------------ |
907 | | |
908 | | TEdge* ClipperBase::AddBoundsToLML(TEdge *e) |
909 | 0 | { |
910 | | //Starting at the top of one bound we progress to the bottom where there's |
911 | | //a local minima. We then go to the top of the next bound. These two bounds |
912 | | //form the left and right (or right and left) bounds of the local minima. |
913 | 0 | e->nextInLML = 0; |
914 | 0 | e = e->next; |
915 | 0 | for (;;) |
916 | 0 | { |
917 | 0 | if (NEAR_EQUAL(e->dx, HORIZONTAL)) |
918 | 0 | { |
919 | | //nb: proceed through horizontals when approaching from their right, |
920 | | // but break on horizontal minima if approaching from their left. |
921 | | // This ensures 'local minima' are always on the left of horizontals. |
922 | 0 | if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break; |
923 | | // coverity[copy_paste_error] |
924 | 0 | if (e->xtop != e->prev->xbot) SwapX(*e); |
925 | 0 | e->nextInLML = e->prev; |
926 | 0 | } |
927 | 0 | else if (e->ycurr == e->prev->ycurr) break; |
928 | 0 | else e->nextInLML = e->prev; |
929 | 0 | e = e->next; |
930 | 0 | } |
931 | | |
932 | | //e and e.prev are now at a local minima ... |
933 | 0 | LocalMinima* newLm = new LocalMinima; |
934 | 0 | newLm->next = 0; |
935 | 0 | newLm->Y = e->prev->ybot; |
936 | |
|
937 | 0 | if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound |
938 | 0 | { |
939 | 0 | if (e->xbot != e->prev->xbot) SwapX(*e); |
940 | 0 | newLm->leftBound = e->prev; |
941 | 0 | newLm->rightBound = e; |
942 | 0 | } else if (e->dx < e->prev->dx) |
943 | 0 | { |
944 | 0 | newLm->leftBound = e->prev; |
945 | 0 | newLm->rightBound = e; |
946 | 0 | } else |
947 | 0 | { |
948 | 0 | newLm->leftBound = e; |
949 | 0 | newLm->rightBound = e->prev; |
950 | 0 | } |
951 | 0 | newLm->leftBound->side = esLeft; |
952 | 0 | newLm->rightBound->side = esRight; |
953 | 0 | InsertLocalMinima( newLm ); |
954 | |
|
955 | 0 | for (;;) |
956 | 0 | { |
957 | 0 | if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break; |
958 | 0 | e->nextInLML = e->next; |
959 | 0 | e = e->next; |
960 | 0 | if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e); |
961 | 0 | } |
962 | 0 | return e->next; |
963 | 0 | } |
964 | | //------------------------------------------------------------------------------ |
965 | | |
966 | | bool ClipperBase::AddPolygons(const Polygons &ppg, PolyType polyType) |
967 | 0 | { |
968 | 0 | bool result = true; |
969 | 0 | for (Polygons::size_type i = 0; i < ppg.size(); ++i) |
970 | 0 | if (AddPolygon(ppg[i], polyType)) result = false; |
971 | 0 | return result; |
972 | 0 | } |
973 | | //------------------------------------------------------------------------------ |
974 | | |
975 | | void ClipperBase::Clear() |
976 | 0 | { |
977 | 0 | DisposeLocalMinimaList(); |
978 | 0 | for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) delete [] m_edges[i]; |
979 | 0 | m_edges.clear(); |
980 | 0 | m_UseFullRange = false; |
981 | 0 | } |
982 | | //------------------------------------------------------------------------------ |
983 | | |
984 | | void ClipperBase::Reset() |
985 | 0 | { |
986 | 0 | m_CurrentLM = m_MinimaList; |
987 | 0 | if( !m_CurrentLM ) return; //ie nothing to process |
988 | | |
989 | | //reset all edges ... |
990 | 0 | LocalMinima* lm = m_MinimaList; |
991 | 0 | while( lm ) |
992 | 0 | { |
993 | 0 | TEdge* e = lm->leftBound; |
994 | 0 | while( e ) |
995 | 0 | { |
996 | 0 | e->xcurr = e->xbot; |
997 | 0 | e->ycurr = e->ybot; |
998 | 0 | e->side = esLeft; |
999 | 0 | e->outIdx = -1; |
1000 | 0 | e = e->nextInLML; |
1001 | 0 | } |
1002 | 0 | e = lm->rightBound; |
1003 | 0 | while( e ) |
1004 | 0 | { |
1005 | 0 | e->xcurr = e->xbot; |
1006 | 0 | e->ycurr = e->ybot; |
1007 | 0 | e->side = esRight; |
1008 | 0 | e->outIdx = -1; |
1009 | 0 | e = e->nextInLML; |
1010 | 0 | } |
1011 | 0 | lm = lm->next; |
1012 | 0 | } |
1013 | 0 | } |
1014 | | //------------------------------------------------------------------------------ |
1015 | | |
1016 | | void ClipperBase::DisposeLocalMinimaList() |
1017 | 0 | { |
1018 | 0 | while( m_MinimaList ) |
1019 | 0 | { |
1020 | 0 | LocalMinima* tmpLm = m_MinimaList->next; |
1021 | 0 | delete m_MinimaList; |
1022 | 0 | m_MinimaList = tmpLm; |
1023 | 0 | } |
1024 | 0 | m_CurrentLM = 0; |
1025 | 0 | } |
1026 | | //------------------------------------------------------------------------------ |
1027 | | |
1028 | | void ClipperBase::PopLocalMinima() |
1029 | 0 | { |
1030 | 0 | if( ! m_CurrentLM ) return; |
1031 | 0 | m_CurrentLM = m_CurrentLM->next; |
1032 | 0 | } |
1033 | | //------------------------------------------------------------------------------ |
1034 | | |
1035 | | IntRect ClipperBase::GetBounds() |
1036 | 0 | { |
1037 | 0 | IntRect result; |
1038 | 0 | LocalMinima* lm = m_MinimaList; |
1039 | 0 | if (!lm) |
1040 | 0 | { |
1041 | 0 | result.left = result.top = result.right = result.bottom = 0; |
1042 | 0 | return result; |
1043 | 0 | } |
1044 | 0 | result.left = lm->leftBound->xbot; |
1045 | 0 | result.top = lm->leftBound->ybot; |
1046 | 0 | result.right = lm->leftBound->xbot; |
1047 | 0 | result.bottom = lm->leftBound->ybot; |
1048 | 0 | while (lm) |
1049 | 0 | { |
1050 | 0 | if (lm->leftBound->ybot > result.bottom) |
1051 | 0 | result.bottom = lm->leftBound->ybot; |
1052 | 0 | TEdge* e = lm->leftBound; |
1053 | 0 | for (;;) { |
1054 | 0 | TEdge* bottomE = e; |
1055 | 0 | while (e->nextInLML) |
1056 | 0 | { |
1057 | 0 | if (e->xbot < result.left) result.left = e->xbot; |
1058 | 0 | if (e->xbot > result.right) result.right = e->xbot; |
1059 | 0 | e = e->nextInLML; |
1060 | 0 | } |
1061 | 0 | if (e->xbot < result.left) result.left = e->xbot; |
1062 | 0 | if (e->xbot > result.right) result.right = e->xbot; |
1063 | 0 | if (e->xtop < result.left) result.left = e->xtop; |
1064 | 0 | if (e->xtop > result.right) result.right = e->xtop; |
1065 | 0 | if (e->ytop < result.top) result.top = e->ytop; |
1066 | |
|
1067 | 0 | if (bottomE == lm->leftBound) e = lm->rightBound; |
1068 | 0 | else break; |
1069 | 0 | } |
1070 | 0 | lm = lm->next; |
1071 | 0 | } |
1072 | 0 | return result; |
1073 | 0 | } |
1074 | | |
1075 | | |
1076 | | //------------------------------------------------------------------------------ |
1077 | | // TClipper methods ... |
1078 | | //------------------------------------------------------------------------------ |
1079 | | |
1080 | 0 | Clipper::Clipper() : ClipperBase() //constructor |
1081 | 0 | { |
1082 | 0 | m_Scanbeam = 0; |
1083 | 0 | m_ActiveEdges = 0; |
1084 | 0 | m_SortedEdges = 0; |
1085 | 0 | m_IntersectNodes = 0; |
1086 | 0 | m_ExecuteLocked = false; |
1087 | 0 | m_UseFullRange = false; |
1088 | 0 | m_ReverseOutput = false; |
1089 | 0 | } Unexecuted instantiation: ClipperLib::Clipper::Clipper() Unexecuted instantiation: ClipperLib::Clipper::Clipper() |
1090 | | //------------------------------------------------------------------------------ |
1091 | | |
1092 | | Clipper::~Clipper() //destructor |
1093 | 0 | { |
1094 | 0 | Clear(); |
1095 | 0 | DisposeScanbeamList(); |
1096 | 0 | } |
1097 | | //------------------------------------------------------------------------------ |
1098 | | |
1099 | | void Clipper::Clear() |
1100 | 0 | { |
1101 | 0 | if (m_edges.size() == 0) return; //avoids problems with ClipperBase destructor |
1102 | 0 | DisposeAllPolyPts(); |
1103 | 0 | ClipperBase::Clear(); |
1104 | 0 | } |
1105 | | //------------------------------------------------------------------------------ |
1106 | | |
1107 | | void Clipper::DisposeScanbeamList() |
1108 | 0 | { |
1109 | 0 | while ( m_Scanbeam ) { |
1110 | 0 | Scanbeam* sb2 = m_Scanbeam->next; |
1111 | 0 | delete m_Scanbeam; |
1112 | 0 | m_Scanbeam = sb2; |
1113 | 0 | } |
1114 | 0 | } |
1115 | | //------------------------------------------------------------------------------ |
1116 | | |
1117 | | void Clipper::Reset() |
1118 | 0 | { |
1119 | 0 | ClipperBase::Reset(); |
1120 | 0 | m_Scanbeam = 0; |
1121 | 0 | m_ActiveEdges = 0; |
1122 | 0 | m_SortedEdges = 0; |
1123 | 0 | LocalMinima* lm = m_MinimaList; |
1124 | 0 | while (lm) |
1125 | 0 | { |
1126 | 0 | InsertScanbeam(lm->Y); |
1127 | 0 | InsertScanbeam(lm->leftBound->ytop); |
1128 | 0 | lm = lm->next; |
1129 | 0 | } |
1130 | 0 | } |
1131 | | //------------------------------------------------------------------------------ |
1132 | | |
1133 | | bool Clipper::Execute(ClipType clipType, Polygons &solution, |
1134 | | PolyFillType subjFillType, PolyFillType clipFillType) |
1135 | 0 | { |
1136 | 0 | if( m_ExecuteLocked ) return false; |
1137 | 0 | m_ExecuteLocked = true; |
1138 | 0 | solution.resize(0); |
1139 | 0 | m_SubjFillType = subjFillType; |
1140 | 0 | m_ClipFillType = clipFillType; |
1141 | 0 | m_ClipType = clipType; |
1142 | 0 | bool succeeded = ExecuteInternal(false); |
1143 | 0 | if (succeeded) BuildResult(solution); |
1144 | 0 | m_ExecuteLocked = false; |
1145 | 0 | return succeeded; |
1146 | 0 | } |
1147 | | //------------------------------------------------------------------------------ |
1148 | | |
1149 | | bool Clipper::Execute(ClipType clipType, ExPolygons &solution, |
1150 | | PolyFillType subjFillType, PolyFillType clipFillType) |
1151 | 0 | { |
1152 | 0 | if( m_ExecuteLocked ) return false; |
1153 | 0 | m_ExecuteLocked = true; |
1154 | 0 | solution.resize(0); |
1155 | 0 | m_SubjFillType = subjFillType; |
1156 | 0 | m_ClipFillType = clipFillType; |
1157 | 0 | m_ClipType = clipType; |
1158 | 0 | bool succeeded = ExecuteInternal(true); |
1159 | 0 | if (succeeded) BuildResultEx(solution); |
1160 | 0 | m_ExecuteLocked = false; |
1161 | 0 | return succeeded; |
1162 | 0 | } |
1163 | | //------------------------------------------------------------------------------ |
1164 | | |
1165 | | bool PolySort(OutRec *or1, OutRec *or2) |
1166 | 0 | { |
1167 | 0 | if (or1 == or2) return false; |
1168 | 0 | if (!or1->pts || !or2->pts) |
1169 | 0 | { |
1170 | 0 | if (or1->pts != or2->pts) |
1171 | 0 | { |
1172 | 0 | if (or1->pts) return true; else return false; |
1173 | 0 | } |
1174 | 0 | else return false; |
1175 | 0 | } |
1176 | 0 | int i1, i2; |
1177 | 0 | if (or1->isHole) |
1178 | 0 | i1 = or1->FirstLeft->idx; else |
1179 | 0 | i1 = or1->idx; |
1180 | 0 | if (or2->isHole) |
1181 | 0 | i2 = or2->FirstLeft->idx; else |
1182 | 0 | i2 = or2->idx; |
1183 | 0 | int result = i1 - i2; |
1184 | 0 | if (result == 0 && (or1->isHole != or2->isHole)) |
1185 | 0 | { |
1186 | 0 | if (or1->isHole) return false; |
1187 | 0 | else return true; |
1188 | 0 | } |
1189 | 0 | else return result < 0; |
1190 | 0 | } |
1191 | | //------------------------------------------------------------------------------ |
1192 | | |
1193 | | OutRec* FindAppendLinkEnd(OutRec *outRec) |
1194 | 0 | { |
1195 | 0 | while (outRec->AppendLink) outRec = outRec->AppendLink; |
1196 | 0 | return outRec; |
1197 | 0 | } |
1198 | | //------------------------------------------------------------------------------ |
1199 | | |
1200 | | void Clipper::FixHoleLinkage(OutRec *outRec) |
1201 | 0 | { |
1202 | 0 | OutRec *tmp; |
1203 | 0 | if (outRec->bottomPt) |
1204 | 0 | tmp = m_PolyOuts[outRec->bottomPt->idx]->FirstLeft; |
1205 | 0 | else |
1206 | 0 | tmp = outRec->FirstLeft; |
1207 | 0 | if (outRec == tmp) throw clipperException("HoleLinkage error"); |
1208 | | |
1209 | 0 | if (tmp) |
1210 | 0 | { |
1211 | 0 | if (tmp->AppendLink) tmp = FindAppendLinkEnd(tmp); |
1212 | 0 | if (tmp == outRec) tmp = 0; |
1213 | 0 | else if (tmp->isHole) |
1214 | 0 | { |
1215 | 0 | FixHoleLinkage(tmp); |
1216 | 0 | tmp = tmp->FirstLeft; |
1217 | 0 | } |
1218 | 0 | } |
1219 | 0 | outRec->FirstLeft = tmp; |
1220 | 0 | if (!tmp) outRec->isHole = false; |
1221 | 0 | outRec->AppendLink = 0; |
1222 | 0 | } |
1223 | | //------------------------------------------------------------------------------ |
1224 | | |
1225 | | bool Clipper::ExecuteInternal(bool fixHoleLinkages) |
1226 | 0 | { |
1227 | 0 | bool succeeded; |
1228 | 0 | try { |
1229 | 0 | Reset(); |
1230 | 0 | if (!m_CurrentLM ) return true; |
1231 | 0 | long64 botY = PopScanbeam(); |
1232 | 0 | do { |
1233 | 0 | InsertLocalMinimaIntoAEL(botY); |
1234 | 0 | ClearHorzJoins(); |
1235 | 0 | ProcessHorizontals(); |
1236 | 0 | long64 topY = PopScanbeam(); |
1237 | 0 | succeeded = ProcessIntersections(botY, topY); |
1238 | 0 | if (!succeeded) break; |
1239 | 0 | ProcessEdgesAtTopOfScanbeam(topY); |
1240 | 0 | botY = topY; |
1241 | 0 | } while( m_Scanbeam ); |
1242 | 0 | } |
1243 | 0 | catch(...) { |
1244 | 0 | succeeded = false; |
1245 | 0 | } |
1246 | | |
1247 | 0 | if (succeeded) |
1248 | 0 | { |
1249 | | //tidy up output polygons and fix orientations where necessary ... |
1250 | 0 | for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) |
1251 | 0 | { |
1252 | 0 | OutRec *outRec = m_PolyOuts[i]; |
1253 | 0 | if (!outRec->pts) continue; |
1254 | 0 | FixupOutPolygon(*outRec); |
1255 | 0 | if (!outRec->pts) continue; |
1256 | 0 | if (outRec->isHole && fixHoleLinkages) FixHoleLinkage(outRec); |
1257 | 0 | if (outRec->isHole == |
1258 | 0 | (m_ReverseOutput ^ Orientation(outRec, m_UseFullRange))) |
1259 | 0 | ReversePolyPtLinks(*outRec->pts); |
1260 | 0 | } |
1261 | |
|
1262 | 0 | JoinCommonEdges(fixHoleLinkages); |
1263 | 0 | if (fixHoleLinkages) |
1264 | 0 | std::sort(m_PolyOuts.begin(), m_PolyOuts.end(), PolySort); |
1265 | 0 | } |
1266 | |
|
1267 | 0 | ClearJoins(); |
1268 | 0 | ClearHorzJoins(); |
1269 | 0 | return succeeded; |
1270 | 0 | } |
1271 | | //------------------------------------------------------------------------------ |
1272 | | |
1273 | | void Clipper::InsertScanbeam(const long64 Y) |
1274 | 0 | { |
1275 | 0 | if( !m_Scanbeam ) |
1276 | 0 | { |
1277 | 0 | m_Scanbeam = new Scanbeam; |
1278 | 0 | m_Scanbeam->next = 0; |
1279 | 0 | m_Scanbeam->Y = Y; |
1280 | 0 | } |
1281 | 0 | else if( Y > m_Scanbeam->Y ) |
1282 | 0 | { |
1283 | 0 | Scanbeam* newSb = new Scanbeam; |
1284 | 0 | newSb->Y = Y; |
1285 | 0 | newSb->next = m_Scanbeam; |
1286 | 0 | m_Scanbeam = newSb; |
1287 | 0 | } else |
1288 | 0 | { |
1289 | 0 | Scanbeam* sb2 = m_Scanbeam; |
1290 | 0 | while( sb2->next && ( Y <= sb2->next->Y ) ) sb2 = sb2->next; |
1291 | 0 | if( Y == sb2->Y ) return; //ie ignores duplicates |
1292 | 0 | Scanbeam* newSb = new Scanbeam; |
1293 | 0 | newSb->Y = Y; |
1294 | 0 | newSb->next = sb2->next; |
1295 | 0 | sb2->next = newSb; |
1296 | 0 | } |
1297 | 0 | } |
1298 | | //------------------------------------------------------------------------------ |
1299 | | |
1300 | | long64 Clipper::PopScanbeam() |
1301 | 0 | { |
1302 | 0 | long64 Y = m_Scanbeam->Y; |
1303 | 0 | Scanbeam* sb2 = m_Scanbeam; |
1304 | 0 | m_Scanbeam = m_Scanbeam->next; |
1305 | 0 | delete sb2; |
1306 | 0 | return Y; |
1307 | 0 | } |
1308 | | //------------------------------------------------------------------------------ |
1309 | | |
1310 | 0 | void Clipper::DisposeAllPolyPts(){ |
1311 | 0 | for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) |
1312 | 0 | DisposeOutRec(i); |
1313 | 0 | m_PolyOuts.clear(); |
1314 | 0 | } |
1315 | | //------------------------------------------------------------------------------ |
1316 | | |
1317 | | void Clipper::DisposeOutRec(PolyOutList::size_type index, bool ignorePts) |
1318 | 0 | { |
1319 | 0 | OutRec *outRec = m_PolyOuts[index]; |
1320 | 0 | if (!ignorePts && outRec->pts) DisposeOutPts(outRec->pts); |
1321 | 0 | delete outRec; |
1322 | 0 | m_PolyOuts[index] = 0; |
1323 | 0 | } |
1324 | | //------------------------------------------------------------------------------ |
1325 | | |
1326 | | void Clipper::SetWindingCount(TEdge &edge) |
1327 | 0 | { |
1328 | 0 | TEdge *e = edge.prevInAEL; |
1329 | | //find the edge of the same polytype that immediately precedes 'edge' in AEL |
1330 | 0 | while ( e && e->polyType != edge.polyType ) e = e->prevInAEL; |
1331 | 0 | if ( !e ) |
1332 | 0 | { |
1333 | 0 | edge.windCnt = edge.windDelta; |
1334 | 0 | edge.windCnt2 = 0; |
1335 | 0 | e = m_ActiveEdges; //ie get ready to calc windCnt2 |
1336 | 0 | } else if ( IsEvenOddFillType(edge) ) |
1337 | 0 | { |
1338 | | //EvenOdd filling ... |
1339 | 0 | edge.windCnt = 1; |
1340 | 0 | edge.windCnt2 = e->windCnt2; |
1341 | 0 | e = e->nextInAEL; //ie get ready to calc windCnt2 |
1342 | 0 | } else |
1343 | 0 | { |
1344 | | //nonZero, Positive or Negative filling ... |
1345 | 0 | if ( e->windCnt * e->windDelta < 0 ) |
1346 | 0 | { |
1347 | 0 | if (Abs(e->windCnt) > 1) |
1348 | 0 | { |
1349 | 0 | if (e->windDelta * edge.windDelta < 0) edge.windCnt = e->windCnt; |
1350 | 0 | else edge.windCnt = e->windCnt + edge.windDelta; |
1351 | 0 | } else |
1352 | 0 | edge.windCnt = e->windCnt + e->windDelta + edge.windDelta; |
1353 | 0 | } else |
1354 | 0 | { |
1355 | 0 | if ( Abs(e->windCnt) > 1 && e->windDelta * edge.windDelta < 0) |
1356 | 0 | edge.windCnt = e->windCnt; |
1357 | 0 | else if ( e->windCnt + edge.windDelta == 0 ) |
1358 | 0 | edge.windCnt = e->windCnt; |
1359 | 0 | else edge.windCnt = e->windCnt + edge.windDelta; |
1360 | 0 | } |
1361 | 0 | edge.windCnt2 = e->windCnt2; |
1362 | 0 | e = e->nextInAEL; //ie get ready to calc windCnt2 |
1363 | 0 | } |
1364 | | |
1365 | | //update windCnt2 ... |
1366 | 0 | if ( IsEvenOddAltFillType(edge) ) |
1367 | 0 | { |
1368 | | //EvenOdd filling ... |
1369 | 0 | while ( e != &edge ) |
1370 | 0 | { |
1371 | 0 | edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0; |
1372 | 0 | e = e->nextInAEL; |
1373 | 0 | } |
1374 | 0 | } else |
1375 | 0 | { |
1376 | | //nonZero, Positive or Negative filling ... |
1377 | 0 | while ( e != &edge ) |
1378 | 0 | { |
1379 | 0 | edge.windCnt2 += e->windDelta; |
1380 | 0 | e = e->nextInAEL; |
1381 | 0 | } |
1382 | 0 | } |
1383 | 0 | } |
1384 | | //------------------------------------------------------------------------------ |
1385 | | |
1386 | | bool Clipper::IsEvenOddFillType(const TEdge& edge) const |
1387 | 0 | { |
1388 | 0 | if (edge.polyType == ptSubject) |
1389 | 0 | return m_SubjFillType == pftEvenOdd; else |
1390 | 0 | return m_ClipFillType == pftEvenOdd; |
1391 | 0 | } |
1392 | | //------------------------------------------------------------------------------ |
1393 | | |
1394 | | bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const |
1395 | 0 | { |
1396 | 0 | if (edge.polyType == ptSubject) |
1397 | 0 | return m_ClipFillType == pftEvenOdd; else |
1398 | 0 | return m_SubjFillType == pftEvenOdd; |
1399 | 0 | } |
1400 | | //------------------------------------------------------------------------------ |
1401 | | |
1402 | | bool Clipper::IsContributing(const TEdge& edge) const |
1403 | 0 | { |
1404 | 0 | PolyFillType pft, pft2; |
1405 | 0 | if (edge.polyType == ptSubject) |
1406 | 0 | { |
1407 | 0 | pft = m_SubjFillType; |
1408 | 0 | pft2 = m_ClipFillType; |
1409 | 0 | } else |
1410 | 0 | { |
1411 | 0 | pft = m_ClipFillType; |
1412 | 0 | pft2 = m_SubjFillType; |
1413 | 0 | } |
1414 | |
|
1415 | 0 | switch(pft) |
1416 | 0 | { |
1417 | 0 | case pftEvenOdd: |
1418 | 0 | case pftNonZero: |
1419 | 0 | if (Abs(edge.windCnt) != 1) return false; |
1420 | 0 | break; |
1421 | 0 | case pftPositive: |
1422 | 0 | if (edge.windCnt != 1) return false; |
1423 | 0 | break; |
1424 | 0 | default: //pftNegative |
1425 | 0 | if (edge.windCnt != -1) return false; |
1426 | 0 | } |
1427 | | |
1428 | 0 | switch(m_ClipType) |
1429 | 0 | { |
1430 | 0 | case ctIntersection: |
1431 | 0 | switch(pft2) |
1432 | 0 | { |
1433 | 0 | case pftEvenOdd: |
1434 | 0 | case pftNonZero: |
1435 | 0 | return (edge.windCnt2 != 0); |
1436 | 0 | case pftPositive: |
1437 | 0 | return (edge.windCnt2 > 0); |
1438 | 0 | default: |
1439 | 0 | return (edge.windCnt2 < 0); |
1440 | 0 | } |
1441 | 0 | case ctUnion: |
1442 | 0 | switch(pft2) |
1443 | 0 | { |
1444 | 0 | case pftEvenOdd: |
1445 | 0 | case pftNonZero: |
1446 | 0 | return (edge.windCnt2 == 0); |
1447 | 0 | case pftPositive: |
1448 | 0 | return (edge.windCnt2 <= 0); |
1449 | 0 | default: |
1450 | 0 | return (edge.windCnt2 >= 0); |
1451 | 0 | } |
1452 | 0 | case ctDifference: |
1453 | 0 | if (edge.polyType == ptSubject) |
1454 | 0 | switch(pft2) |
1455 | 0 | { |
1456 | 0 | case pftEvenOdd: |
1457 | 0 | case pftNonZero: |
1458 | 0 | return (edge.windCnt2 == 0); |
1459 | 0 | case pftPositive: |
1460 | 0 | return (edge.windCnt2 <= 0); |
1461 | 0 | default: |
1462 | 0 | return (edge.windCnt2 >= 0); |
1463 | 0 | } |
1464 | 0 | else |
1465 | 0 | switch(pft2) |
1466 | 0 | { |
1467 | 0 | case pftEvenOdd: |
1468 | 0 | case pftNonZero: |
1469 | 0 | return (edge.windCnt2 != 0); |
1470 | 0 | case pftPositive: |
1471 | 0 | return (edge.windCnt2 > 0); |
1472 | 0 | default: |
1473 | 0 | return (edge.windCnt2 < 0); |
1474 | 0 | } |
1475 | 0 | default: |
1476 | 0 | return true; |
1477 | 0 | } |
1478 | 0 | } |
1479 | | //------------------------------------------------------------------------------ |
1480 | | |
1481 | | void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) |
1482 | 0 | { |
1483 | 0 | if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) ) |
1484 | 0 | { |
1485 | 0 | AddOutPt( e1, e2, pt ); |
1486 | 0 | e2->outIdx = e1->outIdx; |
1487 | 0 | e1->side = esLeft; |
1488 | 0 | e2->side = esRight; |
1489 | 0 | } else |
1490 | 0 | { |
1491 | 0 | AddOutPt( e2, e1, pt ); |
1492 | 0 | e1->outIdx = e2->outIdx; |
1493 | 0 | e1->side = esRight; |
1494 | 0 | e2->side = esLeft; |
1495 | 0 | } |
1496 | 0 | } |
1497 | | //------------------------------------------------------------------------------ |
1498 | | |
1499 | | void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt) |
1500 | 0 | { |
1501 | 0 | AddOutPt( e1, 0, pt ); |
1502 | 0 | if( e1->outIdx == e2->outIdx ) |
1503 | 0 | { |
1504 | 0 | e1->outIdx = -1; |
1505 | 0 | e2->outIdx = -1; |
1506 | 0 | } |
1507 | 0 | else |
1508 | 0 | AppendPolygon( e1, e2 ); |
1509 | 0 | } |
1510 | | //------------------------------------------------------------------------------ |
1511 | | |
1512 | | void Clipper::AddEdgeToSEL(TEdge *edge) |
1513 | 0 | { |
1514 | | //SEL pointers in PEdge are reused to build a list of horizontal edges. |
1515 | | //However, we don't need to worry about order with horizontal edge processing. |
1516 | 0 | if( !m_SortedEdges ) |
1517 | 0 | { |
1518 | 0 | m_SortedEdges = edge; |
1519 | 0 | edge->prevInSEL = 0; |
1520 | 0 | edge->nextInSEL = 0; |
1521 | 0 | } |
1522 | 0 | else |
1523 | 0 | { |
1524 | 0 | edge->nextInSEL = m_SortedEdges; |
1525 | 0 | edge->prevInSEL = 0; |
1526 | 0 | m_SortedEdges->prevInSEL = edge; |
1527 | 0 | m_SortedEdges = edge; |
1528 | 0 | } |
1529 | 0 | } |
1530 | | //------------------------------------------------------------------------------ |
1531 | | |
1532 | | void Clipper::CopyAELToSEL() |
1533 | 0 | { |
1534 | 0 | TEdge* e = m_ActiveEdges; |
1535 | 0 | m_SortedEdges = e; |
1536 | 0 | if (!m_ActiveEdges) return; |
1537 | 0 | m_SortedEdges->prevInSEL = 0; |
1538 | 0 | e = e->nextInAEL; |
1539 | 0 | while ( e ) |
1540 | 0 | { |
1541 | 0 | e->prevInSEL = e->prevInAEL; |
1542 | 0 | e->prevInSEL->nextInSEL = e; |
1543 | 0 | e->nextInSEL = 0; |
1544 | 0 | e = e->nextInAEL; |
1545 | 0 | } |
1546 | 0 | } |
1547 | | //------------------------------------------------------------------------------ |
1548 | | |
1549 | | void Clipper::AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx, int e2OutIdx) |
1550 | 0 | { |
1551 | 0 | JoinRec* jr = new JoinRec; |
1552 | 0 | if (e1OutIdx >= 0) |
1553 | 0 | jr->poly1Idx = e1OutIdx; else |
1554 | 0 | jr->poly1Idx = e1->outIdx; |
1555 | 0 | jr->pt1a = IntPoint(e1->xcurr, e1->ycurr); |
1556 | 0 | jr->pt1b = IntPoint(e1->xtop, e1->ytop); |
1557 | 0 | if (e2OutIdx >= 0) |
1558 | 0 | jr->poly2Idx = e2OutIdx; else |
1559 | 0 | jr->poly2Idx = e2->outIdx; |
1560 | 0 | jr->pt2a = IntPoint(e2->xcurr, e2->ycurr); |
1561 | 0 | jr->pt2b = IntPoint(e2->xtop, e2->ytop); |
1562 | 0 | m_Joins.push_back(jr); |
1563 | 0 | } |
1564 | | //------------------------------------------------------------------------------ |
1565 | | |
1566 | | void Clipper::ClearJoins() |
1567 | 0 | { |
1568 | 0 | for (JoinList::size_type i = 0; i < m_Joins.size(); i++) |
1569 | 0 | delete m_Joins[i]; |
1570 | 0 | m_Joins.resize(0); |
1571 | 0 | } |
1572 | | //------------------------------------------------------------------------------ |
1573 | | |
1574 | | void Clipper::AddHorzJoin(TEdge *e, int idx) |
1575 | 0 | { |
1576 | 0 | HorzJoinRec* hj = new HorzJoinRec; |
1577 | 0 | hj->edge = e; |
1578 | 0 | hj->savedIdx = idx; |
1579 | 0 | m_HorizJoins.push_back(hj); |
1580 | 0 | } |
1581 | | //------------------------------------------------------------------------------ |
1582 | | |
1583 | | void Clipper::ClearHorzJoins() |
1584 | 0 | { |
1585 | 0 | for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++) |
1586 | 0 | delete m_HorizJoins[i]; |
1587 | 0 | m_HorizJoins.resize(0); |
1588 | 0 | } |
1589 | | //------------------------------------------------------------------------------ |
1590 | | |
1591 | | void Clipper::InsertLocalMinimaIntoAEL( const long64 botY) |
1592 | 0 | { |
1593 | 0 | while( m_CurrentLM && ( m_CurrentLM->Y == botY ) ) |
1594 | 0 | { |
1595 | 0 | TEdge* lb = m_CurrentLM->leftBound; |
1596 | 0 | TEdge* rb = m_CurrentLM->rightBound; |
1597 | |
|
1598 | 0 | InsertEdgeIntoAEL( lb ); |
1599 | 0 | InsertScanbeam( lb->ytop ); |
1600 | 0 | InsertEdgeIntoAEL( rb ); |
1601 | |
|
1602 | 0 | if (IsEvenOddFillType(*lb)) |
1603 | 0 | { |
1604 | 0 | lb->windDelta = 1; |
1605 | 0 | rb->windDelta = 1; |
1606 | 0 | } |
1607 | 0 | else |
1608 | 0 | { |
1609 | 0 | rb->windDelta = -lb->windDelta; |
1610 | 0 | } |
1611 | 0 | SetWindingCount( *lb ); |
1612 | 0 | rb->windCnt = lb->windCnt; |
1613 | 0 | rb->windCnt2 = lb->windCnt2; |
1614 | |
|
1615 | 0 | if( NEAR_EQUAL(rb->dx, HORIZONTAL) ) |
1616 | 0 | { |
1617 | | //nb: only rightbounds can have a horizontal bottom edge |
1618 | 0 | AddEdgeToSEL( rb ); |
1619 | 0 | InsertScanbeam( rb->nextInLML->ytop ); |
1620 | 0 | } |
1621 | 0 | else |
1622 | 0 | InsertScanbeam( rb->ytop ); |
1623 | |
|
1624 | 0 | if( IsContributing(*lb) ) |
1625 | 0 | AddLocalMinPoly( lb, rb, IntPoint(lb->xcurr, m_CurrentLM->Y) ); |
1626 | | |
1627 | | //if output polygons share an edge, they'll need joining later ... |
1628 | 0 | if (lb->outIdx >= 0 && lb->prevInAEL && |
1629 | 0 | lb->prevInAEL->outIdx >= 0 && lb->prevInAEL->xcurr == lb->xbot && |
1630 | 0 | SlopesEqual(*lb, *lb->prevInAEL, m_UseFullRange)) |
1631 | 0 | AddJoin(lb, lb->prevInAEL); |
1632 | | |
1633 | | //if any output polygons share an edge, they'll need joining later ... |
1634 | 0 | if (rb->outIdx >= 0) |
1635 | 0 | { |
1636 | 0 | if (NEAR_EQUAL(rb->dx, HORIZONTAL)) |
1637 | 0 | { |
1638 | 0 | for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) |
1639 | 0 | { |
1640 | 0 | IntPoint pt, pt2; //returned by GetOverlapSegment() but unused here. |
1641 | 0 | HorzJoinRec* hj = m_HorizJoins[i]; |
1642 | | //if horizontals rb and hj.edge overlap, flag for joining later ... |
1643 | 0 | if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), |
1644 | 0 | IntPoint(hj->edge->xtop, hj->edge->ytop), |
1645 | 0 | IntPoint(rb->xbot, rb->ybot), |
1646 | 0 | IntPoint(rb->xtop, rb->ytop), pt, pt2)) |
1647 | 0 | AddJoin(hj->edge, rb, hj->savedIdx); |
1648 | 0 | } |
1649 | 0 | } |
1650 | 0 | } |
1651 | |
|
1652 | 0 | if( lb->nextInAEL != rb ) |
1653 | 0 | { |
1654 | 0 | if (rb->outIdx >= 0 && rb->prevInAEL->outIdx >= 0 && |
1655 | 0 | SlopesEqual(*rb->prevInAEL, *rb, m_UseFullRange)) |
1656 | 0 | AddJoin(rb, rb->prevInAEL); |
1657 | |
|
1658 | 0 | TEdge* e = lb->nextInAEL; |
1659 | 0 | IntPoint pt = IntPoint(lb->xcurr, lb->ycurr); |
1660 | 0 | while( e != rb ) |
1661 | 0 | { |
1662 | 0 | if(!e) throw clipperException("InsertLocalMinimaIntoAEL: missing rightbound!"); |
1663 | | //nb: For calculating winding counts etc, IntersectEdges() assumes |
1664 | | //that param1 will be to the right of param2 ABOVE the intersection ... |
1665 | 0 | IntersectEdges( rb , e , pt , ipNone); //order important here |
1666 | 0 | e = e->nextInAEL; |
1667 | 0 | } |
1668 | 0 | } |
1669 | 0 | PopLocalMinima(); |
1670 | 0 | } |
1671 | 0 | } |
1672 | | //------------------------------------------------------------------------------ |
1673 | | |
1674 | | void Clipper::DeleteFromAEL(TEdge *e) |
1675 | 0 | { |
1676 | 0 | TEdge* AelPrev = e->prevInAEL; |
1677 | 0 | TEdge* AelNext = e->nextInAEL; |
1678 | 0 | if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted |
1679 | 0 | if( AelPrev ) AelPrev->nextInAEL = AelNext; |
1680 | 0 | else m_ActiveEdges = AelNext; |
1681 | 0 | if( AelNext ) AelNext->prevInAEL = AelPrev; |
1682 | 0 | e->nextInAEL = 0; |
1683 | 0 | e->prevInAEL = 0; |
1684 | 0 | } |
1685 | | //------------------------------------------------------------------------------ |
1686 | | |
1687 | | void Clipper::DeleteFromSEL(TEdge *e) |
1688 | 0 | { |
1689 | 0 | TEdge* SelPrev = e->prevInSEL; |
1690 | 0 | TEdge* SelNext = e->nextInSEL; |
1691 | 0 | if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted |
1692 | 0 | if( SelPrev ) SelPrev->nextInSEL = SelNext; |
1693 | 0 | else m_SortedEdges = SelNext; |
1694 | 0 | if( SelNext ) SelNext->prevInSEL = SelPrev; |
1695 | 0 | e->nextInSEL = 0; |
1696 | 0 | e->prevInSEL = 0; |
1697 | 0 | } |
1698 | | //------------------------------------------------------------------------------ |
1699 | | |
1700 | | void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, |
1701 | | const IntPoint &pt, IntersectProtects protects) |
1702 | 0 | { |
1703 | | //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before |
1704 | | //e2 in AEL except when e1 is being inserted at the intersection point ... |
1705 | 0 | bool e1stops = !(ipLeft & protects) && !e1->nextInLML && |
1706 | 0 | e1->xtop == pt.X && e1->ytop == pt.Y; |
1707 | 0 | bool e2stops = !(ipRight & protects) && !e2->nextInLML && |
1708 | 0 | e2->xtop == pt.X && e2->ytop == pt.Y; |
1709 | 0 | bool e1Contributing = ( e1->outIdx >= 0 ); |
1710 | 0 | bool e2contributing = ( e2->outIdx >= 0 ); |
1711 | | |
1712 | | //update winding counts... |
1713 | | //assumes that e1 will be to the right of e2 ABOVE the intersection |
1714 | 0 | if ( e1->polyType == e2->polyType ) |
1715 | 0 | { |
1716 | 0 | if ( IsEvenOddFillType( *e1) ) |
1717 | 0 | { |
1718 | 0 | int oldE1WindCnt = e1->windCnt; |
1719 | 0 | e1->windCnt = e2->windCnt; |
1720 | 0 | e2->windCnt = oldE1WindCnt; |
1721 | 0 | } else |
1722 | 0 | { |
1723 | 0 | if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt; |
1724 | 0 | else e1->windCnt += e2->windDelta; |
1725 | 0 | if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt; |
1726 | 0 | else e2->windCnt -= e1->windDelta; |
1727 | 0 | } |
1728 | 0 | } else |
1729 | 0 | { |
1730 | 0 | if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta; |
1731 | 0 | else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0; |
1732 | 0 | if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta; |
1733 | 0 | else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0; |
1734 | 0 | } |
1735 | |
|
1736 | 0 | PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; |
1737 | 0 | if (e1->polyType == ptSubject) |
1738 | 0 | { |
1739 | 0 | e1FillType = m_SubjFillType; |
1740 | 0 | e1FillType2 = m_ClipFillType; |
1741 | 0 | } else |
1742 | 0 | { |
1743 | 0 | e1FillType = m_ClipFillType; |
1744 | 0 | e1FillType2 = m_SubjFillType; |
1745 | 0 | } |
1746 | 0 | if (e2->polyType == ptSubject) |
1747 | 0 | { |
1748 | 0 | e2FillType = m_SubjFillType; |
1749 | 0 | e2FillType2 = m_ClipFillType; |
1750 | 0 | } else |
1751 | 0 | { |
1752 | 0 | e2FillType = m_ClipFillType; |
1753 | 0 | e2FillType2 = m_SubjFillType; |
1754 | 0 | } |
1755 | |
|
1756 | 0 | long64 e1Wc, e2Wc; |
1757 | 0 | switch (e1FillType) |
1758 | 0 | { |
1759 | 0 | case pftPositive: e1Wc = e1->windCnt; break; |
1760 | 0 | case pftNegative: e1Wc = -e1->windCnt; break; |
1761 | 0 | default: e1Wc = Abs(e1->windCnt); |
1762 | 0 | } |
1763 | 0 | switch(e2FillType) |
1764 | 0 | { |
1765 | 0 | case pftPositive: e2Wc = e2->windCnt; break; |
1766 | 0 | case pftNegative: e2Wc = -e2->windCnt; break; |
1767 | 0 | default: e2Wc = Abs(e2->windCnt); |
1768 | 0 | } |
1769 | | |
1770 | 0 | if ( e1Contributing && e2contributing ) |
1771 | 0 | { |
1772 | 0 | if ( e1stops || e2stops || |
1773 | 0 | (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || |
1774 | 0 | (e1->polyType != e2->polyType && m_ClipType != ctXor) ) |
1775 | 0 | AddLocalMaxPoly(e1, e2, pt); |
1776 | 0 | else |
1777 | 0 | DoBothEdges( e1, e2, pt ); |
1778 | 0 | } |
1779 | 0 | else if ( e1Contributing ) |
1780 | 0 | { |
1781 | 0 | if ((e2Wc == 0 || e2Wc == 1) && |
1782 | 0 | (m_ClipType != ctIntersection || |
1783 | 0 | e2->polyType == ptSubject || (e2->windCnt2 != 0))) |
1784 | 0 | DoEdge1(e1, e2, pt); |
1785 | 0 | } |
1786 | 0 | else if ( e2contributing ) |
1787 | 0 | { |
1788 | 0 | if ((e1Wc == 0 || e1Wc == 1) && |
1789 | 0 | (m_ClipType != ctIntersection || |
1790 | 0 | e1->polyType == ptSubject || (e1->windCnt2 != 0))) |
1791 | 0 | DoEdge2(e1, e2, pt); |
1792 | 0 | } |
1793 | 0 | else if ( (e1Wc == 0 || e1Wc == 1) && |
1794 | 0 | (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops ) |
1795 | 0 | { |
1796 | | //neither edge is currently contributing ... |
1797 | |
|
1798 | 0 | long64 e1Wc2, e2Wc2; |
1799 | 0 | switch (e1FillType2) |
1800 | 0 | { |
1801 | 0 | case pftPositive: e1Wc2 = e1->windCnt2; break; |
1802 | 0 | case pftNegative : e1Wc2 = -e1->windCnt2; break; |
1803 | 0 | default: e1Wc2 = Abs(e1->windCnt2); |
1804 | 0 | } |
1805 | 0 | switch (e2FillType2) |
1806 | 0 | { |
1807 | 0 | case pftPositive: e2Wc2 = e2->windCnt2; break; |
1808 | 0 | case pftNegative: e2Wc2 = -e2->windCnt2; break; |
1809 | 0 | default: e2Wc2 = Abs(e2->windCnt2); |
1810 | 0 | } |
1811 | | |
1812 | 0 | if (e1->polyType != e2->polyType) |
1813 | 0 | AddLocalMinPoly(e1, e2, pt); |
1814 | 0 | else if (e1Wc == 1 && e2Wc == 1) |
1815 | 0 | switch( m_ClipType ) { |
1816 | 0 | case ctIntersection: |
1817 | 0 | if (e1Wc2 > 0 && e2Wc2 > 0) |
1818 | 0 | AddLocalMinPoly(e1, e2, pt); |
1819 | 0 | break; |
1820 | 0 | case ctUnion: |
1821 | 0 | if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) |
1822 | 0 | AddLocalMinPoly(e1, e2, pt); |
1823 | 0 | break; |
1824 | 0 | case ctDifference: |
1825 | 0 | if ((e1->polyType == ptClip && e2->polyType == ptClip && |
1826 | 0 | e1Wc2 > 0 && e2Wc2 > 0) || |
1827 | 0 | (e1->polyType == ptSubject && e2->polyType == ptSubject && |
1828 | 0 | e1Wc2 <= 0 && e2Wc2 <= 0)) |
1829 | 0 | AddLocalMinPoly(e1, e2, pt); |
1830 | 0 | break; |
1831 | 0 | case ctXor: |
1832 | 0 | AddLocalMinPoly(e1, e2, pt); |
1833 | 0 | } |
1834 | 0 | else |
1835 | 0 | SwapSides( *e1, *e2 ); |
1836 | 0 | } |
1837 | | |
1838 | 0 | if( (e1stops != e2stops) && |
1839 | 0 | ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) ) |
1840 | 0 | { |
1841 | 0 | SwapSides( *e1, *e2 ); |
1842 | 0 | SwapPolyIndexes( *e1, *e2 ); |
1843 | 0 | } |
1844 | | |
1845 | | //finally, delete any non-contributing maxima edges ... |
1846 | 0 | if( e1stops ) DeleteFromAEL( e1 ); |
1847 | 0 | if( e2stops ) DeleteFromAEL( e2 ); |
1848 | 0 | } |
1849 | | //------------------------------------------------------------------------------ |
1850 | | |
1851 | | void Clipper::SetHoleState(TEdge *e, OutRec *outRec) |
1852 | 0 | { |
1853 | 0 | bool isHole = false; |
1854 | 0 | TEdge *e2 = e->prevInAEL; |
1855 | 0 | while (e2) |
1856 | 0 | { |
1857 | 0 | if (e2->outIdx >= 0) |
1858 | 0 | { |
1859 | 0 | isHole = !isHole; |
1860 | 0 | if (! outRec->FirstLeft) |
1861 | 0 | outRec->FirstLeft = m_PolyOuts[e2->outIdx]; |
1862 | 0 | } |
1863 | 0 | e2 = e2->prevInAEL; |
1864 | 0 | } |
1865 | 0 | if (isHole) outRec->isHole = true; |
1866 | 0 | } |
1867 | | //------------------------------------------------------------------------------ |
1868 | | |
1869 | | bool GetNextNonDupOutPt(OutPt* pp, OutPt*& next) |
1870 | 0 | { |
1871 | 0 | next = pp->next; |
1872 | 0 | while (next != pp && PointsEqual(pp->pt, next->pt)) |
1873 | 0 | next = next->next; |
1874 | 0 | return next != pp; |
1875 | 0 | } |
1876 | | //------------------------------------------------------------------------------ |
1877 | | |
1878 | | bool GetPrevNonDupOutPt(OutPt* pp, OutPt*& prev) |
1879 | 0 | { |
1880 | 0 | prev = pp->prev; |
1881 | 0 | while (prev != pp && PointsEqual(pp->pt, prev->pt)) |
1882 | 0 | prev = prev->prev; |
1883 | 0 | return prev != pp; |
1884 | 0 | } |
1885 | | //------------------------------------------------------------------------------ |
1886 | | |
1887 | | OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) |
1888 | 0 | { |
1889 | | //work out which polygon fragment has the correct hole state ... |
1890 | 0 | OutPt *outPt1 = outRec1->bottomPt; |
1891 | 0 | OutPt *outPt2 = outRec2->bottomPt; |
1892 | 0 | if (outPt1->pt.Y > outPt2->pt.Y) return outRec1; |
1893 | 0 | else if (outPt1->pt.Y < outPt2->pt.Y) return outRec2; |
1894 | 0 | else if (outPt1->pt.X < outPt2->pt.X) return outRec1; |
1895 | 0 | else if (outPt1->pt.X > outPt2->pt.X) return outRec2; |
1896 | 0 | else if (outRec1->bottomE2 == 0) return outRec2; |
1897 | 0 | else if (outRec2->bottomE2 == 0) return outRec1; |
1898 | 0 | else |
1899 | 0 | { |
1900 | 0 | long64 y1 = std::max(outRec1->bottomE1->ybot, outRec1->bottomE2->ybot); |
1901 | 0 | long64 y2 = std::max(outRec2->bottomE1->ybot, outRec2->bottomE2->ybot); |
1902 | 0 | if (y2 == y1 || (y1 > outPt1->pt.Y && y2 > outPt1->pt.Y)) |
1903 | 0 | { |
1904 | 0 | double dx1 = std::max(outRec1->bottomE1->dx, outRec1->bottomE2->dx); |
1905 | 0 | double dx2 = std::max(outRec2->bottomE1->dx, outRec2->bottomE2->dx); |
1906 | 0 | if (dx2 > dx1) return outRec2; else return outRec1; |
1907 | 0 | } |
1908 | 0 | else if (y2 > y1) return outRec2; |
1909 | 0 | else return outRec1; |
1910 | 0 | } |
1911 | 0 | } |
1912 | | //------------------------------------------------------------------------------ |
1913 | | |
1914 | | void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) |
1915 | 0 | { |
1916 | | //get the start and ends of both output polygons ... |
1917 | 0 | OutRec *outRec1 = m_PolyOuts[e1->outIdx]; |
1918 | 0 | OutRec *outRec2 = m_PolyOuts[e2->outIdx]; |
1919 | 0 | OutRec *holeStateRec = GetLowermostRec(outRec1, outRec2); |
1920 | | |
1921 | | //fixup hole status ... |
1922 | 0 | if (holeStateRec == outRec2) |
1923 | 0 | outRec1->isHole = outRec2->isHole; |
1924 | 0 | else |
1925 | 0 | outRec2->isHole = outRec1->isHole; |
1926 | |
|
1927 | 0 | OutPt* p1_lft = outRec1->pts; |
1928 | 0 | OutPt* p1_rt = p1_lft->prev; |
1929 | 0 | OutPt* p2_lft = outRec2->pts; |
1930 | 0 | OutPt* p2_rt = p2_lft->prev; |
1931 | |
|
1932 | 0 | EdgeSide side; |
1933 | | //join e2 poly onto e1 poly and delete pointers to e2 ... |
1934 | 0 | if( e1->side == esLeft ) |
1935 | 0 | { |
1936 | 0 | if( e2->side == esLeft ) |
1937 | 0 | { |
1938 | | //z y x a b c |
1939 | 0 | ReversePolyPtLinks(*p2_lft); |
1940 | 0 | p2_lft->next = p1_lft; |
1941 | 0 | p1_lft->prev = p2_lft; |
1942 | 0 | p1_rt->next = p2_rt; |
1943 | 0 | p2_rt->prev = p1_rt; |
1944 | 0 | outRec1->pts = p2_rt; |
1945 | 0 | } else |
1946 | 0 | { |
1947 | | //x y z a b c |
1948 | 0 | p2_rt->next = p1_lft; |
1949 | 0 | p1_lft->prev = p2_rt; |
1950 | 0 | p2_lft->prev = p1_rt; |
1951 | 0 | p1_rt->next = p2_lft; |
1952 | 0 | outRec1->pts = p2_lft; |
1953 | 0 | } |
1954 | 0 | side = esLeft; |
1955 | 0 | } else |
1956 | 0 | { |
1957 | 0 | if( e2->side == esRight ) |
1958 | 0 | { |
1959 | | //a b c z y x |
1960 | 0 | ReversePolyPtLinks( *p2_lft ); |
1961 | 0 | p1_rt->next = p2_rt; |
1962 | 0 | p2_rt->prev = p1_rt; |
1963 | 0 | p2_lft->next = p1_lft; |
1964 | 0 | p1_lft->prev = p2_lft; |
1965 | 0 | } else |
1966 | 0 | { |
1967 | | //a b c x y z |
1968 | 0 | p1_rt->next = p2_lft; |
1969 | 0 | p2_lft->prev = p1_rt; |
1970 | 0 | p1_lft->prev = p2_rt; |
1971 | 0 | p2_rt->next = p1_lft; |
1972 | 0 | } |
1973 | 0 | side = esRight; |
1974 | 0 | } |
1975 | |
|
1976 | 0 | if (holeStateRec == outRec2) |
1977 | 0 | { |
1978 | 0 | outRec1->bottomPt = outRec2->bottomPt; |
1979 | 0 | outRec1->bottomPt->idx = outRec1->idx; |
1980 | 0 | outRec1->bottomE1 = outRec2->bottomE1; |
1981 | 0 | outRec1->bottomE2 = outRec2->bottomE2; |
1982 | |
|
1983 | 0 | if (outRec2->FirstLeft != outRec1) |
1984 | 0 | outRec1->FirstLeft = outRec2->FirstLeft; |
1985 | 0 | } |
1986 | 0 | outRec2->pts = 0; |
1987 | 0 | outRec2->bottomPt = 0; |
1988 | 0 | outRec2->AppendLink = outRec1; |
1989 | 0 | int OKIdx = e1->outIdx; |
1990 | 0 | int ObsoleteIdx = e2->outIdx; |
1991 | |
|
1992 | 0 | e1->outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly |
1993 | 0 | e2->outIdx = -1; |
1994 | |
|
1995 | 0 | TEdge* e = m_ActiveEdges; |
1996 | 0 | while( e ) |
1997 | 0 | { |
1998 | 0 | if( e->outIdx == ObsoleteIdx ) |
1999 | 0 | { |
2000 | 0 | e->outIdx = OKIdx; |
2001 | 0 | e->side = side; |
2002 | 0 | break; |
2003 | 0 | } |
2004 | 0 | e = e->nextInAEL; |
2005 | 0 | } |
2006 | |
|
2007 | 0 | for (JoinList::size_type i = 0; i < m_Joins.size(); ++i) |
2008 | 0 | { |
2009 | 0 | if (m_Joins[i]->poly1Idx == ObsoleteIdx) m_Joins[i]->poly1Idx = OKIdx; |
2010 | 0 | if (m_Joins[i]->poly2Idx == ObsoleteIdx) m_Joins[i]->poly2Idx = OKIdx; |
2011 | 0 | } |
2012 | |
|
2013 | 0 | for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) |
2014 | 0 | { |
2015 | 0 | if (m_HorizJoins[i]->savedIdx == ObsoleteIdx) |
2016 | 0 | m_HorizJoins[i]->savedIdx = OKIdx; |
2017 | 0 | } |
2018 | |
|
2019 | 0 | } |
2020 | | //------------------------------------------------------------------------------ |
2021 | | |
2022 | | OutRec* Clipper::CreateOutRec() |
2023 | 0 | { |
2024 | 0 | OutRec* result = new OutRec; |
2025 | 0 | result->isHole = false; |
2026 | 0 | result->FirstLeft = 0; |
2027 | 0 | result->AppendLink = 0; |
2028 | 0 | result->pts = 0; |
2029 | 0 | result->bottomPt = 0; |
2030 | 0 | return result; |
2031 | 0 | } |
2032 | | //------------------------------------------------------------------------------ |
2033 | | |
2034 | | void Clipper::AddOutPt(TEdge *e, TEdge *altE, const IntPoint &pt) |
2035 | 0 | { |
2036 | 0 | bool ToFront = (e->side == esLeft); |
2037 | 0 | if( e->outIdx < 0 ) |
2038 | 0 | { |
2039 | 0 | OutRec *outRec = CreateOutRec(); |
2040 | 0 | m_PolyOuts.push_back(outRec); |
2041 | 0 | outRec->idx = (int)m_PolyOuts.size()-1; |
2042 | 0 | e->outIdx = outRec->idx; |
2043 | 0 | OutPt* op = new OutPt; |
2044 | 0 | outRec->pts = op; |
2045 | 0 | outRec->bottomE1 = e; |
2046 | 0 | outRec->bottomE2 = altE; |
2047 | 0 | outRec->bottomPt = op; |
2048 | 0 | op->pt = pt; |
2049 | 0 | op->idx = outRec->idx; |
2050 | 0 | op->next = op; |
2051 | 0 | op->prev = op; |
2052 | 0 | SetHoleState(e, outRec); |
2053 | 0 | } else |
2054 | 0 | { |
2055 | 0 | OutRec *outRec = m_PolyOuts[e->outIdx]; |
2056 | 0 | OutPt* op = outRec->pts; |
2057 | 0 | if ((ToFront && PointsEqual(pt, op->pt)) || |
2058 | 0 | (!ToFront && PointsEqual(pt, op->prev->pt))) return; |
2059 | 0 | OutPt* op2 = new OutPt; |
2060 | 0 | op2->pt = pt; |
2061 | 0 | op2->idx = outRec->idx; |
2062 | 0 | if (op2->pt.Y == outRec->bottomPt->pt.Y && |
2063 | 0 | op2->pt.X < outRec->bottomPt->pt.X) |
2064 | 0 | { |
2065 | 0 | outRec->bottomPt = op2; |
2066 | 0 | outRec->bottomE1 = e; |
2067 | 0 | outRec->bottomE2 = altE; |
2068 | 0 | } |
2069 | 0 | op2->next = op; |
2070 | 0 | op2->prev = op->prev; |
2071 | 0 | op2->prev->next = op2; |
2072 | 0 | op->prev = op2; |
2073 | 0 | if (ToFront) outRec->pts = op2; |
2074 | 0 | } |
2075 | 0 | } |
2076 | | //------------------------------------------------------------------------------ |
2077 | | |
2078 | | void Clipper::ProcessHorizontals() |
2079 | 0 | { |
2080 | 0 | TEdge* horzEdge = m_SortedEdges; |
2081 | 0 | while( horzEdge ) |
2082 | 0 | { |
2083 | 0 | DeleteFromSEL( horzEdge ); |
2084 | 0 | ProcessHorizontal( horzEdge ); |
2085 | 0 | horzEdge = m_SortedEdges; |
2086 | 0 | } |
2087 | 0 | } |
2088 | | //------------------------------------------------------------------------------ |
2089 | | |
2090 | | bool Clipper::IsTopHorz(const long64 XPos) |
2091 | 0 | { |
2092 | 0 | TEdge* e = m_SortedEdges; |
2093 | 0 | while( e ) |
2094 | 0 | { |
2095 | 0 | if( ( XPos >= std::min(e->xcurr, e->xtop) ) && |
2096 | 0 | ( XPos <= std::max(e->xcurr, e->xtop) ) ) return false; |
2097 | 0 | e = e->nextInSEL; |
2098 | 0 | } |
2099 | 0 | return true; |
2100 | 0 | } |
2101 | | //------------------------------------------------------------------------------ |
2102 | | |
2103 | | bool IsMinima(TEdge *e) |
2104 | 0 | { |
2105 | 0 | return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e); |
2106 | 0 | } |
2107 | | //------------------------------------------------------------------------------ |
2108 | | |
2109 | | bool IsMaxima(TEdge *e, const long64 Y) |
2110 | 0 | { |
2111 | 0 | return e && e->ytop == Y && !e->nextInLML; |
2112 | 0 | } |
2113 | | //------------------------------------------------------------------------------ |
2114 | | |
2115 | | bool IsIntermediate(TEdge *e, const long64 Y) |
2116 | 0 | { |
2117 | 0 | return e->ytop == Y && e->nextInLML; |
2118 | 0 | } |
2119 | | //------------------------------------------------------------------------------ |
2120 | | |
2121 | | TEdge *GetMaximaPair(TEdge *e) |
2122 | 0 | { |
2123 | 0 | if( !IsMaxima(e->next, e->ytop) || e->next->xtop != e->xtop ) |
2124 | 0 | return e->prev; else |
2125 | 0 | return e->next; |
2126 | 0 | } |
2127 | | //------------------------------------------------------------------------------ |
2128 | | |
2129 | | void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2) |
2130 | 0 | { |
2131 | 0 | if( !edge1->nextInAEL && !edge1->prevInAEL ) return; |
2132 | 0 | if( !edge2->nextInAEL && !edge2->prevInAEL ) return; |
2133 | | |
2134 | 0 | if( edge1->nextInAEL == edge2 ) |
2135 | 0 | { |
2136 | 0 | TEdge* next = edge2->nextInAEL; |
2137 | 0 | if( next ) next->prevInAEL = edge1; |
2138 | 0 | TEdge* prev = edge1->prevInAEL; |
2139 | 0 | if( prev ) prev->nextInAEL = edge2; |
2140 | 0 | edge2->prevInAEL = prev; |
2141 | 0 | edge2->nextInAEL = edge1; |
2142 | 0 | edge1->prevInAEL = edge2; |
2143 | 0 | edge1->nextInAEL = next; |
2144 | 0 | } |
2145 | 0 | else if( edge2->nextInAEL == edge1 ) |
2146 | 0 | { |
2147 | 0 | TEdge* next = edge1->nextInAEL; |
2148 | 0 | if( next ) next->prevInAEL = edge2; |
2149 | 0 | TEdge* prev = edge2->prevInAEL; |
2150 | 0 | if( prev ) prev->nextInAEL = edge1; |
2151 | 0 | edge1->prevInAEL = prev; |
2152 | 0 | edge1->nextInAEL = edge2; |
2153 | 0 | edge2->prevInAEL = edge1; |
2154 | 0 | edge2->nextInAEL = next; |
2155 | 0 | } |
2156 | 0 | else |
2157 | 0 | { |
2158 | 0 | TEdge* next = edge1->nextInAEL; |
2159 | 0 | TEdge* prev = edge1->prevInAEL; |
2160 | 0 | edge1->nextInAEL = edge2->nextInAEL; |
2161 | 0 | if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1; |
2162 | 0 | edge1->prevInAEL = edge2->prevInAEL; |
2163 | 0 | if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1; |
2164 | 0 | edge2->nextInAEL = next; |
2165 | 0 | if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2; |
2166 | 0 | edge2->prevInAEL = prev; |
2167 | 0 | if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2; |
2168 | 0 | } |
2169 | |
|
2170 | 0 | if( !edge1->prevInAEL ) m_ActiveEdges = edge1; |
2171 | 0 | else if( !edge2->prevInAEL ) m_ActiveEdges = edge2; |
2172 | 0 | } |
2173 | | //------------------------------------------------------------------------------ |
2174 | | |
2175 | | void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2) |
2176 | 0 | { |
2177 | 0 | if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return; |
2178 | 0 | if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return; |
2179 | | |
2180 | 0 | if( edge1->nextInSEL == edge2 ) |
2181 | 0 | { |
2182 | 0 | TEdge* next = edge2->nextInSEL; |
2183 | 0 | if( next ) next->prevInSEL = edge1; |
2184 | 0 | TEdge* prev = edge1->prevInSEL; |
2185 | 0 | if( prev ) prev->nextInSEL = edge2; |
2186 | 0 | edge2->prevInSEL = prev; |
2187 | 0 | edge2->nextInSEL = edge1; |
2188 | 0 | edge1->prevInSEL = edge2; |
2189 | 0 | edge1->nextInSEL = next; |
2190 | 0 | } |
2191 | 0 | else if( edge2->nextInSEL == edge1 ) |
2192 | 0 | { |
2193 | 0 | TEdge* next = edge1->nextInSEL; |
2194 | 0 | if( next ) next->prevInSEL = edge2; |
2195 | 0 | TEdge* prev = edge2->prevInSEL; |
2196 | 0 | if( prev ) prev->nextInSEL = edge1; |
2197 | 0 | edge1->prevInSEL = prev; |
2198 | 0 | edge1->nextInSEL = edge2; |
2199 | 0 | edge2->prevInSEL = edge1; |
2200 | 0 | edge2->nextInSEL = next; |
2201 | 0 | } |
2202 | 0 | else |
2203 | 0 | { |
2204 | 0 | TEdge* next = edge1->nextInSEL; |
2205 | 0 | TEdge* prev = edge1->prevInSEL; |
2206 | 0 | edge1->nextInSEL = edge2->nextInSEL; |
2207 | 0 | if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1; |
2208 | 0 | edge1->prevInSEL = edge2->prevInSEL; |
2209 | 0 | if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1; |
2210 | 0 | edge2->nextInSEL = next; |
2211 | 0 | if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2; |
2212 | 0 | edge2->prevInSEL = prev; |
2213 | 0 | if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2; |
2214 | 0 | } |
2215 | |
|
2216 | 0 | if( !edge1->prevInSEL ) m_SortedEdges = edge1; |
2217 | 0 | else if( !edge2->prevInSEL ) m_SortedEdges = edge2; |
2218 | 0 | } |
2219 | | //------------------------------------------------------------------------------ |
2220 | | |
2221 | | TEdge* GetNextInAEL(TEdge *e, Direction dir) |
2222 | 0 | { |
2223 | 0 | if( dir == dLeftToRight ) return e->nextInAEL; |
2224 | 0 | else return e->prevInAEL; |
2225 | 0 | } |
2226 | | //------------------------------------------------------------------------------ |
2227 | | |
2228 | | void Clipper::ProcessHorizontal(TEdge *horzEdge) |
2229 | 0 | { |
2230 | 0 | Direction dir; |
2231 | 0 | long64 horzLeft, horzRight; |
2232 | |
|
2233 | 0 | if( horzEdge->xcurr < horzEdge->xtop ) |
2234 | 0 | { |
2235 | 0 | horzLeft = horzEdge->xcurr; |
2236 | 0 | horzRight = horzEdge->xtop; |
2237 | 0 | dir = dLeftToRight; |
2238 | 0 | } else |
2239 | 0 | { |
2240 | 0 | horzLeft = horzEdge->xtop; |
2241 | 0 | horzRight = horzEdge->xcurr; |
2242 | 0 | dir = dRightToLeft; |
2243 | 0 | } |
2244 | |
|
2245 | 0 | TEdge* eMaxPair; |
2246 | 0 | if( horzEdge->nextInLML ) eMaxPair = 0; |
2247 | 0 | else eMaxPair = GetMaximaPair(horzEdge); |
2248 | |
|
2249 | 0 | TEdge* e = GetNextInAEL( horzEdge , dir ); |
2250 | 0 | while( e ) |
2251 | 0 | { |
2252 | 0 | TEdge* eNext = GetNextInAEL( e, dir ); |
2253 | |
|
2254 | 0 | if (eMaxPair || |
2255 | 0 | ((dir == dLeftToRight) && (e->xcurr <= horzRight)) || |
2256 | 0 | ((dir == dRightToLeft) && (e->xcurr >= horzLeft))) |
2257 | 0 | { |
2258 | | //ok, so far it looks like we're still in range of the horizontal edge |
2259 | 0 | if ( e->xcurr == horzEdge->xtop && !eMaxPair ) |
2260 | 0 | { |
2261 | 0 | assert(horzEdge->nextInLML); |
2262 | 0 | if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange)) |
2263 | 0 | { |
2264 | | //if output polygons share an edge, they'll need joining later ... |
2265 | 0 | if (horzEdge->outIdx >= 0 && e->outIdx >= 0) |
2266 | 0 | AddJoin(horzEdge->nextInLML, e, horzEdge->outIdx); |
2267 | 0 | break; //we've reached the end of the horizontal line |
2268 | 0 | } |
2269 | 0 | else if (e->dx < horzEdge->nextInLML->dx) |
2270 | | //we really have got to the end of the intermediate horz edge so quit. |
2271 | | //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal. |
2272 | 0 | break; |
2273 | 0 | } |
2274 | | |
2275 | 0 | if( e == eMaxPair ) |
2276 | 0 | { |
2277 | | //horzEdge is evidently a maxima horizontal and we've arrived at its end. |
2278 | 0 | if (dir == dLeftToRight) |
2279 | 0 | IntersectEdges(horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); |
2280 | 0 | else |
2281 | 0 | IntersectEdges(e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), ipNone); |
2282 | 0 | if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); |
2283 | 0 | return; |
2284 | 0 | } |
2285 | 0 | else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) ) |
2286 | 0 | { |
2287 | | //An overlapping horizontal edge. Overlapping horizontal edges are |
2288 | | //processed as if layered with the current horizontal edge (horizEdge) |
2289 | | //being infinitesimally lower that the next (e). Therefore, we |
2290 | | //intersect with e only if e.xcurr is within the bounds of horzEdge ... |
2291 | 0 | if( dir == dLeftToRight ) |
2292 | 0 | IntersectEdges( horzEdge , e, IntPoint(e->xcurr, horzEdge->ycurr), |
2293 | 0 | (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); |
2294 | 0 | else |
2295 | 0 | IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), |
2296 | 0 | (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); |
2297 | 0 | } |
2298 | 0 | else if( dir == dLeftToRight ) |
2299 | 0 | { |
2300 | 0 | IntersectEdges( horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), |
2301 | 0 | (IsTopHorz( e->xcurr ))? ipLeft : ipBoth ); |
2302 | 0 | } |
2303 | 0 | else |
2304 | 0 | { |
2305 | 0 | IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), |
2306 | 0 | (IsTopHorz( e->xcurr ))? ipRight : ipBoth ); |
2307 | 0 | } |
2308 | 0 | SwapPositionsInAEL( horzEdge, e ); |
2309 | 0 | } |
2310 | 0 | else if( (dir == dLeftToRight && e->xcurr > horzRight && m_SortedEdges) || |
2311 | 0 | (dir == dRightToLeft && e->xcurr < horzLeft && m_SortedEdges) ) break; |
2312 | 0 | e = eNext; |
2313 | 0 | } //end while |
2314 | | |
2315 | 0 | if( horzEdge->nextInLML ) |
2316 | 0 | { |
2317 | 0 | if( horzEdge->outIdx >= 0 ) |
2318 | 0 | AddOutPt( horzEdge, 0, IntPoint(horzEdge->xtop, horzEdge->ytop)); |
2319 | 0 | UpdateEdgeIntoAEL( horzEdge ); |
2320 | 0 | } |
2321 | 0 | else |
2322 | 0 | { |
2323 | 0 | assert(eMaxPair); |
2324 | 0 | if ( horzEdge->outIdx >= 0 ) |
2325 | 0 | { |
2326 | 0 | IntersectEdges( horzEdge, eMaxPair, |
2327 | 0 | IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth); |
2328 | 0 | } |
2329 | 0 | if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error"); |
2330 | 0 | DeleteFromAEL(eMaxPair); |
2331 | 0 | DeleteFromAEL(horzEdge); |
2332 | 0 | } |
2333 | 0 | } |
2334 | | //------------------------------------------------------------------------------ |
2335 | | |
2336 | | void Clipper::UpdateEdgeIntoAEL(TEdge *&e) |
2337 | 0 | { |
2338 | 0 | if( !e->nextInLML ) throw |
2339 | 0 | clipperException("UpdateEdgeIntoAEL: invalid call"); |
2340 | 0 | TEdge* AelPrev = e->prevInAEL; |
2341 | 0 | TEdge* AelNext = e->nextInAEL; |
2342 | 0 | e->nextInLML->outIdx = e->outIdx; |
2343 | 0 | if( AelPrev ) AelPrev->nextInAEL = e->nextInLML; |
2344 | 0 | else m_ActiveEdges = e->nextInLML; |
2345 | 0 | if( AelNext ) AelNext->prevInAEL = e->nextInLML; |
2346 | 0 | e->nextInLML->side = e->side; |
2347 | 0 | e->nextInLML->windDelta = e->windDelta; |
2348 | 0 | e->nextInLML->windCnt = e->windCnt; |
2349 | 0 | e->nextInLML->windCnt2 = e->windCnt2; |
2350 | 0 | e = e->nextInLML; |
2351 | 0 | e->prevInAEL = AelPrev; |
2352 | 0 | e->nextInAEL = AelNext; |
2353 | 0 | if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop ); |
2354 | 0 | } |
2355 | | //------------------------------------------------------------------------------ |
2356 | | |
2357 | | bool Clipper::ProcessIntersections(const long64 botY, const long64 topY) |
2358 | 0 | { |
2359 | 0 | if( !m_ActiveEdges ) return true; |
2360 | 0 | try { |
2361 | 0 | BuildIntersectList(botY, topY); |
2362 | 0 | if ( !m_IntersectNodes) return true; |
2363 | 0 | if ( FixupIntersections() ) ProcessIntersectList(); |
2364 | 0 | else return false; |
2365 | 0 | } |
2366 | 0 | catch(...) { |
2367 | 0 | m_SortedEdges = 0; |
2368 | 0 | DisposeIntersectNodes(); |
2369 | 0 | throw clipperException("ProcessIntersections error"); |
2370 | 0 | } |
2371 | 0 | return true; |
2372 | 0 | } |
2373 | | //------------------------------------------------------------------------------ |
2374 | | |
2375 | | void Clipper::DisposeIntersectNodes() |
2376 | 0 | { |
2377 | 0 | while ( m_IntersectNodes ) |
2378 | 0 | { |
2379 | 0 | IntersectNode* iNode = m_IntersectNodes->next; |
2380 | 0 | delete m_IntersectNodes; |
2381 | 0 | m_IntersectNodes = iNode; |
2382 | 0 | } |
2383 | 0 | } |
2384 | | //------------------------------------------------------------------------------ |
2385 | | |
2386 | | void Clipper::BuildIntersectList(const long64 botY, const long64 topY) |
2387 | 0 | { |
2388 | 0 | if ( !m_ActiveEdges ) return; |
2389 | | |
2390 | | //prepare for sorting ... |
2391 | 0 | TEdge* e = m_ActiveEdges; |
2392 | 0 | e->tmpX = TopX( *e, topY ); |
2393 | 0 | m_SortedEdges = e; |
2394 | 0 | m_SortedEdges->prevInSEL = 0; |
2395 | 0 | e = e->nextInAEL; |
2396 | 0 | while( e ) |
2397 | 0 | { |
2398 | 0 | e->prevInSEL = e->prevInAEL; |
2399 | 0 | e->prevInSEL->nextInSEL = e; |
2400 | 0 | e->nextInSEL = 0; |
2401 | 0 | e->tmpX = TopX( *e, topY ); |
2402 | 0 | e = e->nextInAEL; |
2403 | 0 | } |
2404 | | |
2405 | | //bubblesort ... |
2406 | 0 | bool isModified = true; |
2407 | 0 | while( isModified && m_SortedEdges ) |
2408 | 0 | { |
2409 | 0 | isModified = false; |
2410 | 0 | e = m_SortedEdges; |
2411 | 0 | while( e->nextInSEL ) |
2412 | 0 | { |
2413 | 0 | TEdge *eNext = e->nextInSEL; |
2414 | 0 | IntPoint pt; |
2415 | 0 | if(e->tmpX > eNext->tmpX && |
2416 | 0 | IntersectPoint(*e, *eNext, pt, m_UseFullRange)) |
2417 | 0 | { |
2418 | 0 | if (pt.Y > botY) |
2419 | 0 | { |
2420 | 0 | pt.Y = botY; |
2421 | 0 | pt.X = TopX(*e, pt.Y); |
2422 | 0 | } |
2423 | 0 | AddIntersectNode( e, eNext, pt ); |
2424 | 0 | SwapPositionsInSEL(e, eNext); |
2425 | 0 | isModified = true; |
2426 | 0 | } |
2427 | 0 | else |
2428 | 0 | e = eNext; |
2429 | 0 | } |
2430 | 0 | if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0; |
2431 | 0 | else break; |
2432 | 0 | } |
2433 | 0 | m_SortedEdges = 0; |
2434 | 0 | } |
2435 | | //------------------------------------------------------------------------------ |
2436 | | |
2437 | | bool Process1Before2(IntersectNode &node1, IntersectNode &node2) |
2438 | 0 | { |
2439 | 0 | bool result; |
2440 | 0 | if (node1.pt.Y == node2.pt.Y) |
2441 | 0 | { |
2442 | 0 | if (node1.edge1 == node2.edge1 || node1.edge2 == node2.edge1) |
2443 | 0 | { |
2444 | 0 | result = node2.pt.X > node1.pt.X; |
2445 | 0 | if (node2.edge1->dx > 0) return !result; else return result; |
2446 | 0 | } |
2447 | 0 | else if (node1.edge1 == node2.edge2 || node1.edge2 == node2.edge2) |
2448 | 0 | { |
2449 | 0 | result = node2.pt.X > node1.pt.X; |
2450 | 0 | if (node2.edge2->dx > 0) return !result; else return result; |
2451 | 0 | } |
2452 | 0 | else return node2.pt.X > node1.pt.X; |
2453 | 0 | } |
2454 | 0 | else return node1.pt.Y > node2.pt.Y; |
2455 | 0 | } |
2456 | | //------------------------------------------------------------------------------ |
2457 | | |
2458 | | void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt) |
2459 | 0 | { |
2460 | 0 | IntersectNode* newNode = new IntersectNode; |
2461 | 0 | newNode->edge1 = e1; |
2462 | 0 | newNode->edge2 = e2; |
2463 | 0 | newNode->pt = pt; |
2464 | 0 | newNode->next = 0; |
2465 | 0 | if( !m_IntersectNodes ) m_IntersectNodes = newNode; |
2466 | 0 | else if( Process1Before2(*newNode, *m_IntersectNodes) ) |
2467 | 0 | { |
2468 | 0 | newNode->next = m_IntersectNodes; |
2469 | 0 | m_IntersectNodes = newNode; |
2470 | 0 | } |
2471 | 0 | else |
2472 | 0 | { |
2473 | 0 | IntersectNode* iNode = m_IntersectNodes; |
2474 | 0 | while( iNode->next && Process1Before2(*iNode->next, *newNode) ) |
2475 | 0 | iNode = iNode->next; |
2476 | 0 | newNode->next = iNode->next; |
2477 | 0 | iNode->next = newNode; |
2478 | 0 | } |
2479 | 0 | } |
2480 | | //------------------------------------------------------------------------------ |
2481 | | |
2482 | | void Clipper::ProcessIntersectList() |
2483 | 0 | { |
2484 | 0 | while( m_IntersectNodes ) |
2485 | 0 | { |
2486 | 0 | IntersectNode* iNode = m_IntersectNodes->next; |
2487 | 0 | { |
2488 | 0 | IntersectEdges( m_IntersectNodes->edge1 , |
2489 | 0 | m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth ); |
2490 | 0 | SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 ); |
2491 | 0 | } |
2492 | 0 | delete m_IntersectNodes; |
2493 | 0 | m_IntersectNodes = iNode; |
2494 | 0 | } |
2495 | 0 | } |
2496 | | //------------------------------------------------------------------------------ |
2497 | | |
2498 | | void Clipper::DoMaxima(TEdge *e, long64 topY) |
2499 | 0 | { |
2500 | 0 | TEdge* eMaxPair = GetMaximaPair(e); |
2501 | 0 | long64 X = e->xtop; |
2502 | 0 | TEdge* eNext = e->nextInAEL; |
2503 | 0 | while( eNext != eMaxPair ) |
2504 | 0 | { |
2505 | 0 | if (!eNext) throw clipperException("DoMaxima error"); |
2506 | 0 | IntersectEdges( e, eNext, IntPoint(X, topY), ipBoth ); |
2507 | 0 | eNext = eNext->nextInAEL; |
2508 | 0 | } |
2509 | 0 | if( e->outIdx < 0 && eMaxPair->outIdx < 0 ) |
2510 | 0 | { |
2511 | 0 | DeleteFromAEL( e ); |
2512 | 0 | DeleteFromAEL( eMaxPair ); |
2513 | 0 | } |
2514 | 0 | else if( e->outIdx >= 0 && eMaxPair->outIdx >= 0 ) |
2515 | 0 | { |
2516 | 0 | IntersectEdges( e, eMaxPair, IntPoint(X, topY), ipNone ); |
2517 | 0 | } |
2518 | 0 | else throw clipperException("DoMaxima error"); |
2519 | 0 | } |
2520 | | //------------------------------------------------------------------------------ |
2521 | | |
2522 | | void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY) |
2523 | 0 | { |
2524 | 0 | TEdge* e = m_ActiveEdges; |
2525 | 0 | while( e ) |
2526 | 0 | { |
2527 | | //1. process maxima, treating them as if they're 'bent' horizontal edges, |
2528 | | // but exclude maxima with horizontal edges. nb: e can't be a horizontal. |
2529 | 0 | if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) ) |
2530 | 0 | { |
2531 | | //'e' might be removed from AEL, as may any following edges so ... |
2532 | 0 | TEdge* ePrior = e->prevInAEL; |
2533 | 0 | DoMaxima(e, topY); |
2534 | 0 | if( !ePrior ) e = m_ActiveEdges; |
2535 | 0 | else e = ePrior->nextInAEL; |
2536 | 0 | } |
2537 | 0 | else |
2538 | 0 | { |
2539 | | //2. promote horizontal edges, otherwise update xcurr and ycurr ... |
2540 | 0 | if( IsIntermediate(e, topY) && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) ) |
2541 | 0 | { |
2542 | 0 | if (e->outIdx >= 0) |
2543 | 0 | { |
2544 | 0 | AddOutPt(e, 0, IntPoint(e->xtop, e->ytop)); |
2545 | |
|
2546 | 0 | for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i) |
2547 | 0 | { |
2548 | 0 | IntPoint pt, pt2; |
2549 | 0 | HorzJoinRec* hj = m_HorizJoins[i]; |
2550 | 0 | if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot), |
2551 | 0 | IntPoint(hj->edge->xtop, hj->edge->ytop), |
2552 | 0 | IntPoint(e->nextInLML->xbot, e->nextInLML->ybot), |
2553 | 0 | IntPoint(e->nextInLML->xtop, e->nextInLML->ytop), pt, pt2)) |
2554 | 0 | AddJoin(hj->edge, e->nextInLML, hj->savedIdx, e->outIdx); |
2555 | 0 | } |
2556 | |
|
2557 | 0 | AddHorzJoin(e->nextInLML, e->outIdx); |
2558 | 0 | } |
2559 | 0 | UpdateEdgeIntoAEL(e); |
2560 | 0 | AddEdgeToSEL(e); |
2561 | 0 | } else |
2562 | 0 | { |
2563 | | //this just simplifies horizontal processing ... |
2564 | 0 | e->xcurr = TopX( *e, topY ); |
2565 | 0 | e->ycurr = topY; |
2566 | 0 | } |
2567 | 0 | e = e->nextInAEL; |
2568 | 0 | } |
2569 | 0 | } |
2570 | | |
2571 | | //3. Process horizontals at the top of the scanbeam ... |
2572 | 0 | ProcessHorizontals(); |
2573 | | |
2574 | | //4. Promote intermediate vertices ... |
2575 | 0 | e = m_ActiveEdges; |
2576 | 0 | while( e ) |
2577 | 0 | { |
2578 | 0 | if( IsIntermediate( e, topY ) ) |
2579 | 0 | { |
2580 | 0 | if( e->outIdx >= 0 ) AddOutPt(e, 0, IntPoint(e->xtop,e->ytop)); |
2581 | 0 | UpdateEdgeIntoAEL(e); |
2582 | | |
2583 | | //if output polygons share an edge, they'll need joining later ... |
2584 | 0 | if (e->outIdx >= 0 && e->prevInAEL && e->prevInAEL->outIdx >= 0 && |
2585 | 0 | e->prevInAEL->xcurr == e->xbot && e->prevInAEL->ycurr == e->ybot && |
2586 | 0 | SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), |
2587 | 0 | IntPoint(e->xbot,e->ybot), |
2588 | 0 | IntPoint(e->prevInAEL->xtop, e->prevInAEL->ytop), m_UseFullRange)) |
2589 | 0 | { |
2590 | 0 | AddOutPt(e->prevInAEL, 0, IntPoint(e->xbot, e->ybot)); |
2591 | 0 | AddJoin(e, e->prevInAEL); |
2592 | 0 | } |
2593 | 0 | else if (e->outIdx >= 0 && e->nextInAEL && e->nextInAEL->outIdx >= 0 && |
2594 | 0 | e->nextInAEL->ycurr > e->nextInAEL->ytop && |
2595 | 0 | e->nextInAEL->ycurr < e->nextInAEL->ybot && |
2596 | 0 | e->nextInAEL->xcurr == e->xbot && e->nextInAEL->ycurr == e->ybot && |
2597 | 0 | SlopesEqual(IntPoint(e->xbot,e->ybot), IntPoint(e->xtop, e->ytop), |
2598 | 0 | IntPoint(e->xbot,e->ybot), |
2599 | 0 | IntPoint(e->nextInAEL->xtop, e->nextInAEL->ytop), m_UseFullRange)) |
2600 | 0 | { |
2601 | 0 | AddOutPt(e->nextInAEL, 0, IntPoint(e->xbot, e->ybot)); |
2602 | 0 | AddJoin(e, e->nextInAEL); |
2603 | 0 | } |
2604 | 0 | } |
2605 | 0 | e = e->nextInAEL; |
2606 | 0 | } |
2607 | 0 | } |
2608 | | //------------------------------------------------------------------------------ |
2609 | | |
2610 | | void Clipper::FixupOutPolygon(OutRec &outRec) |
2611 | 0 | { |
2612 | | //FixupOutPolygon() - removes duplicate points and simplifies consecutive |
2613 | | //parallel edges by removing the middle vertex. |
2614 | 0 | OutPt *lastOK = 0; |
2615 | 0 | outRec.pts = outRec.bottomPt; |
2616 | 0 | OutPt *pp = outRec.bottomPt; |
2617 | |
|
2618 | 0 | for (;;) |
2619 | 0 | { |
2620 | 0 | if (pp->prev == pp || pp->prev == pp->next ) |
2621 | 0 | { |
2622 | 0 | DisposeOutPts(pp); |
2623 | 0 | outRec.pts = 0; |
2624 | 0 | outRec.bottomPt = 0; |
2625 | 0 | return; |
2626 | 0 | } |
2627 | | //test for duplicate points and for same slope (cross-product) ... |
2628 | 0 | if ( PointsEqual(pp->pt, pp->next->pt) || |
2629 | 0 | SlopesEqual(pp->prev->pt, pp->pt, pp->next->pt, m_UseFullRange) ) |
2630 | 0 | { |
2631 | 0 | lastOK = 0; |
2632 | 0 | OutPt *tmp = pp; |
2633 | 0 | if (pp == outRec.bottomPt) |
2634 | 0 | { |
2635 | 0 | if (tmp->prev->pt.Y > tmp->next->pt.Y) |
2636 | 0 | outRec.bottomPt = tmp->prev; else |
2637 | 0 | outRec.bottomPt = tmp->next; |
2638 | 0 | outRec.pts = outRec.bottomPt; |
2639 | 0 | outRec.bottomPt->idx = outRec.idx; |
2640 | 0 | } |
2641 | 0 | pp->prev->next = pp->next; |
2642 | 0 | pp->next->prev = pp->prev; |
2643 | 0 | pp = pp->prev; |
2644 | 0 | delete tmp; |
2645 | 0 | } |
2646 | 0 | else if (pp == lastOK) break; |
2647 | 0 | else |
2648 | 0 | { |
2649 | 0 | if (!lastOK) lastOK = pp; |
2650 | 0 | pp = pp->next; |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | } |
2654 | | //------------------------------------------------------------------------------ |
2655 | | |
2656 | | void Clipper::BuildResult(Polygons &polys) |
2657 | 0 | { |
2658 | 0 | int k = 0; |
2659 | 0 | polys.resize(m_PolyOuts.size()); |
2660 | 0 | for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) |
2661 | 0 | { |
2662 | 0 | if (m_PolyOuts[i]->pts) |
2663 | 0 | { |
2664 | 0 | Polygon* pg = &polys[k]; |
2665 | 0 | pg->clear(); |
2666 | 0 | OutPt* p = m_PolyOuts[i]->pts; |
2667 | 0 | do |
2668 | 0 | { |
2669 | 0 | pg->push_back(p->pt); |
2670 | 0 | p = p->next; |
2671 | 0 | } while (p != m_PolyOuts[i]->pts); |
2672 | | //make sure each polygon has at least 3 vertices ... |
2673 | 0 | if (pg->size() < 3) pg->clear(); else k++; |
2674 | 0 | } |
2675 | 0 | } |
2676 | 0 | polys.resize(k); |
2677 | 0 | } |
2678 | | //------------------------------------------------------------------------------ |
2679 | | |
2680 | | void Clipper::BuildResultEx(ExPolygons &polys) |
2681 | 0 | { |
2682 | 0 | PolyOutList::size_type i = 0; |
2683 | 0 | int k = 0; |
2684 | 0 | polys.resize(0); |
2685 | 0 | polys.reserve(m_PolyOuts.size()); |
2686 | 0 | while (i < m_PolyOuts.size() && m_PolyOuts[i]->pts) |
2687 | 0 | { |
2688 | 0 | ExPolygon epg; |
2689 | 0 | OutPt* p = m_PolyOuts[i]->pts; |
2690 | 0 | do { |
2691 | 0 | epg.outer.push_back(p->pt); |
2692 | 0 | p = p->next; |
2693 | 0 | } while (p != m_PolyOuts[i]->pts); |
2694 | 0 | i++; |
2695 | | //make sure polygons have at least 3 vertices ... |
2696 | 0 | if (epg.outer.size() < 3) continue; |
2697 | 0 | while (i < m_PolyOuts.size() |
2698 | 0 | && m_PolyOuts[i]->pts && m_PolyOuts[i]->isHole) |
2699 | 0 | { |
2700 | 0 | Polygon pg; |
2701 | 0 | p = m_PolyOuts[i]->pts; |
2702 | 0 | do { |
2703 | 0 | pg.push_back(p->pt); |
2704 | 0 | p = p->next; |
2705 | 0 | } while (p != m_PolyOuts[i]->pts); |
2706 | 0 | epg.holes.push_back(pg); |
2707 | 0 | i++; |
2708 | 0 | } |
2709 | 0 | polys.push_back(epg); |
2710 | 0 | k++; |
2711 | 0 | } |
2712 | 0 | polys.resize(k); |
2713 | 0 | } |
2714 | | //------------------------------------------------------------------------------ |
2715 | | |
2716 | | void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) |
2717 | 0 | { |
2718 | 0 | TEdge *e1 = int1.edge1; |
2719 | 0 | TEdge *e2 = int1.edge2; |
2720 | 0 | IntPoint p = int1.pt; |
2721 | |
|
2722 | 0 | int1.edge1 = int2.edge1; |
2723 | 0 | int1.edge2 = int2.edge2; |
2724 | 0 | int1.pt = int2.pt; |
2725 | |
|
2726 | 0 | int2.edge1 = e1; |
2727 | 0 | int2.edge2 = e2; |
2728 | 0 | int2.pt = p; |
2729 | 0 | } |
2730 | | //------------------------------------------------------------------------------ |
2731 | | |
2732 | | bool Clipper::FixupIntersections() |
2733 | 0 | { |
2734 | 0 | if ( !m_IntersectNodes->next ) return true; |
2735 | | |
2736 | 0 | CopyAELToSEL(); |
2737 | 0 | IntersectNode *int1 = m_IntersectNodes; |
2738 | 0 | IntersectNode *int2 = m_IntersectNodes->next; |
2739 | 0 | while (int2) |
2740 | 0 | { |
2741 | 0 | TEdge *e1 = int1->edge1; |
2742 | 0 | TEdge *e2; |
2743 | 0 | if (e1->prevInSEL == int1->edge2) e2 = e1->prevInSEL; |
2744 | 0 | else if (e1->nextInSEL == int1->edge2) e2 = e1->nextInSEL; |
2745 | 0 | else |
2746 | 0 | { |
2747 | | //The current intersection is out of order, so try and swap it with |
2748 | | //a subsequent intersection ... |
2749 | 0 | while (int2) |
2750 | 0 | { |
2751 | 0 | if (int2->edge1->nextInSEL == int2->edge2 || |
2752 | 0 | int2->edge1->prevInSEL == int2->edge2) break; |
2753 | 0 | else int2 = int2->next; |
2754 | 0 | } |
2755 | 0 | if ( !int2 ) return false; //oops!!! |
2756 | | |
2757 | | //found an intersect node that can be swapped ... |
2758 | 0 | SwapIntersectNodes(*int1, *int2); |
2759 | 0 | e1 = int1->edge1; |
2760 | 0 | e2 = int1->edge2; |
2761 | 0 | } |
2762 | 0 | SwapPositionsInSEL(e1, e2); |
2763 | 0 | int1 = int1->next; |
2764 | 0 | int2 = int1->next; |
2765 | 0 | } |
2766 | | |
2767 | 0 | m_SortedEdges = 0; |
2768 | | |
2769 | | //finally, check the last intersection too ... |
2770 | 0 | return (int1->edge1->prevInSEL == int1->edge2 || |
2771 | 0 | int1->edge1->nextInSEL == int1->edge2); |
2772 | 0 | } |
2773 | | //------------------------------------------------------------------------------ |
2774 | | |
2775 | | bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) |
2776 | 0 | { |
2777 | 0 | if (e2.xcurr == e1.xcurr) return e2.dx > e1.dx; |
2778 | 0 | else return e2.xcurr < e1.xcurr; |
2779 | 0 | } |
2780 | | //------------------------------------------------------------------------------ |
2781 | | |
2782 | | void Clipper::InsertEdgeIntoAEL(TEdge *edge) |
2783 | 0 | { |
2784 | 0 | edge->prevInAEL = 0; |
2785 | 0 | edge->nextInAEL = 0; |
2786 | 0 | if( !m_ActiveEdges ) |
2787 | 0 | { |
2788 | 0 | m_ActiveEdges = edge; |
2789 | 0 | } |
2790 | 0 | else if( E2InsertsBeforeE1(*m_ActiveEdges, *edge) ) |
2791 | 0 | { |
2792 | 0 | edge->nextInAEL = m_ActiveEdges; |
2793 | 0 | m_ActiveEdges->prevInAEL = edge; |
2794 | 0 | m_ActiveEdges = edge; |
2795 | 0 | } else |
2796 | 0 | { |
2797 | 0 | TEdge* e = m_ActiveEdges; |
2798 | 0 | while( e->nextInAEL && !E2InsertsBeforeE1(*e->nextInAEL , *edge) ) |
2799 | 0 | e = e->nextInAEL; |
2800 | 0 | edge->nextInAEL = e->nextInAEL; |
2801 | 0 | if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge; |
2802 | 0 | edge->prevInAEL = e; |
2803 | 0 | e->nextInAEL = edge; |
2804 | 0 | } |
2805 | 0 | } |
2806 | | //---------------------------------------------------------------------- |
2807 | | |
2808 | | void Clipper::DoEdge1(TEdge *edge1, TEdge *edge2, const IntPoint &pt) |
2809 | 0 | { |
2810 | 0 | AddOutPt(edge1, edge2, pt); |
2811 | 0 | SwapSides(*edge1, *edge2); |
2812 | 0 | SwapPolyIndexes(*edge1, *edge2); |
2813 | 0 | } |
2814 | | //---------------------------------------------------------------------- |
2815 | | |
2816 | | void Clipper::DoEdge2(TEdge *edge1, TEdge *edge2, const IntPoint &pt) |
2817 | 0 | { |
2818 | 0 | AddOutPt(edge2, edge1, pt); |
2819 | 0 | SwapSides(*edge1, *edge2); |
2820 | 0 | SwapPolyIndexes(*edge1, *edge2); |
2821 | 0 | } |
2822 | | //---------------------------------------------------------------------- |
2823 | | |
2824 | | void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const IntPoint &pt) |
2825 | 0 | { |
2826 | 0 | AddOutPt(edge1, edge2, pt); |
2827 | 0 | AddOutPt(edge2, edge1, pt); |
2828 | 0 | SwapSides( *edge1 , *edge2 ); |
2829 | 0 | SwapPolyIndexes( *edge1 , *edge2 ); |
2830 | 0 | } |
2831 | | //---------------------------------------------------------------------- |
2832 | | |
2833 | | void Clipper::CheckHoleLinkages1(OutRec *outRec1, OutRec *outRec2) |
2834 | 0 | { |
2835 | | //when a polygon is split into 2 polygons, make sure any holes the original |
2836 | | //polygon contained link to the correct polygon ... |
2837 | 0 | for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) |
2838 | 0 | { |
2839 | 0 | OutRec *orec = m_PolyOuts[i]; |
2840 | 0 | if (orec->isHole && orec->bottomPt && orec->FirstLeft == outRec1 && |
2841 | 0 | !PointInPolygon(orec->bottomPt->pt, outRec1->pts, m_UseFullRange)) |
2842 | 0 | orec->FirstLeft = outRec2; |
2843 | 0 | } |
2844 | 0 | } |
2845 | | //---------------------------------------------------------------------- |
2846 | | |
2847 | | void Clipper::CheckHoleLinkages2(OutRec *outRec1, OutRec *outRec2) |
2848 | 0 | { |
2849 | | //if a hole is owned by outRec2 then make it owned by outRec1 ... |
2850 | 0 | for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) |
2851 | 0 | if (m_PolyOuts[i]->isHole && m_PolyOuts[i]->bottomPt && |
2852 | 0 | m_PolyOuts[i]->FirstLeft == outRec2) |
2853 | 0 | m_PolyOuts[i]->FirstLeft = outRec1; |
2854 | 0 | } |
2855 | | //---------------------------------------------------------------------- |
2856 | | |
2857 | | void Clipper::JoinCommonEdges(bool fixHoleLinkages) |
2858 | 0 | { |
2859 | 0 | for (JoinList::size_type i = 0; i < m_Joins.size(); i++) |
2860 | 0 | { |
2861 | 0 | JoinRec* j = m_Joins[i]; |
2862 | 0 | OutRec *outRec1 = m_PolyOuts[j->poly1Idx]; |
2863 | 0 | OutPt *pp1a = outRec1->pts; |
2864 | 0 | OutRec *outRec2 = m_PolyOuts[j->poly2Idx]; |
2865 | 0 | OutPt *pp2a = outRec2->pts; |
2866 | 0 | IntPoint pt1 = j->pt2a, pt2 = j->pt2b; |
2867 | 0 | IntPoint pt3 = j->pt1a, pt4 = j->pt1b; |
2868 | 0 | if (!FindSegment(pp1a, pt1, pt2)) continue; |
2869 | 0 | if (j->poly1Idx == j->poly2Idx) |
2870 | 0 | { |
2871 | | //we're searching the same polygon for overlapping segments so |
2872 | | //segment 2 mustn't be the same as segment 1 ... |
2873 | 0 | pp2a = pp1a->next; |
2874 | 0 | if (!FindSegment(pp2a, pt3, pt4) || (pp2a == pp1a)) continue; |
2875 | 0 | } |
2876 | 0 | else if (!FindSegment(pp2a, pt3, pt4)) continue; |
2877 | | |
2878 | 0 | if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) continue; |
2879 | | |
2880 | 0 | OutPt *p1, *p2, *p3, *p4; |
2881 | 0 | OutPt *prev = pp1a->prev; |
2882 | | //get p1 & p2 polypts - the overlap start & endpoints on poly1 |
2883 | 0 | if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a; |
2884 | 0 | else if (PointsEqual(prev->pt, pt1)) p1 = prev; |
2885 | 0 | else p1 = InsertPolyPtBetween(pp1a, prev, pt1); |
2886 | |
|
2887 | 0 | if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a; |
2888 | 0 | else if (PointsEqual(prev->pt, pt2)) p2 = prev; |
2889 | 0 | else if ((p1 == pp1a) || (p1 == prev)) |
2890 | 0 | p2 = InsertPolyPtBetween(pp1a, prev, pt2); |
2891 | 0 | else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2)) |
2892 | 0 | p2 = InsertPolyPtBetween(pp1a, p1, pt2); else |
2893 | 0 | p2 = InsertPolyPtBetween(p1, prev, pt2); |
2894 | | |
2895 | | //get p3 & p4 polypts - the overlap start & endpoints on poly2 |
2896 | 0 | prev = pp2a->prev; |
2897 | 0 | if (PointsEqual(pp2a->pt, pt1)) p3 = pp2a; |
2898 | 0 | else if (PointsEqual(prev->pt, pt1)) p3 = prev; |
2899 | 0 | else p3 = InsertPolyPtBetween(pp2a, prev, pt1); |
2900 | |
|
2901 | 0 | if (PointsEqual(pp2a->pt, pt2)) p4 = pp2a; |
2902 | 0 | else if (PointsEqual(prev->pt, pt2)) p4 = prev; |
2903 | 0 | else if ((p3 == pp2a) || (p3 == prev)) |
2904 | 0 | p4 = InsertPolyPtBetween(pp2a, prev, pt2); |
2905 | 0 | else if (Pt3IsBetweenPt1AndPt2(pp2a->pt, p3->pt, pt2)) |
2906 | 0 | p4 = InsertPolyPtBetween(pp2a, p3, pt2); else |
2907 | 0 | p4 = InsertPolyPtBetween(p3, prev, pt2); |
2908 | | |
2909 | | //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ... |
2910 | 0 | if (p1->next == p2 && p3->prev == p4) |
2911 | 0 | { |
2912 | 0 | p1->next = p3; |
2913 | 0 | p3->prev = p1; |
2914 | 0 | p2->prev = p4; |
2915 | 0 | p4->next = p2; |
2916 | 0 | } |
2917 | 0 | else if (p1->prev == p2 && p3->next == p4) |
2918 | 0 | { |
2919 | 0 | p1->prev = p3; |
2920 | 0 | p3->next = p1; |
2921 | 0 | p2->next = p4; |
2922 | 0 | p4->prev = p2; |
2923 | 0 | } |
2924 | 0 | else |
2925 | 0 | continue; //an orientation is probably wrong |
2926 | | |
2927 | 0 | if (j->poly2Idx == j->poly1Idx) |
2928 | 0 | { |
2929 | | //instead of joining two polygons, we've just created a new one by |
2930 | | //splitting one polygon into two. |
2931 | 0 | outRec1->pts = PolygonBottom(p1); |
2932 | 0 | outRec1->bottomPt = outRec1->pts; |
2933 | 0 | outRec1->bottomPt->idx = outRec1->idx; |
2934 | 0 | outRec2 = CreateOutRec(); |
2935 | 0 | m_PolyOuts.push_back(outRec2); |
2936 | 0 | outRec2->idx = (int)m_PolyOuts.size()-1; |
2937 | 0 | j->poly2Idx = outRec2->idx; |
2938 | 0 | outRec2->pts = PolygonBottom(p2); |
2939 | 0 | outRec2->bottomPt = outRec2->pts; |
2940 | 0 | outRec2->bottomPt->idx = outRec2->idx; |
2941 | |
|
2942 | 0 | if (PointInPolygon(outRec2->pts->pt, outRec1->pts, m_UseFullRange)) |
2943 | 0 | { |
2944 | 0 | outRec2->isHole = !outRec1->isHole; |
2945 | 0 | outRec2->FirstLeft = outRec1; |
2946 | 0 | if (outRec2->isHole == Orientation(outRec2, m_UseFullRange)) |
2947 | 0 | ReversePolyPtLinks(*outRec2->pts); |
2948 | 0 | } else if (PointInPolygon(outRec1->pts->pt, outRec2->pts, m_UseFullRange)) |
2949 | 0 | { |
2950 | 0 | outRec2->isHole = outRec1->isHole; |
2951 | 0 | outRec1->isHole = !outRec2->isHole; |
2952 | 0 | outRec2->FirstLeft = outRec1->FirstLeft; |
2953 | 0 | outRec1->FirstLeft = outRec2; |
2954 | 0 | if (outRec1->isHole == Orientation(outRec1, m_UseFullRange)) |
2955 | 0 | ReversePolyPtLinks(*outRec1->pts); |
2956 | 0 | } else |
2957 | 0 | { |
2958 | 0 | outRec2->isHole = outRec1->isHole; |
2959 | 0 | outRec2->FirstLeft = outRec1->FirstLeft; |
2960 | | //make sure any contained holes now link to the correct polygon ... |
2961 | 0 | if (fixHoleLinkages) CheckHoleLinkages1(outRec1, outRec2); |
2962 | 0 | } |
2963 | | |
2964 | | //now fixup any subsequent joins that match this polygon |
2965 | 0 | for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) |
2966 | 0 | { |
2967 | 0 | JoinRec* j2 = m_Joins[k]; |
2968 | 0 | if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, p2)) |
2969 | 0 | j2->poly1Idx = j->poly2Idx; |
2970 | 0 | if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, p2)) |
2971 | 0 | j2->poly2Idx = j->poly2Idx; |
2972 | 0 | } |
2973 | | |
2974 | | //now cleanup redundant edges too ... |
2975 | 0 | FixupOutPolygon(*outRec1); |
2976 | 0 | FixupOutPolygon(*outRec2); |
2977 | 0 | } else |
2978 | 0 | { |
2979 | | //joined 2 polygons together ... |
2980 | | |
2981 | | //make sure any holes contained by outRec2 now link to outRec1 ... |
2982 | 0 | if (fixHoleLinkages) CheckHoleLinkages2(outRec1, outRec2); |
2983 | | |
2984 | | //delete the obsolete pointer ... |
2985 | 0 | int OKIdx = outRec1->idx; |
2986 | 0 | int ObsoleteIdx = outRec2->idx; |
2987 | 0 | outRec2->pts = 0; |
2988 | 0 | outRec2->bottomPt = 0; |
2989 | 0 | outRec2->AppendLink = outRec1; |
2990 | | //holes are practically always joined to outers, not vice versa ... |
2991 | 0 | if (outRec1->isHole && !outRec2->isHole) outRec1->isHole = false; |
2992 | | |
2993 | | //now fixup any subsequent Joins that match this polygon |
2994 | 0 | for (JoinList::size_type k = i+1; k < m_Joins.size(); k++) |
2995 | 0 | { |
2996 | 0 | JoinRec* j2 = m_Joins[k]; |
2997 | 0 | if (j2->poly1Idx == ObsoleteIdx) j2->poly1Idx = OKIdx; |
2998 | 0 | if (j2->poly2Idx == ObsoleteIdx) j2->poly2Idx = OKIdx; |
2999 | 0 | } |
3000 | | |
3001 | | //now cleanup redundant edges too ... |
3002 | 0 | if (outRec1->pts) |
3003 | 0 | FixupOutPolygon(*outRec1); |
3004 | 0 | else |
3005 | 0 | FixupOutPolygon(*outRec2); |
3006 | 0 | } |
3007 | 0 | } |
3008 | 0 | } |
3009 | | //------------------------------------------------------------------------------ |
3010 | | |
3011 | | void ReversePoints(Polygon& p) |
3012 | 0 | { |
3013 | 0 | std::reverse(p.begin(), p.end()); |
3014 | 0 | } |
3015 | | //------------------------------------------------------------------------------ |
3016 | | |
3017 | | void ReversePoints(Polygons& p) |
3018 | 0 | { |
3019 | 0 | for (Polygons::size_type i = 0; i < p.size(); ++i) |
3020 | 0 | ReversePoints(p[i]); |
3021 | 0 | } |
3022 | | |
3023 | | //------------------------------------------------------------------------------ |
3024 | | // OffsetPolygon functions ... |
3025 | | //------------------------------------------------------------------------------ |
3026 | | |
3027 | | struct DoublePoint |
3028 | | { |
3029 | | double X; |
3030 | | double Y; |
3031 | 0 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} |
3032 | | }; |
3033 | | //------------------------------------------------------------------------------ |
3034 | | |
3035 | | Polygon BuildArc(const IntPoint &pt, |
3036 | | const double a1, const double a2, const double r) |
3037 | 0 | { |
3038 | 0 | int steps = std::max(6, int(std::sqrt(std::fabs(r)) * std::fabs(a2 - a1))); |
3039 | 0 | Polygon result(steps); |
3040 | 0 | int n = steps - 1; |
3041 | 0 | double da = (a2 - a1) / n; |
3042 | 0 | double a = a1; |
3043 | 0 | for (int i = 0; i <= n; ++i) |
3044 | 0 | { |
3045 | 0 | result[i].X = pt.X + Round(std::cos(a)*r); |
3046 | 0 | result[i].Y = pt.Y + Round(std::sin(a)*r); |
3047 | 0 | a += da; |
3048 | 0 | } |
3049 | 0 | return result; |
3050 | 0 | } |
3051 | | //------------------------------------------------------------------------------ |
3052 | | |
3053 | | DoublePoint GetUnitNormal( const IntPoint &pt1, const IntPoint &pt2) |
3054 | 0 | { |
3055 | 0 | if(pt2.X == pt1.X && pt2.Y == pt1.Y) |
3056 | 0 | return DoublePoint(0, 0); |
3057 | | |
3058 | 0 | double dx = (double)(pt2.X - pt1.X); |
3059 | 0 | double dy = (double)(pt2.Y - pt1.Y); |
3060 | 0 | double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy ); |
3061 | 0 | dx *= f; |
3062 | 0 | dy *= f; |
3063 | 0 | return DoublePoint(dy, -dx); |
3064 | 0 | } |
3065 | | |
3066 | | //------------------------------------------------------------------------------ |
3067 | | //------------------------------------------------------------------------------ |
3068 | | |
3069 | | class PolyOffsetBuilder |
3070 | | { |
3071 | | private: |
3072 | | Polygons m_p; |
3073 | | Polygon* m_curr_poly = nullptr; |
3074 | | std::vector<DoublePoint> normals; |
3075 | | double m_delta = 0, m_RMin = 0, m_R = 0; |
3076 | | size_t m_i = 0, m_j = 0, m_k = 0; |
3077 | | static const int buffLength = 128; |
3078 | | JoinType m_jointype = jtSquare; |
3079 | | |
3080 | | public: |
3081 | | |
3082 | | PolyOffsetBuilder(const Polygons& in_polys, Polygons& out_polys, |
3083 | | double delta, JoinType jointype, double MiterLimit) |
3084 | 0 | { |
3085 | | //nb precondition - out_polys != ptsin_polys |
3086 | 0 | if (NEAR_ZERO(delta)) |
3087 | 0 | { |
3088 | 0 | out_polys = in_polys; |
3089 | 0 | return; |
3090 | 0 | } |
3091 | | |
3092 | 0 | this->m_p = in_polys; |
3093 | 0 | this->m_delta = delta; |
3094 | 0 | this->m_jointype = jointype; |
3095 | 0 | if (MiterLimit <= 1) MiterLimit = 1; |
3096 | 0 | m_RMin = 2/(MiterLimit*MiterLimit); |
3097 | | |
3098 | 0 | double deltaSq = delta*delta; |
3099 | 0 | out_polys.clear(); |
3100 | 0 | out_polys.resize(in_polys.size()); |
3101 | 0 | for (m_i = 0; m_i < in_polys.size(); m_i++) |
3102 | 0 | { |
3103 | 0 | m_curr_poly = &out_polys[m_i]; |
3104 | 0 | size_t len = in_polys[m_i].size(); |
3105 | 0 | if (len > 1 && m_p[m_i][0].X == m_p[m_i][len - 1].X && |
3106 | 0 | m_p[m_i][0].Y == m_p[m_i][len-1].Y) len--; |
3107 | | |
3108 | | //when 'shrinking' polygons - to minimize artifacts |
3109 | | //strip those polygons that have an area < pi * delta^2 ... |
3110 | 0 | double a1 = Area(in_polys[m_i]); |
3111 | 0 | if (delta < 0) { if (a1 > 0 && a1 < deltaSq *pi) len = 0; } |
3112 | 0 | else if (a1 < 0 && -a1 < deltaSq *pi) len = 0; //holes have neg. area |
3113 | |
|
3114 | 0 | if (len == 0 || (len < 3 && delta <= 0)) |
3115 | 0 | continue; |
3116 | 0 | else if (len == 1) |
3117 | 0 | { |
3118 | 0 | Polygon arc; |
3119 | 0 | arc = BuildArc(in_polys[m_i][len-1], 0, 2 * pi, delta); |
3120 | 0 | out_polys[m_i] = arc; |
3121 | 0 | continue; |
3122 | 0 | } |
3123 | | |
3124 | | //build normals ... |
3125 | 0 | normals.clear(); |
3126 | 0 | normals.resize(len); |
3127 | 0 | normals[len-1] = GetUnitNormal(in_polys[m_i][len-1], in_polys[m_i][0]); |
3128 | 0 | for (m_j = 0; m_j < len -1; ++m_j) |
3129 | 0 | normals[m_j] = GetUnitNormal(in_polys[m_i][m_j], in_polys[m_i][m_j+1]); |
3130 | | |
3131 | 0 | m_k = len -1; |
3132 | 0 | for (m_j = 0; m_j < len; ++m_j) |
3133 | 0 | { |
3134 | 0 | switch (jointype) |
3135 | 0 | { |
3136 | 0 | case jtMiter: |
3137 | 0 | { |
3138 | 0 | m_R = 1 + (normals[m_j].X*normals[m_k].X + |
3139 | 0 | normals[m_j].Y*normals[m_k].Y); |
3140 | 0 | if (m_R >= m_RMin) DoMiter(); else DoSquare(MiterLimit); |
3141 | 0 | break; |
3142 | 0 | } |
3143 | 0 | case jtSquare: DoSquare(); break; |
3144 | 0 | case jtRound: DoRound(); break; |
3145 | 0 | } |
3146 | 0 | m_k = m_j; |
3147 | 0 | } |
3148 | 0 | } |
3149 | | |
3150 | | //finally, clean up untidy corners using Clipper ... |
3151 | 0 | Clipper clpr; |
3152 | 0 | clpr.AddPolygons(out_polys, ptSubject); |
3153 | 0 | if (delta > 0) |
3154 | 0 | { |
3155 | 0 | if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive)) |
3156 | 0 | out_polys.clear(); |
3157 | 0 | } |
3158 | 0 | else |
3159 | 0 | { |
3160 | 0 | IntRect r = clpr.GetBounds(); |
3161 | 0 | Polygon outer(4); |
3162 | 0 | outer[0] = IntPoint(r.left - 10, r.bottom + 10); |
3163 | 0 | outer[1] = IntPoint(r.right + 10, r.bottom + 10); |
3164 | 0 | outer[2] = IntPoint(r.right + 10, r.top - 10); |
3165 | 0 | outer[3] = IntPoint(r.left - 10, r.top - 10); |
3166 | |
|
3167 | 0 | clpr.AddPolygon(outer, ptSubject); |
3168 | 0 | if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative)) |
3169 | 0 | { |
3170 | 0 | out_polys.erase(out_polys.begin()); |
3171 | 0 | ReversePoints(out_polys); |
3172 | |
|
3173 | 0 | } else |
3174 | 0 | out_polys.clear(); |
3175 | 0 | } |
3176 | 0 | } |
3177 | | //------------------------------------------------------------------------------ |
3178 | | |
3179 | | private: |
3180 | | |
3181 | | void AddPoint(const IntPoint& pt) |
3182 | 0 | { |
3183 | 0 | Polygon::size_type len = m_curr_poly->size(); |
3184 | 0 | if (len == m_curr_poly->capacity()) |
3185 | 0 | m_curr_poly->reserve(len + buffLength); |
3186 | 0 | m_curr_poly->push_back(pt); |
3187 | 0 | } |
3188 | | //------------------------------------------------------------------------------ |
3189 | | |
3190 | | void DoSquare(double mul = 1.0) |
3191 | 0 | { |
3192 | 0 | IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), |
3193 | 0 | (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); |
3194 | 0 | IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), |
3195 | 0 | (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); |
3196 | 0 | if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) |
3197 | 0 | { |
3198 | 0 | double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); |
3199 | 0 | double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X); |
3200 | 0 | a1 = std::fabs(a2 - a1); |
3201 | 0 | if (a1 > pi) a1 = pi * 2 - a1; |
3202 | 0 | double dx = std::tan((pi - a1)/4) * std::fabs(m_delta * mul); |
3203 | 0 | pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), |
3204 | 0 | (long64)(pt1.Y + normals[m_k].X * dx)); |
3205 | 0 | AddPoint(pt1); |
3206 | 0 | pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), |
3207 | 0 | (long64)(pt2.Y -normals[m_j].X * dx)); |
3208 | 0 | AddPoint(pt2); |
3209 | 0 | } |
3210 | 0 | else |
3211 | 0 | { |
3212 | 0 | AddPoint(pt1); |
3213 | 0 | AddPoint(m_p[m_i][m_j]); |
3214 | 0 | AddPoint(pt2); |
3215 | 0 | } |
3216 | 0 | } |
3217 | | //------------------------------------------------------------------------------ |
3218 | | |
3219 | | void DoMiter() |
3220 | 0 | { |
3221 | 0 | if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0) |
3222 | 0 | { |
3223 | 0 | double q = m_delta / m_R; |
3224 | 0 | AddPoint(IntPoint((long64)Round(m_p[m_i][m_j].X + |
3225 | 0 | (normals[m_k].X + normals[m_j].X) * q), |
3226 | 0 | (long64)Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q))); |
3227 | 0 | } |
3228 | 0 | else |
3229 | 0 | { |
3230 | 0 | IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * |
3231 | 0 | m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); |
3232 | 0 | IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * |
3233 | 0 | m_delta), (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); |
3234 | 0 | AddPoint(pt1); |
3235 | 0 | AddPoint(m_p[m_i][m_j]); |
3236 | 0 | AddPoint(pt2); |
3237 | 0 | } |
3238 | 0 | } |
3239 | | //------------------------------------------------------------------------------ |
3240 | | |
3241 | | void DoRound() |
3242 | 0 | { |
3243 | 0 | IntPoint pt1 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta), |
3244 | 0 | (long64)Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta)); |
3245 | 0 | IntPoint pt2 = IntPoint((long64)Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta), |
3246 | 0 | (long64)Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta)); |
3247 | 0 | AddPoint(pt1); |
3248 | | //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg). |
3249 | 0 | if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0) |
3250 | 0 | { |
3251 | 0 | if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985) |
3252 | 0 | { |
3253 | 0 | double a1 = std::atan2(normals[m_k].Y, normals[m_k].X); |
3254 | 0 | double a2 = std::atan2(normals[m_j].Y, normals[m_j].X); |
3255 | 0 | if (m_delta > 0 && a2 < a1) a2 += pi *2; |
3256 | 0 | else if (m_delta < 0 && a2 > a1) a2 -= pi *2; |
3257 | 0 | Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta); |
3258 | 0 | for (Polygon::size_type m = 0; m < arc.size(); m++) |
3259 | 0 | AddPoint(arc[m]); |
3260 | 0 | } |
3261 | 0 | } |
3262 | 0 | else |
3263 | 0 | AddPoint(m_p[m_i][m_j]); |
3264 | 0 | AddPoint(pt2); |
3265 | 0 | } |
3266 | | //-------------------------------------------------------------------------- |
3267 | | |
3268 | | }; //end PolyOffsetBuilder |
3269 | | |
3270 | | //------------------------------------------------------------------------------ |
3271 | | //------------------------------------------------------------------------------ |
3272 | | |
3273 | | void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys, |
3274 | | double delta, JoinType jointype, double MiterLimit) |
3275 | 0 | { |
3276 | 0 | if (&out_polys == &in_polys) |
3277 | 0 | { |
3278 | 0 | Polygons poly2(in_polys); |
3279 | 0 | PolyOffsetBuilder(poly2, out_polys, delta, jointype, MiterLimit); |
3280 | 0 | } |
3281 | 0 | else PolyOffsetBuilder(in_polys, out_polys, delta, jointype, MiterLimit); |
3282 | 0 | } |
3283 | | //------------------------------------------------------------------------------ |
3284 | | |
3285 | | std::ostream& operator <<(std::ostream &s, IntPoint& p) |
3286 | 0 | { |
3287 | 0 | s << p.X << ' ' << p.Y << "\n"; |
3288 | 0 | return s; |
3289 | 0 | } |
3290 | | //------------------------------------------------------------------------------ |
3291 | | |
3292 | | std::ostream& operator <<(std::ostream &s, Polygon &p) |
3293 | 0 | { |
3294 | 0 | for (Polygon::size_type i = 0; i < p.size(); i++) |
3295 | 0 | s << p[i]; |
3296 | 0 | s << "\n"; |
3297 | 0 | return s; |
3298 | 0 | } |
3299 | | //------------------------------------------------------------------------------ |
3300 | | |
3301 | | std::ostream& operator <<(std::ostream &s, Polygons &p) |
3302 | 0 | { |
3303 | 0 | for (Polygons::size_type i = 0; i < p.size(); i++) |
3304 | 0 | s << p[i]; |
3305 | 0 | s << "\n"; |
3306 | 0 | return s; |
3307 | 0 | } |
3308 | | //------------------------------------------------------------------------------ |
3309 | | |
3310 | | } //ClipperLib namespace |