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 +7 -0
- data/Gemfile +4 -0
- data/LICENSE.bindings +4 -0
- data/LICENSE.clipper +29 -0
- data/README.md +122 -0
- data/Rakefile +2 -0
- data/ext/clipper/clipper.cpp +2495 -0
- data/ext/clipper/clipper.hpp +247 -0
- data/ext/clipper/extconf.rb +6 -0
- data/ext/clipper/rbclipper.cpp +268 -0
- data/lib/clipper/version.rb +7 -0
- metadata +94 -0
data/Changelog
ADDED
data/Gemfile
ADDED
data/LICENSE.bindings
ADDED
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,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 ¤tY)
|
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
|
+
|