Clipper / ClipperUtils:

1) Let Clipper use int32_t for representing its coordinates. This
   reduces memory and allows to skip conversion between Slic3r Polygon
   and Clipper polygon.
2) Disable additional offset before executing the Clipper Offset algorithm.
   We don't see any reason for that and it required 64bit Clipper coordinates,
   which were disabled with 1).
This commit is contained in:
Vojtech Bubnik 2021-04-13 11:31:54 +02:00
parent dbd1c09523
commit e8643125d8
5 changed files with 155 additions and 41 deletions

View file

@ -287,6 +287,11 @@ bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
#ifdef CLIPPERLIB_INT32
inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool /* UseFullInt64Range */) {
return int64_t(dy1) * int64_t(dx2) == int64_t(dx1) * int64_t(dy2);
}
#else
inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool UseFullInt64Range) { inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cInt dy2, bool UseFullInt64Range) {
return (UseFullInt64Range) ? return (UseFullInt64Range) ?
// |dx1| < 2^63, |dx2| < 2^63 etc, // |dx1| < 2^63, |dx2| < 2^63 etc,
@ -296,6 +301,8 @@ inline bool SlopesEqual(const cInt dx1, const cInt dy1, const cInt dx2, const cI
// therefore the following computation could be done with 64bit arithmetics. // therefore the following computation could be done with 64bit arithmetics.
dy1 * dx2 == dx1 * dy2; dy1 * dx2 == dx1 * dy2;
} }
#endif
inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) inline bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)
{ return SlopesEqual(e1.Delta.X, e1.Delta.Y, e2.Delta.X, e2.Delta.Y, UseFullInt64Range); } { return SlopesEqual(e1.Delta.X, e1.Delta.Y, e2.Delta.X, e2.Delta.Y, UseFullInt64Range); }
inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range) inline bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, const IntPoint &pt3, bool UseFullInt64Range)
@ -363,8 +370,8 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
} }
else else
{ {
b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; b1 = double(Edge1.Bot.X) - double(Edge1.Bot.Y) * Edge1.Dx;
b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; b2 = double(Edge2.Bot.X) - double(Edge2.Bot.Y) * Edge2.Dx;
double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); double q = (b2-b1) / (Edge1.Dx - Edge2.Dx);
ip.Y = Round(q); ip.Y = Round(q);
ip.X = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? ip.X = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ?
@ -569,6 +576,7 @@ bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
// ClipperBase class methods ... // ClipperBase class methods ...
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifndef CLIPPERLIB_INT32
// Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates. // Called from ClipperBase::AddPath() to verify the scale of the input polygon coordinates.
inline void RangeTest(const IntPoint& Pt, bool& useFullRange) inline void RangeTest(const IntPoint& Pt, bool& useFullRange)
{ {
@ -583,6 +591,7 @@ inline void RangeTest(const IntPoint& Pt, bool& useFullRange)
RangeTest(Pt, useFullRange); RangeTest(Pt, useFullRange);
} }
} }
#endif // CLIPPERLIB_INT32
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Called from ClipperBase::AddPath() to construct the Local Minima List. // Called from ClipperBase::AddPath() to construct the Local Minima List.
@ -805,13 +814,17 @@ bool ClipperBase::AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, b
try try
{ {
edges[1].Curr = pg[1]; edges[1].Curr = pg[1];
#ifndef CLIPPERLIB_INT32
RangeTest(pg[0], m_UseFullRange); RangeTest(pg[0], m_UseFullRange);
RangeTest(pg[highI], m_UseFullRange); RangeTest(pg[highI], m_UseFullRange);
#endif // CLIPPERLIB_INT32
InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]);
InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]);
for (int i = highI - 1; i >= 1; --i) for (int i = highI - 1; i >= 1; --i)
{ {
#ifndef CLIPPERLIB_INT32
RangeTest(pg[i], m_UseFullRange); RangeTest(pg[i], m_UseFullRange);
#endif // CLIPPERLIB_INT32
InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
} }
} }
@ -967,7 +980,9 @@ void ClipperBase::Clear()
CLIPPERLIB_PROFILE_FUNC(); CLIPPERLIB_PROFILE_FUNC();
m_MinimaList.clear(); m_MinimaList.clear();
m_edges.clear(); m_edges.clear();
#ifndef CLIPPERLIB_INT32
m_UseFullRange = false; m_UseFullRange = false;
#endif // CLIPPERLIB_INT32
m_HasOpenPaths = false; m_HasOpenPaths = false;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -3322,9 +3337,9 @@ DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
if(pt2.X == pt1.X && pt2.Y == pt1.Y) if(pt2.X == pt1.X && pt2.Y == pt1.Y)
return DoublePoint(0, 0); return DoublePoint(0, 0);
double Dx = (double)(pt2.X - pt1.X); double Dx = double(pt2.X - pt1.X);
double dy = (double)(pt2.Y - pt1.Y); double dy = double(pt2.Y - pt1.Y);
double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); double f = 1.0 / std::sqrt( Dx*Dx + dy*dy );
Dx *= f; Dx *= f;
dy *= f; dy *= f;
return DoublePoint(dy, -Dx); return DoublePoint(dy, -Dx);
@ -3530,8 +3545,9 @@ void ClipperOffset::DoOffset(double delta)
} }
//see offset_triginometry3.svg in the documentation folder ... //see offset_triginometry3.svg in the documentation folder ...
if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); m_miterLim = (MiterLimit > 2) ?
else m_miterLim = 0.5; 2. / (MiterLimit * MiterLimit) :
0.5;
double y; double y;
if (ArcTolerance <= 0.0) y = def_arc_tolerance; if (ArcTolerance <= 0.0) y = def_arc_tolerance;
@ -3633,11 +3649,9 @@ void ClipperOffset::DoOffset(double delta)
if (node.m_endtype == etOpenButt) if (node.m_endtype == etOpenButt)
{ {
int j = len - 1; int j = len - 1;
pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * pt1 = IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
delta), Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
m_destPoly.push_back(pt1); m_destPoly.push_back(pt1);
pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X * pt1 = IntPoint(Round(m_srcPoly[j].X - m_normals[j].X * delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
delta), Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
m_destPoly.push_back(pt1); m_destPoly.push_back(pt1);
} }
else else
@ -3662,11 +3676,9 @@ void ClipperOffset::DoOffset(double delta)
if (node.m_endtype == etOpenButt) if (node.m_endtype == etOpenButt)
{ {
pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta), pt1 = IntPoint(Round(m_srcPoly[0].X - m_normals[0].X * delta), Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
m_destPoly.push_back(pt1); m_destPoly.push_back(pt1);
pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta), pt1 = IntPoint(Round(m_srcPoly[0].X + m_normals[0].X * delta), Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
m_destPoly.push_back(pt1); m_destPoly.push_back(pt1);
} }
else else
@ -3753,7 +3765,7 @@ void ClipperOffset::DoRound(int j, int k)
{ {
double a = std::atan2(m_sinA, double a = std::atan2(m_sinA,
m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); auto steps = std::max<int>(Round(m_StepsPerRad * std::fabs(a)), 1);
double X = m_normals[k].X, Y = m_normals[k].Y, X2; double X = m_normals[k].X, Y = m_normals[k].Y, X2;
for (int i = 0; i < steps; ++i) for (int i = 0; i < steps; ++i)
@ -3885,8 +3897,8 @@ void SimplifyPolygons(Paths &polys, PolyFillType fillType)
inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)
{ {
double Dx = ((double)pt1.X - pt2.X); auto Dx = double(pt1.X - pt2.X);
double dy = ((double)pt1.Y - pt2.Y); auto dy = double(pt1.Y - pt2.Y);
return (Dx*Dx + dy*dy); return (Dx*Dx + dy*dy);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -3937,8 +3949,8 @@ bool SlopesNearCollinear(const IntPoint& pt1,
bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
{ {
double Dx = (double)pt1.X - pt2.X; auto Dx = double(pt1.X - pt2.X);
double dy = (double)pt1.Y - pt2.Y; auto dy = double(pt1.Y - pt2.Y);
return ((Dx * Dx) + (dy * dy) <= distSqrd); return ((Dx * Dx) + (dy * dy) <= distSqrd);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View file

@ -71,12 +71,22 @@ enum PolyType { ptSubject, ptClip };
//see http://glprogramming.com/red/chapter11.html //see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
// If defined, Clipper will work with 32bit signed int coordinates to reduce memory
// consumption and to speed up exact orientation predicate calculation.
// In that case, coordinates and their differences (vectors of the coordinates) have to fit int32_t.
#define CLIPPERLIB_INT32
// Point coordinate type // Point coordinate type
typedef int64_t cInt; #ifdef CLIPPERLIB_INT32
// Maximum cInt value to allow a cross product calculation using 32bit expressions. // Coordinates and their differences (vectors of the coordinates) have to fit int32_t.
static cInt const loRange = 0x3FFFFFFF; typedef int32_t cInt;
// Maximum allowed cInt value. #else
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; typedef int64_t cInt;
// Maximum cInt value to allow a cross product calculation using 32bit expressions.
static constexpr cInt const loRange = 0x3FFFFFFF; // 0x3FFFFFFF = 1 073 741 823
// Maximum allowed cInt value.
static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
#endif // CLIPPERLIB_INT32
struct IntPoint { struct IntPoint {
cInt X; cInt X;
@ -289,7 +299,11 @@ enum EdgeSide { esLeft = 1, esRight = 2};
class ClipperBase class ClipperBase
{ {
public: public:
ClipperBase() : m_UseFullRange(false), m_HasOpenPaths(false) {} ClipperBase() :
#ifndef CLIPPERLIB_INT32
m_UseFullRange(false),
#endif // CLIPPERLIB_INT32
m_HasOpenPaths(false) {}
~ClipperBase() { Clear(); } ~ClipperBase() { Clear(); }
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
@ -310,9 +324,14 @@ protected:
// Local minima (Y, left edge, right edge) sorted by ascending Y. // Local minima (Y, left edge, right edge) sorted by ascending Y.
std::vector<LocalMinimum> m_MinimaList; std::vector<LocalMinimum> m_MinimaList;
#ifdef CLIPPERLIB_INT32
static constexpr const bool m_UseFullRange = false;
#else // CLIPPERLIB_INT32
// True if the input polygons have abs values higher than loRange, but lower than hiRange. // True if the input polygons have abs values higher than loRange, but lower than hiRange.
// False if the input polygons have abs values lower or equal to loRange. // False if the input polygons have abs values lower or equal to loRange.
bool m_UseFullRange; bool m_UseFullRange;
#endif // CLIPPERLIB_INT32
// A vector of edges per each input path. // A vector of edges per each input path.
std::vector<std::vector<TEdge>> m_edges; std::vector<std::vector<TEdge>> m_edges;
// Don't remove intermediate vertices of a collinear sequence of points. // Don't remove intermediate vertices of a collinear sequence of points.

View file

@ -57,6 +57,7 @@ err:
} }
#endif /* CLIPPER_UTILS_DEBUG */ #endif /* CLIPPER_UTILS_DEBUG */
#ifdef CLIPPERUTILS_OFFSET_SCALE
void scaleClipperPolygon(ClipperLib::Path &polygon) void scaleClipperPolygon(ClipperLib::Path &polygon)
{ {
CLIPPERUTILS_PROFILE_FUNC(); CLIPPERUTILS_PROFILE_FUNC();
@ -98,6 +99,7 @@ void unscaleClipperPolygons(ClipperLib::Paths &polygons)
pit->Y >>= CLIPPER_OFFSET_POWER_OF_2; pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
} }
} }
#endif // CLIPPERUTILS_OFFSET_SCALE
//----------------------------------------------------------- //-----------------------------------------------------------
// legacy code from Clipper documentation // legacy code from Clipper documentation
@ -222,8 +224,10 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input)
ClipperLib::Paths _offset(ClipperLib::Paths &&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)
{ {
#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input // scale input
scaleClipperPolygons(input); scaleClipperPolygons(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
// perform offset // perform offset
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
@ -231,14 +235,20 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType
co.ArcTolerance = miterLimit; co.ArcTolerance = miterLimit;
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta * float(CLIPPER_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.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_scaled); co.Execute(retval, delta_scaled);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output // unscale output
unscaleClipperPolygons(retval); unscaleClipperPolygons(retval);
#endif // CLIPPERUTILS_OFFSET_SCALE
return retval; return retval;
} }
@ -257,14 +267,24 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
{ {
// printf("new ExPolygon offset\n"); // printf("new ExPolygon offset\n");
// 1) Offset the outer contour. // 1) Offset the outer contour.
const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); #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::Paths contours;
{ {
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour); ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input); scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
if (joinType == jtRound) if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@ -278,17 +298,23 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
holes.reserve(expolygon.holes.size()); holes.reserve(expolygon.holes.size());
for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) { 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); ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input); scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
if (joinType == jtRound) if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); 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);
holes.insert(holes.end(), out.begin(), out.end()); append(holes, std::move(out));
} }
} }
@ -305,7 +331,9 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
} }
// 4) Unscale the output. // 4) Unscale the output.
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(output); unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return output; return output;
} }
@ -315,7 +343,11 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta,
ClipperLib::JoinType joinType, double miterLimit) ClipperLib::JoinType joinType, double miterLimit)
{ {
const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); #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. // Offsetted ExPolygons before they are united.
ClipperLib::Paths contours_cummulative; ClipperLib::Paths contours_cummulative;
contours_cummulative.reserve(expolygons.size()); contours_cummulative.reserve(expolygons.size());
@ -327,10 +359,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
ClipperLib::Paths contours; ClipperLib::Paths contours;
{ {
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour); ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input); scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
if (joinType == jtRound) if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@ -351,10 +389,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
{ {
for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { 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); ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input); scaleClipperPolygon(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
if (joinType == jtRound) if (joinType == jtRound)
#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
#else // CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit;
#endif // CLIPPERUTILS_OFFSET_SCALE
else else
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@ -413,8 +457,10 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
output = std::move(contours_cummulative); output = std::move(contours_cummulative);
} }
#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output. // 4) Unscale the output.
unscaleClipperPolygons(output); unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return output; return output;
} }
@ -425,8 +471,10 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
// read input // read input
ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons); ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input // scale input
scaleClipperPolygons(input); scaleClipperPolygons(input);
#endif // CLIPPERUTILS_OFFSET_SCALE
// prepare ClipperOffset object // prepare ClipperOffset object
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
@ -435,8 +483,13 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
} else { } else {
co.MiterLimit = miterLimit; co.MiterLimit = miterLimit;
} }
#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled1 = delta1 * float(CLIPPER_OFFSET_SCALE); float delta_scaled1 = delta1 * float(CLIPPER_OFFSET_SCALE);
float delta_scaled2 = delta2 * 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); co.ShortestEdgeLength = double(std::max(std::abs(delta_scaled1), std::abs(delta_scaled2)) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR);
// perform first offset // perform first offset
@ -450,8 +503,10 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
ClipperLib::Paths retval; ClipperLib::Paths retval;
co.Execute(retval, delta_scaled2); co.Execute(retval, delta_scaled2);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output // unscale output
unscaleClipperPolygons(retval); unscaleClipperPolygons(retval);
#endif // CLIPPERUTILS_OFFSET_SCALE
return retval; return retval;
} }
@ -789,8 +844,10 @@ void safety_offset(ClipperLib::Paths* paths)
{ {
CLIPPERUTILS_PROFILE_FUNC(); CLIPPERUTILS_PROFILE_FUNC();
#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input // scale input
scaleClipperPolygons(*paths); scaleClipperPolygons(*paths);
#endif // CLIPPERUTILS_OFFSET_SCALE
// perform offset (delta = scale 1e-05) // perform offset (delta = scale 1e-05)
ClipperLib::ClipperOffset co; ClipperLib::ClipperOffset co;
@ -816,7 +873,11 @@ void safety_offset(ClipperLib::Paths* paths)
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute); CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute);
// offset outside by 10um // offset outside by 10um
ClipperLib::Paths out_this; 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)); 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) { if (! ccw) {
// Reverse the resulting contours once again. // Reverse the resulting contours once again.
for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it) for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it)
@ -830,8 +891,10 @@ void safety_offset(ClipperLib::Paths* paths)
} }
*paths = std::move(out); *paths = std::move(out);
#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output // unscale output
unscaleClipperPolygons(*paths); unscaleClipperPolygons(*paths);
#endif // CLIPPERUTILS_OFFSET_SCALE
} }
Polygons top_level_islands(const Slic3r::Polygons &polygons) Polygons top_level_islands(const Slic3r::Polygons &polygons)
@ -925,8 +988,10 @@ 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. // Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt.
auto add_offset_point = [&out](Vec2d pt) { auto add_offset_point = [&out](Vec2d pt) {
#ifdef CLIPPERUTILS_OFFSET_SCALE
pt *= double(CLIPPER_OFFSET_SCALE); pt *= double(CLIPPER_OFFSET_SCALE);
pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0)); #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())); out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
}; };
@ -1075,8 +1140,10 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::v
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
} }
#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output. // 4) Unscale the output.
unscaleClipperPolygons(output); unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return ClipperPaths_to_Slic3rPolygons(output); return ClipperPaths_to_Slic3rPolygons(output);
} }
@ -1119,8 +1186,10 @@ for (const std::vector<float>& ds : deltas)
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
} }
#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output. // 4) Unscale the output.
unscaleClipperPolygons(output); unscaleClipperPolygons(output);
#endif // CLIPPERUTILS_OFFSET_SCALE
return ClipperPaths_to_Slic3rPolygons(output); return ClipperPaths_to_Slic3rPolygons(output);
} }
@ -1152,7 +1221,9 @@ for (const std::vector<float>& ds : deltas)
#endif /* NDEBUG */ #endif /* NDEBUG */
// 3) Subtract holes from the contours. // 3) Subtract holes from the contours.
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(contours); unscaleClipperPolygons(contours);
#endif // CLIPPERUTILS_OFFSET_SCALE
ExPolygons output; ExPolygons output;
if (holes.empty()) { if (holes.empty()) {
output.reserve(contours.size()); output.reserve(contours.size());
@ -1160,7 +1231,9 @@ for (const std::vector<float>& ds : deltas)
output.emplace_back(ClipperPath_to_Slic3rPolygon(path)); output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
} else { } else {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(holes); unscaleClipperPolygons(holes);
#endif // CLIPPERUTILS_OFFSET_SCALE
clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true); clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree; ClipperLib::PolyTree polytree;
@ -1200,7 +1273,9 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
#endif /* NDEBUG */ #endif /* NDEBUG */
// 3) Subtract holes from the contours. // 3) Subtract holes from the contours.
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(contours); unscaleClipperPolygons(contours);
#endif // CLIPPERUTILS_OFFSET_SCALE
ExPolygons output; ExPolygons output;
if (holes.empty()) { if (holes.empty()) {
output.reserve(contours.size()); output.reserve(contours.size());
@ -1208,7 +1283,9 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector<s
output.emplace_back(ClipperPath_to_Slic3rPolygon(path)); output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
} else { } else {
ClipperLib::Clipper clipper; ClipperLib::Clipper clipper;
#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(holes); unscaleClipperPolygons(holes);
#endif // CLIPPERUTILS_OFFSET_SCALE
clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true); clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree; ClipperLib::PolyTree polytree;

View file

@ -12,17 +12,21 @@ using ClipperLib::jtMiter;
using ClipperLib::jtRound; using ClipperLib::jtRound;
using ClipperLib::jtSquare; using ClipperLib::jtSquare;
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library // #define CLIPPERUTILS_OFFSET_SCALE
// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
// which is optionally executed by other functions (union, intersection, diff). #ifdef CLIPPERUTILS_OFFSET_SCALE
// This scaling (cca 130t) is applied over the usual SCALING_FACTOR. // Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
// By the way, is the scalling for offset needed at all? // for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
// The reason to apply this scaling may be to match the resolution of the double mantissa. // which is optionally executed by other functions (union, intersection, diff).
#define CLIPPER_OFFSET_POWER_OF_2 17 // This scaling (cca 130t) is applied over the usual SCALING_FACTOR.
// 2^17=131072 // By the way, is the scalling for offset needed at all?
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2) // The reason to apply this scaling may be to match the resolution of the double mantissa.
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1) #define CLIPPER_OFFSET_POWER_OF_2 17
#define CLIPPER_MAX_COORD_UNSCALED (ClipperLib::hiRange / CLIPPER_OFFSET_SCALE) // 2^17=131072
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
#define CLIPPER_MAX_COORD_UNSCALED (ClipperLib::hiRange / CLIPPER_OFFSET_SCALE)
#endif // CLIPPERUTILS_OFFSET_SCALE
namespace Slic3r { namespace Slic3r {

View file

@ -1087,11 +1087,13 @@ bool
MedialAxis::validate_edge(const VD::edge_type* edge) MedialAxis::validate_edge(const VD::edge_type* edge)
{ {
// prevent overflows and detect almost-infinite edges // prevent overflows and detect almost-infinite edges
#ifndef CLIPPERLIB_INT32
if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) || std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED)) std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED))
return false; return false;
#endif // CLIPPERLIB_INT32
// construct the line representing this edge of the Voronoi diagram // construct the line representing this edge of the Voronoi diagram
const Line line( const Line line(