diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 7c1c4f3c63..ecc03e3dbc 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ public: float len; const ExtrusionEntity *origin_entity; - bool support_point_generated = false; + std::optional support_point_generated = {}; float form_quality = 1.0f; float curled_up_height = 0.0f; @@ -81,10 +82,6 @@ auto get_b(ExtrusionLine &&l) { return l.b; } namespace SupportSpotsGenerator { -SupportPoint::SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec2f &direction) - : position(position), force(force), spot_radius(spot_radius), direction(direction) -{} - using LD = AABBTreeLines::LinesDistancer; struct SupportGridFilter @@ -270,21 +267,27 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; curr_point.distance *= sign; + SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; + if (entity->role().is_bridge() && !entity->role().is_perimeter()) { + potential_cause = std::abs(curr_point.curvature) > 0.1 ? SupportPointCause::FloatingBridgeAnchor : SupportPointCause::LongBridge; + } + float max_bridge_len = params.bridge_distance / - ((1.0f + std::abs(curr_point.curvature)) * (1.0f + std::abs(curr_point.curvature))); + ((1.0f + std::abs(curr_point.curvature)) * (1.0f + std::abs(curr_point.curvature)) * + (1.0f + std::abs(curr_point.curvature))); if (curr_point.distance > 2.0f * flow_width) { line_out.form_quality = 0.8f; bridged_distance += line_len; if (bridged_distance > max_bridge_len) { - line_out.support_point_generated = true; + line_out.support_point_generated = potential_cause; bridged_distance = 0.0f; } } else if (curr_point.distance > flow_width * (1.0 + std::clamp(curr_point.curvature, -0.30f, 0.20f))) { bridged_distance += line_len; line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f; if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { - line_out.support_point_generated = true; + line_out.support_point_generated = potential_cause; line_out.form_quality = 0.5f; bridged_distance = 0.0f; } @@ -349,11 +352,13 @@ public: Vec3f sticking_centroid_accumulator = Vec3f::Zero(); Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); float sticking_second_moment_of_area_covariance_accumulator{}; + bool connected_to_bed = false; ObjectPart() = default; void add(const ObjectPart &other) { + this->connected_to_bed = this->connected_to_bed || other.connected_to_bed; this->volume_centroid_accumulator += other.volume_centroid_accumulator; this->volume += other.volume; this->sticking_area += other.sticking_area; @@ -416,7 +421,7 @@ public: return elastic_section_modulus; } - float is_stable_while_extruding(const SliceConnection &connection, + std::tuple is_stable_while_extruding(const SliceConnection &connection, const ExtrusionLine &extruded_line, const Vec3f &extreme_point, float layer_z, @@ -434,7 +439,7 @@ public: // section for bed calculations { - if (this->sticking_area < EPSILON) return 1.0f; + if (this->sticking_area < EPSILON) return {1.0f, SupportPointCause::UnstableFloatingPart}; Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; float bed_yield_torque = -compute_elastic_section_modulus(line_dir, extreme_point, this->sticking_centroid_accumulator, @@ -475,16 +480,19 @@ public: BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << bed_total_torque << " layer_z: " << layer_z; #endif - if (bed_total_torque > 0) return bed_total_torque / bed_conflict_torque_arm; + if (bed_total_torque > 0) { + return {bed_total_torque / bed_conflict_torque_arm, + (this->connected_to_bed ? SupportPointCause::SeparationFromBed : SupportPointCause::UnstableFloatingPart)}; + } } // section for weak connection calculations { - if (connection.area < EPSILON) return 1.0f; + if (connection.area < EPSILON) return {1.0f, SupportPointCause::UnstableFloatingPart}; Vec3f conn_centroid = connection.centroid_accumulator / connection.area; - if (layer_z - conn_centroid.z() < 3.0f) { return -1.0f; } + if (layer_z - conn_centroid.z() < 3.0f) { return {-1.0f, SupportPointCause::WeakObjectPart}; } float conn_yield_torque = compute_elastic_section_modulus(line_dir, extreme_point, connection.centroid_accumulator, connection.second_moment_of_area_accumulator, connection.second_moment_of_area_covariance_accumulator, @@ -514,7 +522,7 @@ public: BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << conn_total_torque << " layer_z: " << layer_z; #endif - return conn_total_torque / conn_conflict_torque_arm; + return {conn_total_torque / conn_conflict_torque_arm, SupportPointCause::WeakObjectPart}; } } }; @@ -538,6 +546,7 @@ std::tuple build_object_part_from_slice(const LayerSlice &sli new_object_part.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), slice_z) * volume; if (l->bottom_z() < EPSILON) { // layer attached on bed + new_object_part.connected_to_bed = true; float sticking_area = line.len * flow_width; new_object_part.sticking_area += sticking_area; Vec2f middle = Vec2f((line.a + line.b) / 2.0f); @@ -731,24 +740,25 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance // Function that is used when new support point is generated. It will update the ObjectPart stability, weakest conneciton info, // and the support presence grid and add the point to the issues. auto reckon_new_support_point = [&part, &weakest_conn, &supp_points, &supports_presence_grid, ¶ms, - &layer_idx](const Vec3f &support_point, float force, const Vec2f &dir) { + &layer_idx](SupportPointCause cause, const Vec3f &support_point, float force, + const Vec2f &dir) { // if position is taken and point is for global stability (force > 0) or we are too close to the bed, do not add - // This allows local support points (e.g. bridging) to be generated densely + // This allows local support points (e.g. bridging) to be generated densely if ((supports_presence_grid.position_taken(support_point) && force > 0) || layer_idx <= 1) { return; } float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); - // add the stability effect of the point only if the spot is not taken, so that the densely created local support points do not add - // unrealistic amount of stability to the object (due to overlaping of local support points) + // add the stability effect of the point only if the spot is not taken, so that the densely created local support points do + // not add unrealistic amount of stability to the object (due to overlaping of local support points) if (!(supports_presence_grid.position_taken(support_point))) { part.add_support_point(support_point, area); } float radius = params.support_points_interface_radius; - supp_points.emplace_back(support_point, force, radius, dir); + supp_points.emplace_back(cause, support_point, force, radius, dir); supports_presence_grid.take_position(support_point); - + // The support point also increases the stability of the weakest connection of the object, which should be reflected if (weakest_conn.area > EPSILON) { // Do not add it to the weakest connection if it is not valid - does not exist weakest_conn.area += area; @@ -768,9 +778,11 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx]; if (entity->role() == ExtrusionRole::BridgeInfill) { for (const ExtrusionLine &bridge : - check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines,prev_layer_boundary, params)) { - if (bridge.support_point_generated) { - reckon_new_support_point(create_support_point_position(bridge.b), -EPSILON, Vec2f::Zero()); + check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary, + params)) { + if (bridge.support_point_generated.has_value()) { + reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b), + -EPSILON, Vec2f::Zero()); } } } @@ -783,10 +795,13 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance std::vector perims = check_extrusion_entity_stability(entity, perimeter_region, prev_layer_ext_perim_lines,prev_layer_boundary, params); for (const ExtrusionLine &perim : perims) { - if (perim.support_point_generated) { - reckon_new_support_point(create_support_point_position(perim.b), -EPSILON, Vec2f::Zero()); + if (perim.support_point_generated.has_value()) { + reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), -EPSILON, + Vec2f::Zero()); + } + if (perim.is_external_perimeter()) { + current_slice_ext_perims_lines.push_back(perim); } - if (perim.is_external_perimeter()) { current_slice_ext_perims_lines.push_back(perim); } } } } @@ -795,7 +810,8 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance float unchecked_dist = params.min_distance_between_support_points + 1.0f; for (const ExtrusionLine &line : current_slice_ext_perims_lines) { - if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < 0.3f) || line.len < EPSILON) { + if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < 0.3f) || + line.len < EPSILON) { unchecked_dist += line.len; } else { unchecked_dist = line.len; @@ -803,8 +819,10 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance auto [dist, nidx, nearest_point] = current_slice_lines_distancer.distance_from_lines_extra(pivot_site_search_point); Vec3f support_point = create_support_point_position(nearest_point); - auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, bottom_z, params); - if (force > 0) { reckon_new_support_point(support_point, force, (line.b - line.a).normalized()); } + auto [force, cause] = part.is_stable_while_extruding(weakest_conn, line, support_point, bottom_z, params); + if (force > 0) { + reckon_new_support_point(cause, support_point, force, (line.b - line.a).normalized()); + } } } current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(), @@ -827,13 +845,18 @@ void debug_export(SupportPoints support_points, std::string file_name) } for (size_t i = 0; i < support_points.size(); ++i) { - if (support_points[i].force <= 0) { - fprintf(fp, "v %f %f %f %f %f %f\n", support_points[i].position(0), support_points[i].position(1), - support_points[i].position(2), 0.0, 1.0, 0.0); - } else { - fprintf(fp, "v %f %f %f %f %f %f\n", support_points[i].position(0), support_points[i].position(1), - support_points[i].position(2), 1.0, 0.0, 0.0); + Vec3f color{1.0f, 1.0f, 1.0f}; + switch (support_points[i].cause) { + case SupportPointCause::FloatingBridgeAnchor: color = {0.863281f, 0.109375f, 0.113281f}; break; //RED + case SupportPointCause::LongBridge: color = {0.960938f, 0.90625f, 0.0625f}; break; // YELLOW + case SupportPointCause::FloatingExtrusion: color = {0.921875f, 0.515625f, 0.101563f}; break; // ORANGE + case SupportPointCause::SeparationFromBed: color = {0.0f, 1.0f, 0.0}; break; // GREEN + case SupportPointCause::UnstableFloatingPart: color = {0.105469f, 0.699219f, 0.84375f}; break; // BLUE + case SupportPointCause::WeakObjectPart: color = {0.609375f, 0.210938f, 0.621094f}; break; // PURPLE } + + fprintf(fp, "v %f %f %f %f %f %f\n", support_points[i].position(0), support_points[i].position(1), + support_points[i].position(2), color[0], color[1], color[2]); } fclose(fp); @@ -841,9 +864,6 @@ void debug_export(SupportPoints support_points, std::string file_name) } #endif -// std::vector quick_search(const PrintObject *po, const Params ¶ms) { -// return {}; -// } SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms) { SupportPoints supp_points = check_stability(po, cancel_func, params); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 8e8983ac15..b36566b04d 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -35,7 +35,6 @@ struct Params { const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 1.5f; // mm - const float connections_min_considerable_area = 1.5f; //mm^2 const float min_distance_to_allow_local_supports = 1.0f; //mm std::string filament_type; @@ -52,6 +51,8 @@ struct Params { return 0.018 * 1e6; } else if (filament_type == "PET" || filament_type == "PETG") { return 0.3 * 1e6; + } else if (filament_type == "ABS" || filament_type == "ASA") { + return 0.1 * 1e6; //TODO do measurements } else { //PLA default value - defensive approach, PLA has quite low adhesion return 0.018 * 1e6; } @@ -63,30 +64,48 @@ struct Params { } }; -// The support points are generated for two reasons: -// 1. Local extrusion support for extrusions that are printed in the air and would not +enum class SupportPointCause { + LongBridge, // point generated on bridge extrusion longer than the allowed length + FloatingBridgeAnchor, // point generated on unsupported bridge endpoint + FloatingExtrusion, // point generated on extrusion that does not hold on its own - huge overhangs + SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too low and there is risk of separation (brim may help) + UnstableFloatingPart, // point generated for object parts not connected to the bed, holded only by the other support points (brim will not help here) + WeakObjectPart // point generated when some part of the object is too weak to hold the upper part and may break (imagine hourglass) + }; + +// The support points can be sorted into two groups +// 1. Local extrusion support for extrusions that are printed in the air and would not // withstand on their own (too long bridges, sharp turns in large overhang, concave bridge holes, etc.) // These points have negative force (-EPSILON) and Vec2f::Zero() direction -// The algorithm still expects that these points will be supported and accounts for them in the global stability check -// 2. Global stability support points are generated at each spot, where the algorithm detects that extruding the current line -// may cause separation of the object part from the bed and/or its support spots or crack in the weak connection of the object parts -// The generated point's direction is the estimated falling direction of the object part, and the force is equal to te difference +// The algorithm still expects that these points will be supported and accounts for them in the global stability check. +// 2. Global stability support points are generated at each spot, where the algorithm detects that extruding the current line +// may cause separation of the object part from the bed and/or its support spots or crack in the weak connection of the object parts. +// The generated point's direction is the estimated falling direction of the object part, and the force is equal to te difference // between forces that destabilize the object (extruder conflicts with curled filament, weight if instable center of mass, bed movements etc) -// and forces that stabilize the object (bed adhesion, other support spots adhesion, weight if stable center of mass) +// and forces that stabilize the object (bed adhesion, other support spots adhesion, weight if stable center of mass). // Note that the force is only the difference - the amount needed to stabilize the object again. -struct SupportPoint { - SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec2f &direction); - bool is_local_extrusion_support() const { return force < 0; } +struct SupportPoint +{ + SupportPoint(SupportPointCause cause, const Vec3f &position, float force, float spot_radius, const Vec2f &direction) + : cause(cause), position(position), force(force), spot_radius(spot_radius), direction(direction) + {} + + bool is_local_extrusion_support() const + { + return cause == SupportPointCause::LongBridge || cause == SupportPointCause::FloatingExtrusion; + } bool is_global_object_support() const { return !is_local_extrusion_support(); } - //position is in unscaled coords. The z coordinate is aligned with the layers bottom_z coordiantes + SupportPointCause cause; // reason why this support point was generated. Used for the user alerts + // position is in unscaled coords. The z coordinate is aligned with the layers bottom_z coordiantes Vec3f position; - // force that destabilizes the object to the point of falling/breaking. It is in g*mm/s^2 units - // values gathered from large XL print: Min : 0 | Max : 18713800 | Average : 1361186 | Median : 329103 + // force that destabilizes the object to the point of falling/breaking. g*mm/s^2 units + // It is valid only for global_object_support. For local extrusion support points, the force is -EPSILON + // values gathered from large XL model: Min : 0 | Max : 18713800 | Average : 1361186 | Median : 329103 // For reference 18713800 is weight of 1.8 Kg object, 329103 is weight of 0.03 Kg - // The final printed object weight was approx 0.5 Kg + // The final sliced object weight was approx 0.5 Kg float force; - // Expected spot size. The support point strength is calculated from the area defined by this value. + // Expected spot size. The support point strength is calculated from the area defined by this value. // Currently equal to the support_points_interface_radius parameter above float spot_radius; // direction of the fall of the object (z part is neglected)