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