Skip to content

Commit

Permalink
Fix of #214
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bubnikv committed Apr 4, 2017
1 parent 70fcedb commit bd9ee88
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 25 deletions.
54 changes: 33 additions & 21 deletions xs/src/clipper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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);
}
//------------------------------------------------------------------------------

Expand Down
4 changes: 3 additions & 1 deletion xs/src/clipper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,8 @@ class Clipper : public ClipperBase
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);
Expand All @@ -457,6 +458,7 @@ class ClipperOffset
void Clear();
double MiterLimit;
double ArcTolerance;
double ShortestEdgeLength;
private:
Paths m_destPolys;
Path m_srcPoly;
Expand Down
17 changes: 14 additions & 3 deletions xs/src/libslic3r/ClipperUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <Shiny/Shiny.h>

#define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f)

namespace Slic3r {

#ifdef CLIPPER_UTILS_DEBUG
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions xs/src/libslic3r/Point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit bd9ee88

Please sign in to comment.