1
#include "ClipperUtils.hpp"
2
#include "Geometry.hpp"
6
//-----------------------------------------------------------
7
// legacy code from Clipper documentation
8
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons)
10
size_t cnt = expolygons.size();
11
expolygons.resize(cnt + 1);
12
ClipperPath_to_Slic3rMultiPoint(polynode.Contour, expolygons[cnt].contour);
13
expolygons[cnt].holes.resize(polynode.ChildCount());
14
for (int i = 0; i < polynode.ChildCount(); ++i)
16
ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, expolygons[cnt].holes[i]);
17
//Add outer polygons contained by (nested within) holes ...
18
for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j)
19
AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons);
23
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons)
26
for (int i = 0; i < polytree.ChildCount(); ++i)
27
AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons);
29
//-----------------------------------------------------------
33
ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output)
35
output.points.clear();
36
for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) {
37
output.points.push_back(Slic3r::Point( (*pit).X, (*pit).Y ));
43
ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output)
46
for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) {
47
typename T::value_type p;
48
ClipperPath_to_Slic3rMultiPoint(*it, p);
54
ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output)
57
ClipperLib::Clipper clipper;
61
clipper.AddPaths(input, ClipperLib::ptSubject, true);
62
ClipperLib::PolyTree polytree;
63
clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero
65
// write to ExPolygons object
67
PolyTreeToExPolygons(polytree, output);
71
Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output)
74
for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) {
75
output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
81
Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output)
84
for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) {
86
Slic3rMultiPoint_to_ClipperPath(*it, p);
92
scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale)
94
for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) {
95
for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
103
offset(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta,
104
double scale, ClipperLib::JoinType joinType, double miterLimit)
107
ClipperLib::Paths input;
108
Slic3rMultiPoints_to_ClipperPaths(polygons, input);
111
scaleClipperPolygons(input, scale);
114
ClipperLib::ClipperOffset co;
115
if (joinType == jtRound) {
116
co.ArcTolerance = miterLimit;
118
co.MiterLimit = miterLimit;
120
co.AddPaths(input, joinType, ClipperLib::etClosedPolygon);
121
co.Execute(retval, (delta*scale));
124
scaleClipperPolygons(retval, 1/scale);
128
offset(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta,
129
double scale, ClipperLib::JoinType joinType, double miterLimit)
132
ClipperLib::Paths output;
133
offset(polygons, output, delta, scale, joinType, miterLimit);
135
// convert into ExPolygons
136
ClipperPaths_to_Slic3rMultiPoints(output, retval);
140
offset(const Slic3r::Polylines &polylines, ClipperLib::Paths &retval, const float delta,
141
double scale, ClipperLib::JoinType joinType, double miterLimit)
144
ClipperLib::Paths input;
145
Slic3rMultiPoints_to_ClipperPaths(polylines, input);
148
scaleClipperPolygons(input, scale);
151
ClipperLib::ClipperOffset co;
152
if (joinType == jtRound) {
153
co.ArcTolerance = miterLimit;
155
co.MiterLimit = miterLimit;
157
co.AddPaths(input, joinType, ClipperLib::etOpenButt);
158
co.Execute(retval, (delta*scale));
161
scaleClipperPolygons(retval, 1/scale);
165
offset(const Slic3r::Polylines &polylines, Slic3r::Polygons &retval, const float delta,
166
double scale, ClipperLib::JoinType joinType, double miterLimit)
169
ClipperLib::Paths output;
170
offset(polylines, output, delta, scale, joinType, miterLimit);
172
// convert into ExPolygons
173
ClipperPaths_to_Slic3rMultiPoints(output, retval);
177
offset(const Slic3r::Surface &surface, Slic3r::Surfaces &retval, const float delta,
178
double scale, ClipperLib::JoinType joinType, double miterLimit)
181
Slic3r::ExPolygons expp;
182
offset_ex(surface.expolygon, expp, delta, scale, joinType, miterLimit);
184
// clone the input surface for each expolygon we got
186
retval.reserve(expp.size());
187
for (ExPolygons::iterator it = expp.begin(); it != expp.end(); ++it) {
188
Surface s = surface; // clone
195
offset_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta,
196
double scale, ClipperLib::JoinType joinType, double miterLimit)
199
ClipperLib::Paths output;
200
offset(polygons, output, delta, scale, joinType, miterLimit);
202
// convert into ExPolygons
203
ClipperPaths_to_Slic3rExPolygons(output, retval);
207
offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta1,
208
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
211
ClipperLib::Paths input;
212
Slic3rMultiPoints_to_ClipperPaths(polygons, input);
215
scaleClipperPolygons(input, scale);
217
// prepare ClipperOffset object
218
ClipperLib::ClipperOffset co;
219
if (joinType == jtRound) {
220
co.ArcTolerance = miterLimit;
222
co.MiterLimit = miterLimit;
225
// perform first offset
226
ClipperLib::Paths output1;
227
co.AddPaths(input, joinType, ClipperLib::etClosedPolygon);
228
co.Execute(output1, (delta1*scale));
230
// perform second offset
232
co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon);
233
co.Execute(retval, (delta2*scale));
236
scaleClipperPolygons(retval, 1/scale);
240
offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1,
241
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
244
ClipperLib::Paths output;
245
offset2(polygons, output, delta1, delta2, scale, joinType, miterLimit);
247
// convert into ExPolygons
248
ClipperPaths_to_Slic3rMultiPoints(output, retval);
252
offset2_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta1,
253
const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit)
256
ClipperLib::Paths output;
257
offset2(polygons, output, delta1, delta2, scale, joinType, miterLimit);
259
// convert into ExPolygons
260
ClipperPaths_to_Slic3rExPolygons(output, retval);
264
void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
265
const Slic3r::Polygons &clip, T &retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_)
268
ClipperLib::Paths input_subject, input_clip;
269
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
270
Slic3rMultiPoints_to_ClipperPaths(clip, input_clip);
272
// perform safety offset
273
if (safety_offset_) {
274
if (clipType == ClipperLib::ctUnion) {
275
safety_offset(&input_subject);
277
safety_offset(&input_clip);
282
ClipperLib::Clipper clipper;
286
clipper.AddPaths(input_subject, ClipperLib::ptSubject, true);
287
clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
290
clipper.Execute(clipType, retval, fillType, fillType);
293
void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
294
const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType,
295
const bool safety_offset_)
298
ClipperLib::Paths input_subject, input_clip;
299
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
300
Slic3rMultiPoints_to_ClipperPaths(clip, input_clip);
302
// perform safety offset
303
if (safety_offset_) safety_offset(&input_clip);
306
ClipperLib::Clipper clipper;
310
clipper.AddPaths(input_subject, ClipperLib::ptSubject, false);
311
clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
314
clipper.Execute(clipType, retval, fillType, fillType);
317
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
318
const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_)
321
ClipperLib::Paths output;
322
_clipper_do<ClipperLib::Paths>(clipType, subject, clip, output, ClipperLib::pftNonZero, safety_offset_);
324
// convert into Polygons
325
ClipperPaths_to_Slic3rMultiPoints(output, retval);
328
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
329
const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_)
332
ClipperLib::PolyTree polytree;
333
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
335
// convert into ExPolygons
336
PolyTreeToExPolygons(polytree, retval);
339
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
340
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
343
ClipperLib::PolyTree polytree;
344
_clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
346
// convert into Polylines
347
ClipperLib::Paths output;
348
ClipperLib::PolyTreeToPaths(polytree, output);
349
ClipperPaths_to_Slic3rMultiPoints(output, retval);
352
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
353
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
355
// transform input polygons into polylines
356
Slic3r::Polylines polylines;
357
polylines.reserve(subject.size());
358
for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
359
polylines.push_back(*polygon); // implicit call to split_at_first_point()
362
_clipper(clipType, polylines, clip, retval, safety_offset_);
364
/* If the split_at_first_point() call above happens to split the polygon inside the clipping area
365
we would get two consecutive polylines instead of a single one, so we go through them in order
366
to recombine continuous polylines. */
367
for (size_t i = 0; i < retval.size(); ++i) {
368
for (size_t j = i+1; j < retval.size(); ++j) {
369
if (retval[i].points.back().coincides_with(retval[j].points.front())) {
370
/* If last point of i coincides with first point of j,
371
append points of j to i and delete j */
372
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
373
retval.erase(retval.begin() + j);
375
} else if (retval[i].points.front().coincides_with(retval[j].points.back())) {
376
/* If first point of i coincides with last point of j,
377
prepend points of j to i and delete j */
378
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
379
retval.erase(retval.begin() + j);
381
} else if (retval[i].points.front().coincides_with(retval[j].points.front())) {
382
/* Since Clipper does not preserve orientation of polylines,
383
also check the case when first point of i coincides with first point of j. */
385
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
386
retval.erase(retval.begin() + j);
388
} else if (retval[i].points.back().coincides_with(retval[j].points.back())) {
389
/* Since Clipper does not preserve orientation of polylines,
390
also check the case when last point of i coincides with last point of j. */
392
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
393
retval.erase(retval.begin() + j);
400
template <class SubjectType, class ResultType>
401
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
403
_clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_);
405
template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
406
template void diff<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
407
template void diff<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
408
template void diff<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
410
template <class SubjectType, class ResultType>
411
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
413
_clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_);
415
template void intersection<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
416
template void intersection<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
417
template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
418
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
420
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
423
_clipper(ClipperLib::ctXor, subject, clip, retval, safety_offset_);
427
void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_)
430
_clipper(ClipperLib::ctUnion, subject, p, retval, safety_offset_);
432
template void union_<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool safety_offset_);
433
template void union_<Slic3r::Polygons>(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_);
435
void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_)
437
Slic3r::Polygons clip;
438
_clipper_do<ClipperLib::PolyTree>(ClipperLib::ctUnion, subject, clip, retval, ClipperLib::pftEvenOdd, safety_offset_);
441
void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_)
443
ClipperLib::PolyTree pt;
444
union_pt(subject, pt, safety_offset_);
445
traverse_pt(pt.Childs, retval);
448
static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval)
450
/* use a nearest neighbor search to order these children
451
TODO: supply start_near to chained_path() too? */
453
// collect ordering points
454
Points ordering_points;
455
ordering_points.reserve(nodes.size());
456
for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
457
Point p((*it)->Contour.front().X, (*it)->Contour.front().Y);
458
ordering_points.push_back(p);
461
// perform the ordering
462
ClipperLib::PolyNodes ordered_nodes;
463
Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes);
465
// push results recursively
466
for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) {
467
// traverse the next depth
468
traverse_pt((*it)->Childs, retval);
471
ClipperPath_to_Slic3rMultiPoint((*it)->Contour, p);
473
if ((*it)->IsHole()) retval.back().reverse(); // ccw
477
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear)
479
// convert into Clipper polygons
480
ClipperLib::Paths input_subject, output;
481
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
483
if (preserve_collinear) {
484
ClipperLib::Clipper c;
485
c.PreserveCollinear(true);
486
c.StrictlySimple(true);
487
c.AddPaths(input_subject, ClipperLib::ptSubject, true);
488
c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
490
ClipperLib::SimplifyPolygons(input_subject, output, ClipperLib::pftNonZero);
493
// convert into Slic3r polygons
494
ClipperPaths_to_Slic3rMultiPoints(output, retval);
497
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear)
499
if (!preserve_collinear) {
501
simplify_polygons(subject, polygons, preserve_collinear);
502
union_(polygons, retval);
506
// convert into Clipper polygons
507
ClipperLib::Paths input_subject;
508
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
510
ClipperLib::PolyTree polytree;
512
ClipperLib::Clipper c;
513
c.PreserveCollinear(true);
514
c.StrictlySimple(true);
515
c.AddPaths(input_subject, ClipperLib::ptSubject, true);
516
c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
518
// convert into ExPolygons
519
PolyTreeToExPolygons(polytree, retval);
522
void safety_offset(ClipperLib::Paths* paths)
525
scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE);
527
// perform offset (delta = scale 1e-05)
528
ClipperLib::ClipperOffset co;
530
co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
531
co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE);
534
scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE);
537
///////////////////////
541
polynode_children_2_perl(const ClipperLib::PolyNode& node)
544
const unsigned int len = node.ChildCount();
545
av_extend(av, len-1);
546
for (int i = 0; i < len; ++i) {
547
av_store(av, i, polynode2perl(*node.Childs[i]));
549
return (SV*)newRV_noinc((SV*)av);
553
polynode2perl(const ClipperLib::PolyNode& node)
557
ClipperPath_to_Slic3rMultiPoint(node.Contour, p);
559
(void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) );
561
(void)hv_stores( hv, "outer", Slic3r::perl_to_SV_clone_ref(p) );
563
(void)hv_stores( hv, "children", polynode_children_2_perl(node) );
564
return (SV*)newRV_noinc((SV*)hv);