Fixed polygons_simplify() to correctly handle holes.

This fixes some missing tree / organic supports in the middle
of an object surrounded by walls all around.
This commit is contained in:
Vojtech Bubnik 2022-11-18 17:16:47 +01:00
parent a98467f661
commit da00cedc84
3 changed files with 42 additions and 29 deletions

View File

@ -99,9 +99,11 @@ void Polygon::douglas_peucker(double tolerance)
this->points = std::move(p); this->points = std::move(p);
} }
// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
Polygons Polygon::simplify(double tolerance) const Polygons Polygon::simplify(double tolerance) const
{ {
// Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()!
assert(this->is_counter_clockwise());
// repeat first point at the end in order to apply Douglas-Peucker // repeat first point at the end in order to apply Douglas-Peucker
// on the whole polygon // on the whole polygon
Points points = this->points; Points points = this->points;
@ -114,13 +116,6 @@ Polygons Polygon::simplify(double tolerance) const
return simplify_polygons(pp); return simplify_polygons(pp);
} }
void Polygon::simplify(double tolerance, Polygons &polygons) const
{
Polygons pp = this->simplify(tolerance);
polygons.reserve(polygons.size() + pp.size());
polygons.insert(polygons.end(), pp.begin(), pp.end());
}
// Only call this on convex polygons or it will return invalid results // Only call this on convex polygons or it will return invalid results
void Polygon::triangulate_convex(Polygons* polygons) const void Polygon::triangulate_convex(Polygons* polygons) const
{ {
@ -562,6 +557,27 @@ void remove_collinear(Polygons &polys)
remove_collinear(poly); remove_collinear(poly);
} }
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
{
Polygons out;
out.reserve(source_polygons.size());
for (const Polygon &source_polygon : source_polygons) {
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) {
if (! ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
}
}
return out;
}
// Do polygons match? If they match, they must have the same topology, // Do polygons match? If they match, they must have the same topology,
// however their contours may be rotated. // however their contours may be rotated.
bool polygons_match(const Polygon &l, const Polygon &r) bool polygons_match(const Polygon &l, const Polygon &r)

View File

@ -67,8 +67,8 @@ public:
bool on_boundary(const Point &point, double eps) const bool on_boundary(const Point &point, double eps) const
{ return (this->point_projection(point) - point).cast<double>().squaredNorm() < eps * eps; } { return (this->point_projection(point) - point).cast<double>().squaredNorm() < eps * eps; }
// Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()!
Polygons simplify(double tolerance) const; Polygons simplify(double tolerance) const;
void simplify(double tolerance, Polygons &polygons) const;
void densify(float min_length, std::vector<float>* lengths = nullptr); void densify(float min_length, std::vector<float>* lengths = nullptr);
void triangulate_convex(Polygons* polygons) const; void triangulate_convex(Polygons* polygons) const;
Point centroid() const; Point centroid() const;
@ -148,14 +148,7 @@ inline void polygons_append(Polygons &dst, Polygons &&src)
} }
} }
inline Polygons polygons_simplify(const Polygons &polys, double tolerance) Polygons polygons_simplify(const Polygons &polys, double tolerance);
{
Polygons out;
out.reserve(polys.size());
for (const Polygon &p : polys)
polygons_append(out, p.simplify(tolerance));
return out;
}
inline void polygons_rotate(Polygons &polys, double angle) inline void polygons_rotate(Polygons &polys, double angle)
{ {
@ -216,18 +209,22 @@ inline Lines to_lines(const Polygons &polys)
return lines; return lines;
} }
inline Polylines to_polylines(const Polygons &polys) inline Polyline to_polyline(const Polygon &polygon)
{ {
Polylines polylines; Polyline out;
polylines.assign(polys.size(), Polyline()); out.points.reserve(polygon.size() + 1);
size_t idx = 0; out.points.assign(polygon.points.begin(), polygon.points.end());
for (Polygons::const_iterator it = polys.begin(); it != polys.end(); ++ it) { out.points.push_back(polygon.points.front());
Polyline &pl = polylines[idx ++]; return out;
pl.points = it->points; }
pl.points.push_back(it->points.front());
} inline Polylines to_polylines(const Polygons &polygons)
assert(idx == polylines.size()); {
return polylines; Polylines out;
out.reserve(polygons.size());
for (const Polygon &polygon : polygons)
out.emplace_back(to_polyline(polygon));
return out;
} }
inline Polylines to_polylines(Polygons &&polys) inline Polylines to_polylines(Polygons &&polys)