diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index 06c91bf3a8..0285d9167f 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -759,48 +759,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) return result; } -bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) -{ - CLIPPERLIB_PROFILE_FUNC(); - std::vector num_edges(ppg.size(), 0); - int num_edges_total = 0; - for (size_t i = 0; i < ppg.size(); ++ i) { - const Path &pg = ppg[i]; - // Remove duplicate end point from a closed input path. - // Remove duplicate points from the end of the input path. - int highI = (int)pg.size() -1; - if (Closed) - while (highI > 0 && (pg[highI] == pg[0])) - --highI; - while (highI > 0 && (pg[highI] == pg[highI -1])) - --highI; - if ((Closed && highI < 2) || (!Closed && highI < 1)) - highI = -1; - num_edges[i] = highI + 1; - num_edges_total += highI + 1; - } - if (num_edges_total == 0) - return false; - - // Allocate a new edge array. - std::vector edges(num_edges_total); - // Fill in the edge array. - bool result = false; - TEdge *p_edge = edges.data(); - for (Paths::size_type i = 0; i < ppg.size(); ++i) - if (num_edges[i]) { - bool res = AddPathInternal(ppg[i], num_edges[i] - 1, PolyTyp, Closed, p_edge); - if (res) { - p_edge += num_edges[i]; - result = true; - } - } - if (result) - // At least some edges were generated. Remember the edge array. - m_edges.emplace_back(std::move(edges)); - return result; -} - bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges) { CLIPPERLIB_PROFILE_FUNC(); @@ -1103,7 +1061,7 @@ bool Clipper::Execute(ClipType clipType, Paths &solution, CLIPPERLIB_PROFILE_FUNC(); if (m_HasOpenPaths) throw clipperException("Error: PolyTree struct is needed for open path clipping."); - solution.resize(0); + solution.clear(); m_SubjFillType = subjFillType; m_ClipFillType = clipFillType; m_ClipType = clipType; @@ -3426,13 +3384,6 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType } //------------------------------------------------------------------------------ -void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) -{ - for (const Path &path : paths) - AddPath(path, joinType, endType); -} -//------------------------------------------------------------------------------ - void ClipperOffset::FixOrientations() { //fixup orientations of all closed paths if the orientation of the @@ -3875,28 +3826,16 @@ void ReversePaths(Paths& p) } //------------------------------------------------------------------------------ -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType) { Clipper c; c.StrictlySimple(true); c.AddPath(in_poly, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); + Paths out; + c.Execute(ctUnion, out, fillType, fillType); + return out; } -//------------------------------------------------------------------------------ -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) -{ - Clipper c; - c.StrictlySimple(true); - c.AddPaths(in_polys, ptSubject, true); - c.Execute(ctUnion, out_polys, fillType, fillType); -} -//------------------------------------------------------------------------------ - -void SimplifyPolygons(Paths &polys, PolyFillType fillType) -{ - SimplifyPolygons(polys, polys, fillType); -} //------------------------------------------------------------------------------ inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) diff --git a/src/clipper/clipper.hpp b/src/clipper/clipper.hpp index c32bcf87b2..36b9beee5b 100644 --- a/src/clipper/clipper.hpp +++ b/src/clipper/clipper.hpp @@ -191,9 +191,16 @@ double Area(const Path &poly); inline bool Orientation(const Path &poly) { return Area(poly) >= 0; } int PointInPolygon(const IntPoint &pt, const Path &path); -void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); -void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); +Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftEvenOdd); +template +inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftEvenOdd) { + Clipper c; + c.StrictlySimple(true); + c.AddPaths(std::forward(in_polys), ptSubject, true); + Paths out; + c.Execute(ctUnion, out, fillType, fillType); + return out; +} void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); void CleanPolygon(Path& poly, double distance = 1.415); @@ -300,7 +307,58 @@ public: m_HasOpenPaths(false) {} ~ClipperBase() { Clear(); } bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); - bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + + template + bool AddPaths(PathsProvider &&paths_provider, PolyType PolyTyp, bool Closed) + { + size_t num_paths = paths_provider.size(); + if (num_paths == 0) + return false; + if (num_paths == 1) + return AddPath(*paths_provider.begin(), PolyTyp, Closed); + + std::vector num_edges(num_paths, 0); + int num_edges_total = 0; + size_t i = 0; + for (const Path &pg : paths_provider) { + // Remove duplicate end point from a closed input path. + // Remove duplicate points from the end of the input path. + int highI = (int)pg.size() -1; + if (Closed) + while (highI > 0 && (pg[highI] == pg[0])) + --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) + --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) + highI = -1; + num_edges[i ++] = highI + 1; + num_edges_total += highI + 1; + } + if (num_edges_total == 0) + return false; + + // Allocate a new edge array. + std::vector edges(num_edges_total); + // Fill in the edge array. + bool result = false; + TEdge *p_edge = edges.data(); + i = 0; + for (const Path &pg : paths_provider) { + if (num_edges[i]) { + bool res = AddPathInternal(pg, num_edges[i] - 1, PolyTyp, Closed, p_edge); + if (res) { + p_edge += num_edges[i]; + result = true; + } + } + ++ i; + } + if (result) + // At least some edges were generated. Remember the edge array. + m_edges.emplace_back(std::move(edges)); + return result; + } + void Clear(); IntRect GetBounds(); // By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before clipping. @@ -461,7 +519,11 @@ public: MiterLimit(miterLimit), ArcTolerance(roundPrecision), ShortestEdgeLength(shortestEdgeLength), m_lowest(-1, 0) {} ~ClipperOffset() { Clear(); } void AddPath(const Path& path, JoinType joinType, EndType endType); - void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + template + void AddPaths(PathsProvider &&paths, JoinType joinType, EndType endType) { + for (const Path &path : paths) + AddPath(path, joinType, endType); + } void Execute(Paths& solution, double delta); void Execute(PolyTree& solution, double delta); void Clear(); diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 19f5ae82e4..16b81e4882 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -139,7 +139,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint Polygons no_brim_area_object; for (const ExPolygon &ex_poly : object->layers().front()->lslices) { if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim) - append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset))); + append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset))); if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) append(no_brim_area_object, offset(ex_poly.holes, -no_brim_offset)); @@ -183,14 +183,14 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs if (top_outer_brim) no_brim_area_object.emplace_back(ex_poly); else - append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset))); + append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset))); } if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset))); if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) - append(no_brim_area_object, offset_ex(ex_poly.contour, no_brim_offset)); + append(no_brim_area_object, to_expolygons(offset(ex_poly.contour, no_brim_offset))); if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset)); @@ -317,7 +317,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_ islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare); } - loops = union_pt_chained_outside_in(loops, false); + loops = union_pt_chained_outside_in(loops); std::reverse(loops.begin(), loops.end()); extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); @@ -342,7 +342,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance poly.douglas_peucker(SCALED_RESOLUTION); polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); } - loops = union_pt_chained_outside_in(loops, false); + loops = union_pt_chained_outside_in(loops); std::vector loops_pl_by_levels; { diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 477dbf6f18..1cd4a7c2ff 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -24,7 +24,6 @@ namespace Slic3r { #ifdef CLIPPER_UTILS_DEBUG -bool clipper_export_enabled = false; // For debugging the Clipper library, for providing bug reports to the Clipper author. bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths &input_subject, const ClipperLib::Paths &input_clip) { @@ -57,207 +56,137 @@ err: } #endif /* CLIPPER_UTILS_DEBUG */ -#ifdef CLIPPERUTILS_OFFSET_SCALE -void scaleClipperPolygon(ClipperLib::Path &polygon) -{ - CLIPPERUTILS_PROFILE_FUNC(); - for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { - pit->X <<= CLIPPER_OFFSET_POWER_OF_2; - pit->Y <<= CLIPPER_OFFSET_POWER_OF_2; - } +namespace ClipperUtils { + Points SinglePathProvider::s_end; } -void scaleClipperPolygons(ClipperLib::Paths &polygons) +static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) { - CLIPPERUTILS_PROFILE_FUNC(); - for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) - for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { - pit->X <<= CLIPPER_OFFSET_POWER_OF_2; - pit->Y <<= CLIPPER_OFFSET_POWER_OF_2; + struct Inner { + static void PolyTreeToExPolygonsRecursive(ClipperLib::PolyNode &polynode, ExPolygons *expolygons) + { + size_t cnt = expolygons->size(); + expolygons->resize(cnt + 1); + (*expolygons)[cnt].contour.points = std::move(polynode.Contour); + (*expolygons)[cnt].holes.resize(polynode.ChildCount()); + for (int i = 0; i < polynode.ChildCount(); ++ i) { + (*expolygons)[cnt].holes[i].points = std::move(polynode.Childs[i]->Contour); + // Add outer polygons contained by (nested within) holes. + for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++ j) + PolyTreeToExPolygonsRecursive(*polynode.Childs[i]->Childs[j], expolygons); + } } -} -void unscaleClipperPolygon(ClipperLib::Path &polygon) -{ - CLIPPERUTILS_PROFILE_FUNC(); - for (ClipperLib::Path::iterator pit = polygon.begin(); pit != polygon.end(); ++pit) { - pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; - pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; - pit->X >>= CLIPPER_OFFSET_POWER_OF_2; - pit->Y >>= CLIPPER_OFFSET_POWER_OF_2; - } -} - -void unscaleClipperPolygons(ClipperLib::Paths &polygons) -{ - CLIPPERUTILS_PROFILE_FUNC(); - for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) - for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { - pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; - pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; - pit->X >>= CLIPPER_OFFSET_POWER_OF_2; - pit->Y >>= CLIPPER_OFFSET_POWER_OF_2; + static size_t PolyTreeCountExPolygons(const ClipperLib::PolyNode &polynode) + { + size_t cnt = 1; + for (int i = 0; i < polynode.ChildCount(); ++ i) { + for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++ j) + cnt += PolyTreeCountExPolygons(*polynode.Childs[i]->Childs[j]); + } + return cnt; } -} -#endif // CLIPPERUTILS_OFFSET_SCALE + }; -//----------------------------------------------------------- -// legacy code from Clipper documentation -void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* expolygons) -{ - size_t cnt = expolygons->size(); - expolygons->resize(cnt + 1); - (*expolygons)[cnt].contour = ClipperPath_to_Slic3rPolygon(polynode.Contour); - (*expolygons)[cnt].holes.resize(polynode.ChildCount()); - for (int i = 0; i < polynode.ChildCount(); ++i) - { - (*expolygons)[cnt].holes[i] = ClipperPath_to_Slic3rPolygon(polynode.Childs[i]->Contour); - //Add outer polygons contained by (nested within) holes ... - for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) - AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); - } -} - -ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree) -{ ExPolygons retval; - for (int i = 0; i < polytree.ChildCount(); ++i) - AddOuterPolyNodeToExPolygons(*polytree.Childs[i], &retval); - return retval; -} -//----------------------------------------------------------- - -Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input) -{ - Polygon retval; - for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) - retval.points.emplace_back(pit->x(), pit->y()); + size_t cnt = 0; + for (int i = 0; i < polytree.ChildCount(); ++ i) + cnt += Inner::PolyTreeCountExPolygons(*polytree.Childs[i]); + retval.reserve(cnt); + for (int i = 0; i < polytree.ChildCount(); ++ i) + Inner::PolyTreeToExPolygonsRecursive(std::move(*polytree.Childs[i]), &retval); return retval; } -Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input) +Polylines PolyTreeToPolylines(ClipperLib::PolyTree &&polytree) { - Polyline retval; - for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) - retval.points.emplace_back(pit->x(), pit->y()); - return retval; -} + struct Inner { + static void AddPolyNodeToPaths(ClipperLib::PolyNode &polynode, Polylines &out) + { + if (! polynode.Contour.empty()) + out.emplace_back(std::move(polynode.Contour)); + for (ClipperLib::PolyNode *child : polynode.Childs) + AddPolyNodeToPaths(*child, out); + } + }; -Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input) -{ - Slic3r::Polygons retval; - retval.reserve(input.size()); - for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) - retval.emplace_back(ClipperPath_to_Slic3rPolygon(*it)); - return retval; -} - -Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input) -{ - Slic3r::Polylines retval; - retval.reserve(input.size()); - for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) - retval.emplace_back(ClipperPath_to_Slic3rPolyline(*it)); - return retval; + Polylines out; + out.reserve(polytree.Total()); + Inner::AddPolyNodeToPaths(polytree, out); + return out; } ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input) { - // init Clipper ClipperLib::Clipper clipper; - clipper.Clear(); - - // perform union clipper.AddPaths(input, ClipperLib::ptSubject, true); ClipperLib::PolyTree polytree; clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero - - // write to ExPolygons object - return PolyTreeToExPolygons(polytree); + return PolyTreeToExPolygons(std::move(polytree)); } -ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) +// Offset outside by 10um, one by one. +template +static ClipperLib::Paths safety_offset(PathsProvider &&paths) { - ClipperLib::Path retval; - for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) - retval.emplace_back((*pit)(0), (*pit)(1)); - return retval; -} - -ClipperLib::Path Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input) -{ - ClipperLib::Path output; - output.reserve(input.points.size()); - for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit) - output.emplace_back((*pit)(0), (*pit)(1)); - return output; -} - -ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input) -{ - ClipperLib::Paths retval; - for (Polygons::const_iterator it = input.begin(); it != input.end(); ++it) - retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it)); - return retval; -} - -ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const ExPolygons &input) -{ - ClipperLib::Paths retval; - for (auto &ep : input) { - retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(ep.contour)); - - for (auto &h : ep.holes) - retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(h)); + ClipperLib::ClipperOffset co; + ClipperLib::Paths out; + out.reserve(paths.size()); + ClipperLib::Paths out_this; + for (const ClipperLib::Path &path : paths) { + co.Clear(); + co.MiterLimit = 2.; + co.AddPath(path, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); + co.Execute(out_this, ClipperSafetyOffset); + append(out, std::move(out_this)); } - - return retval; + return out; } -ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input) +static ClipperLib::Paths safety_offset(const ClipperLib::Paths &paths) { - ClipperLib::Paths retval; - for (Polylines::const_iterator it = input.begin(); it != input.end(); ++it) - retval.emplace_back(Slic3rMultiPoint_to_ClipperPath(*it)); - return retval; + return safety_offset(paths); } -ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) +static void safety_offset(ClipperLib::Paths *paths) +{ + *paths = safety_offset(*paths); +} + +template +ClipperLib::Paths _offset(PathsProvider &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) { -#ifdef CLIPPERUTILS_OFFSET_SCALE - // scale input - scaleClipperPolygons(input); -#endif // CLIPPERUTILS_OFFSET_SCALE - // perform offset ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit; else co.MiterLimit = miterLimit; -#ifdef CLIPPERUTILS_OFFSET_SCALE - float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE float delta_scaled = delta; -#endif // CLIPPERUTILS_OFFSET_SCALE co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); - co.AddPaths(input, joinType, endType); + co.AddPaths(std::forward(input), joinType, endType); ClipperLib::Paths retval; co.Execute(retval, delta_scaled); - -#ifdef CLIPPERUTILS_OFFSET_SCALE - // unscale output - unscaleClipperPolygons(retval); -#endif // CLIPPERUTILS_OFFSET_SCALE return retval; } -ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit) -{ - ClipperLib::Paths paths; - paths.emplace_back(std::move(input)); - return _offset(std::move(paths), endType, delta, joinType, miterLimit); -} +Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType, double miterLimit) + { return to_polygons(_offset(ClipperUtils::SinglePathProvider(polygon.points), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } + +#ifdef CLIPPERUTILS_UNSAFE_OFFSET +Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) + { return to_polygons(_offset(ClipperUtils::PolygonsProvider(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +#endif // CLIPPERUTILS_UNSAFE_OFFSET + +Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit) + { return to_polygons(_offset(ClipperUtils::SinglePathProvider(polyline.points), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } +Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType, double miterLimit) + { return to_polygons(_offset(ClipperUtils::PolylinesProvider(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } + +#ifdef CLIPPERUTILS_UNSAFE_OFFSET +Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) + { return ClipperPaths_to_Slic3rExPolygons(_offset(ClipperUtils::PolygonsProvider(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +#endif // CLIPPERUTILS_UNSAFE_OFFSET // This is a safe variant of the polygon offset, tailored for a single ExPolygon: // a single polygon with multiple non-overlapping holes. @@ -267,28 +196,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, { // printf("new ExPolygon offset\n"); // 1) Offset the outer contour. -#ifdef CLIPPERUTILS_OFFSET_SCALE - float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE float delta_scaled = delta; -#endif // CLIPPERUTILS_OFFSET_SCALE ClipperLib::Paths contours; { - ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour); -#ifdef CLIPPERUTILS_OFFSET_SCALE - scaleClipperPolygon(input); -#endif // CLIPPERUTILS_OFFSET_SCALE ClipperLib::ClipperOffset co; if (joinType == jtRound) -#ifdef CLIPPERUTILS_OFFSET_SCALE - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE co.ArcTolerance = miterLimit; -#endif // CLIPPERUTILS_OFFSET_SCALE else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); - co.AddPath(input, joinType, ClipperLib::etClosedPolygon); + co.AddPath(expolygon.contour.points, joinType, ClipperLib::etClosedPolygon); co.Execute(contours, delta_scaled); } @@ -297,22 +214,17 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, { holes.reserve(expolygon.holes.size()); for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) { - ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); -#ifdef CLIPPERUTILS_OFFSET_SCALE - scaleClipperPolygon(input); -#endif // CLIPPERUTILS_OFFSET_SCALE ClipperLib::ClipperOffset co; if (joinType == jtRound) -#ifdef CLIPPERUTILS_OFFSET_SCALE - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE co.ArcTolerance = miterLimit; -#endif // CLIPPERUTILS_OFFSET_SCALE else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); - co.AddPath(input, joinType, ClipperLib::etClosedPolygon); + co.AddPath(it_hole->points, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; + // Execute reorients the contours so that the outer most contour has a positive area. Thus the output + // contours will be CCW oriented even though the input paths are CW oriented. + // Offset is applied after contour reorientation, thus the signum of the offset value is reversed. co.Execute(out, - delta_scaled); append(holes, std::move(out)); } @@ -330,10 +242,6 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } - // 4) Unscale the output. -#ifdef CLIPPERUTILS_OFFSET_SCALE - unscaleClipperPolygons(output); -#endif // CLIPPERUTILS_OFFSET_SCALE return output; } @@ -343,76 +251,59 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) { -#ifdef CLIPPERUTILS_OFFSET_SCALE - float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE float delta_scaled = delta; -#endif // CLIPPERUTILS_OFFSET_SCALE // Offsetted ExPolygons before they are united. ClipperLib::Paths contours_cummulative; contours_cummulative.reserve(expolygons.size()); // How many non-empty offsetted expolygons were actually collected into contours_cummulative? // If only one, then there is no need to do a final union. size_t expolygons_collected = 0; - for (Slic3r::ExPolygons::const_iterator it_expoly = expolygons.begin(); it_expoly != expolygons.end(); ++ it_expoly) { + for (const Slic3r::ExPolygon &expoly : expolygons) { // 1) Offset the outer contour. ClipperLib::Paths contours; { - ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour); -#ifdef CLIPPERUTILS_OFFSET_SCALE - scaleClipperPolygon(input); -#endif // CLIPPERUTILS_OFFSET_SCALE ClipperLib::ClipperOffset co; if (joinType == jtRound) -#ifdef CLIPPERUTILS_OFFSET_SCALE - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE co.ArcTolerance = miterLimit; -#endif // CLIPPERUTILS_OFFSET_SCALE else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); - co.AddPath(input, joinType, ClipperLib::etClosedPolygon); + co.AddPath(expoly.contour.points, joinType, ClipperLib::etClosedPolygon); co.Execute(contours, delta_scaled); } if (contours.empty()) // No need to try to offset the holes. continue; - if (it_expoly->holes.empty()) { + if (expoly.holes.empty()) { // No need to subtract holes from the offsetted expolygon, we are done. - contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); + append(contours_cummulative, std::move(contours)); ++ expolygons_collected; } else { // 2) Offset the holes one by one, collect the offsetted holes. ClipperLib::Paths holes; { - for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { - ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); -#ifdef CLIPPERUTILS_OFFSET_SCALE - scaleClipperPolygon(input); -#endif // CLIPPERUTILS_OFFSET_SCALE + for (const Polygon &hole : expoly.holes) { ClipperLib::ClipperOffset co; if (joinType == jtRound) -#ifdef CLIPPERUTILS_OFFSET_SCALE - co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE co.ArcTolerance = miterLimit; -#endif // CLIPPERUTILS_OFFSET_SCALE else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); - co.AddPath(input, joinType, ClipperLib::etClosedPolygon); + co.AddPath(hole.points, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; + // Execute reorients the contours so that the outer most contour has a positive area. Thus the output + // contours will be CCW oriented even though the input paths are CW oriented. + // Offset is applied after contour reorientation, thus the signum of the offset value is reversed. co.Execute(out, - delta_scaled); - holes.insert(holes.end(), out.begin(), out.end()); + append(holes, std::move(out)); } } // 3) Subtract holes from the contours. if (holes.empty()) { // No hole remaining after an offset. Just copy the outer contour. - contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); + append(contours_cummulative, std::move(contours)); ++ expolygons_collected; } else if (delta < 0) { // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. @@ -424,7 +315,7 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt ClipperLib::Paths output; clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); if (! output.empty()) { - contours_cummulative.insert(contours_cummulative.end(), output.begin(), output.end()); + append(contours_cummulative, std::move(output)); ++ expolygons_collected; } else { // The offsetted holes have eaten up the offsetted outer contour. @@ -434,11 +325,11 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt // area than the original hole or even disappear, therefore there will be no new intersections. // Just collect the reversed holes. contours_cummulative.reserve(contours.size() + holes.size()); - contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); + append(contours_cummulative, std::move(contours)); // Reverse the holes in place. for (size_t i = 0; i < holes.size(); ++ i) std::reverse(holes[i].begin(), holes[i].end()); - contours_cummulative.insert(contours_cummulative.end(), holes.begin(), holes.end()); + append(contours_cummulative, std::move(holes)); ++ expolygons_collected; } } @@ -457,25 +348,11 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt output = std::move(contours_cummulative); } -#ifdef CLIPPERUTILS_OFFSET_SCALE - // 4) Unscale the output. - unscaleClipperPolygons(output); -#endif // CLIPPERUTILS_OFFSET_SCALE return output; } -ClipperLib::Paths -_offset2(const Polygons &polygons, const float delta1, const float delta2, - const ClipperLib::JoinType joinType, const double miterLimit) +ClipperLib::Paths _offset2(const Polygons &polygons, const float delta1, const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { - // read input - ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons); - -#ifdef CLIPPERUTILS_OFFSET_SCALE - // scale input - scaleClipperPolygons(input); -#endif // CLIPPERUTILS_OFFSET_SCALE - // prepare ClipperOffset object ClipperLib::ClipperOffset co; if (joinType == jtRound) { @@ -483,18 +360,13 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2, } else { co.MiterLimit = miterLimit; } -#ifdef CLIPPERUTILS_OFFSET_SCALE - float delta_scaled1 = delta1 * float(CLIPPER_OFFSET_SCALE); - float delta_scaled2 = delta2 * float(CLIPPER_OFFSET_SCALE); -#else // CLIPPERUTILS_OFFSET_SCALE float delta_scaled1 = delta1; float delta_scaled2 = delta2; -#endif // CLIPPERUTILS_OFFSET_SCALE co.ShortestEdgeLength = double(std::max(std::abs(delta_scaled1), std::abs(delta_scaled2)) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR); // perform first offset ClipperLib::Paths output1; - co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); + co.AddPaths(ClipperUtils::PolygonsProvider(polygons), joinType, ClipperLib::etClosedPolygon); co.Execute(output1, delta_scaled1); // perform second offset @@ -503,33 +375,17 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2, ClipperLib::Paths retval; co.Execute(retval, delta_scaled2); -#ifdef CLIPPERUTILS_OFFSET_SCALE - // unscale output - unscaleClipperPolygons(retval); -#endif // CLIPPERUTILS_OFFSET_SCALE return retval; } -Polygons -offset2(const Polygons &polygons, const float delta1, const float delta2, - const ClipperLib::JoinType joinType, const double miterLimit) +Polygons offset2(const Polygons &polygons, const float delta1, const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { - // perform offset - ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit); - - // convert into ExPolygons - return ClipperPaths_to_Slic3rPolygons(output); + return to_polygons(_offset2(polygons, delta1, delta2, joinType, miterLimit)); } -ExPolygons -offset2_ex(const Polygons &polygons, const float delta1, const float delta2, - const ClipperLib::JoinType joinType, const double miterLimit) +ExPolygons offset2_ex(const Polygons &polygons, const float delta1, const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { - // perform offset - ClipperLib::Paths output = _offset2(polygons, delta1, delta2, joinType, miterLimit); - - // convert into ExPolygons - return ClipperPaths_to_Slic3rExPolygons(output); + return ClipperPaths_to_Slic3rExPolygons(_offset2(polygons, delta1, delta2, joinType, miterLimit)); } //FIXME Vojtech: This functon may likely be optimized to avoid some of the Slic3r to Clipper @@ -545,64 +401,57 @@ ExPolygons offset2_ex(const ExPolygons &expolygons, const float delta1, return union_ex(polys); } -template -T _clipper_do(const ClipperLib::ClipType clipType, - TSubj && subject, - TClip && clip, - const ClipperLib::PolyFillType fillType, - const bool safety_offset_) +template +TResult _clipper_do( + const ClipperLib::ClipType clipType, + TSubj && subject, + TClip && clip, + const ClipperLib::PolyFillType fillType) { - // read input - ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(std::forward(subject)); - ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(std::forward(clip)); - - // perform safety offset - if (safety_offset_) { - if (clipType == ClipperLib::ctUnion) { - safety_offset(&input_subject); - } else { - safety_offset(&input_clip); - } - } - - // init Clipper ClipperLib::Clipper clipper; - clipper.Clear(); - - // add polygons - clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); - - // perform operation - T retval; + clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, true); + clipper.AddPaths(std::forward(clip), ClipperLib::ptClip, true); + TResult retval; clipper.Execute(clipType, retval, fillType, fillType); return retval; } +template +TResult _clipper_do( + const ClipperLib::ClipType clipType, + TSubj && subject, + TClip && clip, + const ClipperLib::PolyFillType fillType, + const bool do_safety_offset) +{ + return do_safety_offset ? + (clipType == ClipperLib::ctUnion ? + _clipper_do(clipType, safety_offset(std::forward(subject)), std::forward(clip), fillType) : + _clipper_do(clipType, std::forward(subject), safety_offset(std::forward(clip)), fillType)) : + _clipper_do(clipType, std::forward(subject), std::forward(clip), fillType); +} + // Fix of #117: A large fractal pyramid takes ages to slice // The Clipper library has difficulties processing overlapping polygons. // Namely, the function ClipperLib::JoinCommonEdges() has potentially a terrible time complexity if the output // of the operation is of the PolyTree type. -// This function implmenets a following workaround: +// This function implemenets a following workaround: // 1) Peform the Clipper operation with the output to Paths. This method handles overlaps in a reasonable time. // 2) Run Clipper Union once again to extract the PolyTree from the result of 1). -inline ClipperLib::PolyTree _clipper_do_polytree2(const ClipperLib::ClipType clipType, const Polygons &subject, - const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) +template +inline ClipperLib::PolyTree _clipper_do_polytree2( + const ClipperLib::ClipType clipType, + PathProvider1 &&subject, + PathProvider2 &&clip, + const ClipperLib::PolyFillType fillType) { - // read input - ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); - ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); - - // perform safety offset - if (safety_offset_) - safety_offset((clipType == ClipperLib::ctUnion) ? &input_subject : &input_clip); - ClipperLib::Clipper clipper; - clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); + clipper.AddPaths(std::forward(subject), ClipperLib::ptSubject, true); + clipper.AddPaths(std::forward(clip), ClipperLib::ptClip, true); // Perform the operation with the output to input_subject. // This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library // if there are overapping edges. + ClipperLib::Paths input_subject; clipper.Execute(clipType, input_subject, fillType, fillType); // Perform an additional Union operation to generate the PolyTree ordering. clipper.Clear(); @@ -611,51 +460,75 @@ inline ClipperLib::PolyTree _clipper_do_polytree2(const ClipperLib::ClipType cli clipper.Execute(ClipperLib::ctUnion, retval, fillType, fillType); return retval; } - -ClipperLib::PolyTree _clipper_do_pl(const ClipperLib::ClipType clipType, const Polylines &subject, - const Polygons &clip, const ClipperLib::PolyFillType fillType, - const bool safety_offset_) +template +inline ClipperLib::PolyTree _clipper_do_polytree2( + const ClipperLib::ClipType clipType, + PathProvider1 &&subject, + PathProvider2 &&clip, + const ClipperLib::PolyFillType fillType, + const bool do_safety_offset) +{ + return do_safety_offset ? + (clipType == ClipperLib::ctUnion ? + _clipper_do_polytree2(clipType, safety_offset(std::forward(subject)), std::forward(clip), fillType) : + _clipper_do_polytree2(clipType, std::forward(subject), safety_offset(std::forward(clip)), fillType)) : + _clipper_do_polytree2(clipType, std::forward(subject), std::forward(clip), fillType); +} + +template +static inline Polygons _clipper(ClipperLib::ClipType clipType, TSubj &&subject, TClip &&clip, bool do_safety_offset) +{ + return to_polygons(_clipper_do(clipType, std::forward(subject), std::forward(clip), ClipperLib::pftNonZero, do_safety_offset)); +} + +Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset) + { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset) + { return _clipper(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset) + { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset) + { return _clipper(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool do_safety_offset) + { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), do_safety_offset); } +Slic3r::Polygons union_(const Slic3r::ExPolygons &subject, bool do_safety_offset) + { return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), do_safety_offset); } +Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool do_safety_offset) + { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), do_safety_offset); } + +template +static ExPolygons _clipper_ex(ClipperLib::ClipType clipType, TSubject &&subject, TClip &&clip, bool do_safety_offset) + { return PolyTreeToExPolygons(_clipper_do_polytree2(clipType, std::forward(subject), std::forward(clip), ClipperLib::pftNonZero, do_safety_offset)); } + +Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset) + { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset) + { return _clipper_ex(ClipperLib::ctDifference, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset) + { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset) + { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool do_safety_offset) + { return _clipper_ex(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), do_safety_offset); } +Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons& subject) + { return PolyTreeToExPolygons(_clipper_do_polytree2(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftNonZero)); } +Slic3r::ExPolygons union_ex(const Slic3r::Surfaces& subject) + { return PolyTreeToExPolygons(_clipper_do_polytree2(ClipperLib::ctUnion, ClipperUtils::SurfacesProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftNonZero)); } + +Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, const Polygons &clip, bool do_safety_offset) { - // read input - ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); - ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); - - // perform safety offset - if (safety_offset_) safety_offset(&input_clip); - - // init Clipper ClipperLib::Clipper clipper; - clipper.Clear(); - - // add polygons - clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); - - // perform operation + clipper.AddPaths(ClipperUtils::PolylinesProvider(subject), ClipperLib::ptSubject, false); + if (do_safety_offset) + clipper.AddPaths(safety_offset(ClipperUtils::PolygonsProvider(clip)), ClipperLib::ptClip, true); + else + clipper.AddPaths(ClipperUtils::PolygonsProvider(clip), ClipperLib::ptClip, true); ClipperLib::PolyTree retval; - clipper.Execute(clipType, retval, fillType, fillType); - return retval; + clipper.Execute(clipType, retval, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + return PolyTreeToPolylines(std::move(retval)); } -Polygons _clipper(ClipperLib::ClipType clipType, const Polygons &subject, const Polygons &clip, bool safety_offset_) -{ - return ClipperPaths_to_Slic3rPolygons(_clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_)); -} - -ExPolygons _clipper_ex(ClipperLib::ClipType clipType, const Polygons &subject, const Polygons &clip, bool safety_offset_) -{ - ClipperLib::PolyTree polytree = _clipper_do_polytree2(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_); - return PolyTreeToExPolygons(polytree); -} - -Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, const Polygons &clip, bool safety_offset_) -{ - ClipperLib::Paths output; - ClipperLib::PolyTreeToPaths(_clipper_do_pl(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_), output); - return ClipperPaths_to_Slic3rPolylines(output); -} - -Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, const Polygons &clip, bool safety_offset_) +Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, const Polygons &clip, bool do_safety_offset) { // transform input polygons into polylines Polylines polylines; @@ -664,7 +537,7 @@ Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, co polylines.emplace_back(polygon->operator Polyline()); // implicit call to split_at_first_point() // perform clipping - Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_); + Polylines retval = _clipper_pl(clipType, polylines, clip, do_safety_offset); /* If the split_at_first_point() call above happens to split the polygon inside the clipping area we would get two consecutive polylines instead of a single one, so we go through them in order @@ -703,9 +576,7 @@ Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, co return retval; } -Lines -_clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip, - bool safety_offset_) +Lines _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip, bool do_safety_offset) { // convert Lines to Polylines Polylines polylines; @@ -714,7 +585,7 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons polylines.emplace_back(Polyline(line.a, line.b)); // perform operation - polylines = _clipper_pl(clipType, polylines, clip, safety_offset_); + polylines = _clipper_pl(clipType, polylines, clip, do_safety_offset); // convert Polylines to Lines Lines retval; @@ -723,24 +594,14 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons return retval; } -ClipperLib::PolyTree union_pt(const Polygons &subject, bool safety_offset_) +ClipperLib::PolyTree union_pt(const Polygons &subject) { - return _clipper_do(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_); + return _clipper_do(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftEvenOdd); } -ClipperLib::PolyTree union_pt(const ExPolygons &subject, bool safety_offset_) +ClipperLib::PolyTree union_pt(const ExPolygons &subject) { - return _clipper_do(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_); -} - -ClipperLib::PolyTree union_pt(Polygons &&subject, bool safety_offset_) -{ - return _clipper_do(ClipperLib::ctUnion, std::move(subject), Polygons(), ClipperLib::pftEvenOdd, safety_offset_); -} - -ClipperLib::PolyTree union_pt(ExPolygons &&subject, bool safety_offset_) -{ - return _clipper_do(ClipperLib::ctUnion, std::move(subject), Polygons(), ClipperLib::pftEvenOdd, safety_offset_); + return _clipper_do(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ClipperLib::pftEvenOdd); } // Simple spatial ordering of Polynodes @@ -766,7 +627,7 @@ static void traverse_pt_noholes(const ClipperLib::PolyNodes &nodes, Polygons *ou foreach_node(nodes, [&out](const ClipperLib::PolyNode *node) { traverse_pt_noholes(node->Childs, out); - out->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); + out->emplace_back(node->Contour); if (node->IsHole()) out->back().reverse(); // ccw }); } @@ -782,7 +643,7 @@ static void traverse_pt_outside_in(const ClipperLib::PolyNodes &nodes, Polygons // Perform the ordering, push results recursively. //FIXME pass the last point to chain_clipper_polynodes? for (const ClipperLib::PolyNode *node : chain_clipper_polynodes(ordering_points, nodes)) { - retval->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); + retval->emplace_back(node->Contour); if (node->IsHole()) // Orient a hole, which is clockwise oriented, to CCW. retval->back().reverse(); @@ -791,9 +652,9 @@ static void traverse_pt_outside_in(const ClipperLib::PolyNodes &nodes, Polygons } } -Polygons union_pt_chained_outside_in(const Polygons &subject, bool safety_offset_) +Polygons union_pt_chained_outside_in(const Polygons &subject) { - ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_); + ClipperLib::PolyTree polytree = union_pt(subject); Polygons retval; traverse_pt_outside_in(polytree.Childs, &retval); @@ -802,22 +663,19 @@ Polygons union_pt_chained_outside_in(const Polygons &subject, bool safety_offset Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) { - // convert into Clipper polygons - ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); - ClipperLib::Paths output; if (preserve_collinear) { ClipperLib::Clipper c; c.PreserveCollinear(true); c.StrictlySimple(true); - c.AddPaths(input_subject, ClipperLib::ptSubject, true); + c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } else { - ClipperLib::SimplifyPolygons(input_subject, output, ClipperLib::pftNonZero); + output = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(subject), ClipperLib::pftNonZero); } // convert into Slic3r polygons - return ClipperPaths_to_Slic3rPolygons(output); + return to_polygons(std::move(output)); } ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) @@ -825,76 +683,15 @@ ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear if (! preserve_collinear) return union_ex(simplify_polygons(subject, false)); - // convert into Clipper polygons - ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); - - ClipperLib::PolyTree polytree; - + ClipperLib::PolyTree polytree; ClipperLib::Clipper c; c.PreserveCollinear(true); c.StrictlySimple(true); - c.AddPaths(input_subject, ClipperLib::ptSubject, true); + c.AddPaths(ClipperUtils::PolygonsProvider(subject), ClipperLib::ptSubject, true); c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // convert into ExPolygons - return PolyTreeToExPolygons(polytree); -} - -void safety_offset(ClipperLib::Paths* paths) -{ - CLIPPERUTILS_PROFILE_FUNC(); - -#ifdef CLIPPERUTILS_OFFSET_SCALE - // scale input - scaleClipperPolygons(*paths); -#endif // CLIPPERUTILS_OFFSET_SCALE - - // perform offset (delta = scale 1e-05) - ClipperLib::ClipperOffset co; -#ifdef CLIPPER_UTILS_DEBUG - if (clipper_export_enabled) { - static int iRun = 0; - export_clipper_input_polygons_bin(debug_out_path("safety_offset-polygons-%d", ++iRun).c_str(), *paths, ClipperLib::Paths()); - } -#endif /* CLIPPER_UTILS_DEBUG */ - ClipperLib::Paths out; - for (size_t i = 0; i < paths->size(); ++ i) { - ClipperLib::Path &path = (*paths)[i]; - co.Clear(); - co.MiterLimit = 2; - bool ccw = ClipperLib::Orientation(path); - if (! ccw) - std::reverse(path.begin(), path.end()); - { - CLIPPERUTILS_PROFILE_BLOCK(safety_offset_AddPaths); - co.AddPath((*paths)[i], ClipperLib::jtMiter, ClipperLib::etClosedPolygon); - } - { - CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute); - // offset outside by 10um - ClipperLib::Paths out_this; -#ifdef CLIPPERUTILS_OFFSET_SCALE - co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE)); -#else // CLIPPERUTILS_OFFSET_SCALE - co.Execute(out_this, ccw ? 10.f : -10.f); -#endif // CLIPPERUTILS_OFFSET_SCALE - if (! ccw) { - // Reverse the resulting contours once again. - for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it) - std::reverse(it->begin(), it->end()); - } - if (out.empty()) - out = std::move(out_this); - else - std::move(std::begin(out_this), std::end(out_this), std::back_inserter(out)); - } - } - *paths = std::move(out); - -#ifdef CLIPPERUTILS_OFFSET_SCALE - // unscale output - unscaleClipperPolygons(*paths); -#endif // CLIPPERUTILS_OFFSET_SCALE + return PolyTreeToExPolygons(std::move(polytree)); } Polygons top_level_islands(const Slic3r::Polygons &polygons) @@ -903,14 +700,14 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons) ClipperLib::Clipper clipper; clipper.Clear(); // perform union - clipper.AddPaths(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::ptSubject, true); + clipper.AddPaths(ClipperUtils::PolygonsProvider(polygons), ClipperLib::ptSubject, true); ClipperLib::PolyTree polytree; clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // Convert only the top level islands to the output. Polygons out; out.reserve(polytree.ChildCount()); for (int i = 0; i < polytree.ChildCount(); ++i) - out.emplace_back(ClipperPath_to_Slic3rPolygon(polytree.Childs[i]->Contour)); + out.emplace_back(std::move(polytree.Childs[i]->Contour)); return out; } @@ -988,9 +785,6 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v // Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt. auto add_offset_point = [&out](Vec2d pt) { -#ifdef CLIPPERUTILS_OFFSET_SCALE - pt *= double(CLIPPER_OFFSET_SCALE); -#endif // CLIPPERUTILS_OFFSET_SCALE pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0)); out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y())); }; @@ -1086,7 +880,7 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v { ClipperLib::Path polytmp(out); unscaleClipperPolygon(polytmp); - Slic3r::Polygon offsetted = ClipperPath_to_Slic3rPolygon(polytmp); + Slic3r::Polygon offsetted(std::move(polytmp)); BoundingBox bbox = get_extents(contour); bbox.merge(get_extents(offsetted)); static int iRun = 0; @@ -1140,11 +934,7 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) @@ -1186,11 +976,7 @@ for (const std::vector& ds : deltas) clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } -#ifdef CLIPPERUTILS_OFFSET_SCALE - // 4) Unscale the output. - unscaleClipperPolygons(output); -#endif // CLIPPERUTILS_OFFSET_SCALE - return ClipperPaths_to_Slic3rPolygons(output); + return to_polygons(std::move(output)); } ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit) @@ -1221,24 +1007,18 @@ for (const std::vector& ds : deltas) #endif /* NDEBUG */ // 3) Subtract holes from the contours. -#ifdef CLIPPERUTILS_OFFSET_SCALE - unscaleClipperPolygons(contours); -#endif // CLIPPERUTILS_OFFSET_SCALE ExPolygons output; if (holes.empty()) { output.reserve(contours.size()); for (ClipperLib::Path &path : contours) - output.emplace_back(ClipperPath_to_Slic3rPolygon(path)); + output.emplace_back(std::move(path)); } else { ClipperLib::Clipper clipper; -#ifdef CLIPPERUTILS_OFFSET_SCALE - unscaleClipperPolygons(holes); -#endif // CLIPPERUTILS_OFFSET_SCALE clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(holes, ClipperLib::ptClip, true); ClipperLib::PolyTree polytree; clipper.Execute(ClipperLib::ctDifference, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - output = PolyTreeToExPolygons(polytree); + output = PolyTreeToExPolygons(std::move(polytree)); } return output; @@ -1273,24 +1053,18 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector(nullptr); } + // all iterators point to end. + constexpr bool operator==(const iterator &rhs) const { return true; } + constexpr bool operator!=(const iterator &rhs) const { return false; } + constexpr const Points& operator++(int) { assert(false); return *static_cast(nullptr); } + constexpr iterator& operator++() { assert(false); return *this; } + }; + + constexpr EmptyPathsProvider() {} + static constexpr iterator cend() throw() { return iterator{}; } + static constexpr iterator end() throw() { return cend(); } + static constexpr iterator cbegin() throw() { return cend(); } + static constexpr iterator begin() throw() { return cend(); } + static constexpr size_t size() throw() { return 0; } + }; + + class SinglePathProvider { + public: + SinglePathProvider(const Points &points) : m_points(points) {} + + struct iterator : public PathsProviderIteratorBase { + public: + explicit iterator(const Points &points) : m_ptr(&points) {} + const Points& operator*() const { return *m_ptr; } + bool operator==(const iterator &rhs) const { return m_ptr == rhs.m_ptr; } + bool operator!=(const iterator &rhs) const { return !(*this == rhs); } + const Points& operator++(int) { auto out = m_ptr; m_ptr = &s_end; return *out; } + iterator& operator++() { m_ptr = &s_end; return *this; } + private: + const Points *m_ptr; + }; + + iterator cbegin() const { return iterator(m_points); } + iterator begin() const { return this->cbegin(); } + iterator cend() const { return iterator(s_end); } + iterator end() const { return this->cend(); } + size_t size() const { return 1; } + + private: + const Points &m_points; + static Points s_end; + }; + + template + class MultiPointsProvider { + public: + MultiPointsProvider(const std::vector &multipoints) : m_multipoints(multipoints) {} + + struct iterator : public PathsProviderIteratorBase { + public: + explicit iterator(typename std::vector::const_iterator it) : m_it(it) {} + const Points& operator*() const { return m_it->points; } + bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; } + bool operator!=(const iterator &rhs) const { return !(*this == rhs); } + const Points& operator++(int) { return (m_it ++)->points; } + iterator& operator++() { ++ m_it; return *this; } + private: + typename std::vector::const_iterator m_it; + }; + + iterator cbegin() const { return iterator(m_multipoints.begin()); } + iterator begin() const { return this->cbegin(); } + iterator cend() const { return iterator(m_multipoints.end()); } + iterator end() const { return this->cend(); } + size_t size() const { return m_multipoints.size(); } + + private: + const std::vector &m_multipoints; + }; + + using PolygonsProvider = MultiPointsProvider; + using PolylinesProvider = MultiPointsProvider; + + struct ExPolygonProvider { + ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {} + + struct iterator : public PathsProviderIteratorBase { + public: + explicit iterator(const ExPolygon &expoly, int idx) : m_expoly(expoly), m_idx(idx) {} + const Points& operator*() const { return (m_idx == 0) ? m_expoly.contour.points : m_expoly.holes[m_idx - 1].points; } + bool operator==(const iterator &rhs) const { assert(m_expoly == rhs.m_expoly); return m_idx == rhs.m_idx; } + bool operator!=(const iterator &rhs) const { return !(*this == rhs); } + const Points& operator++(int) { const Points &out = **this; ++ m_idx; return out; } + iterator& operator++() { ++ m_idx; return *this; } + private: + const ExPolygon &m_expoly; + int m_idx; + }; + + iterator cbegin() const { return iterator(m_expoly, 0); } + iterator begin() const { return this->cbegin(); } + iterator cend() const { return iterator(m_expoly, m_expoly.holes.size() + 1); } + iterator end() const { return this->cend(); } + size_t size() const { return m_expoly.holes.size() + 1; } + + private: + const ExPolygon &m_expoly; + }; + + struct ExPolygonsProvider { + ExPolygonsProvider(const ExPolygons &expolygons) : m_expolygons(expolygons) { + m_size = 0; + for (const ExPolygon &expoly : expolygons) + m_size += expoly.holes.size() + 1; + } + + struct iterator : public PathsProviderIteratorBase { + public: + explicit iterator(ExPolygons::const_iterator it) : m_it_expolygon(it), m_idx_contour(0) {} + const Points& operator*() const { return (m_idx_contour == 0) ? m_it_expolygon->contour.points : m_it_expolygon->holes[m_idx_contour - 1].points; } + bool operator==(const iterator &rhs) const { return m_it_expolygon == rhs.m_it_expolygon && m_idx_contour == rhs.m_idx_contour; } + bool operator!=(const iterator &rhs) const { return !(*this == rhs); } + iterator& operator++() { + if (++ m_idx_contour == m_it_expolygon->holes.size() + 1) { + ++ m_it_expolygon; + m_idx_contour = 0; + } + return *this; + } + const Points& operator++(int) { + const Points &out = **this; + ++ (*this); + return out; + } + private: + ExPolygons::const_iterator m_it_expolygon; + int m_idx_contour; + }; + + iterator cbegin() const { return iterator(m_expolygons.cbegin()); } + iterator begin() const { return this->cbegin(); } + iterator cend() const { return iterator(m_expolygons.cend()); } + iterator end() const { return this->cend(); } + size_t size() const { return m_size; } + + private: + const ExPolygons &m_expolygons; + size_t m_size; + }; + + struct SurfacesProvider { + SurfacesProvider(const Surfaces &surfaces) : m_surfaces(surfaces) { + m_size = 0; + for (const Surface &surface : surfaces) + m_size += surface.expolygon.holes.size() + 1; + } + + struct iterator : public PathsProviderIteratorBase { + public: + explicit iterator(Surfaces::const_iterator it) : m_it_surface(it), m_idx_contour(0) {} + const Points& operator*() const { return (m_idx_contour == 0) ? m_it_surface->expolygon.contour.points : m_it_surface->expolygon.holes[m_idx_contour - 1].points; } + bool operator==(const iterator &rhs) const { return m_it_surface == rhs.m_it_surface && m_idx_contour == rhs.m_idx_contour; } + bool operator!=(const iterator &rhs) const { return !(*this == rhs); } + iterator& operator++() { + if (++ m_idx_contour == m_it_surface->expolygon.holes.size() + 1) { + ++ m_it_surface; + m_idx_contour = 0; + } + return *this; + } + const Points& operator++(int) { + const Points &out = **this; + ++ (*this); + return out; + } + private: + Surfaces::const_iterator m_it_surface; + int m_idx_contour; + }; + + iterator cbegin() const { return iterator(m_surfaces.cbegin()); } + iterator begin() const { return this->cbegin(); } + iterator cend() const { return iterator(m_surfaces.cend()); } + iterator end() const { return this->cend(); } + size_t size() const { return m_size; } + + private: + const Surfaces &m_surfaces; + size_t m_size; + }; +} + +ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); // offset Polygons -ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); -ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); -inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); #ifdef CLIPPERUTILS_UNSAFE_OFFSET -inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); #endif // CLIPPERUTILS_UNSAFE_OFFSET // offset Polylines -inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) - { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } -inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) - { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } +Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); +Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); // offset expolygons and surfaces ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit); ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit); inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); } + { return to_polygons(_offset(expolygon, delta, joinType, miterLimit)); } inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); } -inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } + { return to_polygons(_offset(expolygons, delta, joinType, miterLimit)); } #ifdef CLIPPERUTILS_UNSAFE_OFFSET -inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) - { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } +Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); #endif // CLIPPERUTILS_UNSAFE_OFFSET inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) @@ -101,141 +260,68 @@ Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); -Slic3r::Polygons _clipper(ClipperLib::ClipType clipType, - const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType, - const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, - const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, - const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, - const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); +Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); +Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); -// diff -inline Slic3r::Polygons -diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); +Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false); +Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); +Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false); + +inline Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { - return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_); + return _clipper_pl(ClipperLib::ctDifference, subject, clip, do_safety_offset); } -inline Slic3r::ExPolygons -diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +inline Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { - return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_); + return _clipper_pl(ClipperLib::ctDifference, subject, clip, do_safety_offset); } -inline Slic3r::ExPolygons -diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { - return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); + return _clipper_ln(ClipperLib::ctDifference, subject, clip, do_safety_offset); } -inline Slic3r::Polygons -diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); +Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false); +Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false); +Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false); + +inline Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { - return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); + return _clipper_pl(ClipperLib::ctIntersection, subject, clip, do_safety_offset); } -inline Slic3r::Polylines -diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +inline Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { - return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); + return _clipper_pl(ClipperLib::ctIntersection, subject, clip, do_safety_offset); } -inline Slic3r::Polylines -diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { - return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); + return _clipper_ln(ClipperLib::ctIntersection, subject, clip, do_safety_offset); } -inline Slic3r::Lines -diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) -{ - return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_); -} - -// intersection -inline Slic3r::Polygons -intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) -{ - return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_); -} - -inline Slic3r::ExPolygons -intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) -{ - return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_); -} - -inline Slic3r::ExPolygons -intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) -{ - return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); -} - -inline Slic3r::Polygons -intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) -{ - return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); -} - -inline Slic3r::Polylines -intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) -{ - return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); -} - -inline Slic3r::Polylines -intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) -{ - return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); -} - -inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) -{ - return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_); -} - -inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false) { Slic3r::Lines lines; lines.emplace_back(subject); - return _clipper_ln(ClipperLib::ctIntersection, lines, clip, safety_offset_); + return _clipper_ln(ClipperLib::ctIntersection, lines, clip, do_safety_offset); } -// union -inline Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset_ = false) -{ - return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); -} +Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool do_safety_offset = false); +Slic3r::Polygons union_(const Slic3r::ExPolygons &subject, bool do_safety_offset = false); +Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool do_safety_offset = false); +Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool do_safety_offset = false); +Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject); +Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject); -inline Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false) -{ - return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_); -} +ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject); +ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject); -inline Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false) -{ - return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); -} - -inline Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false) -{ - return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); -} - -inline Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false) -{ - return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); -} - -ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); -ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_offset_ = false); -ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false); -ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ = false); - -Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject, bool safety_offset_ = false); +Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject); ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes); @@ -283,7 +369,7 @@ void traverse_pt(const ClipperLib::PolyNode *tree, Polygons *out) if (!tree) return; // terminates recursion // Push the contour of the current level - out->emplace_back(ClipperPath_to_Slic3rPolygon(tree->Contour)); + out->emplace_back(tree->Contour); // Do the recursion for all the children. traverse_pt(tree->Childs, out); @@ -302,13 +388,13 @@ void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *out) } ExPolygon level; - level.contour = ClipperPath_to_Slic3rPolygon(tree->Contour); + level.contour.points = tree->Contour; foreach_node(tree->Childs, [out, &level] (const ClipperLib::PolyNode *node) { // Holes are collected here. - level.holes.emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); + level.holes.emplace_back(node->Contour); // By doing a recursion, a new level expoly is created with the contour // and holes of the lower level. Doing this for all the childs. @@ -331,8 +417,6 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); -void safety_offset(ClipperLib::Paths* paths); - Polygons top_level_islands(const Slic3r::Polygons &polygons); ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector &deltas, double miter_limit); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index fcf3c159ec..04b84f7670 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -83,8 +83,8 @@ inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs. inline size_t number_polygons(const ExPolygons &expolys) { size_t n_polygons = 0; - for (ExPolygons::const_iterator it = expolys.begin(); it != expolys.end(); ++ it) - n_polygons += it->holes.size() + 1; + for (const ExPolygon &ex : expolys) + n_polygons += ex.holes.size() + 1; return n_polygons; } diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 785c93be3b..d5997552b9 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single( // generate paths from the outermost to the innermost, to avoid // adhesion problems of the first central tiny loops - loops = union_pt_chained_outside_in(loops, false); + loops = union_pt_chained_outside_in(loops); // split paths using a nearest neighbor search size_t iPathFirst = polylines_out.size(); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index e86a67b6fb..857c98f52e 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -185,7 +185,7 @@ void Layer::make_perimeters() } // merge the surfaces assigned to each group for (std::pair &surfaces_with_extra_perimeters : slices) - new_slices.append(offset_ex(to_expolygons(surfaces_with_extra_perimeters.second), 10.f), surfaces_with_extra_perimeters.second.front()); + new_slices.append(offset_ex(to_expolygons(surfaces_with_extra_perimeters.second), ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front()); } // make perimeters diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 7c588c67cc..e744272ac0 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -70,7 +70,7 @@ double Polygon::area() const bool Polygon::is_counter_clockwise() const { - return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); + return ClipperLib::Orientation(this->points); } bool Polygon::is_clockwise() const diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 93cd701213..333f1e6b1a 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -222,6 +222,24 @@ inline Polylines to_polylines(Polygons &&polys) return polylines; } +inline Polygons to_polygons(const std::vector &paths) +{ + Polygons out; + out.reserve(paths.size()); + for (const Points &path : paths) + out.emplace_back(path); + return out; +} + +inline Polygons to_polygons(std::vector &&paths) +{ + Polygons out; + out.reserve(paths.size()); + for (const Points &path : paths) + out.emplace_back(std::move(path)); + return out; +} + } // Slic3r // start Boost diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 9c70522bfb..88f910590b 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -124,6 +124,24 @@ inline Lines to_lines(const Polylines &polys) return lines; } +inline Polylines to_polylines(const std::vector &paths) +{ + Polylines out; + out.reserve(paths.size()); + for (const Points &path : paths) + out.emplace_back(path); + return out; +} + +inline Polylines to_polylines(std::vector &&paths) +{ + Polylines out; + out.reserve(paths.size()); + for (const Points &path : paths) + out.emplace_back(std::move(path)); + return out; +} + inline void polylines_append(Polylines &dst, const Polylines &src) { dst.insert(dst.end(), src.begin(), src.end()); diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp index d3c0d10224..1724089894 100644 --- a/src/libslic3r/SLA/ConcaveHull.cpp +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -42,9 +42,10 @@ Point ConcaveHull::centroid(const Points &pp) // As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound // mode -static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths, - coord_t delta, - ClipperLib::JoinType jointype) +template +static ClipperLib::Paths fast_offset(PolygonsProvider &&paths, + coord_t delta, + ClipperLib::JoinType jointype) { using ClipperLib::ClipperOffset; using ClipperLib::etClosedPolygon; @@ -61,7 +62,7 @@ static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths, return {}; } - offs.AddPaths(paths, jointype, etClosedPolygon); + offs.AddPaths(std::forward(paths), jointype, etClosedPolygon); Paths result; offs.Execute(result, static_cast(delta)); @@ -157,11 +158,9 @@ ExPolygons ConcaveHull::to_expolygons() const ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta) { - ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(hull.polygons()); - paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound); - paths = fast_offset(paths, -delta, ClipperLib::jtRound); - ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(paths); - for (ExPolygon &p : ret) p.holes = {}; + ExPolygons ret = ClipperPaths_to_Slic3rExPolygons( + fast_offset(fast_offset(ClipperUtils::PolygonsProvider(hull.polygons()), 2 * delta, ClipperLib::jtRound), -delta, ClipperLib::jtRound)); + for (ExPolygon &p : ret) p.holes.clear(); return ret; } diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index 927c325898..e11914a1cb 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -179,10 +179,10 @@ PadSkeleton divide_blueprint(const ExPolygons &bp) ret.outer.reserve(size_t(ptree.Total())); for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) { - ExPolygon poly(ClipperPath_to_Slic3rPolygon(node->Contour)); + ExPolygon poly; + poly.contour.points = std::move(node->Contour); for (ClipperLib::PolyTree::PolyNode *child : node->Childs) { - poly.holes.emplace_back( - ClipperPath_to_Slic3rPolygon(child->Contour)); + poly.holes.emplace_back(std::move(child->Contour)); traverse_pt(child->Childs, &ret.inner); } @@ -342,18 +342,18 @@ public: template ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args) { - ExPolygons tmp = offset_ex(poly.contour, float(delta), args...); + Polygons tmp = offset(poly.contour, float(delta), args...); if (tmp.empty()) return {}; Polygons holes = poly.holes; for (auto &h : holes) h.reverse(); - tmp = diff_ex(to_polygons(tmp), holes); + ExPolygons tmp2 = diff_ex(tmp, holes); - if (tmp.empty()) return {}; + if (tmp2.empty()) return {}; - return tmp.front(); + return std::move(tmp2.front()); } bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg, diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index f8fa1ca17f..da05833108 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1799,9 +1799,9 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float clos // append to the supplied collection if (safety_offset > 0) - expolygons_append(*slices, offset2_ex(union_ex(loops, false), +safety_offset, -safety_offset)); + expolygons_append(*slices, offset2_ex(union_ex(loops), +safety_offset, -safety_offset)); else - expolygons_append(*slices, union_ex(loops, false)); + expolygons_append(*slices, union_ex(loops)); } void TriangleMeshSlicer::make_expolygons(std::vector &lines, const float closing_radius, ExPolygons* slices) const diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 1c43e7eb04..29551ac15d 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -194,7 +194,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c const BoundingBox& bed_bbox = poly.contour.bounding_box(); calc_gridlines(poly, bed_bbox); - m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; + m_polygon = offset(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0]; reset(); m_texture.reset();