diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 87301278cd..7309c462f7 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -23,10 +23,8 @@ namespace Slic3r { namespace SupportableIssues { void Issues::add(const Issues &layer_issues) { - supports_nedded.insert(supports_nedded.end(), - layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end()); - curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), - layer_issues.curling_up.end()); + supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end()); + curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), layer_issues.curling_up.end()); } bool Issues::empty() const { @@ -56,12 +54,15 @@ struct Cell { }; struct CentroidAccumulator { - //TODO add base height + Polygon convex_hull { }; + Points points { }; + Vec3d accumulated_value { }; + float accumulated_weight { }; + const double base_height { }; - Polygon convex_hull; - Points points; - Vec3d accumulated_value; - float accumulated_weight; + explicit CentroidAccumulator(double base_height) : + base_height(base_height) { + } void calculate_base_hull() { convex_hull = Geometry::convex_hull(points); @@ -72,11 +73,14 @@ struct CentroidAccumulators { std::unordered_map mapping; std::vector acccumulators; - explicit CentroidAccumulators(int count) { - acccumulators.resize(count); - for (int index = 0; index < count; ++index) { - mapping[index - 1] = index; - } + explicit CentroidAccumulators(size_t reserve_count) { + acccumulators.reserve(reserve_count); + } + + CentroidAccumulator& create_accumulator(int id, double base_height) { + mapping[id] = acccumulators.size(); + acccumulators.push_back(CentroidAccumulator { base_height }); + return this->access(id); } CentroidAccumulator& access(int id) { @@ -160,14 +164,12 @@ struct WeightDistributionMatrix { assert(local_cell_coords.z() >= 0); assert(local_cell_coords.z() < local_z_cell_count); - return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() - + local_cell_coords.y() * global_cell_count.x() + - local_cell_coords.x(); + return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() + local_cell_coords.y() * global_cell_count.x() + + local_cell_coords.x(); } Vec3crd get_cell_center(const Vec3i &global_cell_coords) const { - return global_origin + global_cell_coords.cwiseProduct(this->cell_size) - + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); + return global_origin + global_cell_coords.cwiseProduct(this->cell_size) + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); } Cell& access_cell(const Point &p, float print_z) { @@ -210,8 +212,7 @@ struct WeightDistributionMatrix { void merge(const WeightDistributionMatrix &other) { int z_start = std::max(local_z_index_offset, other.local_z_index_offset); - int z_end = std::min(local_z_index_offset + local_z_cell_count, - other.local_z_index_offset + other.local_z_cell_count); + int z_end = std::min(local_z_index_offset + local_z_cell_count, other.local_z_index_offset + other.local_z_cell_count); for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { @@ -226,34 +227,50 @@ struct WeightDistributionMatrix { } void analyze(Issues &issues) { - CentroidAccumulators accumulators(issues.supports_nedded.size() + 1); + CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); - - //TODO split base support points by connectivity!! - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { + int next_island_id = -1; + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, 0)); - if (cell.weight > 0) { - cell.island_id = -1; - Vec3crd cell_center = this->get_cell_center(Vec3i(x, y, local_z_index_offset)); - CentroidAccumulator &acc = accumulators.access(-1); - acc.points.push_back(Point(cell_center.head<2>())); - acc.accumulated_value += cell_center.cast() * cell.weight; - acc.accumulated_weight += cell.weight; + if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { + CentroidAccumulator &acc = accumulators.create_accumulator(next_island_id, 0); + std::set coords_to_check { Vec2i(x, y) }; + while (!coords_to_check.empty()) { + Vec2i current_coords = *coords_to_check.begin(); + coords_to_check.erase(coords_to_check.begin()); + cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); + if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { + cell.island_id = next_island_id; + Vec3crd cell_center = this->get_cell_center( + Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); + acc.points.push_back(Point(cell_center.head<2>())); + acc.accumulated_value += cell_center.cast() * cell.weight; + acc.accumulated_weight += cell.weight; + for (int y_offset = -1; y_offset <= 1; ++y_offset) { + for (int x_offset = -1; x_offset <= 1; ++x_offset) { + if (y_offset != 0 || x_offset != 0) { + coords_to_check.insert(Vec2i(current_coords.x() + x_offset, current_coords.y() + y_offset)); + } + } + } + } + } + next_island_id--; + acc.calculate_base_hull(); } } } - std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), - [](const SupportPoint &left, const SupportPoint &right) { - return left.position.z() < right.position.z(); - }); + std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), [](const SupportPoint &left, const SupportPoint &right) { + return left.position.z() < right.position.z(); + }); for (int index = 0; index < int(issues.supports_nedded.size()); ++index) { - Vec3i local_coords = this->to_local_cell_coords( - this->to_global_cell_coords(issues.supports_nedded[index].position)); + Vec3i local_coords = this->to_local_cell_coords(this->to_global_cell_coords(issues.supports_nedded[index].position)); this->access_cell(local_coords).island_id = index; - accumulators.access(index).points.push_back( - Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); + CentroidAccumulator &acc = accumulators.create_accumulator(index, issues.supports_nedded[index].position.z()); + acc.points.push_back(Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); + acc.calculate_base_hull(); } for (const CurledFilament &curling : issues.curling_up) { @@ -261,8 +278,8 @@ struct WeightDistributionMatrix { } const auto validate_xy_coords = [&](const Vec2i &local_coords) { - return local_coords.x() >= 0 && local_coords.y() >= 0 && - local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y(); + return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() + && local_coords.y() < this->global_cell_count.y(); }; std::unordered_set modified_acc_ids; @@ -283,8 +300,7 @@ struct WeightDistributionMatrix { Cell &under = this->access_cell(Vec3i(x, y, z - 1)); int island_id = std::min(under.island_id, current.island_id); int merging_id = std::max(under.island_id, current.island_id); - if (merging_id != std::numeric_limits::max() - && island_id != merging_id) { + if (merging_id != std::numeric_limits::max() && island_id != merging_id) { accumulators.merge_to(merging_id, island_id); } if (island_id != std::numeric_limits::max()) { @@ -292,8 +308,7 @@ struct WeightDistributionMatrix { modified_acc_ids.insert(current.island_id); } - current.curled_height += under.curled_height - / (2 + std::abs(x_offset) + std::abs(y_offset)); + current.curled_height += under.curled_height / (2 + std::abs(x_offset) + std::abs(y_offset)); } } } @@ -302,22 +317,28 @@ struct WeightDistributionMatrix { //Propagate to accumulators. TODO what to do if no supporter is found? if (current.island_id != std::numeric_limits::max()) { CentroidAccumulator &acc = accumulators.access(current.island_id); - acc.accumulated_value += current.weight * this->get_cell_center( - this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + acc.accumulated_value += current.weight + * this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); acc.accumulated_weight += current.weight; } } } - // check stability of modified centroid accumulators + // check stability of modified centroid accumulators. + // Stability is the amount of work needed to push the object from stable position into unstable. + // This amount of work is proportional to the increase of height of the centroid during toppling. + // image here: https://hgphysics.com/gph/c-forces/2-force-effects/1-moment/stability/ + // better image in Czech here in the first question: https://www.priklady.eu/cs/fyzika/mechanika-tuheho-telesa/stabilita-teles.alej for (int acc_index : modified_acc_ids) { CentroidAccumulator &acc = accumulators.access(acc_index); Vec3d centroid = acc.accumulated_value / acc.accumulated_weight; - - if (!acc.convex_hull.contains(Point(centroid.head<2>().cast()))) { - //TODO :] + //determine signed shortest distance to the convex hull + Point centroid_base_projection = Point(centroid.head<2>().cast()); + double distance_sq = std::numeric_limits::max(); + bool inside = true; + for (Line line : acc.convex_hull.lines()) { + distance_sq = std::min(line.distance_to_squared(centroid_base_projection), distance_sq); } - } } } @@ -348,15 +369,10 @@ struct WeightDistributionMatrix { for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { - Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast< - float>(); + Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); const Cell &cell = access_cell(Vec3i(x, y, z)); if (cell.weight != 0) { - fprintf(fp, "v %f %f %f %f %f %f\n", - center(0), center(1), - center(2), - cell.weight / max_weight, 0.0, 0.0 - ); + fprintf(fp, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), cell.weight / max_weight, 0.0, 0.0); } } } @@ -397,11 +413,8 @@ void debug_export(Issues issues, std::string file_name) { } for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", - issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), - issues.supports_nedded[i].position(2), - 1.0, 0.0, 0.0 - ); + fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), + issues.supports_nedded[i].position(2), 1.0, 0.0, 0.0); } fclose(fp); @@ -415,11 +428,8 @@ void debug_export(Issues issues, std::string file_name) { } for (size_t i = 0; i < issues.curling_up.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", - issues.curling_up[i].position(0), issues.curling_up[i].position(1), - issues.curling_up[i].position(2), - 0.0, 1.0, 0.0 - ); + fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), issues.curling_up[i].position(1), + issues.curling_up[i].position(2), 0.0, 1.0, 0.0); } fclose(fp); } @@ -430,8 +440,7 @@ void debug_export(Issues issues, std::string file_name) { EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { float min_region_flow_width { 1.0f }; for (const auto *region : layer->regions()) { - min_region_flow_width = std::min(min_region_flow_width, - region->flow(FlowRole::frExternalPerimeter).width()); + min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); } std::vector lines; for (const LayerRegion *layer_region : layer->regions()) { @@ -452,26 +461,23 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { //TODO needs revision coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).scaled_width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).scaled_width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).scaled_width(); - default: - return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).scaled_width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).scaled_width(); + default: + return region->flow(FlowRole::frPerimeter).scaled_width(); } } -coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, - const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) - if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) - && (external_perimeters_first) - ) { +coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) && (external_perimeters_first)) { return params.max_first_ex_perim_unsupported_distance_factor * flow_width; } else { return params.max_unsupported_distance_factor * flow_width; @@ -500,19 +506,13 @@ struct SegmentAccumulator { }; -Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, - float print_z, - const LayerRegion *layer_region, - const EdgeGridWrapper &supported_grid, - WeightDistributionMatrix &weight_matrix, - const Params ¶ms) { +Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, const LayerRegion *layer_region, + const EdgeGridWrapper &supported_grid, WeightDistributionMatrix &weight_matrix, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add( - check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, - params)); + issues.add(check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, 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. @@ -535,8 +535,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, Vec3f prev_fpoint = to_vec3f(prev_point); coordf_t flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; - const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, - external_perimters_first, params); + const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, external_perimters_first, + params); while (!points.empty()) { Point point = points.top(); @@ -568,10 +568,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f - + (supports_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { + > params.bridge_distance + / (1.0f + (supports_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(SupportPoint(fpoint)); supports_acc.reset(); } @@ -610,8 +608,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, return issues; } -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, - WeightDistributionMatrix &weight_matrix, const Params ¶ms) { +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, WeightDistributionMatrix &weight_matrix, + const Params ¶ms) { std::cout << "Checking: " << layer_idx << std::endl; if (layer_idx == 0) { // first layer is usually ok @@ -626,18 +624,17 @@ 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->print_z, layer_region, - supported_grid, weight_matrix, params)); + issues.add( + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, weight_matrix, + params)); } // perimeter } // ex_entity 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->print_z, layer_region, - supported_grid, weight_matrix, params)); + if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { + issues.add( + check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, weight_matrix, + params)); } } // fill } // ex_entity @@ -649,9 +646,9 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - issues.add(check_extrusion_entity_stability(perimeter, - layer->print_z, layer_region, - supported_grid, weight_matrix, params)); + issues.add( + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, weight_matrix, + params)); }; // ex_perimeter } // perimeter } // ex_entity @@ -672,23 +669,21 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { 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) { - WeightDistributionMatrix weight_matrix { }; - weight_matrix.init(po, r.begin(), r.end()); + tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { + WeightDistributionMatrix weight_matrix { }; + weight_matrix.init(po, r.begin(), r.end()); - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, - false, weight_matrix, params); - if (!layer_issues.supports_nedded.empty()) { - layer_needs_supports[layer_idx] = true; - } - } + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + auto layer_issues = check_layer_stability(po, layer_idx, false, weight_matrix, params); + if (!layer_issues.supports_nedded.empty()) { + layer_needs_supports[layer_idx] = true; + } + } - matrix_mutex.lock(); - matrix.merge(weight_matrix); - matrix_mutex.unlock(); - }); + matrix_mutex.lock(); + matrix.merge(weight_matrix); + matrix_mutex.unlock(); + }); std::vector problematic_layers; for (size_t index = 0; index < layer_needs_supports.size(); ++index) { @@ -699,8 +694,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { return problematic_layers; } -Issues -full_search(const PrintObject *po, const Params ¶ms) { +Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; WeightDistributionMatrix matrix { };