From 06e602afd3a3bc15b229261d0aea41b24a15ce78 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 2 Feb 2023 14:46:48 +0100 Subject: [PATCH] \#9480 Fix weird path connections in Extra Perimeters when gap fill is applied \#9513 \#9489 Fix crash when brim is used - the expansion of very small drops may result in empty polygon, which the support spot generator did not reflect Fix crashes of stability alert checker, when empty print object was passed to it --- src/libslic3r/PerimeterGenerator.cpp | 138 ++++++++++++++++-------- src/libslic3r/SupportSpotsGenerator.cpp | 8 +- 2 files changed, 101 insertions(+), 45 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 1f306614784..bdc51dea609 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -676,25 +676,61 @@ bool paths_touch(const ExtrusionPath &path_one, const ExtrusionPath &path_two, d ExtrusionPaths reconnect_extrusion_paths(const ExtrusionPaths& paths, double limit_distance) { if (paths.empty()) return paths; - ExtrusionPaths result; - result.push_back(paths.front()); - for (size_t pidx = 1; pidx < paths.size(); pidx++) { - if ((result.back().last_point() - paths[pidx].first_point()).cast().squaredNorm() < limit_distance * limit_distance) { - result.back().polyline.points.insert(result.back().polyline.points.end(), paths[pidx].polyline.points.begin(), - paths[pidx].polyline.points.end()); - } else { - result.push_back(paths[pidx]); + + std::unordered_map connected; + connected.reserve(paths.size()); + for (size_t i = 0; i < paths.size(); i++) { + if (!paths[i].empty()) { + connected.emplace(i, paths[i]); } } + + for (size_t a = 0; a < paths.size(); a++) { + if (connected.find(a) == connected.end()) { + continue; + } + ExtrusionPath &base = connected.at(a); + for (size_t b = a + 1; b < paths.size(); b++) { + if (connected.find(b) == connected.end()) { + continue; + } + ExtrusionPath &next = connected.at(b); + if ((base.last_point() - next.first_point()).cast().squaredNorm() < limit_distance * limit_distance) { + base.polyline.append(std::move(next.polyline)); + connected.erase(b); + } else if ((base.last_point() - next.last_point()).cast().squaredNorm() < limit_distance * limit_distance) { + base.polyline.points.insert(base.polyline.points.end(), next.polyline.points.rbegin(), next.polyline.points.rend()); + connected.erase(b); + } else if ((base.first_point() - next.last_point()).cast().squaredNorm() < limit_distance * limit_distance) { + next.polyline.append(std::move(base.polyline)); + base = std::move(next); + base.reverse(); + connected.erase(b); + } else if ((base.first_point() - next.first_point()).cast().squaredNorm() < limit_distance * limit_distance) { + base.reverse(); + base.polyline.append(std::move(next.polyline)); + base.reverse(); + connected.erase(b); + } + } + } + + ExtrusionPaths result; + for (auto& ext : connected) { + result.push_back(std::move(ext.second)); + } + return result; } -ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector &extra_perims, double touch_distance) +ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector &extra_perims, double extrusion_spacing) { std::vector connected_shells; - for (const ExtrusionPaths& ps : extra_perims) { - connected_shells.push_back(reconnect_extrusion_paths(ps, touch_distance)); + connected_shells.reserve(extra_perims.size()); + for (const ExtrusionPaths &ps : extra_perims) { + connected_shells.push_back(reconnect_extrusion_paths(ps, 1.0 * extrusion_spacing)); } + struct Pidx { size_t shell; @@ -708,8 +744,6 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector, PidxHash>> dependencies; for (size_t shell = 0; shell < connected_shells.size(); shell++) { dependencies.push_back({}); @@ -719,29 +753,37 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector current_dependencies{}; if (shell > 0) { for (const auto &prev_path : dependencies[shell - 1]) { - if (paths_touch(get_path(current_path), get_path(prev_path.first), touch_distance)) { + if (paths_touch(get_path(current_path), get_path(prev_path.first), extrusion_spacing * 2.0)) { current_dependencies.insert(prev_path.first); }; } - current_shell[current_path] = current_dependencies; - if (!any_point_found) { - current_point = get_path(current_path).first_point(); - any_point_found = true; - } + } + current_shell[current_path] = current_dependencies; + } + } + + Point current_point{}; + for (const ExtrusionPaths &ps : connected_shells) { + for (const ExtrusionPath &p : ps) { + if (!p.empty()) { + current_point = p.first_point(); + goto first_point_found; } } } +first_point_found: ExtrusionPaths sorted_paths{}; - Pidx npidx = Pidx{size_t(-1), 0}; - Pidx next_pidx = npidx; - bool reverse = false; + Pidx npidx = Pidx{size_t(-1), 0}; + Pidx next_pidx = npidx; + bool reverse = false; while (true) { if (next_pidx == npidx) { // find next pidx to print double dist = std::numeric_limits::max(); for (size_t shell = 0; shell < dependencies.size(); shell++) { for (const auto &p : dependencies[shell]) { - if (!p.second.empty()) continue; + if (!p.second.empty()) + continue; const auto &path = get_path(p.first); double dist_a = (path.first_point() - current_point).cast().squaredNorm(); if (dist_a < dist) { @@ -757,14 +799,21 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector().squaredNorm(); + if (!p.second.empty()) + continue; + const ExtrusionPath &next_path = get_path(p.first); + double dist_a = (next_path.first_point() - current_point).cast().squaredNorm(); if (dist_a < dist) { dist = dist_a; next_pidx = p.first; @@ -789,17 +839,19 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector scaled(5.0)){ + if (dist > scaled(5.0)) { next_pidx = npidx; } } } - ExtrusionPaths reconnected = reconnect_extrusion_paths(sorted_paths, touch_distance); + ExtrusionPaths reconnected = reconnect_extrusion_paths(sorted_paths, extrusion_spacing * 2.0); ExtrusionPaths filtered; filtered.reserve(reconnected.size()); for (ExtrusionPath &p : reconnected) { - if (p.length() > touch_distance) { filtered.push_back(p); } + if (p.length() > 3 * extrusion_spacing) { + filtered.push_back(p); + } } return filtered; @@ -936,7 +988,7 @@ std::tuple, Polygons> generate_extra_perimeters_over perimeter_polygon = intersection(offset(perimeter_polygon, -overhang_flow.scaled_spacing()), expanded_overhang_to_cover); if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width - Polygons shrinked = offset(prev, -0.4 * overhang_flow.scaled_spacing()); + Polygons shrinked = intersection(offset(prev, -0.3 * overhang_flow.scaled_spacing()), expanded_overhang_to_cover); if (!shrinked.empty()) { overhang_region.emplace_back(); extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), @@ -944,15 +996,13 @@ std::tuple, Polygons> generate_extra_perimeters_over } Polylines fills; - ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) : - offset_ex(prev, -overhang_flow.scaled_spacing() * 0.5); + ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) : to_expolygons(shrinked); - //gap = expolygons_simplify(gap, overhang_flow.scaled_spacing()); for (const ExPolygon &ep : gap) { - ep.medial_axis(0.3 * overhang_flow.scaled_width(), overhang_flow.scaled_spacing() * 2.0, &fills); + ep.medial_axis(0.75 * overhang_flow.scaled_width(), 3.0 * overhang_flow.scaled_spacing(), &fills); } if (!fills.empty()) { - fills = intersection_pl(fills, inset_overhang_area); + fills = intersection_pl(fills, shrinked_overhang_to_cover); overhang_region.emplace_back(); extrusion_paths_append(overhang_region.back(), fills, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), overhang_flow.height()); @@ -981,9 +1031,11 @@ std::tuple, Polygons> generate_extra_perimeters_over } } Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover); - overhang_region.emplace_back(); - extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), - overhang_flow.width(), overhang_flow.height()); + if (!perimeter.empty()) { + overhang_region.emplace_back(); + extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), + overhang_flow.width(), overhang_flow.height()); + } perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing()); perimeter_polygon = union_(perimeter_polygon, anchoring); @@ -1006,7 +1058,7 @@ std::tuple, Polygons> generate_extra_perimeters_over std::vector result{}; for (const std::vector &paths : extra_perims) { - result.push_back(sort_and_connect_extra_perimeters(paths, 2.0 * overhang_flow.scaled_spacing())); + result.push_back(sort_and_connect_extra_perimeters(paths, overhang_flow.scaled_spacing())); } #ifdef EXTRA_PERIM_DEBUG_FILES diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 295d3fe0740..33dce04a256 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -659,7 +659,10 @@ std::tuple build_object_part_from_slice(const size_t &slice_i if (params.brim_type == BrimType::btOuterAndInner || params.brim_type == BrimType::btOuterOnly) { Polygon brim_hole = slice_poly.contour; brim_hole.reverse(); - brim.push_back(ExPolygon{expand(slice_poly.contour, scale_(params.brim_width)).front(), brim_hole}); + Polygons c = expand(slice_poly.contour, scale_(params.brim_width)); // For very small polygons, the expand may result in empty vector, even thought the input is correct. + if (!c.empty()) { + brim.push_back(ExPolygon{c.front(), brim_hole}); + } } if (params.brim_type == BrimType::btOuterAndInner || params.brim_type == BrimType::btInnerOnly) { Polygons brim_contours = slice_poly.holes; @@ -1182,7 +1185,8 @@ std::vector> gather_issues(const SupportPoint std::sort(partial_objects.begin(), partial_objects.end(), [](const PartialObject &left, const PartialObject &right) { return left.volume > right.volume; }); - float max_volume_part = partial_objects.front().volume; + // Object may have zero extrusions and thus no partial objects. (e.g. very tiny object) + float max_volume_part = partial_objects.empty() ? 0.0f : partial_objects.front().volume; for (const PartialObject &p : partial_objects) { if (p.volume > max_volume_part / 200.0f && !p.connected_to_bed) { result.emplace_back(SupportPointCause::UnstableFloatingPart, true);