Fix clipper::union adding extremely small holes even with safety_offset

It check that all polygons are all thicker than scaled_epsilon
This commit is contained in:
remi durand 2021-04-28 21:11:11 +02:00
parent b6a98377ae
commit 42a0c31cb2
3 changed files with 58 additions and 4 deletions

View File

@ -162,6 +162,25 @@ double Area(const Path &poly)
return -a * 0.5;
}
//------------------------------------------------------------------------------
IntPoint Centroid(const Path& poly, double area)
{
double x_temp = 0;
double y_temp = 0;
const int max = poly.size() - 1;
size_t i = 0;
for (; i < max; ++i)
{
size_t j = i + 1;
x_temp += (double)(poly[i].X + poly[j].X) * ((double)poly[i].X * poly[j].Y - (double)poly[j].X * poly[i].Y);
y_temp += (double)(poly[i].Y + poly[j].Y) * ((double)poly[i].X * poly[j].Y - (double)poly[j].X * poly[i].Y);
}
x_temp += (double)(poly[i].X + poly[0].X) * ((double)poly[i].X * poly[0].Y - (double)poly[0].X * poly[i].Y);
y_temp += (double)(poly[i].Y + poly[0].Y) * ((double)poly[i].X * poly[0].Y - (double)poly[0].X * poly[i].Y);
return IntPoint(x_temp / (6 * area), y_temp / (6 * area));
}
//------------------------------------------------------------------------------
double Area(const OutRec &outRec)
{
@ -3895,10 +3914,10 @@ double DistanceFromLineSqrd(
const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2)
{
//The equation of a line in general form (Ax + By + C = 0)
//given 2 points (x¹,y¹) & (x²,y²) is ...
//(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0
//A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹
//perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²)
//given 2 points (x<EFBFBD>,y<>) & (x<>,y<>) is ...
//(y<EFBFBD> - y<>)x + (x<> - x<>)y + (y<> - y<>)x<> - (x<> - x<>)y<> = 0
//A = (y<EFBFBD> - y<>); B = (x<> - x<>); C = (y<> - y<>)x<> - (x<> - x<>)y<>
//perpendicular distance of point (x<EFBFBD>,y<>) = (Ax<41> + By<42> + C)/Sqrt(A<> + B<>)
//see http://en.wikipedia.org/wiki/Perpendicular_distance
double A = double(ln1.Y - ln2.Y);
double B = double(ln2.X - ln1.X);

View File

@ -184,6 +184,7 @@ private:
};
double Area(const Path &poly);
IntPoint Centroid(const Path& poly, double area);
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
int PointInPolygon(const IntPoint &pt, const Path &path);

View File

@ -524,6 +524,22 @@ T _clipper_do(const ClipperLib::ClipType clipType,
return retval;
}
bool test_path(const ClipperLib::Path &path) {
double area = std::abs(ClipperLib::Area(path));
// get highest dist, but as it's n² in complexity, i use 2*dist to center wich is 2n in complexity
ClipperLib::cInt max_dist_sqrd = 0;
ClipperLib::IntPoint centroid = ClipperLib::Centroid(path, area);
for (const ClipperLib::IntPoint& pt : path) {
// &0x3FFFFFFF to let (dx * dx + dy * dy) be storable into a int64
ClipperLib::cInt dx = (pt.X - centroid.X) & 0x3FFFFFFF;
ClipperLib::cInt dy = (pt.Y - centroid.Y) & 0x3FFFFFFF;
ClipperLib::cInt dist_sqrd = (dx * dx + dy * dy);
max_dist_sqrd = std::max(max_dist_sqrd, dist_sqrd);
}
return (area < (SCALED_EPSILON + SCALED_EPSILON) * std::sqrt(max_dist_sqrd));
}
// Fix of #117: A large fractal pyramid takes ages to slice
// The Clipper library has difficulties processing overlapping polygons.
// Namely, the function ClipperLib::JoinCommonEdges() has potentially a terrible time complexity if the output
@ -554,6 +570,24 @@ inline ClipperLib::PolyTree _clipper_do_polytree2(const ClipperLib::ClipType cli
clipper.AddPaths(input_subject, ClipperLib::ptSubject, true);
ClipperLib::PolyTree retval;
clipper.Execute(ClipperLib::ctUnion, retval, fillType, fillType);
// if safety_offset_, remove too small polygons & holes
if (safety_offset_)
for (int idx_poly = 0; idx_poly < retval.ChildCount(); ++idx_poly) {
ClipperLib::PolyNode* ex_polygon = retval.Childs[idx_poly];
if (test_path(ex_polygon->Contour)) {
retval.Childs.erase(retval.Childs.begin() + idx_poly);
--idx_poly;
} else {
for (int i = 0; i < ex_polygon->ChildCount(); ++i)
{
if (test_path(ex_polygon->Childs[i]->Contour)) {
ex_polygon->Childs.erase(ex_polygon->Childs.begin() + i);
--i;
}
}
}
}
return retval;
}