-
Notifications
You must be signed in to change notification settings - Fork 19
/
AreaBoolean.cpp
218 lines (181 loc) · 6.44 KB
/
AreaBoolean.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// AreaBoolean.cpp
// implements CArea methods using Klaas Holwerda's Boolean
// Licence: see kboollicense.txt
#include "Area.h"
#include "kbool/include/_lnk_itr.h"
#include "kbool/include/booleng.h"
bool CArea::HolesLinked(){ return true; }
static void ArmBoolEng( Bool_Engine* booleng )
{
// set some global vals to arm the boolean engine
double DGRID = 1000; // round coordinate X or Y value in calculations to this
double MARGE = 0.001; // snap with in this range points to lines in the intersection routines
// should always be > DGRID a MARGE >= 10*DGRID is oke
// this is also used to remove small segments and to decide when
// two segments are in line.
double CORRECTIONFACTOR = 500.0; // correct the polygons by this number
double CORRECTIONABER = CArea::m_accuracy; // the accuracy for the rounded shapes used in correction
double ROUNDFACTOR = 1.0; // when will we round the correction shape to a circle
double SMOOTHABER = 10.0; // accuracy when smoothing a polygon
double MAXLINEMERGE = 1000.0; // leave as is, segments of this length in smoothen
// DGRID is only meant to make fractional parts of input data which
// are doubles, part of the integers used in vertexes within the boolean algorithm.
// Within the algorithm all input data is multiplied with DGRID
// space for extra intersection inside the boolean algorithms
// only change this if there are problems
int GRID =10000;
booleng->SetMarge( MARGE );
booleng->SetGrid( GRID );
booleng->SetDGrid( DGRID );
booleng->SetCorrectionFactor( CORRECTIONFACTOR );
booleng->SetCorrectionAber( CORRECTIONABER );
booleng->SetSmoothAber( SMOOTHABER );
booleng->SetMaxlinemerge( MAXLINEMERGE );
booleng->SetRoundfactor( ROUNDFACTOR );
}
static void AddVertex(Bool_Engine* booleng, const CVertex& vertex, const CVertex* prev_vertex)
{
if(vertex.m_type == 0 || prev_vertex == NULL)
{
booleng->AddPoint(vertex.m_p.x * CArea::m_units, vertex.m_p.y * CArea::m_units, vertex.m_user_data);
}
else
{
double phi,dphi,dx,dy;
int Segments;
int i;
double ang1,ang2,phit;
dx = (prev_vertex->m_p.x - vertex.m_c.x) * CArea::m_units;
dy = (prev_vertex->m_p.y - vertex.m_c.y) * CArea::m_units;
ang1=atan2(dy,dx);
if (ang1<0) ang1+=2.0*M_PI;
dx = (vertex.m_p.x - vertex.m_c.x) * CArea::m_units;
dy = (vertex.m_p.y - vertex.m_c.y) * CArea::m_units;
ang2=atan2(dy,dx);
if (ang2<0) ang2+=2.0*M_PI;
if (vertex.m_type == -1)
{ //clockwise
if (ang2 > ang1)
phit=2.0*M_PI-ang2+ ang1;
else
phit=ang1-ang2;
}
else
{ //counter_clockwise
if (ang1 > ang2)
phit=-(2.0*M_PI-ang1+ ang2);
else
phit=-(ang2-ang1);
}
//what is the delta phi to get an accurancy of aber
double radius = sqrt(dx*dx + dy*dy);
dphi=2*acos((radius-booleng->GetCorrectionAber())/radius);
//set the number of segments
if (phit > 0)
Segments=(int)ceil(phit/dphi);
else
Segments=(int)ceil(-phit/dphi);
if (Segments <= 1)
Segments=1;
if (Segments > 100)
Segments=100;
dphi=phit/(Segments);
double px = prev_vertex->m_p.x * CArea::m_units;
double py = prev_vertex->m_p.y * CArea::m_units;
for (i=1; i<=Segments; i++)
{
dx = px - vertex.m_c.x * CArea::m_units;
dy = py - vertex.m_c.y * CArea::m_units;
phi=atan2(dy,dx);
double nx = vertex.m_c.x * CArea::m_units + radius * cos(phi-dphi);
double ny = vertex.m_c.y * CArea::m_units + radius * sin(phi-dphi);
booleng->AddPoint(nx, ny, vertex.m_user_data);
px = nx;
py = ny;
}
}
}
static void MakeGroup( const CArea &area, Bool_Engine* booleng, bool a_not_b )
{
booleng->SetLinkHoles(true);
booleng->StartPolygonAdd(a_not_b ? GROUP_A:GROUP_B);
bool first_curve = true;
const CVertex* last_vertex = NULL;
for(std::list<CCurve>::const_iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++)
{
const CCurve& curve = *It;
const CVertex* prev_vertex = NULL;
for(std::list<CVertex>::const_iterator It2 = curve.m_vertices.begin(); It2 != curve.m_vertices.end(); It2++)
{
const CVertex& vertex = *It2;
AddVertex(booleng, vertex, prev_vertex);
prev_vertex = &vertex;
if(first_curve)last_vertex = &vertex;
}
if(!first_curve)
{
booleng->AddPoint(last_vertex->m_p.x * CArea::m_units, last_vertex->m_p.y * CArea::m_units, 0);
}
first_curve = false;
}
booleng->EndPolygonAdd();
}
static void SetFromResult( CArea &area, Bool_Engine* booleng )
{
// delete existing geometry
area.m_curves.clear();
while ( booleng->StartPolygonGet() )
{
area.m_curves.push_back(CCurve());
CCurve &curve = area.m_curves.back();
// foreach point in the polygon
while ( booleng->PolygonHasMorePoints() )
{
CVertex vertex(0, Point(booleng->GetPolygonXPoint() / CArea::m_units, booleng->GetPolygonYPoint() / CArea::m_units), Point(0.0, 0.0), booleng->GetPolygonPointUserData());
curve.m_vertices.push_back(vertex);
}
curve.m_vertices.push_back(curve.m_vertices.front()); // make a copy of the first point at the end
curve.FitArcs();
booleng->EndPolygonGet();
}
}
void CArea::Subtract(const CArea& a2)
{
Bool_Engine* booleng = new Bool_Engine();
ArmBoolEng( booleng );
MakeGroup( *this, booleng, true );
MakeGroup( a2, booleng, false );
booleng->Do_Operation(BOOL_A_SUB_B);
SetFromResult( *this, booleng );
}
void CArea::Intersect(const CArea& a2)
{
Bool_Engine* booleng = new Bool_Engine();
ArmBoolEng( booleng );
MakeGroup( *this, booleng, true );
MakeGroup( a2, booleng, false );
booleng->Do_Operation(BOOL_AND);
SetFromResult( *this, booleng );
}
void CArea::Union(const CArea& a2)
{
Bool_Engine* booleng = new Bool_Engine();
ArmBoolEng( booleng );
MakeGroup( *this, booleng, true );
MakeGroup( a2, booleng, false );
booleng->Do_Operation(BOOL_OR);
SetFromResult( *this, booleng );
}
void CArea::Offset(double inwards_value)
{
Bool_Engine* booleng = new Bool_Engine();
ArmBoolEng( booleng );
MakeGroup( *this, booleng, true);
booleng->SetCorrectionFactor( -inwards_value * m_units );
booleng->Do_Operation(BOOL_CORRECTION);
SetFromResult( *this, booleng );
}
void CArea::Thicken(double value)
{
// only in clipper version, so far
}