diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 417322584..9041b4446 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -20,6 +20,8 @@ struct TravelPoint Point point; // Index of the polygon containing this point. A negative value indicates that the point is not on any border. int border_idx; + // simplify_travel() doesn't remove this point. + bool do_not_remove = false; }; struct Intersection @@ -32,6 +34,8 @@ struct Intersection Point point; // Distance from the first point in the corresponding boundary float distance; + // simplify_travel() doesn't remove this point. + bool do_not_remove = false; }; struct ClosestLine @@ -207,8 +211,8 @@ static std::vector extend_for_closest_lines(const std::vector new_intersections; - new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}); - new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}); + new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true}); + new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true}); return new_intersections; } } @@ -259,7 +263,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) { // If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine. const ClosestLine &cl_start = start_lines[cl_start_idx]; - new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}; + new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true}; } else { // Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the // vector of intersections. This allows in some cases when it is more than one around ClosestLine start point chose that one which @@ -267,7 +271,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) ? start_lines[start_closest_lines_idx] : start_lines.front(); - new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}); + new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true}); } } @@ -276,7 +280,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) { // If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine. const ClosestLine &cl_end = end_lines[cl_end_idx]; - new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}; + new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true}; } else { // Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the // vector of intersections. This allows in some cases when it is more than one around ClosestLine end point chose that one which @@ -284,7 +288,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) ? end_lines[end_closest_lines_idx] : end_lines.front(); - new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}); + new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true}); } } return new_intersections; @@ -359,16 +363,17 @@ static void export_travel_to_svg(const Polygons &boundary, const std::vector &intersections, const std::string &path) { - BoundingBox bbox = get_extents(boundary); + coordf_t stroke_width = scale_(0.05); + BoundingBox bbox = get_extents(boundary); ::Slic3r::SVG svg(path, bbox); - svg.draw_outline(boundary, "green"); - svg.draw(original_travel, "blue"); - svg.draw(result_travel, "red"); - svg.draw(original_travel.a, "black"); - svg.draw(original_travel.b, "grey"); + svg.draw_outline(boundary, "green", stroke_width); + svg.draw(original_travel, "blue", stroke_width); + svg.draw(result_travel, "red", stroke_width); + svg.draw(original_travel.a, "black", stroke_width); + svg.draw(original_travel.b, "grey", stroke_width); for (const Intersection &intersection : intersections) - svg.draw(intersection.point, "lightseagreen"); + svg.draw(intersection.point, "lightseagreen", stroke_width); } static void export_travel_to_svg(const Polygons &boundary, @@ -433,21 +438,22 @@ static std::vector simplify_travel(const AvoidCrossingPerimeters::B visitor.pt_current = ¤t_point; - for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) { - if (travel[point_idx_2].point == current_point) { - next = travel[point_idx_2]; - point_idx = point_idx_2; - continue; - } + if (!next.do_not_remove) + for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size() && !travel[point_idx_2].do_not_remove; ++point_idx_2) { + if (travel[point_idx_2].point == current_point) { + next = travel[point_idx_2]; + point_idx = point_idx_2; + continue; + } - visitor.pt_next = &travel[point_idx_2].point; - boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); - // Check if deleting point causes crossing a boundary - if (!visitor.intersect) { - next = travel[point_idx_2]; - point_idx = point_idx_2; + visitor.pt_next = &travel[point_idx_2].point; + boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); + // Check if deleting point causes crossing a boundary + if (!visitor.intersect) { + next = travel[point_idx_2]; + point_idx = point_idx_2; + } } - } simplified_path.emplace_back(next); } @@ -566,7 +572,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo // Offset of the polygon's point using get_middle_point_offset is used to simplify the calculation of intersection between the // boundary and the travel. The appended point is translated in the direction of inward normal. This translation ensures that the // appended point will be inside the polygon and not on the polygon border. - result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)}); + result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx), intersection_first.do_not_remove}); // Check if intersection line also exit the boundary polygon if (it_second_r != it_last_item) { @@ -590,7 +596,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo // Append the farthest intersection into the path left_idx = intersection_second.line_idx; right_idx = (intersection_second.line_idx >= (boundaries[intersection_second.border_idx].points.size() - 1)) ? 0 : (intersection_second.line_idx + 1); - result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx)}); + result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx), intersection_second.do_not_remove}); // Skip intersections in between it_first = it_second; } @@ -945,55 +951,71 @@ static std::vector contour_distance(const EdgeGrid::Grid &grid, // ExPolygons are handled one by one so returned ExPolygons could intersect. static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset) { - double min_contour_width = 2. * offset + SCALED_EPSILON; - double search_radius = 2. * (offset + min_contour_width); - ExPolygons ex_poly_result = ex_polygons; + const std::vector min_contour_width_values = {offset / 2., offset, 2. * offset + SCALED_EPSILON}; + ExPolygons ex_poly_result = ex_polygons; resample_expolygons(ex_poly_result, offset / 2, scaled(0.5)); for (ExPolygon &ex_poly : ex_poly_result) { BoundingBox bbox(get_extents(ex_poly)); bbox.offset(SCALED_EPSILON); - EdgeGrid::Grid grid; - grid.set_bbox(bbox); - grid.create(ex_poly, coord_t(0.7 * search_radius)); - std::vector> ex_poly_distances; - precompute_expolygon_distances(ex_poly, ex_poly_distances); + // Filter out expolygons smaller than 0.1mm^2 + if (Vec2d bbox_size = bbox.size().cast(); bbox_size.x() * bbox_size.y() < Slic3r::sqr(scale_(0.1f))) + continue; - std::vector> offsets; - offsets.reserve(ex_poly.holes.size() + 1); - for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) { - const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1]; - assert(poly.is_counter_clockwise() == (idx_contour == 0)); - std::vector distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius); - for (float &distance : distances) { - if (distance < min_contour_width) - distance = 0.f; - else if (distance > min_contour_width + 2. * offset) - distance = - float(offset); - else - distance = - (distance - float(min_contour_width)) / 2.f; - } - offsets.emplace_back(distances); - } + for (const double &min_contour_width : min_contour_width_values) { + const size_t min_contour_width_idx = &min_contour_width - &min_contour_width_values.front(); + const double search_radius = 2. * (offset + min_contour_width); - ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets); - // If variable_offset_inner_ex produces empty result, then original ex_polygon is used - if (offset_ex_poly.size() == 1) { - ex_poly = std::move(offset_ex_poly.front()); - } else if (offset_ex_poly.size() > 1) { - // fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces - // tiny artefacts polygons, so these artefacts are removed. - double max_area = offset_ex_poly.front().area(); - size_t max_area_idx = 0; - for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) { - double area = offset_ex_poly[poly_idx].area(); - if (max_area < area) { - max_area = area; - max_area_idx = poly_idx; + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(ex_poly, coord_t(0.7 * search_radius)); + + std::vector> ex_poly_distances; + precompute_expolygon_distances(ex_poly, ex_poly_distances); + + std::vector> offsets; + offsets.reserve(ex_poly.holes.size() + 1); + for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) { + const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1]; + assert(poly.is_counter_clockwise() == (idx_contour == 0)); + std::vector distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius); + for (float &distance : distances) { + if (distance < min_contour_width) + distance = 0.f; + else if (distance > min_contour_width + 2. * offset) + distance = -float(offset); + else + distance = -(distance - float(min_contour_width)) / 2.f; } + offsets.emplace_back(distances); + } + + ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets); + // If variable_offset_inner_ex produces empty result, then original ex_polygon is used + if (offset_ex_poly.size() == 1 && offset_ex_poly.front().holes.size() == ex_poly.holes.size()) { + ex_poly = std::move(offset_ex_poly.front()); + break; + } else if ((min_contour_width_idx + 1) < min_contour_width_values.size()) { + continue; // Try the next round with bigger min_contour_width. + } else if (offset_ex_poly.size() == 1) { + ex_poly = std::move(offset_ex_poly.front()); + break; + } else if (offset_ex_poly.size() > 1) { + // fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces + // tiny artefacts polygons, so these artefacts are removed. + double max_area = offset_ex_poly.front().area(); + size_t max_area_idx = 0; + for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) { + double area = offset_ex_poly[poly_idx].area(); + if (max_area < area) { + max_area = area; + max_area_idx = poly_idx; + } + } + ex_poly = std::move(offset_ex_poly[max_area_idx]); + break; } - ex_poly = std::move(offset_ex_poly[max_area_idx]); } } return ex_poly_result;