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);