\#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
This commit is contained in:
PavelMikus 2023-02-02 14:46:48 +01:00
parent e4ccfda0b0
commit 06e602afd3
2 changed files with 101 additions and 45 deletions

View File

@ -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;
std::unordered_map<size_t, ExtrusionPath> 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<double>().squaredNorm() < limit_distance * limit_distance) {
base.polyline.append(std::move(next.polyline));
connected.erase(b);
} else if ((base.last_point() - next.last_point()).cast<double>().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<double>().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<double>().squaredNorm() < limit_distance * limit_distance) {
base.reverse();
base.polyline.append(std::move(next.polyline));
base.reverse();
connected.erase(b);
}
}
}
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<double>().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]);
}
for (auto& ext : connected) {
result.push_back(std::move(ext.second));
}
return result;
}
ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPaths> &extra_perims, double touch_distance)
ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPaths> &extra_perims, double extrusion_spacing)
{
std::vector<ExtrusionPaths> connected_shells;
connected_shells.reserve(extra_perims.size());
for (const ExtrusionPaths &ps : extra_perims) {
connected_shells.push_back(reconnect_extrusion_paths(ps, touch_distance));
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<ExtrusionPath
auto get_path = [&](Pidx i) { return connected_shells[i.shell][i.path]; };
Point current_point{};
bool any_point_found = false;
std::vector<std::unordered_map<Pidx, std::unordered_set<Pidx, PidxHash>, PidxHash>> dependencies;
for (size_t shell = 0; shell < connected_shells.size(); shell++) {
dependencies.push_back({});
@ -719,18 +753,25 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
std::unordered_set<Pidx, PidxHash> 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;
}
}
}
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};
@ -741,7 +782,8 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
double dist = std::numeric_limits<double>::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<double>().squaredNorm();
if (dist_a < dist) {
@ -757,14 +799,21 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
}
}
}
if (next_pidx == npidx) { break; }
} else { // we have valid next_pidx, add it to the sorted paths, update dependencies, update current point and potentialy set new next_pidx
if (next_pidx == npidx) {
break;
}
} else {
// we have valid next_pidx, add it to the sorted paths, update dependencies, update current point and potentialy set new next_pidx
ExtrusionPath path = get_path(next_pidx);
if (reverse) { path.reverse(); }
if (reverse) {
path.reverse();
}
sorted_paths.push_back(path);
current_point = sorted_paths.back().last_point();
if (next_pidx.shell < dependencies.size() - 1) {
for (auto &p : dependencies[next_pidx.shell + 1]) { p.second.erase(next_pidx); }
for (auto &p : dependencies[next_pidx.shell + 1]) {
p.second.erase(next_pidx);
}
}
dependencies[next_pidx.shell].erase(next_pidx);
// check current and next shell for next pidx
@ -773,7 +822,8 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
next_pidx = npidx;
for (size_t shell = current_shell; shell < std::min(current_shell + 2, dependencies.size()); shell++) {
for (const auto &p : dependencies[shell]) {
if (!p.second.empty()) continue;
if (!p.second.empty())
continue;
const ExtrusionPath &next_path = get_path(p.first);
double dist_a = (next_path.first_point() - current_point).cast<double>().squaredNorm();
if (dist_a < dist) {
@ -795,11 +845,13 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
}
}
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<std::vector<ExtrusionPaths>, 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<std::vector<ExtrusionPaths>, 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<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
}
}
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
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<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
std::vector<ExtrusionPaths> result{};
for (const std::vector<ExtrusionPaths> &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

View File

@ -659,7 +659,10 @@ std::tuple<ObjectPart, float> 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<std::pair<SupportPointCause, bool>> 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);