clipper 2.9.0

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.
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
+