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.
This commit is contained in:
bubnikv 2017-04-04 11:17:25 +02:00
parent 70fcedb113
commit bd9ee88e2f
4 changed files with 51 additions and 25 deletions

View File

@ -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) //removes e from double_linked_list (but without removing from memory)
e->Prev->Next = e->Next; e->Prev->Next = e->Next;
@ -3486,14 +3487,6 @@ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
// ClipperOffset class // ClipperOffset class
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance)
{
this->MiterLimit = miterLimit;
this->ArcTolerance = arcTolerance;
m_lowest.X = -1;
}
//------------------------------------------------------------------------------
void ClipperOffset::Clear() void ClipperOffset::Clear()
{ {
for (int i = 0; i < m_polyNodes.ChildCount(); ++i) 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; newNode->m_endtype = endType;
//strip duplicate points from path and also get index to the lowest point ... //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) 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.reserve(highI + 1);
newNode->Contour.push_back(path[0]); newNode->Contour.push_back(path[0]);
int j = 0, k = 0; int j = 0, k = 0;
for (int i = 1; i <= highI; i++) for (int i = 1; i <= highI; i++) {
if (newNode->Contour[j] != path[i]) bool same = false;
{ if (has_shortest_edge_length) {
j++; double dx = double(path[i].X - newNode->Contour[j].X);
newNode->Contour.push_back(path[i]); double dy = double(path[i].Y - newNode->Contour[j].Y);
if (path[i].Y > newNode->Contour[k].Y || same = dx*dx + dy*dy < shortest_edge_length2;
(path[i].Y == newNode->Contour[k].Y && } else
path[i].X < newNode->Contour[k].X)) k = j; 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) if (endType == etClosedPolygon && j < 2)
{ {
delete newNode; 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) void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType)
{ {
for (Paths::size_type i = 0; i < paths.size(); ++i) for (const Path &path : paths)
AddPath(paths[i], joinType, endType); AddPath(path, joinType, endType);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -448,7 +448,8 @@ private:
class ClipperOffset class ClipperOffset
{ {
public: 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(); } ~ClipperOffset() { Clear(); }
void AddPath(const Path& path, JoinType joinType, EndType endType); void AddPath(const Path& path, JoinType joinType, EndType endType);
void AddPaths(const Paths& paths, JoinType joinType, EndType endType); void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
@ -457,6 +458,7 @@ public:
void Clear(); void Clear();
double MiterLimit; double MiterLimit;
double ArcTolerance; double ArcTolerance;
double ShortestEdgeLength;
private: private:
Paths m_destPolys; Paths m_destPolys;
Path m_srcPoly; Path m_srcPoly;

View File

@ -9,6 +9,8 @@
#include <Shiny/Shiny.h> #include <Shiny/Shiny.h>
#define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f)
namespace Slic3r { namespace Slic3r {
#ifdef CLIPPER_UTILS_DEBUG #ifdef CLIPPER_UTILS_DEBUG
@ -210,9 +212,11 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType
co.ArcTolerance = miterLimit; co.ArcTolerance = miterLimit;
else else
co.MiterLimit = miterLimit; 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); co.AddPaths(input, joinType, endType);
ClipperLib::Paths retval; ClipperLib::Paths retval;
co.Execute(retval, delta * float(CLIPPER_OFFSET_SCALE)); co.Execute(retval, delta_scaled);
// unscale output // unscale output
unscaleClipperPolygons(retval); unscaleClipperPolygons(retval);
@ -244,6 +248,7 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPath(input, joinType, ClipperLib::etClosedPolygon); co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
co.Execute(contours, delta_scaled); 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); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPath(input, joinType, ClipperLib::etClosedPolygon); co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths out; ClipperLib::Paths out;
co.Execute(out, - delta_scaled); 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); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPath(input, joinType, ClipperLib::etClosedPolygon); co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
co.Execute(contours, delta_scaled); 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); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPath(input, joinType, ClipperLib::etClosedPolygon); co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths out; ClipperLib::Paths out;
co.Execute(out, - delta_scaled); co.Execute(out, - delta_scaled);
@ -408,17 +416,20 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
} else { } else {
co.MiterLimit = miterLimit; 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 // perform first offset
ClipperLib::Paths output1; ClipperLib::Paths output1;
co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); co.AddPaths(input, joinType, ClipperLib::etClosedPolygon);
co.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE)); co.Execute(output1, delta_scaled1);
// perform second offset // perform second offset
co.Clear(); co.Clear();
co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths retval; ClipperLib::Paths retval;
co.Execute(retval, delta2 * float(CLIPPER_OFFSET_SCALE)); co.Execute(retval, delta_scaled2);
// unscale output // unscale output
unscaleClipperPolygons(retval); unscaleClipperPolygons(retval);

View File

@ -38,6 +38,7 @@ class Point
return Point(scale_(x), scale_(y)); 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->x == rhs.x && this->y == rhs.y; }
bool operator!=(const Point& rhs) const { return ! (*this == rhs); }
std::string wkt() const; std::string wkt() const;
std::string dump_perl() const; std::string dump_perl() const;
void scale(double factor); void scale(double factor);