From 15819b36ba6d85be880f2092bc6436eac7121ed2 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 8 May 2023 21:57:27 +0200 Subject: [PATCH 1/3] Fix `removeCollinear` function CURA-10255 --- include/utils/polygon.h | 6 +-- src/WallToolPaths.cpp | 2 +- src/utils/polygon.cpp | 112 +++++++++++++++++++--------------------- 3 files changed, 56 insertions(+), 64 deletions(-) diff --git a/include/utils/polygon.h b/include/utils/polygon.h index 8bd2303429..5c71e713cf 100644 --- a/include/utils/polygon.h +++ b/include/utils/polygon.h @@ -545,7 +545,7 @@ class PolygonRef : public ConstPolygonRef } } - void removeColinearEdges(const AngleRadians max_deviation_angle); + void removeCollinearPoints(const AngleRadians max_deviation_angle); /*! * Removes consecutive line segments with same orientation and changes this polygon. @@ -1145,12 +1145,12 @@ class Polygons Polygons smooth2(int remove_length, int min_area) const; //!< removes points connected to small lines - void removeColinearEdges(const AngleRadians max_deviation_angle = AngleRadians(0.0005)) + void removeCollinearPoints(const AngleRadians max_deviation_angle = AngleRadians(0.0005)) { Polygons& thiss = *this; for (size_t p = 0; p < size(); p++) { - thiss[p].removeColinearEdges(max_deviation_angle); + thiss[p].removeCollinearPoints(max_deviation_angle); if (thiss[p].size() < 3) { remove(p); diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index ab1094915d..bd355f8946 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -74,7 +74,7 @@ const std::vector& WallToolPaths::generate() prepared_outline = Simplify(settings).polygon(prepared_outline); PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); prepared_outline.removeDegenerateVerts(); - prepared_outline.removeColinearEdges(AngleRadians(0.005)); + prepared_outline.removeCollinearPoints(AngleRadians(0.005)); // Removing collinear edges may introduce self intersections, so we need to fix them again PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); prepared_outline.removeDegenerateVerts(); diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 5c8884a6ad..9bc32259d6 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -427,80 +427,72 @@ Polygons ConstPolygonRef::offset(int distance, ClipperLib::JoinType join_type, d return ret; } -void PolygonRef::removeColinearEdges(const AngleRadians max_deviation_angle) +void PolygonRef::removeCollinearPoints(const AngleRadians max_deviation_angle) { - // TODO: Can be made more efficient (for example, use pointer-types for process-/skip-indices, so we can swap them without copy). + auto is_collinear = [max_deviation_angle](const Point& p1, const Point& p2, const Point& p3) { + auto angle = LinearAlg2D::getAngleLeft(p1, p2, p3); - size_t num_removed_in_iteration = 0; - do - { - num_removed_in_iteration = 0; + if (angle >= M_PI) + { + angle -= M_PI; + } - std::vector process_indices(path->size(), true); + return angle < max_deviation_angle || angle > M_PI - max_deviation_angle; + }; - bool go = true; - while (go) - { - go = false; + if (path->size() < 3) { + if (is_collinear((*path)[0], (*path)[1], (*path)[2])) { + path->clear(); + } + return; + } - const auto& rpath = *path; - const size_t pathlen = rpath.size(); - if (pathlen <= 3) - { - return; - } + ClipperLib::Path new_path; - std::vector skip_indices(path->size(), false); + new_path.push_back((*path)[0]); + new_path.push_back((*path)[1]); - ClipperLib::Path new_path; - for (size_t point_idx = 0; point_idx < pathlen; ++point_idx) - { - // Don't iterate directly over process-indices, but do it this way, because there are points _in_ process-indices that should nonetheless be skipped: - if (! process_indices[point_idx]) - { - new_path.push_back(rpath[point_idx]); - continue; - } + for (int i = 2; i < path->size(); i++) { + while (new_path.size() >= 2) { + auto prev = new_path[new_path.size() - 2]; + auto pt = new_path[new_path.size() - 1]; + auto next = (*path)[i]; - // Should skip the last point for this iteration if the old first was removed (which can be seen from the fact that the new first was skipped): - if (point_idx == (pathlen - 1) && skip_indices[0]) - { - skip_indices[new_path.size()] = true; - go = true; - new_path.push_back(rpath[point_idx]); - break; - } + if (is_collinear(prev, pt, next)) + { + new_path.pop_back(); + } + else + { + break; + } + } + new_path.push_back((*path)[i]); + } - const Point& prev = rpath[(point_idx - 1 + pathlen) % pathlen]; - const Point& pt = rpath[point_idx]; - const Point& next = rpath[(point_idx + 1) % pathlen]; + { + auto prev = new_path[new_path.size() - 1]; + auto pt = new_path[0]; + auto next = new_path[1]; - float angle = LinearAlg2D::getAngleLeft(prev, pt, next); // [0 : 2 * pi] - if (angle >= M_PI) {angle -= M_PI;} // map [pi : 2 * pi] to [0 : pi] + if (is_collinear(prev, pt, next)) + { + new_path.erase(new_path.begin()); + } + } - // Check if the angle is within limits for the point to 'make sense', given the maximum deviation. - // If the angle indicates near-parallel segments ignore the point 'pt' - if (angle > max_deviation_angle && angle < M_PI - max_deviation_angle) - { - new_path.push_back(pt); - } - else if (point_idx != (pathlen - 1)) - { - // Skip the next point, since the current one was removed: - skip_indices[new_path.size()] = true; - go = true; - new_path.push_back(next); - ++point_idx; - } - } - *path = new_path; - num_removed_in_iteration += pathlen - path->size(); + { + auto prev = new_path[new_path.size() - 2]; + auto pt = new_path[new_path.size() - 1]; + auto next = new_path[0]; - process_indices.clear(); - process_indices.insert(process_indices.end(), skip_indices.begin(), skip_indices.end()); + if (is_collinear(prev, pt, next)) + { + new_path.pop_back(); } } - while (num_removed_in_iteration > 0); + + *path = new_path; } void PolygonRef::applyMatrix(const PointMatrix& matrix) From 1712c3ff81edbacfed4c94dc809c043166ed826a Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 8 May 2023 23:33:48 +0200 Subject: [PATCH 2/3] Update boost polygon cleanup CURA-10465 --- src/WallToolPaths.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index bd355f8946..534f235bf7 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -56,30 +56,27 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0 const std::vector& WallToolPaths::generate() { - const coord_t allowed_distance = settings.get("meshfix_maximum_deviation"); - // Sometimes small slivers of polygons mess up the prepared_outline. By performing an open-close operation // with half the minimum printable feature size or minimum line width, these slivers are removed, while still // keeping enough information to not degrade the print quality; // These features can't be printed anyhow. See PR CuraEngine#1811 for some screenshots - const coord_t open_close_distance = settings.get("fill_outline_gaps") ? settings.get("min_feature_size") / 2 - 5 : settings.get("min_wall_line_width") / 2 - 5; - const coord_t epsilon_offset = (allowed_distance / 2) - 1; + const coord_t close_distance = settings.get("fill_outline_gaps") ? settings.get("min_feature_size") / 2 - 3 : settings.get("min_wall_line_width") / 2 - 3; + const coord_t open_distance = 300; const AngleRadians transitioning_angle = settings.get("wall_transition_angle"); constexpr coord_t discretization_step_size = MM2INT(0.8); - // Simplify outline for boost::voronoi consumption. Absolutely no self intersections or near-self intersections allowed: - // TODO: Open question: Does this indeed fix all (or all-but-one-in-a-million) cases for manifold but otherwise possibly complex polygons? - Polygons prepared_outline = outline.offset(-open_close_distance).offset(open_close_distance * 2).offset(-open_close_distance); - prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); + // Simplify outline for boost::voronoi consumption. Absolutely no + // - self intersections, + // - near-self intersections or + // - collinear points + // allowed: + Polygons prepared_outline = outline + .offset(open_distance) + .offset(-open_distance - close_distance, ClipperLib::jtRound) + .offset(close_distance, ClipperLib::jtRound); prepared_outline = Simplify(settings).polygon(prepared_outline); - PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); - prepared_outline.removeDegenerateVerts(); + prepared_outline.removeSmallAreas(small_area_length * small_area_length, false); prepared_outline.removeCollinearPoints(AngleRadians(0.005)); - // Removing collinear edges may introduce self intersections, so we need to fix them again - PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline); - prepared_outline.removeDegenerateVerts(); - prepared_outline = prepared_outline.unionPolygons(); - prepared_outline = Simplify(settings).polygon(prepared_outline); if (prepared_outline.area() <= 0) { From 305c6adf47b2db13a6fec9a021500ad6333301b7 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 9 May 2023 07:41:11 +0200 Subject: [PATCH 3/3] Various fixes in `removeCollinearPoints` - if polygon has 3 points, and they are collinear the path is cleared - `removeCollinearPoints` can now remove multiple first/lasts points if needed CURA-1255 --- src/utils/polygon.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/utils/polygon.cpp b/src/utils/polygon.cpp index 9bc32259d6..1e1c2f0a5a 100644 --- a/src/utils/polygon.cpp +++ b/src/utils/polygon.cpp @@ -441,22 +441,22 @@ void PolygonRef::removeCollinearPoints(const AngleRadians max_deviation_angle) }; if (path->size() < 3) { - if (is_collinear((*path)[0], (*path)[1], (*path)[2])) { - path->clear(); - } + return; + } + else if (path->size() == 3 && is_collinear((*path)[0], (*path)[1], (*path)[2])) + { + // if the polygon is a triangle and collinear, remove all points + path->clear(); return; } ClipperLib::Path new_path; - new_path.push_back((*path)[0]); - new_path.push_back((*path)[1]); - - for (int i = 2; i < path->size(); i++) { + for (int i = 0; i < path->size(); i++) { + auto next = (*path)[i]; while (new_path.size() >= 2) { auto prev = new_path[new_path.size() - 2]; auto pt = new_path[new_path.size() - 1]; - auto next = (*path)[i]; if (is_collinear(prev, pt, next)) { @@ -470,6 +470,7 @@ void PolygonRef::removeCollinearPoints(const AngleRadians max_deviation_angle) new_path.push_back((*path)[i]); } + while (new_path.size() >= 3) { auto prev = new_path[new_path.size() - 1]; auto pt = new_path[0]; @@ -479,8 +480,13 @@ void PolygonRef::removeCollinearPoints(const AngleRadians max_deviation_angle) { new_path.erase(new_path.begin()); } + else + { + break; + } } + while (new_path.size() >= 3) { auto prev = new_path[new_path.size() - 2]; auto pt = new_path[new_path.size() - 1]; @@ -490,6 +496,10 @@ void PolygonRef::removeCollinearPoints(const AngleRadians max_deviation_angle) { new_path.pop_back(); } + else + { + break; + } } *path = new_path;