From bd9ee88e2fa695aed43915e4df2c328a664034d4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Apr 2017 11:17:25 +0200 Subject: [PATCH] Fix of https://github.com/prusa3d/Slic3r/issues/214 Dent in perimeters around a hole Clipper Offset has been extended to remove tiny edges before the offset. This is important as the tiny edges pose difficulty for normal calculation, leading to various adverse effects like kinks and dents in the offsetted contour. --- xs/src/clipper.cpp | 54 +++++++++++++++++++------------ xs/src/clipper.hpp | 4 ++- xs/src/libslic3r/ClipperUtils.cpp | 17 ++++++++-- xs/src/libslic3r/Point.hpp | 1 + 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index 2b205fd58..4d40d4aa6 100644 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -595,7 +595,8 @@ void InitEdge2(TEdge& e, PolyType Pt) } //------------------------------------------------------------------------------ -TEdge* RemoveEdge(TEdge* e) +// Called from ClipperBase::AddPathInternal() to remove collinear and duplicate points. +inline TEdge* RemoveEdge(TEdge* e) { //removes e from double_linked_list (but without removing from memory) e->Prev->Next = e->Next; @@ -3486,14 +3487,6 @@ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) // ClipperOffset class //------------------------------------------------------------------------------ -ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) -{ - this->MiterLimit = miterLimit; - this->ArcTolerance = arcTolerance; - m_lowest.X = -1; -} -//------------------------------------------------------------------------------ - void ClipperOffset::Clear() { for (int i = 0; i < m_polyNodes.ChildCount(); ++i) @@ -3512,20 +3505,39 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType newNode->m_endtype = endType; //strip duplicate points from path and also get index to the lowest point ... + bool has_shortest_edge_length = ShortestEdgeLength > 0.; + double shortest_edge_length2 = has_shortest_edge_length ? ShortestEdgeLength * ShortestEdgeLength : 0.; if (endType == etClosedLine || endType == etClosedPolygon) - while (highI > 0 && path[0] == path[highI]) highI--; + for (; highI > 0; -- highI) { + bool same = false; + if (has_shortest_edge_length) { + double dx = double(path[highI].X - path[0].X); + double dy = double(path[highI].Y - path[0].Y); + same = dx*dx + dy*dy < shortest_edge_length2; + } else + same = path[0] == path[highI]; + if (! same) + break; + } newNode->Contour.reserve(highI + 1); newNode->Contour.push_back(path[0]); int j = 0, k = 0; - for (int i = 1; i <= highI; i++) - if (newNode->Contour[j] != path[i]) - { - j++; - newNode->Contour.push_back(path[i]); - if (path[i].Y > newNode->Contour[k].Y || - (path[i].Y == newNode->Contour[k].Y && - path[i].X < newNode->Contour[k].X)) k = j; - } + for (int i = 1; i <= highI; i++) { + bool same = false; + if (has_shortest_edge_length) { + double dx = double(path[i].X - newNode->Contour[j].X); + double dy = double(path[i].Y - newNode->Contour[j].Y); + same = dx*dx + dy*dy < shortest_edge_length2; + } else + same = newNode->Contour[j] == path[i]; + if (same) + continue; + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } if (endType == etClosedPolygon && j < 2) { delete newNode; @@ -3550,8 +3562,8 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) { - for (Paths::size_type i = 0; i < paths.size(); ++i) - AddPath(paths[i], joinType, endType); + for (const Path &path : paths) + AddPath(path, joinType, endType); } //------------------------------------------------------------------------------ diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 85bc5cdc4..aabe4f53b 100644 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -448,7 +448,8 @@ private: class ClipperOffset { public: - ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25, double shortestEdgeLength = 0.) : + 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); @@ -457,6 +458,7 @@ public: void Clear(); double MiterLimit; double ArcTolerance; + double ShortestEdgeLength; private: Paths m_destPolys; Path m_srcPoly; diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 8aa72cae4..1a4550248 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -9,6 +9,8 @@ #include +#define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f) + namespace Slic3r { #ifdef CLIPPER_UTILS_DEBUG @@ -210,9 +212,11 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType co.ArcTolerance = miterLimit; else co.MiterLimit = miterLimit; + float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); + co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPaths(input, joinType, endType); ClipperLib::Paths retval; - co.Execute(retval, delta * float(CLIPPER_OFFSET_SCALE)); + co.Execute(retval, delta_scaled); // unscale output unscaleClipperPolygons(retval); @@ -244,6 +248,7 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, co.ArcTolerance = miterLimit * double(CLIPPER_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.Execute(contours, delta_scaled); } @@ -260,6 +265,7 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; + co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPath(input, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; co.Execute(out, - delta_scaled); @@ -308,6 +314,7 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt co.ArcTolerance = miterLimit * double(CLIPPER_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.Execute(contours, delta_scaled); } @@ -331,6 +338,7 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; + co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPath(input, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; co.Execute(out, - delta_scaled); @@ -408,17 +416,20 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2, } else { co.MiterLimit = miterLimit; } + float delta_scaled1 = delta1 * float(CLIPPER_OFFSET_SCALE); + float delta_scaled2 = delta2 * float(CLIPPER_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.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE)); + co.Execute(output1, delta_scaled1); // perform second offset co.Clear(); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths retval; - co.Execute(retval, delta2 * float(CLIPPER_OFFSET_SCALE)); + co.Execute(retval, delta_scaled2); // unscale output unscaleClipperPolygons(retval); diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 63040718b..88b585307 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -38,6 +38,7 @@ class Point return Point(scale_(x), scale_(y)); }; bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; } + bool operator!=(const Point& rhs) const { return ! (*this == rhs); } std::string wkt() const; std::string dump_perl() const; void scale(double factor);