Modified variable offset in the avoid crossing perimeters to not cause scars on thin objects (#7699).
Previously, the minimum contour width was chosen too conservative and, on some thin objects, only allowed minimal (or non) offset. This could result in travels being planned along the outer perimeter. Now, the minimum contour width is chosen much smaller at the start and tested if the variable offset wasn't failed (the outer contour broke up into more parts, more or fewer holes, etc.). If any problem is detected, the variable offset is recalculated with a larger minimum contour width.
This commit is contained in:
parent
7ab39f6255
commit
dc00f0bf98
@ -20,6 +20,8 @@ struct TravelPoint
|
|||||||
Point point;
|
Point point;
|
||||||
// Index of the polygon containing this point. A negative value indicates that the point is not on any border.
|
// Index of the polygon containing this point. A negative value indicates that the point is not on any border.
|
||||||
int border_idx;
|
int border_idx;
|
||||||
|
// simplify_travel() doesn't remove this point.
|
||||||
|
bool do_not_remove = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Intersection
|
struct Intersection
|
||||||
@ -32,6 +34,8 @@ struct Intersection
|
|||||||
Point point;
|
Point point;
|
||||||
// Distance from the first point in the corresponding boundary
|
// Distance from the first point in the corresponding boundary
|
||||||
float distance;
|
float distance;
|
||||||
|
// simplify_travel() doesn't remove this point.
|
||||||
|
bool do_not_remove = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClosestLine
|
struct ClosestLine
|
||||||
@ -207,8 +211,8 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||||||
const ClosestLine &cl_start = start_lines[cl_indices.first];
|
const ClosestLine &cl_start = start_lines[cl_indices.first];
|
||||||
const ClosestLine &cl_end = end_lines[cl_indices.second];
|
const ClosestLine &cl_end = end_lines[cl_indices.second];
|
||||||
std::vector<Intersection> new_intersections;
|
std::vector<Intersection> 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_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)});
|
new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true});
|
||||||
return new_intersections;
|
return new_intersections;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,7 +263,7 @@ static std::vector<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||||||
if (cl_start_idx != std::numeric_limits<size_t>::max()) {
|
if (cl_start_idx != std::numeric_limits<size_t>::max()) {
|
||||||
// If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine.
|
// 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];
|
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 {
|
} else {
|
||||||
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
|
// 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
|
// 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<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||||||
// use the first one, which is the closest one to the start point.
|
// use the first one, which is the closest one to the start point.
|
||||||
size_t start_closest_lines_idx = find_closest_line_with_same_boundary_idx(start_lines, new_intersections, true);
|
size_t start_closest_lines_idx = find_closest_line_with_same_boundary_idx(start_lines, new_intersections, true);
|
||||||
const ClosestLine &cl_start = (start_closest_lines_idx != std::numeric_limits<size_t>::max()) ? start_lines[start_closest_lines_idx] : start_lines.front();
|
const ClosestLine &cl_start = (start_closest_lines_idx != std::numeric_limits<size_t>::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<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||||||
if (cl_end_idx != std::numeric_limits<size_t>::max()) {
|
if (cl_end_idx != std::numeric_limits<size_t>::max()) {
|
||||||
// If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine.
|
// 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];
|
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 {
|
} else {
|
||||||
// Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the
|
// 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
|
// 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<Intersection> extend_for_closest_lines(const std::vector<Inte
|
|||||||
// use the first one, which is the closest one to the end point.
|
// use the first one, which is the closest one to the end point.
|
||||||
size_t end_closest_lines_idx = find_closest_line_with_same_boundary_idx(end_lines, new_intersections, false);
|
size_t end_closest_lines_idx = find_closest_line_with_same_boundary_idx(end_lines, new_intersections, false);
|
||||||
const ClosestLine &cl_end = (end_closest_lines_idx != std::numeric_limits<size_t>::max()) ? end_lines[end_closest_lines_idx] : end_lines.front();
|
const ClosestLine &cl_end = (end_closest_lines_idx != std::numeric_limits<size_t>::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;
|
return new_intersections;
|
||||||
@ -359,16 +363,17 @@ static void export_travel_to_svg(const Polygons &boundary,
|
|||||||
const std::vector<Intersection> &intersections,
|
const std::vector<Intersection> &intersections,
|
||||||
const std::string &path)
|
const std::string &path)
|
||||||
{
|
{
|
||||||
|
coordf_t stroke_width = scale_(0.05);
|
||||||
BoundingBox bbox = get_extents(boundary);
|
BoundingBox bbox = get_extents(boundary);
|
||||||
::Slic3r::SVG svg(path, bbox);
|
::Slic3r::SVG svg(path, bbox);
|
||||||
svg.draw_outline(boundary, "green");
|
svg.draw_outline(boundary, "green", stroke_width);
|
||||||
svg.draw(original_travel, "blue");
|
svg.draw(original_travel, "blue", stroke_width);
|
||||||
svg.draw(result_travel, "red");
|
svg.draw(result_travel, "red", stroke_width);
|
||||||
svg.draw(original_travel.a, "black");
|
svg.draw(original_travel.a, "black", stroke_width);
|
||||||
svg.draw(original_travel.b, "grey");
|
svg.draw(original_travel.b, "grey", stroke_width);
|
||||||
|
|
||||||
for (const Intersection &intersection : intersections)
|
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,
|
static void export_travel_to_svg(const Polygons &boundary,
|
||||||
@ -433,7 +438,8 @@ static std::vector<TravelPoint> simplify_travel(const AvoidCrossingPerimeters::B
|
|||||||
|
|
||||||
visitor.pt_current = ¤t_point;
|
visitor.pt_current = ¤t_point;
|
||||||
|
|
||||||
for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) {
|
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) {
|
if (travel[point_idx_2].point == current_point) {
|
||||||
next = travel[point_idx_2];
|
next = travel[point_idx_2];
|
||||||
point_idx = point_idx_2;
|
point_idx = point_idx_2;
|
||||||
@ -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
|
// 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
|
// 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.
|
// 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
|
// Check if intersection line also exit the boundary polygon
|
||||||
if (it_second_r != it_last_item) {
|
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
|
// Append the farthest intersection into the path
|
||||||
left_idx = intersection_second.line_idx;
|
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);
|
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
|
// Skip intersections in between
|
||||||
it_first = it_second;
|
it_first = it_second;
|
||||||
}
|
}
|
||||||
@ -945,14 +951,22 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid,
|
|||||||
// ExPolygons are handled one by one so returned ExPolygons could intersect.
|
// ExPolygons are handled one by one so returned ExPolygons could intersect.
|
||||||
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
|
static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
|
||||||
{
|
{
|
||||||
double min_contour_width = 2. * offset + SCALED_EPSILON;
|
const std::vector<double> min_contour_width_values = {offset / 2., offset, 2. * offset + SCALED_EPSILON};
|
||||||
double search_radius = 2. * (offset + min_contour_width);
|
|
||||||
ExPolygons ex_poly_result = ex_polygons;
|
ExPolygons ex_poly_result = ex_polygons;
|
||||||
resample_expolygons(ex_poly_result, offset / 2, scaled<double>(0.5));
|
resample_expolygons(ex_poly_result, offset / 2, scaled<double>(0.5));
|
||||||
|
|
||||||
for (ExPolygon &ex_poly : ex_poly_result) {
|
for (ExPolygon &ex_poly : ex_poly_result) {
|
||||||
BoundingBox bbox(get_extents(ex_poly));
|
BoundingBox bbox(get_extents(ex_poly));
|
||||||
bbox.offset(SCALED_EPSILON);
|
bbox.offset(SCALED_EPSILON);
|
||||||
|
|
||||||
|
// Filter out expolygons smaller than 0.1mm^2
|
||||||
|
if (Vec2d bbox_size = bbox.size().cast<double>(); bbox_size.x() * bbox_size.y() < Slic3r::sqr(scale_(0.1f)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
EdgeGrid::Grid grid;
|
EdgeGrid::Grid grid;
|
||||||
grid.set_bbox(bbox);
|
grid.set_bbox(bbox);
|
||||||
grid.create(ex_poly, coord_t(0.7 * search_radius));
|
grid.create(ex_poly, coord_t(0.7 * search_radius));
|
||||||
@ -979,8 +993,14 @@ static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
|
|||||||
|
|
||||||
ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets);
|
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 variable_offset_inner_ex produces empty result, then original ex_polygon is used
|
||||||
if (offset_ex_poly.size() == 1) {
|
if (offset_ex_poly.size() == 1 && offset_ex_poly.front().holes.size() == ex_poly.holes.size()) {
|
||||||
ex_poly = std::move(offset_ex_poly.front());
|
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) {
|
} else if (offset_ex_poly.size() > 1) {
|
||||||
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
|
// fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces
|
||||||
// tiny artefacts polygons, so these artefacts are removed.
|
// tiny artefacts polygons, so these artefacts are removed.
|
||||||
@ -994,6 +1014,8 @@ static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ex_poly = std::move(offset_ex_poly[max_area_idx]);
|
ex_poly = std::move(offset_ex_poly[max_area_idx]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ex_poly_result;
|
return ex_poly_result;
|
||||||
|
Loading…
Reference in New Issue
Block a user