Skip to content

Commit

Permalink
Merge branch 'CURA-10255_voronoi_fixes' into CURA-10255_add_unit_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
casperlamboo committed Jun 2, 2023
2 parents cb2adfa + 305c6ad commit fc95172
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 78 deletions.
6 changes: 3 additions & 3 deletions include/utils/polygon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
29 changes: 13 additions & 16 deletions src/WallToolPaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,30 +56,27 @@ WallToolPaths::WallToolPaths(const Polygons& outline, const coord_t bead_width_0

const std::vector<VariableWidthLines>& WallToolPaths::generate()
{
const coord_t allowed_distance = settings.get<coord_t>("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<bool>("fill_outline_gaps") ? settings.get<coord_t>("min_feature_size") / 2 - 5 : settings.get<coord_t>("min_wall_line_width") / 2 - 5;
const coord_t epsilon_offset = (allowed_distance / 2) - 1;
const coord_t close_distance = settings.get<bool>("fill_outline_gaps") ? settings.get<coord_t>("min_feature_size") / 2 - 3 : settings.get<coord_t>("min_wall_line_width") / 2 - 3;
const coord_t open_distance = 300;
const AngleRadians transitioning_angle = settings.get<AngleRadians>("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);
prepared_outline = Simplify(settings).polygon(prepared_outline);
PolygonUtils::fixSelfIntersections(epsilon_offset, prepared_outline);
prepared_outline.removeDegenerateVerts();
prepared_outline.removeColinearEdges(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();
// 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);
prepared_outline.removeSmallAreas(small_area_length * small_area_length, false);
prepared_outline.removeCollinearPoints(AngleRadians(0.005));

if (prepared_outline.area() <= 0)
{
Expand Down
120 changes: 61 additions & 59 deletions src/utils/polygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,80 +427,82 @@ 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
if (angle >= M_PI)
{
angle -= M_PI;
}

return angle < max_deviation_angle || angle > M_PI - max_deviation_angle;
};

if (path->size() < 3) {
return;
}
else if (path->size() == 3 && is_collinear((*path)[0], (*path)[1], (*path)[2]))
{
num_removed_in_iteration = 0;
// if the polygon is a triangle and collinear, remove all points
path->clear();
return;
}

std::vector<bool> process_indices(path->size(), true);
ClipperLib::Path new_path;

bool go = true;
while (go)
{
go = false;
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];

const auto& rpath = *path;
const size_t pathlen = rpath.size();
if (pathlen <= 3)
if (is_collinear(prev, pt, next))
{
return;
new_path.pop_back();
}

std::vector<bool> skip_indices(path->size(), false);

ClipperLib::Path new_path;
for (size_t point_idx = 0; point_idx < pathlen; ++point_idx)
else
{
// 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;
}

// 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;
}
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];
while (new_path.size() >= 3)
{
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());
}
else
{
break;
}
}

// 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();
while (new_path.size() >= 3)
{
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();
}
else
{
break;
}
}
while (num_removed_in_iteration > 0);

*path = new_path;
}

void PolygonRef::applyMatrix(const PointMatrix& matrix)
Expand Down

0 comments on commit fc95172

Please sign in to comment.