From d41b20547decc38e03e2e19eeb0ebcf41db909a6 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 11 Apr 2022 17:20:29 +0200 Subject: [PATCH] greatly upgraded the algorithm for support placement - added dynamic splitting of long paths, included flow width of current and previous layer, refactored and renamed parameters --- src/libslic3r/PrintObject.cpp | 19 +- src/libslic3r/SupportableIssuesSearch.cpp | 198 +++++++++++-------- src/libslic3r/SupportableIssuesSearch.hpp | 9 +- src/libslic3r/TriangleSelectorWrapper.cpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 4 + 5 files changed, 147 insertions(+), 88 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5c1d918aea..c9f64cfb3c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -18,6 +18,7 @@ #include "Format/STL.hpp" #include "SupportableIssuesSearch.hpp" #include "TriangleSelectorWrapper.hpp" +#include "format.hpp" #include #include @@ -397,18 +398,26 @@ void PrintObject::ironing() } } + void PrintObject::find_supportable_issues() { if (this->set_started(posSupportableIssuesSearch)) { BOOST_LOG_TRIVIAL(debug) << "Searching supportable issues - start"; - //TODO status number? m_print->set_status(75, L("Searching supportable issues")); if (!this->m_config.support_material) { std::vector problematic_layers = SupportableIssues::quick_search(this); - if (!problematic_layers.empty()){ - //TODO report problems + if (!problematic_layers.empty()) { + std::cout << "Object needs supports" << std::endl; + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Supportable issues found. Consider enabling supports for this object")); + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Supportable issues found. Consider enabling supports for this object")); + for (size_t index = 0; index < std::min(problematic_layers.size(), size_t(4)); ++index) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + format(L("Layer with issues: %1%"), problematic_layers[index] + 1)); + } } } else { SupportableIssues::Issues issues = SupportableIssues::full_search(this); @@ -421,7 +430,7 @@ void PrintObject::find_supportable_issues() TriangleSelectorWrapper selector { model_volume->mesh() }; for (const Vec3f &support_point : issues.supports_nedded) { - selector.enforce_spot(Vec3f(inv_transform.cast() * support_point), 1.0f); + selector.enforce_spot(Vec3f(inv_transform.cast() * support_point), 0.3f); } model_volume->supported_facets.set(selector.selector); @@ -430,7 +439,7 @@ void PrintObject::find_supportable_issues() indexed_triangle_set copy = model_volume->mesh().its; its_transform(copy, obj_transform * model_transformation); its_write_obj(copy, - debug_out_path(("model" + std::to_string(model_volume->id().id) + ".obj").c_str()).c_str()); + debug_out_path("model.obj").c_str()); #endif } } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 51a425a7ed..ea2ba41063 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -5,6 +5,7 @@ #include "tbb/parallel_reduce.h" #include #include +#include #include "libslic3r/Layer.hpp" #include "libslic3r/EdgeGrid.hpp" @@ -39,14 +40,24 @@ struct LayerDescriptor { }; struct EdgeGridWrapper { - EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : - ex_polys(ex_polys) { + EdgeGridWrapper(coord_t edge_width, ExPolygons ex_polys) : + ex_polys(ex_polys), edge_width(edge_width) { - grid.create(this->ex_polys, resolution); + grid.create(this->ex_polys, edge_width); grid.calculate_sdf(); } + + bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { + coordf_t tmp_dist_out; + bool found = grid.signed_distance(point, edge_width, tmp_dist_out); + dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; + return found; + + } + EdgeGrid::Grid grid; ExPolygons ex_polys; + coord_t edge_width; }; #ifdef DEBUG_FILES @@ -99,8 +110,13 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { - ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points }); + if (perimeter->role() == ExtrusionRole::erExternalPerimeter + || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { + Points perimeter_points { }; + perimeter->collect_points(perimeter_points); + assert(perimeter->is_loop()); + perimeter_points.pop_back(); + ex_polygons.push_back(ExPolygon { perimeter_points }); } // ex_perimeter } // perimeter } // ex_entity @@ -109,7 +125,16 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons); } -Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t layer_idx, +coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if (!params.external_perimeter_first + && (role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter)) { + return params.max_ex_perim_unsupported_distance_factor * flow_width; + } else { + return params.max_unsupported_distance_factor * flow_width; + } +} + +Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float slice_z, coordf_t flow_width, const EdgeGridWrapper &supported_grid, @@ -118,74 +143,87 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t la Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add(check_extrusion_entity_stability(e, layer_idx, slice_z, flow_width, supported_grid, params)); + issues.add(check_extrusion_entity_stability(e, slice_z, flow_width, supported_grid, params)); } } else { //single extrusion path, with possible varying parameters - Points points = entity->as_polyline().points; + std::stack points { }; + for (const auto &p : entity->as_polyline().points) { + points.push(p); + } + float unsupported_distance = params.bridge_distance + 1.0f; float curvature = 0; float max_curvature = 0; - Vec2f tmp = unscale(points[0]).cast(); - Vec3f prev_point = Vec3f(tmp.x(), tmp.y(), slice_z); + Vec2f tmp = unscale(points.top()).cast(); + Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); - for (size_t point_index = 0; point_index < points.size(); ++point_index) { - std::cout << "index: " << point_index << " dist: " << unsupported_distance << " curvature: " - << curvature << " max curvature: " << max_curvature << std::endl; + const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, + params); - Vec2f tmp = unscale(points[point_index]).cast(); - Vec3f u_point = Vec3f(tmp.x(), tmp.y(), slice_z); + while (!points.empty()) { + Point point = points.top(); + points.pop(); + Vec2f tmp = unscale(point).cast(); + Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); coordf_t dist_from_prev_layer { 0 }; - if (!supported_grid.grid.signed_distance(points[point_index], flow_width, dist_from_prev_layer)) { - issues.supports_nedded.push_back(u_point); + if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { + issues.supports_nedded.push_back(fpoint); continue; } - constexpr float limit_overlap_factor = 0.5; + if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //unsupported + unsupported_distance += (fpoint - prev_fpoint).norm(); - if (dist_from_prev_layer > flow_width) { //unsupported - std::cout << "index: " << point_index << " unsupported " << std::endl; - unsupported_distance += (u_point - prev_point).norm(); + if (!points.empty()) { + const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); + const Vec2f v2 = unscale(points.top()).cast() - fpoint.head<2>(); + float dot = v1(0) * v2(0) + v1(1) * v2(1); + float cross = v1(0) * v2(1) - v1(1) * v2(0); + float angle = float(atan2(float(cross), float(dot))); + + curvature += angle; + max_curvature = std::max(abs(curvature), max_curvature); + } + + if (unsupported_distance + > params.bridge_distance + / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { + issues.supports_nedded.push_back(fpoint); + + std::cout << "SUPP: " << "udis: " << unsupported_distance << " curv: " << curvature << " max curv: " + << max_curvature << std::endl; + std::cout << "max dist from layer: " << max_allowed_dist_from_prev_layer << " measured dist: " + << dist_from_prev_layer << " FW: " << flow_width << std::endl; + + unsupported_distance = 0; + curvature = 0; + max_curvature = 0; + } } else { - std::cout << "index: " << point_index << " grounded " << std::endl; - unsupported_distance = 0; - } - - std::cout << "index: " << point_index << " dfromprev: " << dist_from_prev_layer << std::endl; - - if (dist_from_prev_layer > flow_width * limit_overlap_factor && point_index < points.size() - 1) { - const Vec2f v1 = (u_point - prev_point).head<2>(); - const Vec2f v2 = unscale(points[point_index + 1]).cast() - u_point.head<2>(); - float dot = v1(0) * v2(0) + v1(1) * v2(1); - float cross = v1(0) * v2(1) - v1(1) * v2(0); - float angle = float(atan2(float(cross), float(dot))); - - std::cout << "index: " << point_index << " angle: " << angle << std::endl; - - curvature += angle; - max_curvature = std::max(abs(curvature), max_curvature); - } - - if (!(dist_from_prev_layer > flow_width * limit_overlap_factor)) { - std::cout << "index: " << point_index << " reset curvature" << std::endl; - max_curvature = 0; - curvature = 0; - } - - if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 7 / PI))) { - issues.supports_nedded.push_back(u_point); unsupported_distance = 0; curvature = 0; max_curvature = 0; } if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { - issues.curling_up.push_back(u_point); - curvature = 0; - max_curvature = 0; + issues.curling_up.push_back(fpoint); + } + + prev_fpoint = fpoint; + + if (!points.empty()) { + Vec2f next = unscale(points.top()).cast(); + Vec2f reverse_v = fpoint.head<2>() - next; + float dist_to_next = reverse_v.norm(); + reverse_v.normalize(); + int new_points_count = dist_to_next / params.bridge_distance; + float step_size = dist_to_next / (new_points_count + 1); + for (int i = 1; i <= new_points_count; ++i) { + points.push(Point::new_scale(Vec2f(next + reverse_v * (i * step_size)))); + } } - prev_point = u_point; } } return issues; @@ -205,7 +243,7 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { case ExtrusionRole::erSolidInfill: return region->flow(FlowRole::frSolidInfill).scaled_width(); default: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + return region->flow(FlowRole::frPerimeter).scaled_width(); } } @@ -223,7 +261,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - issues.add(check_extrusion_entity_stability(perimeter, layer_idx, + issues.add(check_extrusion_entity_stability(perimeter, layer->slice_z, get_flow_width(layer_region, perimeter->role()), supported_grid, params)); } // perimeter @@ -231,7 +269,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { - issues.add(check_extrusion_entity_stability(fill, layer_idx, + issues.add(check_extrusion_entity_stability(fill, layer->slice_z, get_flow_width(layer_region, fill->role()), supported_grid, params)); } @@ -242,9 +280,9 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { - std::cout << "checking ex perimeter " << std::endl; - issues.add(check_extrusion_entity_stability(perimeter, layer_idx, + if (perimeter->role() == ExtrusionRole::erExternalPerimeter + || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { + issues.add(check_extrusion_entity_stability(perimeter, layer->slice_z, get_flow_width(layer_region, perimeter->role()), supported_grid, params)); }; // ex_perimeter @@ -273,7 +311,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { assert(perimeter->is_loop()); descriptor.segments_count++; const ExtrusionLoop *loop = static_cast(perimeter); @@ -327,8 +365,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { [&](tbb::blocked_range r) { for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) { auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index], - false, - params); + false, params); if (!layer_issues.supports_nedded.empty()) { layer_needs_supports[suspicious_index] = true; } @@ -337,8 +374,8 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { std::vector problematic_layers; - for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) { - if (!layer_needs_supports[index]) { + for (size_t index = 0; index < suspicious_layers_indices.size(); ++index) { + if (layer_needs_supports[index]) { problematic_layers.push_back(suspicious_layers_indices[index]); } } @@ -347,26 +384,29 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - Issues issues { }; - for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, true, params); - if (!layer_issues.empty()) { - issues.add(layer_issues); - } - } + size_t layer_count = po->layer_count(); + Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, + [&](tbb::blocked_range r, const Issues &init) { + Issues issues = init; + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + auto layer_issues = check_layer_stability(po, layer_idx, true, params); + if (!layer_issues.empty()) { + issues.add(layer_issues); + } + } + return issues; + }, + [](Issues left, const Issues &right) { + left.add(right); + return left; + } + ); #ifdef DEBUG_FILES - Impl::debug_export(issues, "issues"); + Impl::debug_export(found_issues, "issues"); #endif -// tbb::parallel_for(tbb::blocked_range(0, suspicious_layers_indices.size()), -// [&](tbb::blocked_range r) { -// for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { -// check_layer_stability(po, suspicious_layers_indices[layer_idx], params); -// } -// }); - - return issues; + return found_issues; } } diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 22289c923c..4ff0e32faa 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -8,8 +8,13 @@ namespace Slic3r { namespace SupportableIssues { struct Params { - float bridge_distance = 5.0f; - float limit_curvature = 0.25f; + float bridge_distance = 10.0f; + float limit_curvature = 0.3f; + + bool external_perimeter_first = false; + float max_unsupported_distance_factor = 0.0f; + float max_ex_perim_unsupported_distance_factor = 1.0f; + float bridge_distance_decrease_by_curvature_factor = 5.0f; float perimeter_length_diff_tolerance = 8.0f; float centroid_offset_tolerance = 1.0f; diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index 98d00bc985..02a3b8a1ab 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -16,10 +16,11 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, float radius) { auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, triangles_tree, point, hit_face_index, hit_point); - if (dist < 0 || dist > radius) + if (dist < 0 || dist > radius * radius) { return; + } - std::unique_ptr cursor = std::make_unique(point, point, + std::unique_ptr cursor = std::make_unique(hit_point, point, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index df9cdce56f..f4c21a174b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -39,6 +39,10 @@ private: // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; + + + bool has_backend_supports() const; + void reslice_FDM_supports(bool postpone_error_messages = false) const; };