clipper 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog ADDED
@@ -0,0 +1,7 @@
1
+ 2010-12-08 Mike Owens <http://mike.filespanker.com>
2
+ * Gemification and Ruby 1.8.7 fixes courtesy of Tom Agnew
3
+ * Upstream 2.9
4
+
5
+ 2010-10-17 Mike Owens <http://mike.filespanker.com>
6
+ * Pull upstream 2.522
7
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in clipper.gemspec
4
+ gemspec
data/LICENSE.bindings ADDED
@@ -0,0 +1,4 @@
1
+ These bindings are derived from and include Angus Johnson's Clipper Library,
2
+ the license to which can be found in LICENSE.clipper.
3
+
4
+ These bindings are released under the same terms.
data/LICENSE.clipper ADDED
@@ -0,0 +1,29 @@
1
+ The Clipper code library, the "Software" (including Delphi and C++ code
2
+ and accompanying documentation), has been released under the following
3
+ license, terms and conditions:
4
+
5
+ Boost Software License - Version 1.0 - August 17th, 2003
6
+ http://www.boost.org/LICENSE_1_0.txt
7
+
8
+ Permission is hereby granted, free of charge, to any person or organization
9
+ obtaining a copy of the software and accompanying documentation covered by
10
+ this license (the "Software") to use, reproduce, display, distribute,
11
+ execute, and transmit the Software, and to prepare derivative works of the
12
+ Software, and to permit third-parties to whom the Software is furnished to
13
+ do so, all subject to the following:
14
+
15
+ The copyright notices in the Software and this entire statement, including
16
+ the above license grant, this restriction and the following disclaimer,
17
+ must be included in all copies of the Software, in whole or in part, and
18
+ all derivative works of the Software, unless such copies or derivative
19
+ works are solely in the form of machine-executable object code generated by
20
+ a source language processor.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
+ DEALINGS IN THE SOFTWARE.
29
+
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ Synopsis
2
+ ==========
3
+ These are Ruby bindings to Clipper, Angus Johnson's Polygon clipping
4
+ library. Because Clipper is not readily packaged, and is so beautifully
5
+ self-contained, I've included the two required files in the package.
6
+
7
+ This release contains version 2.9 of Clipper.
8
+
9
+ * [Clipper Homepage](http://angusj.com/delphi/clipper.php)
10
+ * [rbclipper](http://github.com/mieko/rbclipper)
11
+
12
+ To install:
13
+
14
+ gem install clipper
15
+
16
+ Build locally:
17
+
18
+ rake install
19
+
20
+
21
+ Simple Usage:
22
+ ===========
23
+ This shold be enough to get you started. Full documentation is below.
24
+
25
+ require 'clipper'
26
+
27
+ a = [[0, 0], [0, 100], [100, 100], [100, 0]]
28
+ b = [[-5, 50], [200, 50], [100, 5]]
29
+
30
+ c = Clipper::Clipper.new
31
+
32
+ c.add_subject_polygon(a)
33
+ c.add_clip_polygon(b)
34
+ c.union :non_zero, :non_zero
35
+
36
+ => [[[100.0, 0.0], [0.0, 0.0], [0.0, 47.85714326530613], [-4.999999, 50.0],
37
+ [0.0, 50.0], [0.0, 100.0], [100.0, 100.0], [100.0, 50.0],
38
+ [200.0, 50.0], [100.0, 5.0]]]
39
+
40
+ Documentation
41
+ ================
42
+
43
+ Clipper is a two-dimensional polygon clipping library. `rbclipper`, the Ruby
44
+ bindings can be accessed by:
45
+
46
+ require 'clipper'
47
+
48
+
49
+ Polygons
50
+ --------
51
+ Operations that accept or return polygons are specified as an array of `[x,y]`
52
+ coordinates, for example, to specify a triangle:
53
+
54
+ triangle = [[0,0], [0,100], [50, -100]]
55
+
56
+ Clipper supports both holes and complex polygons. Coordinates for output
57
+ polygons are clockwise for shells, and and counter-clockwise for holes.
58
+ See force_orientation.
59
+
60
+ Note that since 2.8, Clipper defines orientation with respect to a
61
+ _downward-increasing Y axis_, similar to how many 2D GUI/drawing APIs position
62
+ coordinate (0,0) at the top-left corner. The bindings have followed Clipper
63
+ proper in this regard.
64
+
65
+ Multiple polygons are represented as simply an array of polygons.
66
+
67
+ Fill Types
68
+ -----------
69
+ * `:even_odd`
70
+
71
+ A point is considered inside the polygon if the number of edge-crossings to
72
+ get there from outside the shape is an even number.
73
+
74
+ * `:non_zero`
75
+
76
+ A point is considered inside the polygon if the number of edge-crossings to
77
+ get there is greater than zero.
78
+
79
+ Clipper::Clipper Methods
80
+ -------
81
+
82
+ * `Clipper#initialize`
83
+
84
+ Creates a new clipper object.
85
+
86
+ * `Clipper#add_subject_polygon(polygon)`
87
+
88
+ `Clipper#add_clip_polygon(polygon)`
89
+
90
+ Adds a subject or clip polygon to the engine. Boolean operations are
91
+ calculated as `SUBJECT` *operatation* `CLIP`. Multiple polygons can Pay attention
92
+ to the orientation of the coordinates given; counter-clockwise for shells and
93
+ clockwise for holes.
94
+
95
+ Multiple subject and clip polygons can be added to the engine for operations.
96
+
97
+ * `Clipper#add_subject_poly_polygon(poly_polygon)`
98
+
99
+ `Clipper#add_clip_poly_polygon(poly_polygon)`
100
+
101
+ Add a "Poly-Polygon" to the engine. Which is basically a set of polygons.
102
+ Boolean operations consider every poly-polygon added in this manner to be the
103
+ same object.
104
+
105
+ * `Clipper#force_orientation`
106
+
107
+ `Clipper#force_orientation=`
108
+
109
+ Defaults to true. Ensures that the simple result of boolean operations have
110
+ the orientation as described in section Polygons. Only useful with simple
111
+ polygons.
112
+
113
+ * `Clipper#intersection(subject_fill=:even_odd, clip_fill=:even_odd)`
114
+
115
+ `Clipper#union(subject_fill=:even_odd, clip_fill=:even_odd)`
116
+
117
+ `Clipper#difference(subject_fill=:even_odd, clip_fill=:even_odd)`
118
+
119
+ `Clipper#xor(subject_fill=:even_odd, clip_fill=:even_odd)`
120
+
121
+ Performs a boolean operation on the polygons that have been added to the
122
+ clipper object. The result is a list of polygons.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,2495 @@
1
+ /*******************************************************************************
2
+ * *
3
+ * Author : Angus Johnson *
4
+ * Version : 2.9 *
5
+ * Date : 7 December 2010 *
6
+ * Copyright : Angus Johnson *
7
+ * *
8
+ * License: *
9
+ * Use, modification & distribution is subject to Boost Software License Ver 1. *
10
+ * http://www.boost.org/LICENSE_1_0.txt *
11
+ * *
12
+ * Attributions: *
13
+ * The code in this library is an extension of Bala Vatti's clipping algorithm: *
14
+ * "A generic solution to polygon clipping" *
15
+ * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
16
+ * http://portal.acm.org/citation.cfm?id=129906 *
17
+ * *
18
+ * Computer graphics and geometric modeling: implementation and algorithms *
19
+ * By Max K. Agoston *
20
+ * Springer; 1 edition (January 4, 2005) *
21
+ * Pages 98 - 106. *
22
+ * http://books.google.com/books?q=vatti+clipping+agoston *
23
+ * *
24
+ *******************************************************************************/
25
+
26
+ /*******************************************************************************
27
+ * *
28
+ * This is a translation of my Delphi clipper code and is the very first stuff *
29
+ * I've written in C++ (or C). My apologies if the coding style is unorthodox. *
30
+ * Please see the accompanying Delphi Clipper library (clipper.pas) for a more *
31
+ * detailed explanation of the code algorithms. *
32
+ * *
33
+ *******************************************************************************/
34
+
35
+ #include "clipper.hpp"
36
+ #include <cmath>
37
+ #include <vector>
38
+ #include <cstring>
39
+ #include <algorithm>
40
+
41
+ namespace clipper {
42
+
43
+ //infinite: simply used to define inverse slope (dx/dy) of horizontal edges
44
+ static double const infinite = -3.4E+38;
45
+ static double const almost_infinite = -3.39E+38;
46
+
47
+ //tolerance: is needed because vertices are floating point values and any
48
+ //comparison of floating point values requires a degree of tolerance. Ideally
49
+ //this value should vary depending on how big (or small) the supplied polygon
50
+ //coordinate values are. If coordinate values are greater than 1.0E+5
51
+ //(ie 100,000+) then tolerance should be adjusted up (since the significand
52
+ //of type double is 15 decimal places). However, for the vast majority
53
+ //of uses ... tolerance = 1.0e-10 will be just fine.
54
+ static double const tolerance = 1.0E-10;
55
+ static double const minimal_tolerance = 1.0E-14;
56
+ //precision: defines when adjacent vertices will be considered duplicates
57
+ //and hence ignored. This circumvents edges having indeterminate slope.
58
+ static double const precision = 1.0E-6;
59
+ static double const slope_precision = 1.0E-3;
60
+ static double const pi = 3.14159265358979;
61
+ typedef enum _Direction { dRightToLeft, dLeftToRight } TDirection;
62
+ static const TDoubleRect nullRect = {0,0,0,0};
63
+
64
+ using namespace std;
65
+
66
+ //------------------------------------------------------------------------------
67
+ //------------------------------------------------------------------------------
68
+
69
+ TDoubleRect GetBounds(const TPolygon& poly)
70
+ {
71
+ if (poly.size() == 0) return nullRect;
72
+ TDoubleRect result;
73
+ result.left = poly[0].X; result.top = poly[0].Y;
74
+ result.right = poly[0].X; result.bottom = poly[0].Y;
75
+ for (int i = 1; i < int(poly.size()); ++i)
76
+ {
77
+ if (poly[i].X < result.left) result.left = poly[i].X;
78
+ else if (poly[i].X > result.right) result.right = poly[i].X;
79
+ if (poly[i].Y < result.top) result.top = poly[i].Y;
80
+ else if (poly[i].Y > result.bottom) result.bottom = poly[i].Y;
81
+ }
82
+ return result;
83
+ }
84
+ //------------------------------------------------------------------------------
85
+
86
+ TDoublePoint GetUnitNormal( const TDoublePoint &pt1, const TDoublePoint &pt2)
87
+ {
88
+ double dx = ( pt2.X - pt1.X );
89
+ double dy = ( pt2.Y - pt1.Y );
90
+ if( ( dx == 0 ) && ( dy == 0 ) ) return DoublePoint( 0, 0 );
91
+
92
+ double f = 1 *1.0/ hypot( dx , dy );
93
+ dx = dx * f;
94
+ dy = dy * f;
95
+ return DoublePoint(dy, -dx);
96
+ }
97
+ //------------------------------------------------------------------------------
98
+
99
+ TPolygon BuildArc(const TDoublePoint &pt,
100
+ const double a1, const double a2, const double r)
101
+ {
102
+ int steps = max(6, int(sqrt(abs(r)) * abs(a2 - a1)));
103
+ TPolygon result(steps);
104
+ int n = steps - 1;
105
+ double da = (a2 - a1) / n;
106
+ double a = a1;
107
+ for (int i = 0; i <= n; ++i)
108
+ {
109
+ double dy = sin(a)*r;
110
+ double dx = cos(a)*r;
111
+ result[i].X = pt.X + dx;
112
+ result[i].Y = pt.Y + dy;
113
+ a = a + da;
114
+ }
115
+ return result;
116
+ }
117
+ //------------------------------------------------------------------------------
118
+
119
+ double Area(const TPolygon &pts)
120
+ {
121
+ int highI = (int)pts.size() -1;
122
+ if (highI < 2) return 0;
123
+ double area = pts[highI].X * pts[0].Y - pts[0].X * pts[highI].Y;
124
+ for (int i = 0; i < highI; ++i)
125
+ area += pts[i].X * pts[i+1].Y - pts[i+1].X * pts[i].Y;
126
+ return area/2;
127
+ }
128
+ //------------------------------------------------------------------------------
129
+
130
+ TPolyPolygon OffsetPolygons(const TPolyPolygon &pts, const double &delta)
131
+ {
132
+ //A positive delta will offset each polygon edge towards its left, so
133
+ //polygons orientated clockwise (ie outer polygons) will expand but
134
+ //inner polyons (holes) will shrink. Conversely, negative deltas will
135
+ //offset polygon edges towards their right so outer polygons will shrink
136
+ //and inner polygons will expand.
137
+
138
+ double deltaSq = delta*delta;
139
+ TPolyPolygon result(pts.size());
140
+
141
+ for (int j = 0; j < (int)pts.size(); ++j)
142
+ {
143
+ int highI = (int)pts[j].size() -1;
144
+ //to minimize artefacts, strip out those polygons where
145
+ //it's shrinking and where its area < Sqr(delta) ...
146
+ double a1 = Area(pts[j]);
147
+ if (delta < 0) { if (a1 > 0 && a1 < deltaSq) highI = 0;}
148
+ else if (a1 < 0 && -a1 < deltaSq) highI = 0; //nb: a hole if area < 0
149
+
150
+ TPolygon pg;
151
+ pg.reserve(highI*2+2);
152
+
153
+ if (highI < 2)
154
+ {
155
+ result.push_back(pg);
156
+ continue;
157
+ }
158
+
159
+ TPolygon normals(highI+1);
160
+ normals[0] = GetUnitNormal(pts[j][highI], pts[j][0]);
161
+ for (int i = 1; i <= highI; ++i)
162
+ normals[i] = GetUnitNormal(pts[j][i-1], pts[j][i]);
163
+
164
+ for (int i = 0; i < highI; ++i)
165
+ {
166
+ pg.push_back(DoublePoint(pts[j][i].X + delta *normals[i].X,
167
+ pts[j][i].Y + delta *normals[i].Y));
168
+ pg.push_back(DoublePoint(pts[j][i].X + delta *normals[i+1].X,
169
+ pts[j][i].Y + delta *normals[i+1].Y));
170
+ }
171
+ pg.push_back(DoublePoint(pts[j][highI].X + delta *normals[highI].X,
172
+ pts[j][highI].Y + delta *normals[highI].Y));
173
+ pg.push_back(DoublePoint(pts[j][highI].X + delta *normals[0].X,
174
+ pts[j][highI].Y + delta *normals[0].Y));
175
+
176
+ //round off reflex angles (ie > 180 deg) unless it's almost flat (ie < 10deg angle) ...
177
+ //cross product normals < 0 -> reflex angle; dot product normals == 1 -> no angle
178
+ if ((normals[highI].X *normals[0].Y - normals[0].X *normals[highI].Y) *delta > 0 &&
179
+ (normals[0].X *normals[highI].X + normals[0].Y *normals[highI].Y) < 0.985)
180
+ {
181
+ double a1 = atan2(normals[highI].Y, normals[highI].X);
182
+ double a2 = atan2(normals[0].Y, normals[0].X);
183
+ if (delta > 0 && a2 < a1) a2 = a2 + pi*2;
184
+ else if (delta < 0 && a2 > a1) a2 = a2 - pi*2;
185
+ TPolygon arc = BuildArc(pts[j][highI], a1, a2, delta);
186
+ TPolygon::iterator it = pg.begin() +highI*2+1;
187
+ pg.insert(it, arc.begin(), arc.end());
188
+ }
189
+ for (int i = highI; i > 0; --i)
190
+ if ((normals[i-1].X*normals[i].Y - normals[i].X*normals[i-1].Y) *delta > 0 &&
191
+ (normals[i].X*normals[i-1].X + normals[i].Y*normals[i-1].Y) < 0.985)
192
+ {
193
+ double a1 = atan2(normals[i-1].Y, normals[i-1].X);
194
+ double a2 = atan2(normals[i].Y, normals[i].X);
195
+ if (delta > 0 && a2 < a1) a2 = a2 + pi*2;
196
+ else if (delta < 0 && a2 > a1) a2 = a2 - pi*2;
197
+ TPolygon arc = BuildArc(pts[j][i-1], a1, a2, delta);
198
+ TPolygon::iterator it = pg.begin() +(i-1)*2+1;
199
+ pg.insert(it, arc.begin(), arc.end());
200
+ }
201
+ result.push_back(pg);
202
+ }
203
+
204
+ //finally, clean up untidy corners ...
205
+ Clipper c;
206
+ c.AddPolyPolygon(result, ptSubject);
207
+ if (delta > 0){
208
+ if(!c.Execute(ctUnion, result, pftNonZero, pftNonZero)) result.clear();
209
+ }
210
+ else
211
+ {
212
+ TDoubleRect r = c.GetBounds();
213
+ TPolygon outer(4);
214
+ outer[0] = DoublePoint(r.left-10, r.bottom+10);
215
+ outer[1] = DoublePoint(r.right+10, r.bottom+10);
216
+ outer[2] = DoublePoint(r.right+10, r.top-10);
217
+ outer[3] = DoublePoint(r.left-10, r.top-10);
218
+ c.AddPolygon(outer, ptSubject);
219
+ if (c.Execute(ctUnion, result, pftNonZero, pftNonZero))
220
+ {
221
+ TPolyPolygon::iterator it = result.begin();
222
+ result.erase(it);
223
+ }
224
+ else
225
+ result.clear();
226
+ }
227
+ return result;
228
+ }
229
+ //------------------------------------------------------------------------------
230
+
231
+ bool IsClockwise(const TPolygon &poly)
232
+ {
233
+ int highI = poly.size() -1;
234
+ if (highI < 2) return false;
235
+ double area = poly[highI].X * poly[0].Y - poly[0].X * poly[highI].Y;
236
+ for (int i = 0; i < highI; ++i)
237
+ area += poly[i].X * poly[i+1].Y - poly[i+1].X * poly[i].Y;
238
+ //area := area/2;
239
+ return area > 0; //ie reverse of normal formula because Y axis inverted
240
+ }
241
+ //------------------------------------------------------------------------------
242
+
243
+ TDoublePoint DoublePoint(const double &X, const double &Y)
244
+ {
245
+ TDoublePoint p;
246
+ p.X = X;
247
+ p.Y = Y;
248
+ return p;
249
+ }
250
+ //------------------------------------------------------------------------------
251
+
252
+ bool PointsEqual( const TDoublePoint &pt1, const TDoublePoint &pt2)
253
+ {
254
+ return ( fabs( pt1.X - pt2.X ) < precision + tolerance ) &&
255
+ ( fabs( (pt1.Y - pt2.Y) ) < precision + tolerance );
256
+ }
257
+ //------------------------------------------------------------------------------
258
+
259
+ bool PointsEqual( const double &pt1x, const double &pt1y,
260
+ const double &pt2x, const double &pt2y)
261
+ {
262
+ return ( fabs( pt1x - pt2x ) < precision + tolerance ) &&
263
+ ( fabs( (pt1y - pt2y) ) < precision + tolerance );
264
+ }
265
+ //------------------------------------------------------------------------------
266
+
267
+ void DisposePolyPts(TPolyPt *&pp)
268
+ {
269
+ if (pp == 0) return;
270
+ TPolyPt *tmpPp;
271
+ pp->prev->next = 0;
272
+ while( pp )
273
+ {
274
+ tmpPp = pp;
275
+ pp = pp->next;
276
+ delete tmpPp ;
277
+ }
278
+ }
279
+ //------------------------------------------------------------------------------
280
+
281
+ void Clipper::DisposeAllPolyPts(){
282
+ for (unsigned i = 0; i < m_PolyPts.size(); ++i)
283
+ DisposePolyPts(m_PolyPts[i]);
284
+ m_PolyPts.clear();
285
+ }
286
+ //------------------------------------------------------------------------------
287
+
288
+ void ReversePolyPtLinks(TPolyPt &pp)
289
+ {
290
+ TPolyPt *pp1, *pp2;
291
+ pp1 = &pp;
292
+ do {
293
+ pp2 = pp1->next;
294
+ pp1->next = pp1->prev;
295
+ pp1->prev = pp2;
296
+ pp1 = pp2;
297
+ } while( pp1 != &pp );
298
+ }
299
+ //------------------------------------------------------------------------------
300
+
301
+ bool PtInPoly(const TDoublePoint pt, TPolyPt*& polyStartPt)
302
+ {
303
+ if (!polyStartPt) return false;
304
+ TPolyPt* p = polyStartPt;
305
+ do {
306
+ if (PointsEqual(pt, polyStartPt->pt)) return true;
307
+ polyStartPt = polyStartPt->next;
308
+ }
309
+ while (polyStartPt != p);
310
+ return false;
311
+ }
312
+ //------------------------------------------------------------------------------
313
+
314
+ void SetDx(TEdge &e)
315
+ {
316
+ double dx = fabs(e.x - e.next->x);
317
+ double dy = fabs(e.y - e.next->y);
318
+ //Very short, nearly horizontal edges can cause problems by very
319
+ //inaccurately determining intermediate X values - see TopX().
320
+ //Therefore treat very short, nearly horizontal edges as horizontal too ...
321
+ if ( (dx < 0.1 && dy *10 < dx) || dy < slope_precision ) {
322
+ e.dx = infinite;
323
+ if (e.y != e.next->y) e.y = e.next->y;
324
+ }
325
+ else e.dx =
326
+ (e.x - e.next->x)/(e.y - e.next->y);
327
+ }
328
+ //------------------------------------------------------------------------------
329
+
330
+ bool IsHorizontal(const TEdge &e)
331
+ {
332
+ return &e && ( e.dx < almost_infinite );
333
+ }
334
+ //------------------------------------------------------------------------------
335
+
336
+ bool IsHorizontal(TPolyPt* pp1, TPolyPt* pp2)
337
+ {
338
+ return (fabs(pp1->pt.X - pp2->pt.X) > precision &&
339
+ fabs(pp1->pt.Y - pp2->pt.Y) < precision);
340
+ }
341
+ //------------------------------------------------------------------------------
342
+
343
+ void SwapSides(TEdge &edge1, TEdge &edge2)
344
+ {
345
+ TEdgeSide side = edge1.side;
346
+ edge1.side = edge2.side;
347
+ edge2.side = side;
348
+ }
349
+ //------------------------------------------------------------------------------
350
+
351
+ void SwapPolyIndexes(TEdge &edge1, TEdge &edge2)
352
+ {
353
+ int outIdx = edge1.outIdx;
354
+ edge1.outIdx = edge2.outIdx;
355
+ edge2.outIdx = outIdx;
356
+ }
357
+ //------------------------------------------------------------------------------
358
+
359
+ double TopX(TEdge *edge, const double &currentY)
360
+ {
361
+ if( currentY == edge->ytop ) return edge->xtop;
362
+ return edge->x + edge->dx *( currentY - edge->y );
363
+ }
364
+ //------------------------------------------------------------------------------
365
+
366
+ bool EdgesShareSamePoly(TEdge &e1, TEdge &e2)
367
+ {
368
+ return &e1 && &e2 && ( e1.outIdx == e2.outIdx );
369
+ }
370
+ //------------------------------------------------------------------------------
371
+
372
+ bool SlopesEqual(TEdge &e1, TEdge &e2)
373
+ {
374
+ if (IsHorizontal(e1)) return IsHorizontal(e2);
375
+ if (IsHorizontal(e2)) return false;
376
+ return fabs((e1.ytop - e1.y)*(e2.xtop - e2.x) -
377
+ (e1.xtop - e1.x)*(e2.ytop - e2.y)) < slope_precision;
378
+ }
379
+ //------------------------------------------------------------------------------
380
+
381
+ bool IntersectPoint(TEdge &edge1, TEdge &edge2, TDoublePoint &ip)
382
+ {
383
+ double b1, b2;
384
+ if( edge1.dx == 0 )
385
+ {
386
+ ip.X = edge1.x;
387
+ b2 = edge2.y - edge2.x/edge2.dx;
388
+ ip.Y = ip.X/edge2.dx + b2;
389
+ }
390
+ else if( edge2.dx == 0 )
391
+ {
392
+ ip.X = edge2.x;
393
+ b1 = edge1.y - edge1.x/edge1.dx;
394
+ ip.Y = ip.X/edge1.dx + b1;
395
+ }
396
+ else
397
+ {
398
+ if( edge1.dx == edge2.dx ) return false;
399
+ b1 = edge1.x - edge1.y *edge1.dx;
400
+ b2 = edge2.x - edge2.y *edge2.dx;
401
+ ip.Y = (b2-b1)/(edge1.dx - edge2.dx);
402
+ ip.X = edge1.dx * ip.Y + b1;
403
+ }
404
+ return (ip.Y > edge1.ytop + tolerance) && (ip.Y > edge2.ytop + tolerance);
405
+ }
406
+ //------------------------------------------------------------------------------
407
+
408
+ bool IsClockwise(TPolyPt *pt)
409
+ {
410
+ double area = 0;
411
+ TPolyPt* startPt = pt;
412
+ do
413
+ {
414
+ area = area + (pt->pt.X * pt->next->pt.Y) - (pt->next->pt.X * pt->pt.Y);
415
+ pt = pt->next;
416
+ }
417
+ while (pt != startPt);
418
+ //area = area /2;
419
+ return area > 0; //ie reverse of normal formula because Y axis inverted
420
+ }
421
+ //------------------------------------------------------------------------------
422
+
423
+ bool ValidateOrientation(TPolyPt *pt)
424
+ {
425
+ //check that orientation matches the hole status ...
426
+
427
+ //first, find the hole state of the bottom-most point (because
428
+ //the hole state of other points is not reliable) ...
429
+ TPolyPt* bottomPt = pt;
430
+ TPolyPt* ptStart = pt;
431
+ pt = pt->next;
432
+ while( ( pt != ptStart ) )
433
+ {
434
+ if( ( pt->pt.Y > bottomPt->pt.Y ) ||
435
+ ( ( pt->pt.Y == bottomPt->pt.Y ) && ( pt->pt.X > bottomPt->pt.X ) ) )
436
+ bottomPt = pt;
437
+ pt = pt->next;
438
+ }
439
+
440
+ while (bottomPt->isHole == sUndefined &&
441
+ bottomPt->next->pt.Y >= bottomPt->pt.Y) bottomPt = bottomPt->next;
442
+ while (bottomPt->isHole == sUndefined &&
443
+ bottomPt->prev->pt.Y >= bottomPt->pt.Y) bottomPt = bottomPt->prev;
444
+ return (IsClockwise(pt) == (bottomPt->isHole == sFalse));
445
+ }
446
+ //------------------------------------------------------------------------------
447
+
448
+ void InitEdge(TEdge *e, TEdge *eNext, TEdge *ePrev, const TDoublePoint &pt)
449
+ {
450
+ memset( e, 0, sizeof( TEdge ));
451
+ e->x = pt.X;
452
+ e->y = pt.Y;
453
+ e->next = eNext;
454
+ e->prev = ePrev;
455
+ SetDx(*e);
456
+ }
457
+ //------------------------------------------------------------------------------
458
+
459
+ void ReInitEdge(TEdge *e, const double &nextX,
460
+ const double &nextY, TPolyType polyType)
461
+ {
462
+ if ( e->y > nextY )
463
+ {
464
+ e->xbot = e->x;
465
+ e->ybot = e->y;
466
+ e->xtop = nextX;
467
+ e->ytop = nextY;
468
+ e->nextAtTop = true;
469
+ } else {
470
+ e->xbot = nextX;
471
+ e->ybot = nextY;
472
+ e->xtop = e->x;
473
+ e->ytop = e->y;
474
+ e->x = e->xbot;
475
+ e->y = e->ybot;
476
+ e->nextAtTop = false;
477
+ }
478
+ e->polyType = polyType;
479
+ e->outIdx = -1;
480
+ }
481
+ //------------------------------------------------------------------------------
482
+
483
+ bool SlopesEqualInternal(TEdge &e1, TEdge &e2)
484
+ {
485
+ if (IsHorizontal(e1)) return IsHorizontal(e2);
486
+ if (IsHorizontal(e2)) return false;
487
+ return fabs((e1.y - e1.next->y) *
488
+ (e2.x - e2.next->x) -
489
+ (e1.x - e1.next->x) *
490
+ (e2.y - e2.next->y)) < slope_precision;
491
+ }
492
+ //------------------------------------------------------------------------------
493
+
494
+ bool FixupForDupsAndColinear( TEdge *&e, TEdge *edges)
495
+ {
496
+ bool result = false;
497
+ while ( e->next != e->prev &&
498
+ (PointsEqual(e->prev->x, e->prev->y, e->x, e->y) ||
499
+ SlopesEqualInternal(*e->prev, *e)) )
500
+ {
501
+ result = true;
502
+ //remove 'e' from the double-linked-list ...
503
+ if ( e == edges )
504
+ {
505
+ //move the content of e.next to e before removing e.next from DLL ...
506
+ e->x = e->next->x;
507
+ e->y = e->next->y;
508
+ e->next->next->prev = e;
509
+ e->next = e->next->next;
510
+ } else
511
+ {
512
+ //remove 'e' from the loop ...
513
+ e->prev->next = e->next;
514
+ e->next->prev = e->prev;
515
+ e = e->prev; //ie get back into the loop
516
+ }
517
+ SetDx(*e->prev);
518
+ SetDx(*e);
519
+ }
520
+ return result;
521
+ }
522
+ //------------------------------------------------------------------------------
523
+
524
+ void SwapX(TEdge &e)
525
+ {
526
+ //swap horizontal edges' top and bottom x's so they follow the natural
527
+ //progression of the bounds - ie so their xbots will align with the
528
+ //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
529
+ e.xbot = e.xtop;
530
+ e.xtop = e.x;
531
+ e.x = e.xbot;
532
+ e.nextAtTop = !e.nextAtTop; //but really redundant for horizontals
533
+ }
534
+
535
+ //------------------------------------------------------------------------------
536
+ // ClipperBase methods ...
537
+ //------------------------------------------------------------------------------
538
+
539
+ ClipperBase::ClipperBase() //constructor
540
+ {
541
+ m_localMinimaList = 0;
542
+ m_CurrentLM = 0;
543
+ m_edges.reserve(32);
544
+ }
545
+ //------------------------------------------------------------------------------
546
+
547
+ ClipperBase::~ClipperBase() //destructor
548
+ {
549
+ Clear();
550
+ }
551
+ //------------------------------------------------------------------------------
552
+
553
+ void ClipperBase::InsertLocalMinima(TLocalMinima *newLm)
554
+ {
555
+ if( ! m_localMinimaList )
556
+ {
557
+ m_localMinimaList = newLm;
558
+ }
559
+ else if( newLm->Y >= m_localMinimaList->Y )
560
+ {
561
+ newLm->nextLm = m_localMinimaList;
562
+ m_localMinimaList = newLm;
563
+ } else
564
+ {
565
+ TLocalMinima* tmpLm = m_localMinimaList;
566
+ while( tmpLm->nextLm && ( newLm->Y < tmpLm->nextLm->Y ) )
567
+ tmpLm = tmpLm->nextLm;
568
+ newLm->nextLm = tmpLm->nextLm;
569
+ tmpLm->nextLm = newLm;
570
+ }
571
+ }
572
+ //------------------------------------------------------------------------------
573
+
574
+ TEdge *ClipperBase::AddBoundsToLML(TEdge *e)
575
+ {
576
+ //Starting at the top of one bound we progress to the bottom where there's
577
+ //a local minima. We then go to the top of the next bound. These two bounds
578
+ //form the left and right (or right and left) bounds of the local minima.
579
+ e->nextInLML = 0;
580
+ e = e->next;
581
+ for (;;)
582
+ {
583
+ if ( IsHorizontal(*e) )
584
+ {
585
+ //nb: proceed through horizontals when approaching from their right,
586
+ // but break on horizontal minima if approaching from their left.
587
+ // This ensures 'local minima' are always on the left of horizontals.
588
+ if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break;
589
+ if (e->xtop != e->prev->xbot) SwapX( *e );
590
+ e->nextInLML = e->prev;
591
+ }
592
+ else if (e->ybot == e->prev->ybot) break;
593
+ else e->nextInLML = e->prev;
594
+ e = e->next;
595
+ }
596
+
597
+ //e and e.prev are now at a local minima ...
598
+ TLocalMinima* newLm = new TLocalMinima;
599
+ newLm->nextLm = 0;
600
+ newLm->Y = e->prev->ybot;
601
+
602
+ if ( IsHorizontal(*e) ) //horizontal edges never start a left bound
603
+ {
604
+ if (e->xbot != e->prev->xbot) SwapX(*e);
605
+ newLm->leftBound = e->prev;
606
+ newLm->rightBound = e;
607
+ } else if (e->dx < e->prev->dx)
608
+ {
609
+ newLm->leftBound = e->prev;
610
+ newLm->rightBound = e;
611
+ } else
612
+ {
613
+ newLm->leftBound = e;
614
+ newLm->rightBound = e->prev;
615
+ }
616
+ newLm->leftBound->side = esLeft;
617
+ newLm->rightBound->side = esRight;
618
+ InsertLocalMinima( newLm );
619
+
620
+ for (;;)
621
+ {
622
+ if ( e->next->ytop == e->ytop && !IsHorizontal(*e->next) ) break;
623
+ e->nextInLML = e->next;
624
+ e = e->next;
625
+ if ( IsHorizontal(*e) && e->xbot != e->prev->xtop) SwapX(*e);
626
+ }
627
+ return e->next;
628
+ }
629
+ //------------------------------------------------------------------------------
630
+
631
+ TDoublePoint RoundToPrecision(const TDoublePoint &pt){
632
+ TDoublePoint result;
633
+ result.X = (pt.X >= 0.0) ?
634
+ (floor( pt.X/precision + 0.5 ) * precision):
635
+ (ceil ( pt.X/precision + 0.5 ) * precision);
636
+ result.Y = (pt.Y >= 0.0) ?
637
+ (floor( pt.Y/precision + 0.5 ) * precision):
638
+ (ceil ( pt.Y/precision + 0.5 ) * precision);
639
+ return result;
640
+ }
641
+ //------------------------------------------------------------------------------
642
+
643
+ void ClipperBase::AddPolygon( const TPolygon &pg, TPolyType polyType)
644
+ {
645
+ int highI = pg.size() -1;
646
+ TPolygon p(highI + 1);
647
+ for (int i = 0; i <= highI; ++i) p[i] = RoundToPrecision(pg[i]);
648
+ while( (highI > 1) && PointsEqual(p[0] , p[highI]) ) highI--;
649
+ if( highI < 2 ) return;
650
+
651
+ //make sure this is still a sensible polygon (ie with at least one minima) ...
652
+ int i = 1;
653
+ while( i <= highI && fabs(p[i].Y - p[0].Y) < precision ) i++;
654
+ if( i > highI ) return;
655
+
656
+ //create a new edge array ...
657
+ TEdge *edges = new TEdge [highI +1];
658
+ m_edges.push_back(edges);
659
+
660
+ //convert 'edges' to a double-linked-list and initialize a few of the vars ...
661
+ edges[0].x = p[0].X;
662
+ edges[0].y = p[0].Y;
663
+ InitEdge(&edges[highI], &edges[0], &edges[highI-1], p[highI]);
664
+ for (i = highI-1; i > 0; --i)
665
+ InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i]);
666
+ InitEdge(&edges[0], &edges[1], &edges[highI], p[0]);
667
+
668
+ //fixup by deleting any duplicate points and amalgamating co-linear edges ...
669
+ TEdge* e = edges;
670
+ do {
671
+ FixupForDupsAndColinear(e, edges);
672
+ e = e->next;
673
+ }
674
+ while ( e != edges );
675
+ while ( FixupForDupsAndColinear(e, edges))
676
+ {
677
+ e = e->prev;
678
+ if ( !FixupForDupsAndColinear(e, edges) ) break;
679
+ e = edges;
680
+ }
681
+
682
+ //make sure we still have a valid polygon ...
683
+ if( e->next == e->prev )
684
+ {
685
+ m_edges.pop_back();
686
+ delete [] edges;
687
+ return;
688
+ }
689
+
690
+ //now properly re-initialize edges and also find 'eHighest' ...
691
+ e = edges->next;
692
+ TEdge* eHighest = e;
693
+ do {
694
+ ReInitEdge(e, e->next->x, e->next->y, polyType);
695
+ if( e->ytop < eHighest->ytop ) eHighest = e;
696
+ e = e->next;
697
+ } while( e != edges );
698
+
699
+ TDoublePoint nextPt;
700
+ if ( e->next->nextAtTop )
701
+ ReInitEdge(e, e->next->x, e->next->y, polyType); else
702
+ ReInitEdge(e, e->next->xtop, e->next->ytop, polyType);
703
+ if ( e->ytop < eHighest->ytop ) eHighest = e;
704
+
705
+ //make sure eHighest is positioned so the following loop works safely ...
706
+ if ( eHighest->nextAtTop ) eHighest = eHighest->next;
707
+ if ( IsHorizontal( *eHighest) ) eHighest = eHighest->next;
708
+
709
+ //finally insert each local minima ...
710
+ e = eHighest;
711
+ do {
712
+ e = AddBoundsToLML(e);
713
+ } while( e != eHighest );
714
+
715
+ }
716
+ //------------------------------------------------------------------------------
717
+
718
+ void ClipperBase::AddPolyPolygon( const TPolyPolygon &ppg, TPolyType polyType)
719
+ {
720
+ for (unsigned i = 0; i < ppg.size(); ++i)
721
+ AddPolygon(ppg[i], polyType);
722
+ }
723
+ //------------------------------------------------------------------------------
724
+
725
+ void ClipperBase::Clear()
726
+ {
727
+ DisposeLocalMinimaList();
728
+ for (unsigned i = 0; i < m_edges.size(); ++i) delete [] m_edges[i];
729
+ m_edges.clear();
730
+ }
731
+ //------------------------------------------------------------------------------
732
+
733
+ TDoubleRect ClipperBase::GetBounds()
734
+ {
735
+ TDoubleRect result;
736
+ TLocalMinima* lm = m_localMinimaList;
737
+ if (!lm)
738
+ {
739
+ result.left = 0;
740
+ result.top = 0;
741
+ result.right = 0;
742
+ result.bottom = 0;
743
+ return result;
744
+ }
745
+ result.left = -infinite;
746
+ result.top = -infinite;
747
+ result.right = infinite;
748
+ result.bottom = infinite;
749
+ while (lm)
750
+ {
751
+ if (lm->leftBound->y > result.bottom) result.bottom = lm->leftBound->y;
752
+ TEdge* e = lm->leftBound;
753
+ while (e->nextInLML)
754
+ {
755
+ if (e->x < result.left) result.left = e->x;
756
+ e = e->nextInLML;
757
+ }
758
+ if (e->x < result.left) result.left = e->x;
759
+ else if (e->xtop < result.left) result.left = e->xtop;
760
+ if (e->ytop < result.top) result.top = e->ytop;
761
+
762
+ e = lm->rightBound;
763
+ while (e->nextInLML)
764
+ {
765
+ if (e->x > result.right) result.right = e->x;
766
+ e = e->nextInLML;
767
+ }
768
+ if (e->x > result.right) result.right = e->x;
769
+ else if (e->xtop > result.right) result.right = e->xtop;
770
+
771
+ lm = lm->nextLm;
772
+ }
773
+ return result;
774
+ }
775
+ //------------------------------------------------------------------------------
776
+
777
+ bool ClipperBase::Reset()
778
+ {
779
+ m_CurrentLM = m_localMinimaList;
780
+ if( !m_CurrentLM ) return false; //ie nothing to process
781
+
782
+ //reset all edges ...
783
+ TLocalMinima* lm = m_localMinimaList;
784
+ while( lm )
785
+ {
786
+ TEdge* e = lm->leftBound;
787
+ while( e )
788
+ {
789
+ e->xbot = e->x;
790
+ e->ybot = e->y;
791
+ e->side = esLeft;
792
+ e->outIdx = -1;
793
+ e = e->nextInLML;
794
+ }
795
+ e = lm->rightBound;
796
+ while( e )
797
+ {
798
+ e->xbot = e->x;
799
+ e->ybot = e->y;
800
+ e->side = esRight;
801
+ e->outIdx = -1;
802
+ e = e->nextInLML;
803
+ }
804
+ lm = lm->nextLm;
805
+ }
806
+ return true;
807
+ }
808
+ //------------------------------------------------------------------------------
809
+
810
+ void ClipperBase::PopLocalMinima()
811
+ {
812
+ if( ! m_CurrentLM ) return;
813
+ m_CurrentLM = m_CurrentLM->nextLm;
814
+ }
815
+ //------------------------------------------------------------------------------
816
+
817
+ void ClipperBase::DisposeLocalMinimaList()
818
+ {
819
+ while( m_localMinimaList )
820
+ {
821
+ TLocalMinima* tmpLm = m_localMinimaList->nextLm;
822
+ delete m_localMinimaList;
823
+ m_localMinimaList = tmpLm;
824
+ }
825
+ m_CurrentLM = 0;
826
+ }
827
+
828
+ //------------------------------------------------------------------------------
829
+ // Clipper methods ...
830
+ //------------------------------------------------------------------------------
831
+
832
+ Clipper::Clipper() : ClipperBase() //constructor
833
+ {
834
+ m_Scanbeam = 0;
835
+ m_ActiveEdges = 0;
836
+ m_SortedEdges = 0;
837
+ m_IntersectNodes = 0;
838
+ m_ExecuteLocked = false;
839
+ m_ForceOrientation = true;
840
+ m_PolyPts.reserve(32);
841
+ };
842
+ //------------------------------------------------------------------------------
843
+
844
+ Clipper::~Clipper() //destructor
845
+ {
846
+ DisposeScanbeamList();
847
+ DisposeAllPolyPts();
848
+ };
849
+ //------------------------------------------------------------------------------
850
+
851
+ void Clipper::DisposeScanbeamList()
852
+ {
853
+ while ( m_Scanbeam ) {
854
+ TScanbeam* sb2 = m_Scanbeam->nextSb;
855
+ delete m_Scanbeam;
856
+ m_Scanbeam = sb2;
857
+ }
858
+ }
859
+ //------------------------------------------------------------------------------
860
+
861
+ bool Clipper::InitializeScanbeam()
862
+ {
863
+ DisposeScanbeamList();
864
+ if( !Reset() ) return false;
865
+ //add all the local minima into a fresh fScanbeam list ...
866
+ TLocalMinima* lm = m_CurrentLM;
867
+ while( lm )
868
+ {
869
+ InsertScanbeam( lm->Y );
870
+ InsertScanbeam(lm->leftBound->ytop); //this is necessary too!
871
+ lm = lm->nextLm;
872
+ }
873
+ return true;
874
+ }
875
+ //------------------------------------------------------------------------------
876
+
877
+ void Clipper::InsertScanbeam( const double &Y)
878
+ {
879
+ if( !m_Scanbeam )
880
+ {
881
+ m_Scanbeam = new TScanbeam;
882
+ m_Scanbeam->nextSb = 0;
883
+ m_Scanbeam->Y = Y;
884
+ }
885
+ else if( Y > m_Scanbeam->Y )
886
+ {
887
+ TScanbeam* newSb = new TScanbeam;
888
+ newSb->Y = Y;
889
+ newSb->nextSb = m_Scanbeam;
890
+ m_Scanbeam = newSb;
891
+ } else
892
+ {
893
+ TScanbeam* sb2 = m_Scanbeam;
894
+ while( sb2->nextSb && ( Y <= sb2->nextSb->Y ) ) sb2 = sb2->nextSb;
895
+ if( Y == sb2->Y ) return; //ie ignores duplicates
896
+ TScanbeam* newSb = new TScanbeam;
897
+ newSb->Y = Y;
898
+ newSb->nextSb = sb2->nextSb;
899
+ sb2->nextSb = newSb;
900
+ }
901
+ }
902
+ //------------------------------------------------------------------------------
903
+
904
+ double Clipper::PopScanbeam()
905
+ {
906
+ double Y = m_Scanbeam->Y;
907
+ TScanbeam* sb2 = m_Scanbeam;
908
+ m_Scanbeam = m_Scanbeam->nextSb;
909
+ delete sb2;
910
+ return Y;
911
+ }
912
+ //------------------------------------------------------------------------------
913
+
914
+ void Clipper::SetWindingDelta(TEdge *edge)
915
+ {
916
+ if ( !IsNonZeroFillType(edge) ) edge->windDelta = 1;
917
+ else if ( edge->nextAtTop ) edge->windDelta = 1;
918
+ else edge->windDelta = -1;
919
+ }
920
+ //------------------------------------------------------------------------------
921
+
922
+ void Clipper::SetWindingCount(TEdge *edge)
923
+ {
924
+ TEdge* e = edge->prevInAEL;
925
+ //find the edge of the same polytype that immediately preceeds 'edge' in AEL
926
+ while ( e && e->polyType != edge->polyType ) e = e->prevInAEL;
927
+ if ( !e )
928
+ {
929
+ edge->windCnt = edge->windDelta;
930
+ edge->windCnt2 = 0;
931
+ e = m_ActiveEdges; //ie get ready to calc windCnt2
932
+ } else if ( IsNonZeroFillType(edge) )
933
+ {
934
+ //nonZero filling ...
935
+ if ( e->windCnt * e->windDelta < 0 )
936
+ {
937
+ if (abs(e->windCnt) > 1)
938
+ {
939
+ if (e->windDelta * edge->windDelta < 0) edge->windCnt = e->windCnt;
940
+ else edge->windCnt = e->windCnt + edge->windDelta;
941
+ } else
942
+ edge->windCnt = e->windCnt + e->windDelta + edge->windDelta;
943
+ } else
944
+ {
945
+ if ( abs(e->windCnt) > 1 && e->windDelta * edge->windDelta < 0)
946
+ edge->windCnt = e->windCnt;
947
+ else if ( e->windCnt + edge->windDelta == 0 )
948
+ edge->windCnt = e->windCnt;
949
+ else edge->windCnt = e->windCnt + edge->windDelta;
950
+ }
951
+ edge->windCnt2 = e->windCnt2;
952
+ e = e->nextInAEL; //ie get ready to calc windCnt2
953
+ } else
954
+ {
955
+ //even-odd filling ...
956
+ edge->windCnt = 1;
957
+ edge->windCnt2 = e->windCnt2;
958
+ e = e->nextInAEL; //ie get ready to calc windCnt2
959
+ }
960
+
961
+ //update windCnt2 ...
962
+ if ( IsNonZeroAltFillType(edge) )
963
+ {
964
+ //nonZero filling ...
965
+ while ( e != edge )
966
+ {
967
+ edge->windCnt2 += e->windDelta;
968
+ e = e->nextInAEL;
969
+ }
970
+ } else
971
+ {
972
+ //even-odd filling ...
973
+ while ( e != edge )
974
+ {
975
+ edge->windCnt2 = (edge->windCnt2 == 0) ? 1 : 0;
976
+ e = e->nextInAEL;
977
+ }
978
+ }
979
+ }
980
+ //------------------------------------------------------------------------------
981
+
982
+ bool Clipper::IsNonZeroFillType(TEdge *edge)
983
+ {
984
+ switch (edge->polyType) {
985
+ case ptSubject: return m_SubjFillType == pftNonZero;
986
+ default: return m_ClipFillType == pftNonZero;
987
+ }
988
+ }
989
+ //------------------------------------------------------------------------------
990
+
991
+ bool Clipper::IsNonZeroAltFillType(TEdge *edge)
992
+ {
993
+ switch (edge->polyType) {
994
+ case ptSubject: return m_ClipFillType == pftNonZero;
995
+ default: return m_SubjFillType == pftNonZero;
996
+ }
997
+ }
998
+ //------------------------------------------------------------------------------
999
+
1000
+ bool Edge2InsertsBeforeEdge1(TEdge &e1, TEdge &e2)
1001
+ {
1002
+ if( e2.xbot - tolerance > e1.xbot ) return false;
1003
+ if( e2.xbot + tolerance < e1.xbot ) return true;
1004
+ if( IsHorizontal(e2) ) return false;
1005
+ return (e2.dx > e1.dx);
1006
+ }
1007
+ //------------------------------------------------------------------------------
1008
+
1009
+ void Clipper::InsertEdgeIntoAEL(TEdge *edge)
1010
+ {
1011
+ edge->prevInAEL = 0;
1012
+ edge->nextInAEL = 0;
1013
+ if( !m_ActiveEdges )
1014
+ {
1015
+ m_ActiveEdges = edge;
1016
+ }
1017
+ else if( Edge2InsertsBeforeEdge1(*m_ActiveEdges, *edge) )
1018
+ {
1019
+ edge->nextInAEL = m_ActiveEdges;
1020
+ m_ActiveEdges->prevInAEL = edge;
1021
+ m_ActiveEdges = edge;
1022
+ } else
1023
+ {
1024
+ TEdge* e = m_ActiveEdges;
1025
+ while( e->nextInAEL && !Edge2InsertsBeforeEdge1(*e->nextInAEL , *edge) )
1026
+ e = e->nextInAEL;
1027
+ edge->nextInAEL = e->nextInAEL;
1028
+ if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge;
1029
+ edge->prevInAEL = e;
1030
+ e->nextInAEL = edge;
1031
+ }
1032
+ }
1033
+ //----------------------------------------------------------------------
1034
+
1035
+ bool HorizOverlap(const double h1a,
1036
+ const double h1b, const double h2a, const double h2b)
1037
+ {
1038
+ //returns true if (h1a between h2a and h2b) or
1039
+ // (h1a == min2 and h1b > min2) or (h1a == max2 and h1b < max2)
1040
+ double min2, max2;
1041
+ if (h2a < h2b)
1042
+ {
1043
+ min2 = h2a;
1044
+ max2 = h2b;
1045
+ }
1046
+ else
1047
+ {
1048
+ min2 = h2b;
1049
+ max2 = h2a;
1050
+ }
1051
+ return (h1a > min2 + tolerance && h1a < max2 - tolerance) ||
1052
+ (fabs(h1a - min2) < tolerance && h1b > min2 + tolerance) ||
1053
+ (fabs(h1a - max2) < tolerance && h1b < max2 - tolerance);
1054
+ }
1055
+ //------------------------------------------------------------------------------
1056
+
1057
+ void Clipper::InsertLocalMinimaIntoAEL( const double &botY)
1058
+ {
1059
+ while( m_CurrentLM && ( m_CurrentLM->Y == botY ) )
1060
+ {
1061
+ InsertEdgeIntoAEL( m_CurrentLM->leftBound );
1062
+ InsertScanbeam( m_CurrentLM->leftBound->ytop );
1063
+ InsertEdgeIntoAEL( m_CurrentLM->rightBound );
1064
+
1065
+ SetWindingDelta( m_CurrentLM->leftBound );
1066
+ if ( IsNonZeroFillType(m_CurrentLM->leftBound) )
1067
+ m_CurrentLM->rightBound->windDelta =
1068
+ -m_CurrentLM->leftBound->windDelta; else
1069
+ m_CurrentLM->rightBound->windDelta = 1;
1070
+
1071
+ SetWindingCount( m_CurrentLM->leftBound );
1072
+ m_CurrentLM->rightBound->windCnt =
1073
+ m_CurrentLM->leftBound->windCnt;
1074
+ m_CurrentLM->rightBound->windCnt2 =
1075
+ m_CurrentLM->leftBound->windCnt2;
1076
+
1077
+ if( IsHorizontal( *m_CurrentLM->rightBound ) )
1078
+ {
1079
+ //nb: only rightbounds can have a horizontal bottom edge
1080
+ AddEdgeToSEL( m_CurrentLM->rightBound );
1081
+ InsertScanbeam( m_CurrentLM->rightBound->nextInLML->ytop );
1082
+ }
1083
+ else
1084
+ InsertScanbeam( m_CurrentLM->rightBound->ytop );
1085
+
1086
+ TLocalMinima* lm = m_CurrentLM;
1087
+ if( IsContributing(lm->leftBound) )
1088
+ AddLocalMinPoly( lm->leftBound,
1089
+ lm->rightBound, DoublePoint( lm->leftBound->xbot , lm->Y ) );
1090
+
1091
+ //flag polygons that share colinear edges, so they can be merged later ...
1092
+ if (lm->leftBound->outIdx >= 0 && lm->leftBound->prevInAEL &&
1093
+ lm->leftBound->prevInAEL->outIdx >= 0 &&
1094
+ fabs(lm->leftBound->prevInAEL->xbot - lm->leftBound->x) < tolerance &&
1095
+ SlopesEqual(*lm->leftBound, *lm->leftBound->prevInAEL))
1096
+ {
1097
+ TDoublePoint pt = DoublePoint(lm->leftBound->x,lm->leftBound->y);
1098
+ AddPolyPt(lm->leftBound->prevInAEL, pt);
1099
+ int i = m_Joins.size();
1100
+ m_Joins.resize(i+1);
1101
+ m_Joins[i].idx1 = lm->leftBound->outIdx;
1102
+ m_Joins[i].idx2 = lm->leftBound->prevInAEL->outIdx;
1103
+ m_Joins[i].pt = pt;
1104
+ }
1105
+ if (lm->rightBound->outIdx >= 0 && IsHorizontal(*lm->rightBound))
1106
+ {
1107
+ //check for overlap with m_CurrentHorizontals
1108
+ for (unsigned i = 0; i < m_CurrentHorizontals.size(); ++i)
1109
+ {
1110
+ int hIdx = m_CurrentHorizontals[i].idx1;
1111
+ TDoublePoint hPt = m_CurrentHorizontals[i].pt;
1112
+ TPolyPt* p = m_CurrentHorizontals[i].outPPt;
1113
+
1114
+ TPolyPt* p2;
1115
+ if (IsHorizontal(p, p->prev)) p2 = p->prev;
1116
+ else if (IsHorizontal(p, p->next)) p2 = p->next;
1117
+ else continue;
1118
+
1119
+
1120
+ if (HorizOverlap(p->pt.X, p2->pt.X,
1121
+ lm->rightBound->x, lm->rightBound->xtop))
1122
+ {
1123
+ AddPolyPt(lm->rightBound, hPt);
1124
+ int j = m_Joins.size();
1125
+ m_Joins.resize(j+1);
1126
+ m_Joins[j].idx1 = hIdx;
1127
+ m_Joins[j].idx2 = lm->rightBound->outIdx;
1128
+ m_Joins[j].pt = hPt;
1129
+ }
1130
+ else if (HorizOverlap(lm->rightBound->x, lm->rightBound->xtop,
1131
+ hPt.X, p2->pt.X))
1132
+ {
1133
+ TDoublePoint pt = DoublePoint(lm->rightBound->x, lm->rightBound->y);
1134
+ int j = m_Joins.size();
1135
+ m_Joins.resize(j+1);
1136
+ InsertPolyPtBetween(pt, p, p2);
1137
+ m_Joins[j].idx1 = hIdx;
1138
+ m_Joins[j].idx2 = lm->rightBound->outIdx;
1139
+ m_Joins[j].pt = pt;
1140
+
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ if( lm->leftBound->nextInAEL != lm->rightBound )
1146
+ {
1147
+ TEdge* e = lm->leftBound->nextInAEL;
1148
+ TDoublePoint pt = DoublePoint( lm->leftBound->xbot, lm->leftBound->ybot );
1149
+ while( e != lm->rightBound )
1150
+ {
1151
+ if(!e) throw clipperException("AddLocalMinima: missing rightbound!");
1152
+ IntersectEdges( lm->rightBound , e , pt , ipNone); //order important here
1153
+ e = e->nextInAEL;
1154
+ }
1155
+ }
1156
+ PopLocalMinima();
1157
+ }
1158
+ m_CurrentHorizontals.clear();
1159
+ }
1160
+ //------------------------------------------------------------------------------
1161
+
1162
+ void Clipper::AddEdgeToSEL(TEdge *edge)
1163
+ {
1164
+ //SEL pointers in PEdge are reused to build a list of horizontal edges.
1165
+ //However, we don't need to worry about order with horizontal edge processing.
1166
+ if( !m_SortedEdges )
1167
+ {
1168
+ m_SortedEdges = edge;
1169
+ edge->prevInSEL = 0;
1170
+ edge->nextInSEL = 0;
1171
+ }
1172
+ else
1173
+ {
1174
+ edge->nextInSEL = m_SortedEdges;
1175
+ edge->prevInSEL = 0;
1176
+ m_SortedEdges->prevInSEL = edge;
1177
+ m_SortedEdges = edge;
1178
+ }
1179
+ }
1180
+ //------------------------------------------------------------------------------
1181
+
1182
+ void Clipper::CopyAELToSEL()
1183
+ {
1184
+ TEdge* e = m_ActiveEdges;
1185
+ m_SortedEdges = e;
1186
+ if (!m_ActiveEdges) return;
1187
+ m_SortedEdges->prevInSEL = 0;
1188
+ e = e->nextInAEL;
1189
+ while ( e )
1190
+ {
1191
+ e->prevInSEL = e->prevInAEL;
1192
+ e->prevInSEL->nextInSEL = e;
1193
+ e->nextInSEL = 0;
1194
+ e = e->nextInAEL;
1195
+ }
1196
+ }
1197
+ //------------------------------------------------------------------------------
1198
+
1199
+ void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2)
1200
+ {
1201
+ if( !( edge1->nextInAEL ) && !( edge1->prevInAEL ) ) return;
1202
+ if( !( edge2->nextInAEL ) && !( edge2->prevInAEL ) ) return;
1203
+
1204
+ if( edge1->nextInAEL == edge2 )
1205
+ {
1206
+ TEdge* next = edge2->nextInAEL;
1207
+ if( next ) next->prevInAEL = edge1;
1208
+ TEdge* prev = edge1->prevInAEL;
1209
+ if( prev ) prev->nextInAEL = edge2;
1210
+ edge2->prevInAEL = prev;
1211
+ edge2->nextInAEL = edge1;
1212
+ edge1->prevInAEL = edge2;
1213
+ edge1->nextInAEL = next;
1214
+ }
1215
+ else if( edge2->nextInAEL == edge1 )
1216
+ {
1217
+ TEdge* next = edge1->nextInAEL;
1218
+ if( next ) next->prevInAEL = edge2;
1219
+ TEdge* prev = edge2->prevInAEL;
1220
+ if( prev ) prev->nextInAEL = edge1;
1221
+ edge1->prevInAEL = prev;
1222
+ edge1->nextInAEL = edge2;
1223
+ edge2->prevInAEL = edge1;
1224
+ edge2->nextInAEL = next;
1225
+ }
1226
+ else
1227
+ {
1228
+ TEdge* next = edge1->nextInAEL;
1229
+ TEdge* prev = edge1->prevInAEL;
1230
+ edge1->nextInAEL = edge2->nextInAEL;
1231
+ if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1;
1232
+ edge1->prevInAEL = edge2->prevInAEL;
1233
+ if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1;
1234
+ edge2->nextInAEL = next;
1235
+ if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2;
1236
+ edge2->prevInAEL = prev;
1237
+ if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2;
1238
+ }
1239
+
1240
+ if( !edge1->prevInAEL ) m_ActiveEdges = edge1;
1241
+ else if( !edge2->prevInAEL ) m_ActiveEdges = edge2;
1242
+ }
1243
+ //------------------------------------------------------------------------------
1244
+
1245
+ void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2)
1246
+ {
1247
+ if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return;
1248
+ if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return;
1249
+
1250
+ if( edge1->nextInSEL == edge2 )
1251
+ {
1252
+ TEdge* next = edge2->nextInSEL;
1253
+ if( next ) next->prevInSEL = edge1;
1254
+ TEdge* prev = edge1->prevInSEL;
1255
+ if( prev ) prev->nextInSEL = edge2;
1256
+ edge2->prevInSEL = prev;
1257
+ edge2->nextInSEL = edge1;
1258
+ edge1->prevInSEL = edge2;
1259
+ edge1->nextInSEL = next;
1260
+ }
1261
+ else if( edge2->nextInSEL == edge1 )
1262
+ {
1263
+ TEdge* next = edge1->nextInSEL;
1264
+ if( next ) next->prevInSEL = edge2;
1265
+ TEdge* prev = edge2->prevInSEL;
1266
+ if( prev ) prev->nextInSEL = edge1;
1267
+ edge1->prevInSEL = prev;
1268
+ edge1->nextInSEL = edge2;
1269
+ edge2->prevInSEL = edge1;
1270
+ edge2->nextInSEL = next;
1271
+ }
1272
+ else
1273
+ {
1274
+ TEdge* next = edge1->nextInSEL;
1275
+ TEdge* prev = edge1->prevInSEL;
1276
+ edge1->nextInSEL = edge2->nextInSEL;
1277
+ if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1;
1278
+ edge1->prevInSEL = edge2->prevInSEL;
1279
+ if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1;
1280
+ edge2->nextInSEL = next;
1281
+ if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2;
1282
+ edge2->prevInSEL = prev;
1283
+ if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2;
1284
+ }
1285
+
1286
+ if( !edge1->prevInSEL ) m_SortedEdges = edge1;
1287
+ else if( !edge2->prevInSEL ) m_SortedEdges = edge2;
1288
+ }
1289
+ //------------------------------------------------------------------------------
1290
+
1291
+ TEdge *GetNextInAEL(TEdge *e, TDirection Direction)
1292
+ {
1293
+ if( Direction == dLeftToRight ) return e->nextInAEL;
1294
+ else return e->prevInAEL;
1295
+ }
1296
+ //------------------------------------------------------------------------------
1297
+
1298
+ TEdge *GetPrevInAEL(TEdge *e, TDirection Direction)
1299
+ {
1300
+ if( Direction == dLeftToRight ) return e->prevInAEL;
1301
+ else return e->nextInAEL;
1302
+ }
1303
+ //------------------------------------------------------------------------------
1304
+
1305
+ bool IsMinima(TEdge *e)
1306
+ {
1307
+ return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e);
1308
+ }
1309
+ //------------------------------------------------------------------------------
1310
+
1311
+ bool IsMaxima(TEdge *e, const double &Y)
1312
+ {
1313
+ return e && fabs(e->ytop - Y) < tolerance && !e->nextInLML;
1314
+ }
1315
+ //------------------------------------------------------------------------------
1316
+
1317
+ bool IsIntermediate(TEdge *e, const double &Y)
1318
+ {
1319
+ return fabs( e->ytop - Y ) < tolerance && e->nextInLML;
1320
+ }
1321
+ //------------------------------------------------------------------------------
1322
+
1323
+ TEdge *GetMaximaPair(TEdge *e)
1324
+ {
1325
+ if( !IsMaxima(e->next, e->ytop) || (e->next->xtop != e->xtop) )
1326
+ return e->prev; else
1327
+ return e->next;
1328
+ }
1329
+ //------------------------------------------------------------------------------
1330
+
1331
+ void Clipper::DoMaxima(TEdge *e, const double &topY)
1332
+ {
1333
+ TEdge* eMaxPair = GetMaximaPair(e);
1334
+ double X = e->xtop;
1335
+ TEdge* eNext = e->nextInAEL;
1336
+ while( eNext != eMaxPair )
1337
+ {
1338
+ if (!eNext) throw clipperException("DoMaxima error");
1339
+ IntersectEdges( e , eNext , DoublePoint( X , topY ), ipBoth );
1340
+ eNext = eNext->nextInAEL;
1341
+ }
1342
+ if( ( e->outIdx < 0 ) && ( eMaxPair->outIdx < 0 ) )
1343
+ {
1344
+ DeleteFromAEL( e );
1345
+ DeleteFromAEL( eMaxPair );
1346
+ }
1347
+ else if( ( e->outIdx >= 0 ) && ( eMaxPair->outIdx >= 0 ) )
1348
+ {
1349
+ IntersectEdges( e , eMaxPair , DoublePoint(X, topY), ipNone );
1350
+ }
1351
+ else throw clipperException("DoMaxima error");
1352
+ }
1353
+ //------------------------------------------------------------------------------
1354
+
1355
+ void Clipper::ProcessHorizontals()
1356
+ {
1357
+ TEdge* horzEdge = m_SortedEdges;
1358
+ while( horzEdge )
1359
+ {
1360
+ DeleteFromSEL( horzEdge );
1361
+ ProcessHorizontal( horzEdge );
1362
+ horzEdge = m_SortedEdges;
1363
+ }
1364
+ }
1365
+ //------------------------------------------------------------------------------
1366
+
1367
+ bool Clipper::IsTopHorz(TEdge *horzEdge, const double &XPos)
1368
+ {
1369
+ TEdge* e = m_SortedEdges;
1370
+ while( e )
1371
+ {
1372
+ if( ( XPos >= min(e->xbot, e->xtop) ) &&
1373
+ ( XPos <= max(e->xbot, e->xtop) ) ) return false;
1374
+ e = e->nextInSEL;
1375
+ }
1376
+ return true;
1377
+ }
1378
+ //------------------------------------------------------------------------------
1379
+
1380
+ void Clipper::ProcessHorizontal(TEdge *horzEdge)
1381
+ {
1382
+ TDirection Direction;
1383
+ double horzLeft, horzRight;
1384
+
1385
+ if( horzEdge->xbot < horzEdge->xtop )
1386
+ {
1387
+ horzLeft = horzEdge->xbot;
1388
+ horzRight = horzEdge->xtop;
1389
+ Direction = dLeftToRight;
1390
+ } else
1391
+ {
1392
+ horzLeft = horzEdge->xtop;
1393
+ horzRight = horzEdge->xbot;
1394
+ Direction = dRightToLeft;
1395
+ }
1396
+
1397
+ TEdge* eMaxPair;
1398
+ if( horzEdge->nextInLML ) eMaxPair = 0;
1399
+ else eMaxPair = GetMaximaPair(horzEdge);
1400
+
1401
+ TEdge* e = GetNextInAEL( horzEdge , Direction );
1402
+ while( e )
1403
+ {
1404
+ TEdge* eNext = GetNextInAEL( e, Direction );
1405
+ if((e->xbot >= horzLeft - tolerance) && (e->xbot <= horzRight + tolerance))
1406
+ {
1407
+ //ok, so far it looks like we're still in range of the horizontal edge
1408
+ if ( fabs(e->xbot - horzEdge->xtop) < tolerance && horzEdge->nextInLML)
1409
+ {
1410
+ if ( SlopesEqual(*e, *horzEdge->nextInLML) )
1411
+ {
1412
+ //we've got 2 colinear edges at the end of the horz. line ...
1413
+ if (horzEdge->outIdx >= 0 && e->outIdx >= 0)
1414
+ {
1415
+ int i = m_Joins.size();
1416
+ m_Joins.resize(i+1);
1417
+ TDoublePoint pt = DoublePoint(horzEdge->xtop, horzEdge->ytop);
1418
+ AddPolyPt(horzEdge, pt);
1419
+ AddPolyPt(e, pt);
1420
+ m_Joins[i].idx1 = horzEdge->outIdx;
1421
+ m_Joins[i].idx2 = e->outIdx;
1422
+ m_Joins[i].pt = pt;
1423
+ }
1424
+ break; //we've reached the end of the horizontal line
1425
+ }
1426
+ else if (e->dx < horzEdge->nextInLML->dx)
1427
+ break; //we've reached the end of the horizontal line
1428
+ }
1429
+
1430
+ if( e == eMaxPair )
1431
+ {
1432
+ //horzEdge is evidently a maxima horizontal and we've arrived at its end.
1433
+ if (Direction == dLeftToRight)
1434
+ IntersectEdges(horzEdge, e, DoublePoint(e->xbot, horzEdge->ybot), ipNone);
1435
+ else
1436
+ IntersectEdges(e, horzEdge, DoublePoint(e->xbot, horzEdge->ybot), ipNone);
1437
+ return;
1438
+ }
1439
+ else if( IsHorizontal(*e) && !IsMinima(e) && !(e->xbot > e->xtop) )
1440
+ {
1441
+ if( Direction == dLeftToRight )
1442
+ IntersectEdges( horzEdge , e , DoublePoint(e->xbot, horzEdge->ybot),
1443
+ (IsTopHorz( horzEdge , e->xbot ))? ipLeft : ipBoth );
1444
+ else
1445
+ IntersectEdges( e , horzEdge , DoublePoint(e->xbot, horzEdge->ybot),
1446
+ (IsTopHorz( horzEdge , e->xbot ))? ipRight : ipBoth );
1447
+ }
1448
+ else if( Direction == dLeftToRight )
1449
+ {
1450
+ IntersectEdges( horzEdge , e , DoublePoint(e->xbot, horzEdge->ybot),
1451
+ (IsTopHorz( horzEdge , e->xbot ))? ipLeft : ipBoth );
1452
+ }
1453
+ else
1454
+ {
1455
+ IntersectEdges( e , horzEdge , DoublePoint(e->xbot, horzEdge->ybot),
1456
+ (IsTopHorz( horzEdge , e->xbot ))? ipRight : ipBoth );
1457
+ }
1458
+ SwapPositionsInAEL( horzEdge , e );
1459
+ }
1460
+ else if( ( Direction == dLeftToRight ) &&
1461
+ ( e->xbot > horzRight + tolerance ) && !horzEdge->nextInSEL ) break;
1462
+ else if( ( Direction == dRightToLeft ) &&
1463
+ ( e->xbot < horzLeft - tolerance ) && !horzEdge->nextInSEL ) break;
1464
+ e = eNext;
1465
+ } //end while ( e )
1466
+
1467
+ if( horzEdge->nextInLML )
1468
+ {
1469
+ if( horzEdge->outIdx >= 0 )
1470
+ AddPolyPt( horzEdge, DoublePoint(horzEdge->xtop, horzEdge->ytop));
1471
+ UpdateEdgeIntoAEL( horzEdge );
1472
+ }
1473
+ else
1474
+ {
1475
+ if ( horzEdge->outIdx >= 0 )
1476
+ IntersectEdges( horzEdge, eMaxPair,
1477
+ DoublePoint(horzEdge->xtop, horzEdge->ybot), ipBoth);
1478
+ DeleteFromAEL(eMaxPair);
1479
+ DeleteFromAEL(horzEdge);
1480
+ }
1481
+ }
1482
+ //------------------------------------------------------------------------------
1483
+
1484
+ TPolyPt* Clipper::InsertPolyPtBetween(const TDoublePoint &pt, TPolyPt* pp1, TPolyPt* pp2)
1485
+ {
1486
+ TPolyPt* pp = new TPolyPt;
1487
+ pp->pt = pt;
1488
+ pp->isHole = sUndefined;
1489
+ if (pp2 == pp1->next)
1490
+ {
1491
+ pp->next = pp2;
1492
+ pp->prev = pp1;
1493
+ pp1->next = pp;
1494
+ pp2->prev = pp;
1495
+ }
1496
+ else if (pp1 == pp2->next)
1497
+ {
1498
+ pp->next = pp1;
1499
+ pp->prev = pp2;
1500
+ pp2->next = pp;
1501
+ pp1->prev = pp;
1502
+ }
1503
+ else
1504
+ throw clipperException("InsertPolyPtBetween error");
1505
+ return pp;
1506
+ }
1507
+ //------------------------------------------------------------------------------
1508
+
1509
+ TPolyPt* Clipper::AddPolyPt(TEdge *e, const TDoublePoint &pt)
1510
+ {
1511
+ bool ToFront = (e->side == esLeft);
1512
+ if( e->outIdx < 0 )
1513
+ {
1514
+ TPolyPt* newPolyPt = new TPolyPt;
1515
+ newPolyPt->pt = pt;
1516
+ m_PolyPts.push_back(newPolyPt);
1517
+ newPolyPt->next = newPolyPt;
1518
+ newPolyPt->prev = newPolyPt;
1519
+ newPolyPt->isHole = sUndefined;
1520
+ e->outIdx = m_PolyPts.size()-1;
1521
+ return newPolyPt;
1522
+ } else
1523
+ {
1524
+ TPolyPt* pp = m_PolyPts[e->outIdx];
1525
+ if (ToFront && PointsEqual(pt, pp->pt)) return pp;
1526
+ if (!ToFront && PointsEqual(pt, pp->prev->pt)) return pp->prev;
1527
+ TPolyPt* newPolyPt = new TPolyPt;
1528
+ newPolyPt->pt = pt;
1529
+ newPolyPt->isHole = sUndefined;
1530
+ newPolyPt->next = pp;
1531
+ newPolyPt->prev = pp->prev;
1532
+ newPolyPt->prev->next = newPolyPt;
1533
+ pp->prev = newPolyPt;
1534
+ if (ToFront) m_PolyPts[e->outIdx] = newPolyPt;
1535
+ return newPolyPt;
1536
+ }
1537
+ }
1538
+ //------------------------------------------------------------------------------
1539
+
1540
+ void Clipper::ProcessIntersections( const double &topY)
1541
+ {
1542
+ if( !m_ActiveEdges ) return;
1543
+ try {
1544
+ m_IntersectTolerance = tolerance;
1545
+ BuildIntersectList( topY );
1546
+ if (!m_IntersectNodes) return;
1547
+ //Test pending intersections for errors and, if any are found, redo
1548
+ //BuildIntersectList (twice if necessary) with adjusted tolerances.
1549
+ //While this adds ~2% extra to processing time, I believe this is justified
1550
+ //by further halving of the algorithm's failure rate, though admittedly
1551
+ //failures were already extremely rare ...
1552
+ if ( !TestIntersections() )
1553
+ {
1554
+ m_IntersectTolerance = minimal_tolerance;
1555
+ DisposeIntersectNodes();
1556
+ BuildIntersectList( topY );
1557
+ if ( !TestIntersections() )
1558
+ {
1559
+ m_IntersectTolerance = slope_precision;
1560
+ DisposeIntersectNodes();
1561
+ BuildIntersectList( topY );
1562
+ if (!TestIntersections())
1563
+ //try eliminating near duplicate points in the input polygons
1564
+ //eg by adjusting precision ... to say 0.1;
1565
+ throw clipperException("Intersection error");
1566
+ }
1567
+ }
1568
+ ProcessIntersectList();
1569
+ }
1570
+ catch(...) {
1571
+ m_SortedEdges = 0;
1572
+ DisposeIntersectNodes();
1573
+ throw clipperException("ProcessIntersections error");
1574
+ }
1575
+ }
1576
+ //------------------------------------------------------------------------------
1577
+
1578
+ void Clipper::DisposeIntersectNodes()
1579
+ {
1580
+ while ( m_IntersectNodes )
1581
+ {
1582
+ TIntersectNode* iNode = m_IntersectNodes->next;
1583
+ delete m_IntersectNodes;
1584
+ m_IntersectNodes = iNode;
1585
+ }
1586
+ }
1587
+ //------------------------------------------------------------------------------
1588
+
1589
+ bool E1PrecedesE2inAEL(TEdge *e1, TEdge *e2)
1590
+ {
1591
+ while( e1 ){
1592
+ if( e1 == e2 ) return true;
1593
+ else e1 = e1->nextInAEL;
1594
+ }
1595
+ return false;
1596
+ }
1597
+ //------------------------------------------------------------------------------
1598
+
1599
+ bool Clipper::Process1Before2(TIntersectNode *Node1, TIntersectNode *Node2)
1600
+ {
1601
+ if ( fabs(Node1->pt.Y - Node2->pt.Y) < m_IntersectTolerance )
1602
+ {
1603
+ if ( fabs(Node1->pt.X - Node2->pt.X) > precision )
1604
+ return Node1->pt.X < Node2->pt.X;
1605
+ //a complex intersection (with more than 2 edges intersecting) ...
1606
+ if ( Node1->edge1 == Node2->edge1 || SlopesEqual(*Node1->edge1, *Node2->edge1) )
1607
+ {
1608
+ if (Node1->edge2 == Node2->edge2 )
1609
+ //(N1.E1 & N2.E1 are co-linear) and (N1.E2 == N2.E2) ...
1610
+ return !E1PrecedesE2inAEL(Node1->edge1, Node2->edge1);
1611
+ else if ( SlopesEqual(*Node1->edge2, *Node2->edge2) )
1612
+ //(N1.E1 == N2.E1) and (N1.E2 & N2.E2 are co-linear) ...
1613
+ return E1PrecedesE2inAEL(Node1->edge2, Node2->edge2);
1614
+ else if //check if minima **
1615
+ ( (fabs(Node1->edge2->y - Node1->pt.Y) < slope_precision ||
1616
+ fabs(Node2->edge2->y - Node2->pt.Y) < slope_precision ) &&
1617
+ (Node1->edge2->next == Node2->edge2 || Node1->edge2->prev == Node2->edge2) )
1618
+ {
1619
+ if ( Node1->edge1->dx < 0 ) return Node1->edge2->dx > Node2->edge2->dx;
1620
+ else return Node1->edge2->dx < Node2->edge2->dx;
1621
+ }
1622
+ else if ( (Node1->edge2->dx - Node2->edge2->dx) < precision )
1623
+ return E1PrecedesE2inAEL(Node1->edge2, Node2->edge2);
1624
+ else
1625
+ return (Node1->edge2->dx < Node2->edge2->dx);
1626
+
1627
+ } else if ( Node1->edge2 == Node2->edge2 && //check if maxima ***
1628
+ (fabs(Node1->edge1->ytop - Node1->pt.Y) < slope_precision ||
1629
+ fabs(Node2->edge1->ytop - Node2->pt.Y) < slope_precision) )
1630
+ return (Node1->edge1->dx > Node2->edge1->dx);
1631
+ else
1632
+ return (Node1->edge1->dx < Node2->edge1->dx);
1633
+ } else
1634
+ return (Node1->pt.Y > Node2->pt.Y);
1635
+ //**a minima that very slightly overlaps an edge can appear like
1636
+ //a complex intersection but it's not. (Minima can't have parallel edges.)
1637
+ //***a maxima that very slightly overlaps an edge can appear like
1638
+ //a complex intersection but it's not. (Maxima can't have parallel edges.)
1639
+ }
1640
+ //------------------------------------------------------------------------------
1641
+
1642
+ void Clipper::AddIntersectNode(TEdge *e1, TEdge *e2, const TDoublePoint &pt)
1643
+ {
1644
+ TIntersectNode* IntersectNode = new TIntersectNode;
1645
+ IntersectNode->edge1 = e1;
1646
+ IntersectNode->edge2 = e2;
1647
+ IntersectNode->pt = pt;
1648
+ IntersectNode->next = 0;
1649
+ IntersectNode->prev = 0;
1650
+ if( !m_IntersectNodes )
1651
+ m_IntersectNodes = IntersectNode;
1652
+ else if( Process1Before2(IntersectNode , m_IntersectNodes) )
1653
+ {
1654
+ IntersectNode->next = m_IntersectNodes;
1655
+ m_IntersectNodes->prev = IntersectNode;
1656
+ m_IntersectNodes = IntersectNode;
1657
+ }
1658
+ else
1659
+ {
1660
+ TIntersectNode* iNode = m_IntersectNodes;
1661
+ while( iNode->next && Process1Before2(iNode->next, IntersectNode) )
1662
+ iNode = iNode->next;
1663
+ if( iNode->next ) iNode->next->prev = IntersectNode;
1664
+ IntersectNode->next = iNode->next;
1665
+ IntersectNode->prev = iNode;
1666
+ iNode->next = IntersectNode;
1667
+ }
1668
+ }
1669
+ //------------------------------------------------------------------------------
1670
+
1671
+ void Clipper::BuildIntersectList( const double &topY)
1672
+ {
1673
+ //prepare for sorting ...
1674
+ TEdge* e = m_ActiveEdges;
1675
+ e->tmpX = TopX( e, topY );
1676
+ m_SortedEdges = e;
1677
+ m_SortedEdges->prevInSEL = 0;
1678
+ e = e->nextInAEL;
1679
+ while( e )
1680
+ {
1681
+ e->prevInSEL = e->prevInAEL;
1682
+ e->prevInSEL->nextInSEL = e;
1683
+ e->nextInSEL = 0;
1684
+ e->tmpX = TopX( e, topY );
1685
+ e = e->nextInAEL;
1686
+ }
1687
+
1688
+ //bubblesort ...
1689
+ bool isModified = true;
1690
+ while( isModified && m_SortedEdges )
1691
+ {
1692
+ isModified = false;
1693
+ e = m_SortedEdges;
1694
+ while( e->nextInSEL )
1695
+ {
1696
+ TEdge *eNext = e->nextInSEL;
1697
+ TDoublePoint pt;
1698
+ if((e->tmpX > eNext->tmpX + tolerance) && IntersectPoint(*e, *eNext, pt))
1699
+ {
1700
+ AddIntersectNode( e, eNext, pt );
1701
+ SwapPositionsInSEL(e, eNext);
1702
+ isModified = true;
1703
+ }
1704
+ else
1705
+ e = eNext;
1706
+ }
1707
+ if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0;
1708
+ else break;
1709
+ }
1710
+ m_SortedEdges = 0;
1711
+ }
1712
+ //------------------------------------------------------------------------------
1713
+
1714
+ bool Clipper::TestIntersections()
1715
+ {
1716
+ if ( !m_IntersectNodes ) return true;
1717
+ //do the test sort using SEL ...
1718
+ CopyAELToSEL();
1719
+ TIntersectNode* iNode = m_IntersectNodes;
1720
+ while ( iNode )
1721
+ {
1722
+ SwapPositionsInSEL(iNode->edge1, iNode->edge2);
1723
+ iNode = iNode->next;
1724
+ }
1725
+ //now check that tmpXs are in the right order ...
1726
+ TEdge* e = m_SortedEdges;
1727
+ while ( e->nextInSEL )
1728
+ {
1729
+ if ( e->nextInSEL->tmpX < e->tmpX - precision ) return false;
1730
+ e = e->nextInSEL;
1731
+ }
1732
+ m_SortedEdges = 0;
1733
+ return true;
1734
+ }
1735
+ //------------------------------------------------------------------------------
1736
+
1737
+ void Clipper::ProcessIntersectList()
1738
+ {
1739
+ while( m_IntersectNodes )
1740
+ {
1741
+ TIntersectNode* iNode = m_IntersectNodes->next;
1742
+ {
1743
+ IntersectEdges( m_IntersectNodes->edge1 ,
1744
+ m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth );
1745
+ SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 );
1746
+ }
1747
+ delete m_IntersectNodes;
1748
+ m_IntersectNodes = iNode;
1749
+ }
1750
+ }
1751
+ //------------------------------------------------------------------------------
1752
+
1753
+ void Clipper::DoEdge1(TEdge *edge1, TEdge *edge2, const TDoublePoint &pt)
1754
+ {
1755
+ AddPolyPt(edge1, pt);
1756
+ SwapSides(*edge1, *edge2);
1757
+ SwapPolyIndexes(*edge1, *edge2);
1758
+ }
1759
+ //----------------------------------------------------------------------
1760
+
1761
+ void Clipper::DoEdge2(TEdge *edge1, TEdge *edge2, const TDoublePoint &pt)
1762
+ {
1763
+ AddPolyPt(edge2, pt);
1764
+ SwapSides(*edge1, *edge2);
1765
+ SwapPolyIndexes(*edge1, *edge2);
1766
+ }
1767
+ //----------------------------------------------------------------------
1768
+
1769
+ void Clipper::DoBothEdges(TEdge *edge1, TEdge *edge2, const TDoublePoint &pt)
1770
+ {
1771
+ AddPolyPt(edge1, pt);
1772
+ AddPolyPt(edge2, pt);
1773
+ SwapSides( *edge1 , *edge2 );
1774
+ SwapPolyIndexes( *edge1 , *edge2 );
1775
+ }
1776
+ //----------------------------------------------------------------------
1777
+
1778
+ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
1779
+ const TDoublePoint &pt, TIntersectProtects protects)
1780
+ {
1781
+ bool e1stops = !(ipLeft & protects) && !e1->nextInLML &&
1782
+ ( fabs( e1->xtop - pt.X ) < tolerance ) && //nb: not precision
1783
+ ( fabs( e1->ytop - pt.Y ) < precision );
1784
+ bool e2stops = !(ipRight & protects) && !e2->nextInLML &&
1785
+ ( fabs( e2->xtop - pt.X ) < tolerance ) && //nb: not precision
1786
+ ( fabs( e2->ytop - pt.Y ) < precision );
1787
+ bool e1Contributing = ( e1->outIdx >= 0 );
1788
+ bool e2contributing = ( e2->outIdx >= 0 );
1789
+
1790
+ //update winding counts ...
1791
+ if ( e1->polyType == e2->polyType )
1792
+ {
1793
+ if ( IsNonZeroFillType(e1) )
1794
+ {
1795
+ if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt;
1796
+ else e1->windCnt += e2->windDelta;
1797
+ if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt;
1798
+ else e2->windCnt -= e1->windDelta;
1799
+ } else
1800
+ {
1801
+ int oldE1WindCnt = e1->windCnt;
1802
+ e1->windCnt = e2->windCnt;
1803
+ e2->windCnt = oldE1WindCnt;
1804
+ }
1805
+ } else
1806
+ {
1807
+ if ( IsNonZeroFillType(e2) ) e1->windCnt2 += e2->windDelta;
1808
+ else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0;
1809
+ if ( IsNonZeroFillType(e1) ) e2->windCnt2 -= e1->windDelta;
1810
+ else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0;
1811
+ }
1812
+
1813
+ if ( e1Contributing && e2contributing )
1814
+ {
1815
+ if ( e1stops || e2stops || abs(e1->windCnt) > 1 ||
1816
+ abs(e2->windCnt) > 1 ||
1817
+ (e1->polyType != e2->polyType && m_ClipType != ctXor) )
1818
+ AddLocalMaxPoly(e1, e2, pt); else
1819
+ DoBothEdges( e1, e2, pt );
1820
+ }
1821
+ else if ( e1Contributing )
1822
+ {
1823
+ switch( m_ClipType ) {
1824
+ case ctIntersection:
1825
+ if ( (e2->polyType == ptSubject || e2->windCnt2 != 0) &&
1826
+ abs(e2->windCnt) < 2 ) DoEdge1( e1, e2, pt);
1827
+ break;
1828
+ default:
1829
+ if ( abs(e2->windCnt) < 2 ) DoEdge1(e1, e2, pt);
1830
+ }
1831
+ }
1832
+ else if ( e2contributing )
1833
+ {
1834
+ switch( m_ClipType ) {
1835
+ case ctIntersection:
1836
+ if ( (e1->polyType == ptSubject || e1->windCnt2 != 0) &&
1837
+ abs(e1->windCnt) < 2 ) DoEdge2( e1, e2, pt );
1838
+ break;
1839
+ default:
1840
+ if (abs(e1->windCnt) < 2) DoEdge2( e1, e2, pt );
1841
+ }
1842
+ } else
1843
+ {
1844
+ //neither edge is currently contributing ...
1845
+ if ( abs(e1->windCnt) > 1 && abs(e2->windCnt) > 1 ) ;// do nothing
1846
+ else if ( e1->polyType != e2->polyType && !e1stops && !e2stops &&
1847
+ abs(e1->windCnt) < 2 && abs(e2->windCnt) < 2 )
1848
+ AddLocalMinPoly(e1, e2, pt);
1849
+ else if ( abs(e1->windCnt) == 1 && abs(e2->windCnt) == 1 )
1850
+ switch( m_ClipType ) {
1851
+ case ctIntersection:
1852
+ if ( abs(e1->windCnt2) > 0 && abs(e2->windCnt2) > 0 )
1853
+ AddLocalMinPoly(e1, e2, pt);
1854
+ break;
1855
+ case ctUnion:
1856
+ if ( e1->windCnt2 == 0 && e2->windCnt2 == 0 )
1857
+ AddLocalMinPoly(e1, e2, pt);
1858
+ break;
1859
+ case ctDifference:
1860
+ if ( (e1->polyType == ptClip && e2->polyType == ptClip &&
1861
+ e1->windCnt2 != 0 && e2->windCnt2 != 0) ||
1862
+ (e1->polyType == ptSubject && e2->polyType == ptSubject &&
1863
+ e1->windCnt2 == 0 && e2->windCnt2 == 0) )
1864
+ AddLocalMinPoly(e1, e2, pt);
1865
+ break;
1866
+ case ctXor:
1867
+ AddLocalMinPoly(e1, e2, pt);
1868
+ }
1869
+ else if ( abs(e1->windCnt) < 2 && abs(e2->windCnt) < 2 )
1870
+ SwapSides( *e1, *e2 );
1871
+ }
1872
+
1873
+ if( (e1stops != e2stops) &&
1874
+ ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) )
1875
+ {
1876
+ SwapSides( *e1, *e2 );
1877
+ SwapPolyIndexes( *e1, *e2 );
1878
+ }
1879
+
1880
+ //finally, delete any non-contributing maxima edges ...
1881
+ if( e1stops ) DeleteFromAEL( e1 );
1882
+ if( e2stops ) DeleteFromAEL( e2 );
1883
+ }
1884
+ //------------------------------------------------------------------------------
1885
+
1886
+ void Clipper::DeleteFromAEL(TEdge *e)
1887
+ {
1888
+ TEdge* AelPrev = e->prevInAEL;
1889
+ TEdge* AelNext = e->nextInAEL;
1890
+ if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted
1891
+ if( AelPrev ) AelPrev->nextInAEL = AelNext;
1892
+ else m_ActiveEdges = AelNext;
1893
+ if( AelNext ) AelNext->prevInAEL = AelPrev;
1894
+ e->nextInAEL = 0;
1895
+ e->prevInAEL = 0;
1896
+ }
1897
+ //------------------------------------------------------------------------------
1898
+
1899
+ void Clipper::DeleteFromSEL(TEdge *e)
1900
+ {
1901
+ TEdge* SelPrev = e->prevInSEL;
1902
+ TEdge* SelNext = e->nextInSEL;
1903
+ if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted
1904
+ if( SelPrev ) SelPrev->nextInSEL = SelNext;
1905
+ else m_SortedEdges = SelNext;
1906
+ if( SelNext ) SelNext->prevInSEL = SelPrev;
1907
+ e->nextInSEL = 0;
1908
+ e->prevInSEL = 0;
1909
+ }
1910
+ //------------------------------------------------------------------------------
1911
+
1912
+ void Clipper::UpdateEdgeIntoAEL(TEdge *&e)
1913
+ {
1914
+ if( !e->nextInLML ) throw
1915
+ clipperException("UpdateEdgeIntoAEL: invalid call");
1916
+ TEdge* AelPrev = e->prevInAEL;
1917
+ TEdge* AelNext = e->nextInAEL;
1918
+ e->nextInLML->outIdx = e->outIdx;
1919
+ if( AelPrev ) AelPrev->nextInAEL = e->nextInLML;
1920
+ else m_ActiveEdges = e->nextInLML;
1921
+ if( AelNext ) AelNext->prevInAEL = e->nextInLML;
1922
+ e->nextInLML->side = e->side;
1923
+ e->nextInLML->windDelta = e->windDelta;
1924
+ e->nextInLML->windCnt = e->windCnt;
1925
+ e->nextInLML->windCnt2 = e->windCnt2;
1926
+ e = e->nextInLML;
1927
+ e->prevInAEL = AelPrev;
1928
+ e->nextInAEL = AelNext;
1929
+ if( !IsHorizontal(*e) )
1930
+ {
1931
+ InsertScanbeam( e->ytop );
1932
+
1933
+ //if output polygons share an edge, they'll need joining later ...
1934
+ if (e->outIdx >= 0 && AelPrev && AelPrev->outIdx >= 0 &&
1935
+ fabs(AelPrev->xbot - e->x) < tolerance && SlopesEqual(*e, *AelPrev))
1936
+ {
1937
+ int i = m_Joins.size();
1938
+ m_Joins.resize(i+1);
1939
+ TDoublePoint pt = DoublePoint(e->x, e->y);
1940
+ AddPolyPt(AelPrev, pt);
1941
+ AddPolyPt(e, pt);
1942
+ m_Joins[i].idx1 = AelPrev->outIdx;
1943
+ m_Joins[i].idx2 = e->outIdx;
1944
+ m_Joins[i].pt = pt;
1945
+ }
1946
+ }
1947
+ }
1948
+ //------------------------------------------------------------------------------
1949
+
1950
+ bool Clipper::IsContributing(TEdge *edge)
1951
+ {
1952
+ switch( m_ClipType ){
1953
+ case ctIntersection:
1954
+ if ( edge->polyType == ptSubject )
1955
+ return abs(edge->windCnt) == 1 && edge->windCnt2 != 0; else
1956
+ return abs(edge->windCnt2) > 0 && abs(edge->windCnt) == 1;
1957
+ case ctUnion:
1958
+ return abs(edge->windCnt) == 1 && edge->windCnt2 == 0;
1959
+ case ctDifference:
1960
+ if ( edge->polyType == ptSubject )
1961
+ return abs(edge->windCnt) == 1 && edge->windCnt2 == 0; else
1962
+ return abs(edge->windCnt) == 1 && edge->windCnt2 != 0;
1963
+ default: //case ctXor:
1964
+ return abs(edge->windCnt) == 1;
1965
+ }
1966
+ }
1967
+ //------------------------------------------------------------------------------
1968
+
1969
+ bool Clipper::Execute(TClipType clipType, TPolyPolygon &solution,
1970
+ TPolyFillType subjFillType, TPolyFillType clipFillType)
1971
+ {
1972
+ m_SubjFillType = subjFillType;
1973
+ m_ClipFillType = clipFillType;
1974
+
1975
+ bool succeeded = false;
1976
+ solution.resize(0);
1977
+ if( m_ExecuteLocked || !InitializeScanbeam() ) return false;
1978
+ try {
1979
+ m_ExecuteLocked = true;
1980
+ m_ActiveEdges = 0;
1981
+ m_SortedEdges = 0;
1982
+ m_ClipType = clipType;
1983
+ m_Joins.clear();
1984
+ m_CurrentHorizontals.clear();
1985
+
1986
+ double ybot = PopScanbeam();
1987
+ do {
1988
+ InsertLocalMinimaIntoAEL( ybot );
1989
+ ProcessHorizontals();
1990
+ double ytop = PopScanbeam();
1991
+ ProcessIntersections( ytop );
1992
+ ProcessEdgesAtTopOfScanbeam( ytop );
1993
+ ybot = ytop;
1994
+ } while( m_Scanbeam );
1995
+
1996
+ //build the return polygons ...
1997
+ BuildResult(solution);
1998
+ succeeded = true;
1999
+ }
2000
+ catch(...) {
2001
+ solution.resize(0);
2002
+ //returns false ...
2003
+ }
2004
+ DisposeAllPolyPts();
2005
+ m_Joins.clear();
2006
+ m_ExecuteLocked = false;
2007
+ return succeeded;
2008
+ }
2009
+ //------------------------------------------------------------------------------
2010
+
2011
+ TPolyPt* FixupOutPolygon(TPolyPt *p, bool stripPointyEdgesOnly = false)
2012
+ {
2013
+ //FixupOutPolygon() - removes duplicate points and simplifies consecutive
2014
+ //parallel edges by removing the middle vertex.
2015
+ //stripPointyEdgesOnly: removes the middle vertex only when consecutive
2016
+ //parallel edges reflect back on themselves ('pointy' edges). However, it
2017
+ //doesn't remove the middle vertex when edges are parallel continuations.
2018
+ //Given 3 consecutive vertices - o, *, and o ...
2019
+ //the form of 'non-pointy' parallel edges is : o--*----------o
2020
+ //the form of 'pointy' parallel edges is : o--o----------*
2021
+ //(While merging polygons that share common edges, it's necessary to
2022
+ //temporarily retain 'non-pointy' parallel edges.)
2023
+ bool firstPass = true;
2024
+ if (!p) return 0;
2025
+ TPolyPt *pp = p, *result = p;
2026
+ bool ptDeleted;
2027
+ for (;;)
2028
+ {
2029
+ if (pp->prev == pp)
2030
+ {
2031
+ delete pp;
2032
+ return 0;
2033
+ }
2034
+ //test for duplicate points and for same slope (cross-product) ...
2035
+ if ( PointsEqual(pp->pt, pp->next->pt) ||
2036
+ (fabs((pp->pt.Y - pp->prev->pt.Y)*(pp->next->pt.X - pp->pt.X) -
2037
+ (pp->pt.X - pp->prev->pt.X)*(pp->next->pt.Y - pp->pt.Y)) < precision &&
2038
+ (!stripPointyEdgesOnly ||
2039
+ ((pp->pt.X - pp->prev->pt.X > 0) != (pp->next->pt.X - pp->pt.X > 0)) ||
2040
+ ((pp->pt.Y - pp->prev->pt.Y > 0) != (pp->next->pt.Y - pp->pt.Y > 0)))))
2041
+ {
2042
+ if (pp->isHole != sUndefined && pp->next->isHole == sUndefined)
2043
+ pp->next->isHole = pp->isHole;
2044
+ pp->prev->next = pp->next;
2045
+ pp->next->prev = pp->prev;
2046
+ TPolyPt* tmp = pp;
2047
+ if (pp == result)
2048
+ {
2049
+ firstPass = true;
2050
+ result = pp->prev;
2051
+ }
2052
+ pp = pp->prev;
2053
+ delete tmp;
2054
+ ptDeleted = true;
2055
+ } else {
2056
+ pp = pp->next;
2057
+ ptDeleted = false;
2058
+ }
2059
+ if (!firstPass) break;
2060
+ if (pp == result && !ptDeleted) firstPass = false;
2061
+ }
2062
+ return result;
2063
+ }
2064
+ //------------------------------------------------------------------------------
2065
+
2066
+ void Clipper::BuildResult(TPolyPolygon &polypoly){
2067
+ unsigned k = 0;
2068
+ MergePolysWithCommonEdges();
2069
+ polypoly.resize(m_PolyPts.size());
2070
+ for (unsigned i = 0; i < m_PolyPts.size(); ++i) {
2071
+ if (m_PolyPts[i]) {
2072
+
2073
+ m_PolyPts[i] = FixupOutPolygon(m_PolyPts[i]);
2074
+ if (!m_PolyPts[i]) continue;
2075
+
2076
+ TPolyPt* pt = m_PolyPts[i];
2077
+ unsigned cnt = 0;
2078
+ double y = pt->pt.Y;
2079
+ bool isHorizontalOnly = true;
2080
+ do {
2081
+ pt = pt->next;
2082
+ if (isHorizontalOnly && fabs(pt->pt.Y - y) > precision)
2083
+ isHorizontalOnly = false;
2084
+ ++cnt;
2085
+ } while (pt != m_PolyPts[i]);
2086
+ if ( cnt < 3 || isHorizontalOnly ) continue;
2087
+
2088
+ //validate the orientation of simple polygons ...
2089
+ if ( ForceOrientation() &&
2090
+ !ValidateOrientation(pt) ) ReversePolyPtLinks(*pt);
2091
+
2092
+ polypoly[k].resize(cnt);
2093
+ for (unsigned j = 0; j < cnt; ++j) {
2094
+ polypoly[k][j].X = pt->pt.X;
2095
+ polypoly[k][j].Y = pt->pt.Y;
2096
+ pt = pt->next;
2097
+ }
2098
+ ++k;
2099
+ }
2100
+ }
2101
+ polypoly.resize(k);
2102
+ }
2103
+ //------------------------------------------------------------------------------
2104
+
2105
+ bool Clipper::ForceOrientation(){
2106
+ return m_ForceOrientation;
2107
+ }
2108
+ //------------------------------------------------------------------------------
2109
+
2110
+ void Clipper::ForceOrientation(bool value){
2111
+ m_ForceOrientation = value;
2112
+ }
2113
+ //------------------------------------------------------------------------------
2114
+
2115
+ TEdge* Clipper::BubbleSwap(TEdge *edge)
2116
+ {
2117
+ int cnt = 1;
2118
+ TEdge* result = edge->nextInAEL;
2119
+ while( result && ( fabs(result->xbot - edge->xbot) <= tolerance ) )
2120
+ {
2121
+ ++cnt;
2122
+ result = result->nextInAEL;
2123
+ }
2124
+
2125
+ //let e = no edges in a complex intersection
2126
+ //let cnt = no intersection ops between those edges at that intersection
2127
+ //then ... e =1, cnt =0; e =2, cnt =1; e =3, cnt =3; e =4, cnt =6; ...
2128
+ //series s (where s = intersections per no edges) ... s = 0,1,3,6,10,15 ...
2129
+ //generalising: given i = e-1, and s[0] = 0, then ... cnt = i + s[i-1]
2130
+ //example: no. intersect ops required by 4 edges in a complex intersection ...
2131
+ // cnt = 3 + 2 + 1 + 0 = 6 intersection ops
2132
+ if( cnt > 2 )
2133
+ {
2134
+ //create the sort list ...
2135
+ try {
2136
+ m_SortedEdges = edge;
2137
+ edge->prevInSEL = 0;
2138
+ TEdge *e = edge->nextInAEL;
2139
+ for( int i = 2 ; i <= cnt ; ++i )
2140
+ {
2141
+ e->prevInSEL = e->prevInAEL;
2142
+ e->prevInSEL->nextInSEL = e;
2143
+ if( i == cnt ) e->nextInSEL = 0;
2144
+ e = e->nextInAEL;
2145
+ }
2146
+ while( m_SortedEdges && m_SortedEdges->nextInSEL )
2147
+ {
2148
+ e = m_SortedEdges;
2149
+ while( e->nextInSEL )
2150
+ {
2151
+ if( e->nextInSEL->dx > e->dx )
2152
+ {
2153
+ IntersectEdges( e, e->nextInSEL,
2154
+ DoublePoint(e->xbot, e->ybot), ipBoth );
2155
+ SwapPositionsInAEL( e , e->nextInSEL );
2156
+ SwapPositionsInSEL( e , e->nextInSEL );
2157
+ }
2158
+ else
2159
+ e = e->nextInSEL;
2160
+ }
2161
+ e->prevInSEL->nextInSEL = 0; //removes 'e' from SEL
2162
+ }
2163
+ }
2164
+ catch(...) {
2165
+ m_SortedEdges = 0;
2166
+ throw clipperException("BubbleSwap error");
2167
+ }
2168
+ m_SortedEdges = 0;
2169
+ }
2170
+ return result;
2171
+ }
2172
+ //------------------------------------------------------------------------------
2173
+
2174
+ void Clipper::ProcessEdgesAtTopOfScanbeam( const double &topY)
2175
+ {
2176
+ TEdge* e = m_ActiveEdges;
2177
+ while( e )
2178
+ {
2179
+ //1. process maxima, treating them as if they're 'bent' horizontal edges,
2180
+ // but exclude maxima with horizontal edges. nb: e can't be a horizontal.
2181
+ if( IsMaxima(e, topY) && !IsHorizontal(*GetMaximaPair(e)) )
2182
+ {
2183
+ //'e' might be removed from AEL, as may any following edges so ...
2184
+ TEdge* ePrior = e->prevInAEL;
2185
+ DoMaxima( e , topY );
2186
+ if( !ePrior ) e = m_ActiveEdges;
2187
+ else e = ePrior->nextInAEL;
2188
+ }
2189
+ else
2190
+ {
2191
+ //2. promote horizontal edges, otherwise update xbot and ybot ...
2192
+ if( IsIntermediate( e , topY ) && IsHorizontal( *e->nextInLML ) )
2193
+ {
2194
+ if (e->outIdx >= 0)
2195
+ {
2196
+ TPolyPt* pp = AddPolyPt(e, DoublePoint(e->xtop, e->ytop));
2197
+ //add the polyPt to a list that later checks for overlaps with
2198
+ //contributing horizontal minima since they'll need joining...
2199
+ int i = m_CurrentHorizontals.size();
2200
+ m_CurrentHorizontals.resize(i+1);
2201
+ m_CurrentHorizontals[i].idx1 = e->outIdx;
2202
+ m_CurrentHorizontals[i].pt = pp->pt;
2203
+ m_CurrentHorizontals[i].outPPt = pp;
2204
+ }
2205
+ //very rarely an edge just below a horizontal edge in a contour
2206
+ //intersects with another edge at the very top of a scanbeam.
2207
+ //If this happens that intersection must be managed first ...
2208
+ if ( e->prevInAEL && e->prevInAEL->xbot > e->xtop + tolerance )
2209
+ {
2210
+ IntersectEdges(e->prevInAEL, e, DoublePoint(e->prevInAEL->xbot,
2211
+ e->prevInAEL->ybot), ipBoth);
2212
+ SwapPositionsInAEL(e->prevInAEL, e);
2213
+ UpdateEdgeIntoAEL(e);
2214
+ AddEdgeToSEL(e);
2215
+ e = e->nextInAEL;
2216
+ UpdateEdgeIntoAEL(e);
2217
+ AddEdgeToSEL(e);
2218
+ }
2219
+ else if (e->nextInAEL && e->xtop > TopX(e->nextInAEL, topY) + tolerance)
2220
+ {
2221
+ e->nextInAEL->xbot = TopX(e->nextInAEL, topY);
2222
+ e->nextInAEL->ybot = topY;
2223
+ IntersectEdges(e, e->nextInAEL, DoublePoint(e->nextInAEL->xbot,
2224
+ e->nextInAEL->ybot), ipBoth);
2225
+ SwapPositionsInAEL(e, e->nextInAEL);
2226
+ UpdateEdgeIntoAEL(e);
2227
+ AddEdgeToSEL(e);
2228
+ } else
2229
+ {
2230
+ UpdateEdgeIntoAEL(e);
2231
+ AddEdgeToSEL(e);
2232
+ }
2233
+ } else
2234
+ {
2235
+ //this just simplifies horizontal processing ...
2236
+ e->xbot = TopX( e , topY );
2237
+ e->ybot = topY;
2238
+ }
2239
+ e = e->nextInAEL;
2240
+ }
2241
+ }
2242
+
2243
+ //3. Process horizontals at the top of the scanbeam ...
2244
+ ProcessHorizontals();
2245
+
2246
+ //4. Promote intermediate vertices ...
2247
+ e = m_ActiveEdges;
2248
+ while( e )
2249
+ {
2250
+ if( IsIntermediate( e, topY ) )
2251
+ {
2252
+ if( e->outIdx >= 0 ) AddPolyPt(e, DoublePoint(e->xtop,e->ytop));
2253
+ UpdateEdgeIntoAEL(e);
2254
+ }
2255
+ e = e->nextInAEL;
2256
+ }
2257
+
2258
+ //5. Process (non-horizontal) intersections at the top of the scanbeam ...
2259
+ e = m_ActiveEdges;
2260
+ if (e && !e->nextInAEL)
2261
+ throw clipperException("ProcessEdgesAtTopOfScanbeam() error");
2262
+ while( e )
2263
+ {
2264
+ if( !e->nextInAEL ) break;
2265
+ if( e->nextInAEL->xbot < e->xbot - precision )
2266
+ throw clipperException("ProcessEdgesAtTopOfScanbeam() error");
2267
+ if( e->nextInAEL->xbot > e->xbot + tolerance )
2268
+ e = e->nextInAEL;
2269
+ else
2270
+ e = BubbleSwap( e );
2271
+ }
2272
+ }
2273
+ //------------------------------------------------------------------------------
2274
+
2275
+ void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const TDoublePoint &pt)
2276
+ {
2277
+ AddPolyPt( e1, pt );
2278
+ if( EdgesShareSamePoly(*e1, *e2) )
2279
+ {
2280
+ e1->outIdx = -1;
2281
+ e2->outIdx = -1;
2282
+ }
2283
+ else AppendPolygon( e1, e2 );
2284
+ }
2285
+ //------------------------------------------------------------------------------
2286
+
2287
+ void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const TDoublePoint &pt)
2288
+ {
2289
+ AddPolyPt( e1, pt );
2290
+
2291
+ if( IsHorizontal( *e2 ) || ( e1->dx > e2->dx ) )
2292
+ {
2293
+ e1->side = esLeft;
2294
+ e2->side = esRight;
2295
+ } else
2296
+ {
2297
+ e1->side = esRight;
2298
+ e2->side = esLeft;
2299
+ }
2300
+
2301
+ if (m_ForceOrientation) {
2302
+ TPolyPt* pp = m_PolyPts[e1->outIdx];
2303
+ bool isAHole = false;
2304
+ TEdge* e = m_ActiveEdges;
2305
+ while (e) {
2306
+ if (e->outIdx >= 0 && TopX(e,pp->pt.Y) < pp->pt.X - precision)
2307
+ isAHole = !isAHole;
2308
+ e = e->nextInAEL;
2309
+ }
2310
+ if (isAHole) pp->isHole = sTrue; else pp->isHole = sFalse;
2311
+ }
2312
+ e2->outIdx = e1->outIdx;
2313
+ }
2314
+ //------------------------------------------------------------------------------
2315
+
2316
+ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
2317
+ {
2318
+ if( (e1->outIdx < 0) || (e2->outIdx < 0) )
2319
+ throw clipperException("AppendPolygon error");
2320
+
2321
+ //get the start and ends of both output polygons ...
2322
+ TPolyPt* p1_lft = m_PolyPts[e1->outIdx];
2323
+ TPolyPt* p1_rt = p1_lft->prev;
2324
+ TPolyPt* p2_lft = m_PolyPts[e2->outIdx];
2325
+ TPolyPt* p2_rt = p2_lft->prev;
2326
+ TEdgeSide side;
2327
+
2328
+ //join e2 poly onto e1 poly and delete pointers to e2 ...
2329
+ if( e1->side == esLeft )
2330
+ {
2331
+ if( e2->side == esLeft )
2332
+ {
2333
+ //z y x a b c
2334
+ ReversePolyPtLinks(*p2_lft);
2335
+ p2_lft->next = p1_lft;
2336
+ p1_lft->prev = p2_lft;
2337
+ p1_rt->next = p2_rt;
2338
+ p2_rt->prev = p1_rt;
2339
+ m_PolyPts[e1->outIdx] = p2_rt;
2340
+ } else
2341
+ {
2342
+ //x y z a b c
2343
+ p2_rt->next = p1_lft;
2344
+ p1_lft->prev = p2_rt;
2345
+ p2_lft->prev = p1_rt;
2346
+ p1_rt->next = p2_lft;
2347
+ m_PolyPts[e1->outIdx] = p2_lft;
2348
+ }
2349
+ side = esLeft;
2350
+ } else
2351
+ {
2352
+ if( e2->side == esRight )
2353
+ {
2354
+ //a b c z y x
2355
+ ReversePolyPtLinks( *p2_lft );
2356
+ p1_rt->next = p2_rt;
2357
+ p2_rt->prev = p1_rt;
2358
+ p2_lft->next = p1_lft;
2359
+ p1_lft->prev = p2_lft;
2360
+ } else
2361
+ {
2362
+ //a b c x y z
2363
+ p1_rt->next = p2_lft;
2364
+ p2_lft->prev = p1_rt;
2365
+ p1_lft->prev = p2_rt;
2366
+ p2_rt->next = p1_lft;
2367
+ }
2368
+ side = esRight;
2369
+ }
2370
+
2371
+ int ObsoleteIdx = e2->outIdx;
2372
+ e2->outIdx = -1;
2373
+ TEdge* e = m_ActiveEdges;
2374
+ while( e )
2375
+ {
2376
+ if( e->
2377
+ outIdx == ObsoleteIdx )
2378
+ {
2379
+ e->outIdx = e1->outIdx;
2380
+ e->side = side;
2381
+ break;
2382
+ }
2383
+ e = e->nextInAEL;
2384
+ }
2385
+ e1->outIdx = -1;
2386
+ m_PolyPts[ObsoleteIdx] = 0;
2387
+ }
2388
+ //------------------------------------------------------------------------------
2389
+
2390
+ bool SlopesEqual(const TDoublePoint& pt1a, const TDoublePoint& pt1b,
2391
+ const TDoublePoint& pt2a, const TDoublePoint& pt2b)
2392
+ {
2393
+ return fabs((pt1b.Y - pt1a.Y) * (pt2b.X - pt2a.X) -
2394
+ (pt1b.X - pt1a.X) * (pt2b.Y - pt2a.Y)) < slope_precision;
2395
+ }
2396
+ //------------------------------------------------------------------------------
2397
+
2398
+ TPolyPt* InsertPolyPt(TPolyPt* afterPolyPt, const TDoublePoint& pt)
2399
+ {
2400
+ TPolyPt* polyPt = new TPolyPt;
2401
+ polyPt->pt = pt;
2402
+ polyPt->prev = afterPolyPt;
2403
+ polyPt->next = afterPolyPt->next;
2404
+ afterPolyPt->next->prev = polyPt;
2405
+ afterPolyPt->next = polyPt;
2406
+ polyPt->isHole = sUndefined;
2407
+ return polyPt;
2408
+ }
2409
+ //------------------------------------------------------------------------------
2410
+
2411
+ void Clipper::FixupJoins(int joinIdx)
2412
+ {
2413
+ int oldIdx = m_Joins[joinIdx].idx2;
2414
+ int newIdx = m_Joins[joinIdx].idx1;
2415
+ for (unsigned i = joinIdx+1; i < m_Joins.size(); ++i)
2416
+ if (m_Joins[i].idx1 == oldIdx) m_Joins[i].idx1 = newIdx;
2417
+ else if (m_Joins[i].idx2 == oldIdx) m_Joins[i].idx2 = newIdx;
2418
+ }
2419
+ //------------------------------------------------------------------------------
2420
+
2421
+ void Clipper::MergePolysWithCommonEdges()
2422
+ {
2423
+ for (unsigned i = 0; i < m_Joins.size(); ++i)
2424
+ {
2425
+ //It's problematic merging overlapping edges in the same output polygon.
2426
+ //While creating 2 polygons from one is straightforward, one of the
2427
+ //polygons may become a hole and determining hole state here is difficult.
2428
+ if (m_Joins[i].idx1 == m_Joins[i].idx2) continue;
2429
+
2430
+ TPolyPt* p1 = m_PolyPts[m_Joins[i].idx1];
2431
+ p1 = FixupOutPolygon(p1, true);
2432
+ m_PolyPts[m_Joins[i].idx1] = p1;
2433
+
2434
+ TPolyPt* p2 = m_PolyPts[m_Joins[i].idx2];
2435
+ p2 = FixupOutPolygon(p2, true);
2436
+ m_PolyPts[m_Joins[i].idx2] = p2;
2437
+
2438
+ if (!PtInPoly(m_Joins[i].pt, p1) || !PtInPoly(m_Joins[i].pt, p2)) continue;
2439
+
2440
+ if (p1->next->pt.Y < p1->pt.Y && p2->next->pt.Y < p2->pt.Y &&
2441
+ SlopesEqual(p1->pt, p1->next->pt, p2->pt, p2->next->pt))
2442
+ {
2443
+ TPolyPt* pp1 = InsertPolyPt(p1, p1->pt);
2444
+ TPolyPt* pp2 = InsertPolyPt(p2, p2->pt);
2445
+ ReversePolyPtLinks( *p2 );
2446
+ pp1->prev = pp2;
2447
+ pp2->next = pp1;
2448
+ p1->next = p2;
2449
+ p2->prev = p1;
2450
+ }
2451
+ else if (p1->next->pt.Y <= p1->pt.Y && p2->prev->pt.Y <= p2->pt.Y &&
2452
+ SlopesEqual(p1->pt, p1->next->pt, p2->pt, p2->prev->pt))
2453
+ {
2454
+ TPolyPt* pp1 = InsertPolyPt(p1, p1->pt);
2455
+ TPolyPt* pp2 = InsertPolyPt(p2->prev, p2->pt);
2456
+ p1->next = p2;
2457
+ p2->prev = p1;
2458
+ pp2->next = pp1;
2459
+ pp1->prev = pp2;
2460
+ }
2461
+ else if (p1->prev->pt.Y <= p1->pt.Y && p2->next->pt.Y <= p2->pt.Y &&
2462
+ SlopesEqual(p1->pt, p1->prev->pt, p2->pt, p2->next->pt))
2463
+ {
2464
+ TPolyPt* pp1 = InsertPolyPt(p1->prev, p1->pt);
2465
+ TPolyPt* pp2 = InsertPolyPt(p2, p2->pt);
2466
+ pp1->next = pp2;
2467
+ pp2->prev = pp1;
2468
+ p1->prev = p2;
2469
+ p2->next = p1;
2470
+ }
2471
+ else if (p1->prev->pt.Y < p1->pt.Y && p2->prev->pt.Y < p2->pt.Y &&
2472
+ SlopesEqual(p1->pt, p1->prev->pt, p2->pt, p2->prev->pt))
2473
+ {
2474
+ TPolyPt* pp1 = InsertPolyPt(p1->prev, p1->pt);
2475
+ TPolyPt* pp2 = InsertPolyPt(p2->prev, p2->pt);
2476
+ ReversePolyPtLinks(*p2);
2477
+ p1->prev = p2;
2478
+ p2->next = p1;
2479
+ pp1->next = pp2;
2480
+ pp2->prev = pp1;
2481
+ }
2482
+ else
2483
+ continue;
2484
+
2485
+ //When polygons are joined, one polygon is effectively deleted. The joins
2486
+ //referencing the 'deleted' polygon must now reference the merged polygon.
2487
+ m_PolyPts[m_Joins[i].idx2] = 0;
2488
+ FixupJoins(i);
2489
+ }
2490
+ }
2491
+ //------------------------------------------------------------------------------
2492
+
2493
+ } //namespace clipper
2494
+
2495
+