diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index e60f859302..6510c35468 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -33,12 +33,6 @@ bool Issues::empty() const { namespace Impl { -struct LayerDescriptor { - Vec2f centroid { 0.0f, 0.0f }; - size_t segments_count { 0 }; - float perimeter_length { 0.0f }; -}; - struct EdgeGridWrapper { EdgeGridWrapper(coord_t edge_width, ExPolygons ex_polys) : ex_polys(ex_polys), edge_width(edge_width) { @@ -50,6 +44,7 @@ struct EdgeGridWrapper { 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); + // decrease the distance by half of edge width of previous layer and half of flow width of current layer dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; return found; @@ -115,7 +110,7 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { Points perimeter_points { }; perimeter->collect_points(perimeter_points); assert(perimeter->is_loop()); - perimeter_points.pop_back(); + perimeter_points.pop_back(); // EdgeGrid structure does not like repetition of the first/last point ex_polygons.push_back(ExPolygon { perimeter_points }); } // ex_perimeter } // perimeter @@ -166,17 +161,18 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, issues.add(check_extrusion_entity_stability(e, slice_z, layer_region, supported_grid, params)); } } else { //single extrusion path, with possible varying parameters - + //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. 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; + float unsupported_distance = params.bridge_distance + 1.0f; // initialize unsupported distance with larger than tolerable distance -> + // -> it prevents extruding perimeter start and short loops into air. + float curvature = 0; // current curvature of the unsupported part of the extrusion - it is accumulated value of signed ccw angles of continuously unsupported points. + float max_curvature = 0; // max curvature (in abs value) for the current unsupported segment. Vec2f tmp = unscale(points.top()).cast(); - Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); + Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); // prev point of the path. Initialize with first point. coordf_t flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; @@ -190,30 +186,33 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); coordf_t dist_from_prev_layer { 0 }; - if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { + if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer issues.supports_nedded.push_back(fpoint); - continue; + unsupported_distance = 0; + curvature = 0; + max_curvature = 0; } - if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //unsupported - unsupported_distance += (fpoint - prev_fpoint).norm(); + if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported + unsupported_distance += (fpoint - prev_fpoint).norm(); // for algortihm simplicity, expect that the whole line between prev and current point is unsupported 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))); + float angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master 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))) { + if (unsupported_distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(fpoint); + //DEBUG stuff TODO remove 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: " @@ -229,15 +228,17 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, max_curvature = 0; } + // Estimation of short curvy segments which are not supported -> problems with curling + // Currently the curling issues are ignored if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { issues.curling_up.push_back(fpoint); } prev_fpoint = fpoint; - if (!points.empty()) { + if (!points.empty()) { //oversampling if necessary Vec2f next = unscale(points.top()).cast(); - Vec2f reverse_v = fpoint.head<2>() - next; + Vec2f reverse_v = fpoint.head<2>() - next; // vector from next to current float dist_to_next = reverse_v.norm(); reverse_v.normalize(); int new_points_count = dist_to_next / params.bridge_distance; @@ -259,10 +260,11 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ return {}; } const Layer *layer = po->get_layer(layer_idx); + //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer); Issues issues { }; - if (full_check) { + if (full_check) { // If full checkm check stability of perimeters, gap fills, and bridges. 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) { @@ -282,7 +284,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ } // ex_entity } // region - } else { //check only external perimeters + } else { // If NOT full check, check only external perimeters 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) { @@ -304,85 +306,24 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ std::vector quick_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - std::vector descriptors(po->layer_count()); - tbb::parallel_for(tbb::blocked_range(0, po->layer_count()), + size_t layer_count = po->layer_count(); + std::vector layer_needs_supports(layer_count, false); + tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - const Layer *layer = po->get_layer(layer_idx); - - LayerDescriptor &descriptor = descriptors[layer_idx]; - size_t point_count { 0 }; - - 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 || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - assert(perimeter->is_loop()); - descriptor.segments_count++; - const ExtrusionLoop *loop = static_cast(perimeter); - for (const ExtrusionPath& path : loop->paths) { - Vec2f prev_pos = unscale(path.polyline.last_point()).cast(); - for (size_t p_idx = 0; p_idx < path.polyline.points.size(); ++p_idx) { - point_count++; - Vec2f point_pos = unscale(path.polyline.points[p_idx]).cast(); - descriptor.centroid += point_pos; - descriptor.perimeter_length += (point_pos - prev_pos).norm(); - prev_pos = point_pos; - } //point - } //path - } // ex_perimeter - } // perimeter -} // ex_entity -} // region - - descriptor.centroid /= float(point_count); - - } // layer - } // thread - ); - - std::vector suspicious_layers_indices { }; - - for (size_t desc_idx = 1; desc_idx < descriptors.size(); ++desc_idx) { - const LayerDescriptor &prev = descriptors[desc_idx - 1]; - const LayerDescriptor &descriptor = descriptors[desc_idx]; - if (descriptor.segments_count - prev.segments_count != 0 - || - std::abs(descriptor.perimeter_length - prev.perimeter_length) - > params.perimeter_length_diff_tolerance || - (descriptor.centroid - prev.centroid).norm() > params.centroid_offset_tolerance - ) { - suspicious_layers_indices.push_back(desc_idx); - } -#ifdef DEBUG_FILES - std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: " - << descriptor.perimeter_length << - " centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl; - std::cout << "SIS diff: " << desc_idx << " reg count: " - << (int(descriptor.segments_count) - int(prev.segments_count)) << - " len: " << (descriptor.perimeter_length - prev.perimeter_length) << - " centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl; -#endif - } - - std::vector layer_needs_supports(suspicious_layers_indices.size(), false); - tbb::parallel_for(tbb::blocked_range(0, suspicious_layers_indices.size()), - [&](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], + auto layer_issues = check_layer_stability(po, layer_idx, false, params); if (!layer_issues.supports_nedded.empty()) { - layer_needs_supports[suspicious_index] = true; + layer_needs_supports[layer_idx] = true; } } }); std::vector problematic_layers; - - for (size_t index = 0; index < suspicious_layers_indices.size(); ++index) { + for (size_t index = 0; index < layer_needs_supports.size(); ++index) { if (layer_needs_supports[index]) { - problematic_layers.push_back(suspicious_layers_indices[index]); + problematic_layers.push_back(index); } } return problematic_layers; diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 80127520af..4e8a541f94 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -9,14 +9,12 @@ namespace SupportableIssues { struct Params { float bridge_distance = 10.0f; - float limit_curvature = 0.3f; + float limit_curvature = 0.3f; // used to detect curling issues, but they are currently not considered anyway float max_unsupported_distance_factor = 0.0f; + // allow printing external perimeter in the air to some extent. it hopefully attaches to the internal perimeter. 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; + float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) };