\#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:
parent
e4ccfda0b0
commit
06e602afd3
@ -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) {
|
ExtrusionPaths reconnect_extrusion_paths(const ExtrusionPaths& paths, double limit_distance) {
|
||||||
if (paths.empty()) return paths;
|
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;
|
ExtrusionPaths result;
|
||||||
result.push_back(paths.front());
|
for (auto& ext : connected) {
|
||||||
for (size_t pidx = 1; pidx < paths.size(); pidx++) {
|
result.push_back(std::move(ext.second));
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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;
|
std::vector<ExtrusionPaths> connected_shells;
|
||||||
|
connected_shells.reserve(extra_perims.size());
|
||||||
for (const ExtrusionPaths &ps : extra_perims) {
|
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
|
struct Pidx
|
||||||
{
|
{
|
||||||
size_t shell;
|
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]; };
|
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;
|
std::vector<std::unordered_map<Pidx, std::unordered_set<Pidx, PidxHash>, PidxHash>> dependencies;
|
||||||
for (size_t shell = 0; shell < connected_shells.size(); shell++) {
|
for (size_t shell = 0; shell < connected_shells.size(); shell++) {
|
||||||
dependencies.push_back({});
|
dependencies.push_back({});
|
||||||
@ -719,18 +753,25 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
|
|||||||
std::unordered_set<Pidx, PidxHash> current_dependencies{};
|
std::unordered_set<Pidx, PidxHash> current_dependencies{};
|
||||||
if (shell > 0) {
|
if (shell > 0) {
|
||||||
for (const auto &prev_path : dependencies[shell - 1]) {
|
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_dependencies.insert(prev_path.first);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
current_shell[current_path] = current_dependencies;
|
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{};
|
ExtrusionPaths sorted_paths{};
|
||||||
Pidx npidx = Pidx{size_t(-1), 0};
|
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();
|
double dist = std::numeric_limits<double>::max();
|
||||||
for (size_t shell = 0; shell < dependencies.size(); shell++) {
|
for (size_t shell = 0; shell < dependencies.size(); shell++) {
|
||||||
for (const auto &p : dependencies[shell]) {
|
for (const auto &p : dependencies[shell]) {
|
||||||
if (!p.second.empty()) continue;
|
if (!p.second.empty())
|
||||||
|
continue;
|
||||||
const auto &path = get_path(p.first);
|
const auto &path = get_path(p.first);
|
||||||
double dist_a = (path.first_point() - current_point).cast<double>().squaredNorm();
|
double dist_a = (path.first_point() - current_point).cast<double>().squaredNorm();
|
||||||
if (dist_a < dist) {
|
if (dist_a < dist) {
|
||||||
@ -757,14 +799,21 @@ ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPath
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (next_pidx == npidx) { break; }
|
if (next_pidx == npidx) {
|
||||||
} else { // we have valid next_pidx, add it to the sorted paths, update dependencies, update current point and potentialy set new next_pidx
|
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);
|
ExtrusionPath path = get_path(next_pidx);
|
||||||
if (reverse) { path.reverse(); }
|
if (reverse) {
|
||||||
|
path.reverse();
|
||||||
|
}
|
||||||
sorted_paths.push_back(path);
|
sorted_paths.push_back(path);
|
||||||
current_point = sorted_paths.back().last_point();
|
current_point = sorted_paths.back().last_point();
|
||||||
if (next_pidx.shell < dependencies.size() - 1) {
|
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);
|
dependencies[next_pidx.shell].erase(next_pidx);
|
||||||
// check current and next shell for 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;
|
next_pidx = npidx;
|
||||||
for (size_t shell = current_shell; shell < std::min(current_shell + 2, dependencies.size()); shell++) {
|
for (size_t shell = current_shell; shell < std::min(current_shell + 2, dependencies.size()); shell++) {
|
||||||
for (const auto &p : dependencies[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);
|
const ExtrusionPath &next_path = get_path(p.first);
|
||||||
double dist_a = (next_path.first_point() - current_point).cast<double>().squaredNorm();
|
double dist_a = (next_path.first_point() - current_point).cast<double>().squaredNorm();
|
||||||
if (dist_a < dist) {
|
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;
|
ExtrusionPaths filtered;
|
||||||
filtered.reserve(reconnected.size());
|
filtered.reserve(reconnected.size());
|
||||||
for (ExtrusionPath &p : reconnected) {
|
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;
|
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);
|
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
|
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()) {
|
if (!shrinked.empty()) {
|
||||||
overhang_region.emplace_back();
|
overhang_region.emplace_back();
|
||||||
extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
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;
|
Polylines fills;
|
||||||
ExPolygons gap = shrinked.empty() ? 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);
|
||||||
offset_ex(prev, -overhang_flow.scaled_spacing() * 0.5);
|
|
||||||
|
|
||||||
//gap = expolygons_simplify(gap, overhang_flow.scaled_spacing());
|
|
||||||
for (const ExPolygon &ep : gap) {
|
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()) {
|
if (!fills.empty()) {
|
||||||
fills = intersection_pl(fills, inset_overhang_area);
|
fills = intersection_pl(fills, shrinked_overhang_to_cover);
|
||||||
overhang_region.emplace_back();
|
overhang_region.emplace_back();
|
||||||
extrusion_paths_append(overhang_region.back(), fills, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
extrusion_paths_append(overhang_region.back(), fills, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||||
overhang_flow.width(), overhang_flow.height());
|
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);
|
Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover);
|
||||||
|
if (!perimeter.empty()) {
|
||||||
overhang_region.emplace_back();
|
overhang_region.emplace_back();
|
||||||
extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||||
overhang_flow.width(), overhang_flow.height());
|
overhang_flow.width(), overhang_flow.height());
|
||||||
|
}
|
||||||
|
|
||||||
perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
|
perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing());
|
||||||
perimeter_polygon = union_(perimeter_polygon, anchoring);
|
perimeter_polygon = union_(perimeter_polygon, anchoring);
|
||||||
@ -1006,7 +1058,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
|||||||
|
|
||||||
std::vector<ExtrusionPaths> result{};
|
std::vector<ExtrusionPaths> result{};
|
||||||
for (const std::vector<ExtrusionPaths> &paths : extra_perims) {
|
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
|
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||||
|
@ -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) {
|
if (params.brim_type == BrimType::btOuterAndInner || params.brim_type == BrimType::btOuterOnly) {
|
||||||
Polygon brim_hole = slice_poly.contour;
|
Polygon brim_hole = slice_poly.contour;
|
||||||
brim_hole.reverse();
|
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) {
|
if (params.brim_type == BrimType::btOuterAndInner || params.brim_type == BrimType::btInnerOnly) {
|
||||||
Polygons brim_contours = slice_poly.holes;
|
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(),
|
std::sort(partial_objects.begin(), partial_objects.end(),
|
||||||
[](const PartialObject &left, const PartialObject &right) { return left.volume > right.volume; });
|
[](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) {
|
for (const PartialObject &p : partial_objects) {
|
||||||
if (p.volume > max_volume_part / 200.0f && !p.connected_to_bed) {
|
if (p.volume > max_volume_part / 200.0f && !p.connected_to_bed) {
|
||||||
result.emplace_back(SupportPointCause::UnstableFloatingPart, true);
|
result.emplace_back(SupportPointCause::UnstableFloatingPart, true);
|
||||||
|
Loading…
Reference in New Issue
Block a user