clipper 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|