From a1445da22ca63ed71f5a74a3ccd591ff271efefc Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:20:07 +0100 Subject: [PATCH 01/22] Fixed PVB --- resources/profiles/PrusaResearch.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 188e744b6..90486e438 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -4499,6 +4499,8 @@ filament_soluble = 1 filament_type = PVB filament_colour = #FFFF6F filament_spool_weight = 201 +bed_temperature = 75 +first_layer_bed_temperature = 75 slowdown_below_layer_time = 20 filament_ramming_parameters = "120 110 1.74194 1.90323 2.16129 2.48387 2.83871 3.25806 3.83871 4.6129 5.41935 5.96774| 0.05 1.69677 0.45 1.96128 0.95 2.63872 1.45 3.46129 1.95 4.99031 2.45 6.12908 2.95 8.30974 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6" start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.05{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" From b20188c99481e0e926644f96556c107ccf9d467c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 25 Jan 2023 16:13:05 +0100 Subject: [PATCH 02/22] Disable debug benchmarks in sla print --- src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d4023f64d..60c1209e6 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -13,7 +13,7 @@ #include #include - #define SLAPRINT_DO_BENCHMARK +// #define SLAPRINT_DO_BENCHMARK #ifdef SLAPRINT_DO_BENCHMARK #include From c4bd071295cb3d39ebbe9a9b665df46eaf60dd07 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Jan 2023 16:25:42 +0100 Subject: [PATCH 03/22] typo in text --- src/slic3r/GUI/ConfigWizard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 6ba866ac1..224c0e30b 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1751,7 +1751,7 @@ PageBuildVolume::PageBuildVolume(ConfigWizard* parent) : ConfigWizardPage(parent, _L("Build Volume"), _L("Build Volume"), 1) , build_volume(new DiamTextCtrl(this)) { - append_text(_L("Set verctical size of your printer.")); + append_text(_L("Set vertical size of your printer.")); wxString value = "200"; build_volume->SetValue(value); From 821d2391b4224bfa3d8c8c3c50eb7ebb9059f1e7 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 20 Jan 2023 16:37:54 +0100 Subject: [PATCH 04/22] Added SupportPointCause describing the reason for the support point --- src/libslic3r/SupportSpotsGenerator.cpp | 96 +++++++++++++++---------- src/libslic3r/SupportSpotsGenerator.hpp | 51 ++++++++----- 2 files changed, 93 insertions(+), 54 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 7c1c4f3c6..ecc03e3db 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 8e8983ac1..b36566b04 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) From f2deefd1dec7d95b95c41299510f16f9d1a58857 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 23 Jan 2023 13:00:36 +0100 Subject: [PATCH 05/22] Support spot generator improvement - supporting bridges only in one direction --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 56 ++++++++++--------- src/libslic3r/SupportSpotsGenerator.cpp | 65 +++++++++++++++++++--- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 3 files changed, 88 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 5a13e5484..7d8cc4c73 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -122,39 +122,18 @@ std::vector estimate_points_properties(const std::vector

CurvatureEstimator cestim; auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; - std::vector

extrusion_points; - { - if (max_line_length <= 0) { - extrusion_points = input_points; - } else { - extrusion_points.reserve(input_points.size() * 2); - for (size_t i = 0; i + 1 < input_points.size(); i++) { - const P &curr = input_points[i]; - const P &next = input_points[i + 1]; - extrusion_points.push_back(curr); - auto len = maybe_unscale(next - curr).squaredNorm(); - double t = sqrt((max_line_length * max_line_length) / len); - size_t new_point_count = 1.0 / (t + EPSILON); - for (size_t j = 1; j < new_point_count + 1; j++) { - extrusion_points.push_back(curr * (1.0 - j * t) + next * (j * t)); - } - } - extrusion_points.push_back(input_points.back()); - } - } - std::vector points; - points.reserve(extrusion_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); + points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); { - ExtendedPoint start_point{maybe_unscale(extrusion_points.front())}; + ExtendedPoint start_point{maybe_unscale(input_points.front())}; auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); start_point.distance = distance + boundary_offset; start_point.nearest_prev_layer_line = nearest_line; points.push_back(start_point); } - for (size_t i = 1; i < extrusion_points.size(); i++) { - ExtendedPoint next_point{maybe_unscale(extrusion_points[i])}; + for (size_t i = 1; i < input_points.size(); i++) { + ExtendedPoint next_point{maybe_unscale(input_points[i])}; auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); next_point.distance = distance + boundary_offset; next_point.nearest_prev_layer_line = nearest_line; @@ -172,7 +151,7 @@ std::vector estimate_points_properties(const std::vector

if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) { std::vector new_points; - new_points.reserve(points.size() * 2); + new_points.reserve(points.size()*2); new_points.push_back(points.front()); for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) { const ExtendedPoint &curr = points[point_idx]; @@ -201,7 +180,30 @@ std::vector estimate_points_properties(const std::vector

} new_points.push_back(next); } - points = std::move(new_points); + points = new_points; + } + + if (max_line_length > 0) { + std::vector new_points; + new_points.reserve(points.size()*2); + { + for (size_t i = 0; i + 1 < points.size(); i++) { + const ExtendedPoint &curr = points[i]; + const ExtendedPoint &next = points[i + 1]; + new_points.push_back(curr); + double len = (next.position - curr.position).squaredNorm(); + double t = sqrt((max_line_length * max_line_length) / len); + size_t new_point_count = 1.0 / t; + for (size_t j = 1; j < new_point_count + 1; j++) { + Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t); + auto [p_dist, p_near_l, + p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.cast()); + new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); + } + } + new_points.push_back(new_points.back()); + } + points = new_points; } for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index ecc03e3db..73eae069f 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -237,13 +237,66 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end()); } return checked_lines_out; + } else if (entity->role().is_bridge() && !entity->role().is_perimeter()) { + // pure bridges are handled separately, beacuse we need to align the forward and backward direction support points + if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { + return {}; + } + const float flow_width = get_flow_width(layer_region, entity->role()); + std::vector annotated_points = estimate_points_properties(entity->as_polyline().points, + prev_layer_boundary, flow_width, + params.bridge_distance); + + std::vector lines_out; + lines_out.reserve(annotated_points.size()); + float bridged_distance = 0.0f; + + std::optional bridging_dir{}; + + for (size_t i = 0; i < annotated_points.size(); ++i) { + ExtendedPoint &curr_point = annotated_points[i]; + ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i - 1]; + + SupportPointCause potential_cause = std::abs(curr_point.curvature) > 0.1 ? SupportPointCause::FloatingBridgeAnchor : + SupportPointCause::LongBridge; + float line_len = i > 0 ? ((annotated_points[i - 1].position - curr_point.position).norm()) : 0.0f; + Vec2d line_dir = (curr_point.position - prev_point.position).normalized(); + + ExtrusionLine line_out{i > 0 ? annotated_points[i - 1].position.cast() : curr_point.position.cast(), + curr_point.position.cast(), line_len, entity}; + + float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, + params.bridge_distance / + ((1.0f + std::abs(curr_point.curvature)) * (1.0f + std::abs(curr_point.curvature)) * + (1.0f + std::abs(curr_point.curvature)))); + + if (!bridging_dir.has_value() && curr_point.distance > flow_width && line_len > params.bridge_distance * 0.6) { + bridging_dir = (prev_point.position - curr_point.position).normalized(); + } + + if (curr_point.distance > flow_width && potential_cause == SupportPointCause::LongBridge && bridging_dir.has_value() && + bridging_dir->dot(line_dir) < 0.8) { // skip backward direction of bridge - supported by forward points enough + bridged_distance += line_len; + } else if (curr_point.distance > flow_width) { + bridged_distance += line_len; + if (bridged_distance > max_bridge_len) { + bridged_distance = 0.0f; + line_out.support_point_generated = potential_cause; + } + } else { + bridged_distance = 0.0f; + } + + lines_out.push_back(line_out); + } + return lines_out; + } else { // single extrusion path, with possible varying parameters if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; } const float flow_width = get_flow_width(layer_region, entity->role()); - // Compute only unsigned distance - prev_layer_lines can contain unconnected paths, thus the sign of the distance is unreliable std::vector annotated_points = estimate_points_properties(entity->as_polyline().points, prev_layer_lines, flow_width, @@ -268,13 +321,11 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit 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))); + float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, + params.bridge_distance / + ((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; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index b36566b04..9ef3b8637 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -68,7 +68,7 @@ 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) + SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a 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) }; From 41f1b83ae490acdc607aacb38e0aa3c3771bc28c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 23 Jan 2023 16:45:06 +0100 Subject: [PATCH 06/22] raft layers, partial objects memory, params acceleration --- src/libslic3r/PrintObject.cpp | 10 +++-- src/libslic3r/SupportSpotsGenerator.cpp | 56 ++++++++++++++++++------- src/libslic3r/SupportSpotsGenerator.hpp | 47 +++++++++++++++------ 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index dc75fb8a3..83d25aefb 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -427,9 +427,10 @@ void PrintObject::generate_support_spots() BOOST_LOG_TRIVIAL(debug) << "Searching support spots - start"; m_print->set_status(75, L("Searching support spots")); if (!this->shared_regions()->generated_support_points.has_value()) { - PrintTryCancel cancel_func = m_print->make_try_cancel(); - SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values}; - SupportSpotsGenerator::SupportPoints supp_points = SupportSpotsGenerator::full_search(this, cancel_func, params); + PrintTryCancel cancel_func = m_print->make_try_cancel(); + SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, + float(this->print()->m_config.perimeter_acceleration.getFloat())}; + auto [supp_points, partial_objects] = SupportSpotsGenerator::full_search(this, cancel_func, params); this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points}; m_print->throw_if_canceled(); } @@ -468,7 +469,8 @@ void PrintObject::estimate_curled_extrusions() // Estimate curling of support material and add it to the malformaition lines of each layer float support_flow_width = support_material_flow(this, this->config().layer_height).width(); - SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values}; + SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, + float(this->print()->config().perimeter_acceleration.getFloat())}; SupportSpotsGenerator::estimate_supports_malformations(this->support_layers(), support_flow_width, params); SupportSpotsGenerator::estimate_malformations(this->layers(), params); m_print->throw_if_canceled(); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 73eae069f..0b0c1b457 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -579,12 +579,13 @@ public: }; // return new object part and actual area covered by extrusions -std::tuple build_object_part_from_slice(const LayerSlice &slice, const Layer *layer) +std::tuple build_object_part_from_slice(const LayerSlice &slice, const Layer *layer, const Params& params) { ObjectPart new_object_part; float area_covered_by_extrusions = 0; - auto add_extrusions_to_object = [&new_object_part, &area_covered_by_extrusions](const ExtrusionEntity *e, const LayerRegion *region) { + auto add_extrusions_to_object = [&new_object_part, &area_covered_by_extrusions, ¶ms](const ExtrusionEntity *e, + const LayerRegion *region) { float flow_width = get_flow_width(region, e->role()); const Layer *l = region->layer(); float slice_z = l->slice_z; @@ -596,9 +597,9 @@ std::tuple build_object_part_from_slice(const LayerSlice &sli new_object_part.volume += volume; 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 + if (l->id() == params.raft_layers_count) { // layer attached on bed/raft new_object_part.connected_to_bed = true; - float sticking_area = line.len * flow_width; + float sticking_area = line.len * flow_width; new_object_part.sticking_area += sticking_area; Vec2f middle = Vec2f((line.a + line.b) / 2.0f); new_object_part.sticking_centroid_accumulator += sticking_area * to_3d(middle, slice_z); @@ -681,11 +682,12 @@ public: } }; -SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms) +std::tuple check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params ¶ms) { - SupportPoints supp_points{}; + SupportPoints supp_points{}; SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points); ActiveObjectParts active_object_parts{}; + PartialObjects partial_objects{}; LD prev_layer_ext_perim_lines; std::unordered_map prev_slice_idx_to_object_part_mapping; @@ -693,6 +695,14 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance std::unordered_map prev_slice_idx_to_weakest_connection; std::unordered_map next_slice_idx_to_weakest_connection; + auto remember_partial_object = [&active_object_parts, &partial_objects](size_t object_part_id) { + auto object_part = active_object_parts.access(object_part_id); + if (object_part.volume > EPSILON) { + partial_objects.emplace_back(object_part.volume_centroid_accumulator / object_part.volume, object_part.volume, + object_part.connected_to_bed); + } + }; + for (size_t layer_idx = 0; layer_idx < po->layer_count(); ++layer_idx) { cancel_func(); const Layer *layer = po->get_layer(layer_idx); @@ -701,7 +711,7 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { const LayerSlice &slice = layer->lslices_ex.at(slice_idx); - auto [new_part, covered_area] = build_object_part_from_slice(slice, layer); + auto [new_part, covered_area] = build_object_part_from_slice(slice, layer, params); SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer); #ifdef DETAILED_DEBUG_LOGS @@ -730,7 +740,10 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance final_part_id = *parts_ids.begin(); for (size_t part_id : parts_ids) { - if (final_part_id != part_id) { active_object_parts.merge(part_id, final_part_id); } + if (final_part_id != part_id) { + remember_partial_object(part_id); + active_object_parts.merge(part_id, final_part_id); + } } } auto estimate_conn_strength = [bottom_z](const SliceConnection &conn) { @@ -881,11 +894,16 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance } // slice iterations prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines); } // layer iterations - return supp_points; + + for (const auto& active_obj_pair : prev_slice_idx_to_object_part_mapping) { + remember_partial_object(active_obj_pair.second); + } + + return {supp_points, partial_objects}; } #ifdef DEBUG_FILES -void debug_export(SupportPoints support_points, std::string file_name) +void debug_export(const SupportPoints& support_points,const PartialObjects& objects, std::string file_name) { Slic3r::CNumericLocalesSetter locales_setter; { @@ -910,19 +928,29 @@ void debug_export(SupportPoints support_points, std::string file_name) support_points[i].position(2), color[0], color[1], color[2]); } + for (size_t i = 0; i < objects.size(); ++i) { + Vec3f color{1.0f, 0.0f, 1.0f}; + if (objects[i].connected_to_bed) { + color = {1.0f, 0.0f, 0.0f}; + } + fprintf(fp, "v %f %f %f %f %f %f\n", objects[i].centroid(0), objects[i].centroid(1), objects[i].centroid(2), color[0], + color[1], color[2]); + } + fclose(fp); } } #endif -SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms) +std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms) { - SupportPoints supp_points = check_stability(po, cancel_func, params); + auto results = check_stability(po, cancel_func, params); #ifdef DEBUG_FILES - debug_export(supp_points, "issues"); + auto [supp_points, objects] = results; + debug_export(supp_points, objects, "issues"); #endif - return supp_points; + return results; } void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, const Params ¶ms) diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 9ef3b8637..498795993 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -5,31 +5,38 @@ #include "Line.hpp" #include "PrintBase.hpp" #include +#include #include namespace Slic3r { namespace SupportSpotsGenerator { -struct Params { - Params(const std::vector &filament_types) { +struct Params +{ + Params(const std::vector &filament_types, float max_acceleration, size_t raft_layers_count) + : max_acceleration(max_acceleration), raft_layers_count(raft_layers_count) + { if (filament_types.size() > 1) { BOOST_LOG_TRIVIAL(warning) - << "SupportSpotsGenerator does not currently handle different materials properly, only first will be used"; + << "SupportSpotsGenerator does not currently handle different materials properly, only first will be used"; } if (filament_types.empty() || filament_types[0].empty()) { - BOOST_LOG_TRIVIAL(error) - << "SupportSpotsGenerator error: empty filament_type"; + BOOST_LOG_TRIVIAL(error) << "SupportSpotsGenerator error: empty filament_type"; filament_type = std::string("PLA"); } else { filament_type = filament_types[0]; - BOOST_LOG_TRIVIAL(debug) - << "SupportSpotsGenerator: applying filament type: " << filament_type; + BOOST_LOG_TRIVIAL(debug) << "SupportSpotsGenerator: applying filament type: " << filament_type; } } // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] - const float bridge_distance = 12.0f; //mm + const float bridge_distance = 12.0f; // mm + const float max_acceleration; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the + // jerk phase, so the usual machine limits are too low) + const size_t raft_layers_count; + std::string filament_type; + const std::pair malformation_distance_factors = std::pair { 0.4, 1.2 }; const float max_curled_height_factor = 10.0f; @@ -37,9 +44,7 @@ struct Params { const float support_points_interface_radius = 1.5f; // mm const float min_distance_to_allow_local_supports = 1.0f; //mm - std::string filament_type; const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - const float max_acceleration = 9 * 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) const double filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const double material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); @@ -47,6 +52,10 @@ struct Params { // MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface double get_bed_adhesion_yield_strength() const { + if (raft_layers_count > 0) { + return get_support_spots_adhesion_strength(); + } + if (filament_type == "PLA") { return 0.018 * 1e6; } else if (filament_type == "PET" || filament_type == "PETG") { @@ -67,7 +76,7 @@ struct Params { 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 + FloatingExtrusion, // point generated on extrusion that does not hold on its own SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a 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) @@ -118,8 +127,20 @@ struct Malformations { std::vector layers; //for each layer }; -// std::vector quick_search(const PrintObject *po, const Params ¶ms); -SupportPoints full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms); +struct PartialObject +{ + PartialObject(Vec3f centroid, float volume, bool connected_to_bed) + : centroid(centroid), volume(volume), connected_to_bed(connected_to_bed) + {} + + Vec3f centroid; + float volume; + bool connected_to_bed; +}; + +using PartialObjects = std::vector; + +std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms); void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); void estimate_malformations(std::vector &layers, const Params ¶ms); From fb4c1bf61275d986fb3c70e19547fb0f2d7f8b3c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 23 Jan 2023 16:59:45 +0100 Subject: [PATCH 07/22] compilation fix --- src/libslic3r/PrintObject.cpp | 7 +++++-- src/libslic3r/SupportSpotsGenerator.hpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 83d25aefb..95e34bd05 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -429,7 +429,8 @@ void PrintObject::generate_support_spots() if (!this->shared_regions()->generated_support_points.has_value()) { PrintTryCancel cancel_func = m_print->make_try_cancel(); SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, - float(this->print()->m_config.perimeter_acceleration.getFloat())}; + float(this->print()->m_config.perimeter_acceleration.getFloat()), + this->config().raft_layers.getInt()}; auto [supp_points, partial_objects] = SupportSpotsGenerator::full_search(this, cancel_func, params); this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points}; m_print->throw_if_canceled(); @@ -470,7 +471,9 @@ void PrintObject::estimate_curled_extrusions() // Estimate curling of support material and add it to the malformaition lines of each layer float support_flow_width = support_material_flow(this, this->config().layer_height).width(); SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, - float(this->print()->config().perimeter_acceleration.getFloat())}; + float(this->print()->config().perimeter_acceleration.getFloat()), + this->config().raft_layers.getInt() + }; SupportSpotsGenerator::estimate_supports_malformations(this->support_layers(), support_flow_width, params); SupportSpotsGenerator::estimate_malformations(this->layers(), params); m_print->throw_if_canceled(); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 498795993..98dda20e2 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -14,7 +14,7 @@ namespace SupportSpotsGenerator { struct Params { - Params(const std::vector &filament_types, float max_acceleration, size_t raft_layers_count) + Params(const std::vector &filament_types, float max_acceleration, int raft_layers_count) : max_acceleration(max_acceleration), raft_layers_count(raft_layers_count) { if (filament_types.size() > 1) { @@ -34,7 +34,7 @@ struct Params const float bridge_distance = 12.0f; // mm const float max_acceleration; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the // jerk phase, so the usual machine limits are too low) - const size_t raft_layers_count; + const int raft_layers_count; std::string filament_type; const std::pair malformation_distance_factors = std::pair { 0.4, 1.2 }; From a4de5c6553678f7ccc631c1c74c129aae69e699a Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 24 Jan 2023 11:54:16 +0100 Subject: [PATCH 08/22] initial warnings version --- src/libslic3r/PrintObject.cpp | 138 +++++++++++++++++++++--- src/libslic3r/SupportSpotsGenerator.cpp | 7 ++ src/libslic3r/SupportSpotsGenerator.hpp | 7 +- 3 files changed, 134 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 95e34bd05..f163bfbc8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,4 +1,5 @@ #include "Exception.hpp" +#include "Point.hpp" #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" @@ -21,9 +22,13 @@ #include "SupportSpotsGenerator.hpp" #include "TriangleSelectorWrapper.hpp" #include "format.hpp" +#include "libslic3r.h" +#include +#include #include #include +#include #include #include @@ -406,21 +411,6 @@ void PrintObject::ironing() } } - -/* -std::vector problematic_layers = SupportSpotsGenerator::quick_search(this); - 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)); - } - } - */ void PrintObject::generate_support_spots() { if (this->set_started(posSupportSpotsSearch)) { @@ -434,6 +424,124 @@ void PrintObject::generate_support_spots() auto [supp_points, partial_objects] = SupportSpotsGenerator::full_search(this, cancel_func, params); this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points}; m_print->throw_if_canceled(); + + auto check_problems = [&]() { + std::unordered_map sp_by_cause{}; + for (const SupportSpotsGenerator::SupportPoint &sp : supp_points) { + sp_by_cause[sp.cause].push_back(sp); + } + + if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::SeparationFromBed].empty()) { + this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, + L("Object part may break from the bed. Consider adding brim and/or supports.")); + } + + std::reverse(partial_objects.begin(), partial_objects.end()); + std::sort(partial_objects.begin(), partial_objects.end(), + [](const SupportSpotsGenerator::PartialObject &left, const SupportSpotsGenerator::PartialObject &right) { + return left.volume > right.volume; + }); + + float max_volume_part = partial_objects.front().volume; + for (const SupportSpotsGenerator::PartialObject &p : partial_objects) { + if (p.volume > max_volume_part / 1000.0f && !p.connected_to_bed) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Floating object parts detected. Please add supports.")); + return; + } + } + + if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::WeakObjectPart].empty()) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Thin parts of the object may break. Please add supports.")); + return; + } + + if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor].empty()) { + Vec3f last_pos = Vec3f::Zero(); + size_t count = 0; + for (const SupportSpotsGenerator::SupportPoint &sp : + sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor]) { + if ((sp.position - last_pos).squaredNorm() < 9.0f) { + count++; + last_pos = sp.position; + } else { + last_pos = sp.position; + count = 1; + } + if (count > 1) { + this->active_step_add_warning( + PrintStateBase::WarningLevel::CRITICAL, + L("Bridges without supported endpoints will collapse. Please add supports. ")); + break; + } + } + } + + if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongUnsupportedExtrusion].empty()) { + Vec3f last_pos = Vec3f::Zero(); + size_t count = 0; + for (const SupportSpotsGenerator::SupportPoint &sp : + sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongUnsupportedExtrusion]) { + if ((sp.position - last_pos).squaredNorm() < 9.0f) { + count++; + last_pos = sp.position; + } else { + last_pos = sp.position; + count = 1; + } + if (count > 1) { + this->active_step_add_warning( + PrintStateBase::WarningLevel::CRITICAL, + L("Long unsupported extrusions will collapse. Please add supports. ")); + break; + } + } + } + + if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongBridge].empty()) { + this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, + L("There are bridges longer than allowed distance. Consider adding supports. ")); + } + + if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion].empty()) { + Vec3f last_pos = Vec3f::Zero(); + size_t count = 0; + bool small_warning = false; + for (const SupportSpotsGenerator::SupportPoint &sp : + sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion]) { + if ((sp.position - last_pos).squaredNorm() < + params.bridge_distance + EPSILON) { + count++; + last_pos = sp.position; + } else { + if (count > 6) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Object has large part with loose extrusions. Please enable supports. ")); + small_warning = false; + break; + } + if (count > 3) { + small_warning = true; + } + + last_pos = sp.position; + count = 1; + } + } + if (small_warning) { + this->active_step_add_warning( + PrintStateBase::WarningLevel::NON_CRITICAL, + L("Object has parts with loose extrusions and may look bad. Consider enabling supports. ")); + } else if (sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion].size() > max_volume_part / 100) { + this->active_step_add_warning( + PrintStateBase::WarningLevel::NON_CRITICAL, + L("There are many loose surface extrusions on this object. Consider enabling supports. ")); + } + } + }; + + check_problems(); } BOOST_LOG_TRIVIAL(debug) << "Searching support spots - end"; this->set_done(posSupportSpotsSearch); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 0b0c1b457..464ee1d67 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -321,6 +321,12 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit curr_point.distance *= sign; SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; + if (curr_point.distance > flow_width * 5.0) { + if (std::abs(curr_point.curvature) > 0.1) + potential_cause = SupportPointCause::LongUnsupportedExtrusion; + else + potential_cause = SupportPointCause::LongBridge; + } float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, params.bridge_distance / @@ -919,6 +925,7 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje 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::LongUnsupportedExtrusion: color = {0.863281f, 0.109375f, 0.113281f}; break; // RED 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 diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 98dda20e2..56762d544 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -32,8 +32,8 @@ struct Params // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] const float bridge_distance = 12.0f; // mm - const float max_acceleration; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the - // jerk phase, so the usual machine limits are too low) + const float max_acceleration; // mm/s^2 ; max acceleration of object in XY -- should be applicable only to printers with bed slinger, + // however we do not have such info yet. The force is usually small anyway, so not such a big deal to include it everytime const int raft_layers_count; std::string filament_type; @@ -74,9 +74,10 @@ struct Params }; enum class SupportPointCause { - LongBridge, // point generated on bridge extrusion longer than the allowed length + LongBridge, // point generated on bridge and straight perimeter 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 + LongUnsupportedExtrusion, // similar to above, but with large distance to object. This really needs supports. SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a 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) From c31e3ec1a2436b3fda69ad343bc6a45c96faabb0 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 25 Jan 2023 14:18:14 +0100 Subject: [PATCH 09/22] Bugfix in extrusion quality estimator, Refactoring of alerts, rename of autogenerate button --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 2 +- src/libslic3r/PrintObject.cpp | 140 ++++--------------- src/libslic3r/SupportSpotsGenerator.cpp | 111 +++++++++++++-- src/libslic3r/SupportSpotsGenerator.hpp | 12 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 12 +- 5 files changed, 143 insertions(+), 134 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 7d8cc4c73..1525624da 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -201,7 +201,7 @@ std::vector estimate_points_properties(const std::vector

new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); } } - new_points.push_back(new_points.back()); + new_points.push_back(points.back()); } points = new_points; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f163bfbc8..09b35157c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,4 +1,5 @@ #include "Exception.hpp" +#include "KDTreeIndirect.hpp" #include "Point.hpp" #include "Print.hpp" #include "BoundingBox.hpp" @@ -34,6 +35,7 @@ #include #include +#include using namespace std::literals; @@ -425,123 +427,37 @@ void PrintObject::generate_support_spots() this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points}; m_print->throw_if_canceled(); - auto check_problems = [&]() { - std::unordered_map sp_by_cause{}; - for (const SupportSpotsGenerator::SupportPoint &sp : supp_points) { - sp_by_cause[sp.cause].push_back(sp); - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::SeparationFromBed].empty()) { - this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("Object part may break from the bed. Consider adding brim and/or supports.")); - } - - std::reverse(partial_objects.begin(), partial_objects.end()); - std::sort(partial_objects.begin(), partial_objects.end(), - [](const SupportSpotsGenerator::PartialObject &left, const SupportSpotsGenerator::PartialObject &right) { - return left.volume > right.volume; - }); - - float max_volume_part = partial_objects.front().volume; - for (const SupportSpotsGenerator::PartialObject &p : partial_objects) { - if (p.volume > max_volume_part / 1000.0f && !p.connected_to_bed) { - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Floating object parts detected. Please add supports.")); - return; + auto alert_fn = [&](PrintStateBase::WarningLevel level, SupportSpotsGenerator::SupportPointCause cause) { + switch (cause) { + case SupportSpotsGenerator::SupportPointCause::LongBridge: + this->active_step_add_warning(level, L("There are bridges longer than allowed distance. Consider adding supports. ")); + break; + case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: + this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed.")); + break; + case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion: + if (level == PrintStateBase::WarningLevel::CRITICAL) { + this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed.")); + } else { + this->active_step_add_warning(level, L("Some unspported extrusions found. Consider adding supports. ")); } - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::WeakObjectPart].empty()) { + break; + case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: + this->active_step_add_warning(level, L("Object part may break from the bed. Consider adding brim and/or supports.")); + break; + case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: + this->active_step_add_warning(level, L("Floating object parts detected. Supports are needed.")); + break; + case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Thin parts of the object may break. Please add supports.")); - return; - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor].empty()) { - Vec3f last_pos = Vec3f::Zero(); - size_t count = 0; - for (const SupportSpotsGenerator::SupportPoint &sp : - sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor]) { - if ((sp.position - last_pos).squaredNorm() < 9.0f) { - count++; - last_pos = sp.position; - } else { - last_pos = sp.position; - count = 1; - } - if (count > 1) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::CRITICAL, - L("Bridges without supported endpoints will collapse. Please add supports. ")); - break; - } - } - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongUnsupportedExtrusion].empty()) { - Vec3f last_pos = Vec3f::Zero(); - size_t count = 0; - for (const SupportSpotsGenerator::SupportPoint &sp : - sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongUnsupportedExtrusion]) { - if ((sp.position - last_pos).squaredNorm() < 9.0f) { - count++; - last_pos = sp.position; - } else { - last_pos = sp.position; - count = 1; - } - if (count > 1) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::CRITICAL, - L("Long unsupported extrusions will collapse. Please add supports. ")); - break; - } - } - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongBridge].empty()) { - this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("There are bridges longer than allowed distance. Consider adding supports. ")); - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion].empty()) { - Vec3f last_pos = Vec3f::Zero(); - size_t count = 0; - bool small_warning = false; - for (const SupportSpotsGenerator::SupportPoint &sp : - sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion]) { - if ((sp.position - last_pos).squaredNorm() < - params.bridge_distance + EPSILON) { - count++; - last_pos = sp.position; - } else { - if (count > 6) { - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Object has large part with loose extrusions. Please enable supports. ")); - small_warning = false; - break; - } - if (count > 3) { - small_warning = true; - } - - last_pos = sp.position; - count = 1; - } - } - if (small_warning) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - L("Object has parts with loose extrusions and may look bad. Consider enabling supports. ")); - } else if (sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion].size() > max_volume_part / 100) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - L("There are many loose surface extrusions on this object. Consider enabling supports. ")); - } + L("Thin parts of the object may break. Supports are needed.")); + break; } }; - check_problems(); + if (!this->has_support()) { + SupportSpotsGenerator::raise_alerts_for_issues(supp_points, partial_objects, alert_fn); + } } BOOST_LOG_TRIVIAL(debug) << "Searching support spots - end"; this->set_done(posSupportSpotsSearch); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 464ee1d67..7eec832c1 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -37,7 +37,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -// #define DEBUG_FILES +#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -313,19 +313,16 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit curr_point.position.cast(), line_len, entity}; const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : - ExtrusionLine{}; + prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : + ExtrusionLine{}; // correctify the distance sign using slice polygons 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 (curr_point.distance > flow_width * 5.0) { - if (std::abs(curr_point.curvature) > 0.1) - potential_cause = SupportPointCause::LongUnsupportedExtrusion; - else - potential_cause = SupportPointCause::LongBridge; + if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { + potential_cause = SupportPointCause::FloatingExtrusion; } float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, @@ -337,6 +334,14 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit line_out.form_quality = 0.8f; bridged_distance += line_len; if (bridged_distance > max_bridge_len) { + std::cout << "Problem found A: " << std::endl; + std::cout << "bridged_distance: " << bridged_distance << std::endl; + std::cout << "max_bridge_len: " << max_bridge_len << std::endl; + std::cout << "line_out.form_quality: " << line_out.form_quality << std::endl; + std::cout << "curr_point.distance: " << curr_point.distance << std::endl; + std::cout << "curr_point.curvature: " << curr_point.curvature << std::endl; + std::cout << "flow_width: " << flow_width << std::endl; + line_out.support_point_generated = potential_cause; bridged_distance = 0.0f; } @@ -344,6 +349,14 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit 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) { + std::cout << "Problem found B: " << std::endl; + std::cout << "bridged_distance: " << bridged_distance << std::endl; + std::cout << "max_bridge_len: " << max_bridge_len << std::endl; + std::cout << "line_out.form_quality: " << line_out.form_quality << std::endl; + std::cout << "curr_point.distance: " << curr_point.distance << std::endl; + std::cout << "curr_point.curvature: " << curr_point.curvature << std::endl; + std::cout << "flow_width: " << flow_width << std::endl; + line_out.support_point_generated = potential_cause; line_out.form_quality = 0.5f; bridged_distance = 0.0f; @@ -557,7 +570,7 @@ public: params.material_yield_strength; float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); - float conn_weight_torque = conn_weight_arm * weight * (1.0f - conn_centroid.z() / layer_z); + float conn_weight_torque = conn_weight_arm * weight * (1.0f - conn_centroid.z() / layer_z) * (1.0f - conn_centroid.z() / layer_z); float conn_movement_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z()); float conn_movement_torque = movement_force * conn_movement_arm; @@ -925,7 +938,6 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje 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::LongUnsupportedExtrusion: color = {0.863281f, 0.109375f, 0.113281f}; break; // RED 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 @@ -1104,5 +1116,84 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) #endif } +void raise_alerts_for_issues(const SupportPoints &support_points, + PartialObjects &partial_objects, + std::function alert_fn) +{ + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::SeparationFromBed) { + alert_fn(PrintStateBase::WarningLevel::NON_CRITICAL, SupportPointCause::SeparationFromBed); + break; + } + } + + std::reverse(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; }); + + float max_volume_part = partial_objects.front().volume; + for (const PartialObject &p : partial_objects) { + if (p.volume > max_volume_part / 500.0f && !p.connected_to_bed) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::UnstableFloatingPart); + return; + } + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::UnstableFloatingPart) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::UnstableFloatingPart); + return; + } + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::WeakObjectPart) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::WeakObjectPart); + return; + } + } + + std::vector ext_supp_points{}; + ext_supp_points.reserve(support_points.size()); + for (const SupportPoint &sp : support_points) { + switch (sp.cause) { + case SupportPointCause::FloatingBridgeAnchor: + case SupportPointCause::FloatingExtrusion: ext_supp_points.push_back(sp); break; + default: break; + } + } + + auto coord_fn = [&ext_supp_points](size_t idx, size_t dim) { return ext_supp_points[idx].position[dim]; }; + KDTreeIndirect<3, float, decltype(coord_fn)> ext_points_tree{coord_fn, ext_supp_points.size()}; + for (const SupportPoint &sp : ext_supp_points) { + auto cluster = find_nearby_points(ext_points_tree, sp.position, 3.0); + int score = 0; + bool floating_bridge = false; + for (size_t idx : cluster) { + score += ext_supp_points[idx].cause == SupportPointCause::FloatingBridgeAnchor ? 3 : 1; + floating_bridge = floating_bridge || ext_supp_points[idx].cause == SupportPointCause::FloatingBridgeAnchor; + } + if (score > 5) { + if (floating_bridge) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::FloatingBridgeAnchor); + } else { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::FloatingExtrusion); + } + return; + } + } + + if (ext_supp_points.size() > 5) { + alert_fn(PrintStateBase::WarningLevel::NON_CRITICAL, SupportPointCause::FloatingExtrusion); + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::LongBridge) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::LongBridge); + return; + } + } +} + } // namespace SupportSpotsGenerator } // namespace Slic3r diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 56762d544..bd3301874 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -77,7 +77,6 @@ enum class SupportPointCause { LongBridge, // point generated on bridge and straight perimeter 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 - LongUnsupportedExtrusion, // similar to above, but with large distance to object. This really needs supports. SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a 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) @@ -143,10 +142,13 @@ using PartialObjects = std::vector; std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms); -void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); -void estimate_malformations(std::vector &layers, const Params ¶ms); +void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); +void estimate_malformations(std::vector &layers, const Params ¶ms); -} // namespace SupportSpotsGenerator -} +void raise_alerts_for_issues(const SupportPoints &support_points, + PartialObjects &partial_objects, + std::function alert_fn); + +}} // namespace Slic3r::SupportSpotsGenerator #endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 6e12527d5..ec993fcd4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -43,8 +43,8 @@ bool GLGizmoFdmSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; - m_desc["auto_generate"] = _L("Auto-generate supports"); - m_desc["generating"] = _L("Generating supports..."); + m_desc["autopaint"] = _L("Automatic painting"); + m_desc["painting"] = _L("painting..."); m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Brush size") + ": "; @@ -160,9 +160,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::Separator(); if (waiting_for_autogenerated_supports) { - m_imgui->text(m_desc.at("generating")); + m_imgui->text(m_desc.at("painting")); } else { - bool generate = m_imgui->button(m_desc.at("auto_generate")); + bool generate = m_imgui->button(m_desc.at("autopaint")); if (generate) auto_generate(); } @@ -525,12 +525,12 @@ void GLGizmoFdmSupports::auto_generate() }); MessageDialog dlg(GUI::wxGetApp().plater(), - _L("Autogeneration will erase all currently painted areas.") + "\n\n" + + _L("Automatic painting will erase all currently painted areas.") + "\n\n" + _L("Are you sure you want to do it?") + "\n", _L("Warning"), wxICON_WARNING | wxYES | wxNO); if (not_painted || dlg.ShowModal() == wxID_YES) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Automatic painting support points")); int mesh_id = -1.0f; for (ModelVolume *mv : mo->volumes) { if (mv->is_model_part()) { From c09a44779d2094711c60b889d82a3ca8b5239042 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 25 Jan 2023 16:57:52 +0100 Subject: [PATCH 10/22] brim integration into SupportSpotGenerator --- src/libslic3r/ExPolygon.hpp | 4 +- src/libslic3r/PrintObject.cpp | 32 +++++++----- src/libslic3r/SupportSpotsGenerator.cpp | 67 +++++++++++++++++++++---- src/libslic3r/SupportSpotsGenerator.hpp | 11 ++-- 4 files changed, 86 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 98ad9e2e6..83b264803 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -32,8 +32,8 @@ public: ExPolygon& operator=(const ExPolygon &other) = default; ExPolygon& operator=(ExPolygon &&other) = default; - Polygon contour; - Polygons holes; + Polygon contour; //CCW + Polygons holes; //CW void clear() { contour.points.clear(); holes.clear(); } void scale(double factor); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 09b35157c..887caa8bf 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -422,7 +422,8 @@ void PrintObject::generate_support_spots() PrintTryCancel cancel_func = m_print->make_try_cancel(); SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, float(this->print()->m_config.perimeter_acceleration.getFloat()), - this->config().raft_layers.getInt()}; + this->config().raft_layers.getInt(), this->config().brim_type.value, + float(this->config().brim_width.getFloat())}; auto [supp_points, partial_objects] = SupportSpotsGenerator::full_search(this, cancel_func, params); this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points}; m_print->throw_if_canceled(); @@ -430,27 +431,33 @@ void PrintObject::generate_support_spots() auto alert_fn = [&](PrintStateBase::WarningLevel level, SupportSpotsGenerator::SupportPointCause cause) { switch (cause) { case SupportSpotsGenerator::SupportPointCause::LongBridge: - this->active_step_add_warning(level, L("There are bridges longer than allowed distance. Consider adding supports. ")); + this->active_step_add_warning(level, L("There are bridges longer than recommended length. Consider adding supports.") + + (L("Object name")) + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: - this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed.")); + this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed.") + (L("Object name")) + + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion: if (level == PrintStateBase::WarningLevel::CRITICAL) { - this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed.")); + this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed.") + + (L("Object name")) + ": " + this->model_object()->name); } else { - this->active_step_add_warning(level, L("Some unspported extrusions found. Consider adding supports. ")); + this->active_step_add_warning(level, L("Some unspported extrusions found. Consider adding supports. ") + + (L("Object name")) + ": " + this->model_object()->name); } break; case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: - this->active_step_add_warning(level, L("Object part may break from the bed. Consider adding brim and/or supports.")); + this->active_step_add_warning(level, L("Object part may break from the bed. Consider adding brim and/or supports.") + + (L("Object name")) + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: - this->active_step_add_warning(level, L("Floating object parts detected. Supports are needed.")); + this->active_step_add_warning(level, L("Floating object parts detected. Supports are needed.") + (L("Object name")) + + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Thin parts of the object may break. Supports are needed.")); + this->active_step_add_warning(level, L("Thin parts of the object may break. Consider adding supports.") + + (L("Object name")) + ": " + this->model_object()->name); break; } }; @@ -495,9 +502,9 @@ void PrintObject::estimate_curled_extrusions() // Estimate curling of support material and add it to the malformaition lines of each layer float support_flow_width = support_material_flow(this, this->config().layer_height).width(); SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values, - float(this->print()->config().perimeter_acceleration.getFloat()), - this->config().raft_layers.getInt() - }; + float(this->print()->m_config.perimeter_acceleration.getFloat()), + this->config().raft_layers.getInt(), this->config().brim_type.value, + float(this->config().brim_width.getFloat())}; SupportSpotsGenerator::estimate_supports_malformations(this->support_layers(), support_flow_width, params); SupportSpotsGenerator::estimate_malformations(this->layers(), params); m_print->throw_if_canceled(); @@ -609,6 +616,7 @@ bool PrintObject::invalidate_state_by_config_options( if ( opt_key == "brim_width" || opt_key == "brim_separation" || opt_key == "brim_type") { + steps.emplace_back(posSupportSpotsSearch); // Brim is printed below supports, support invalidates brim and skirt. steps.emplace_back(posSupportMaterial); } else if ( diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 7eec832c1..7a77ae767 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -11,6 +11,7 @@ #include "PrincipalComponents2D.hpp" #include "Print.hpp" #include "PrintBase.hpp" +#include "PrintConfig.hpp" #include "Tesselate.hpp" #include "libslic3r.h" #include "tbb/parallel_for.h" @@ -598,10 +599,11 @@ public: }; // return new object part and actual area covered by extrusions -std::tuple build_object_part_from_slice(const LayerSlice &slice, const Layer *layer, const Params& params) +std::tuple build_object_part_from_slice(const size_t &slice_idx, const Layer *layer, const Params& params) { ObjectPart new_object_part; float area_covered_by_extrusions = 0; + const LayerSlice& slice = layer->lslices_ex.at(slice_idx); auto add_extrusions_to_object = [&new_object_part, &area_covered_by_extrusions, ¶ms](const ExtrusionEntity *e, const LayerRegion *region) { @@ -659,6 +661,49 @@ std::tuple build_object_part_from_slice(const LayerSlice &sli } } + // BRIM HANDLING + if (layer->id() == params.raft_layers_count && params.raft_layers_count == 0 && params.brim_type != BrimType::btNoBrim) { + // TODO: The algorithm here should take into account that multiple slices may have coliding Brim areas and the final brim area is + // smaller, + // thus has lower adhesion. For now this effect will be neglected. + ExPolygon slice_poly = layer->lslices[slice_idx]; + ExPolygons brim; + if (params.brim_type == BrimType::btOuterAndInner || params.brim_type == BrimType::btOuterOnly) { + Polygon brim_hole = slice_poly.contour; + brim_hole.reverse(); + brim.push_back(ExPolygon{expand(slice_poly.contour, scale_(params.brim_width)).front(), brim_hole}); + } + if (params.brim_type == BrimType::btOuterAndInner || params.brim_type == BrimType::btInnerOnly) { + Polygons brim_contours = slice_poly.holes; + polygons_reverse(brim_contours); + for (const Polygon &brim_contour : brim_contours) { + Polygons brim_holes = shrink({brim_contour}, scale_(params.brim_width)); + polygons_reverse(brim_holes); + ExPolygon inner_brim{brim_contour}; + inner_brim.holes = brim_holes; + brim.push_back(inner_brim); + } + } + + for (const Polygon &poly : to_polygons(brim)) { + Vec2f p0 = unscaled(poly.first_point()).cast(); + for (size_t i = 2; i < poly.points.size(); i++) { + Vec2f p1 = unscaled(poly.points[i - 1]).cast(); + Vec2f p2 = unscaled(poly.points[i]).cast(); + + float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f; + + auto [area, first_moment_of_area, second_moment_area, + second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2); + new_object_part.sticking_area += sign * area; + new_object_part.sticking_centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), + layer->print_z * area); + new_object_part.sticking_second_moment_of_area_accumulator += sign * second_moment_area; + new_object_part.sticking_second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance; + } + } + } + return {new_object_part, area_covered_by_extrusions}; } @@ -730,7 +775,7 @@ std::tuple check_stability(const PrintObject *po, for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { const LayerSlice &slice = layer->lslices_ex.at(slice_idx); - auto [new_part, covered_area] = build_object_part_from_slice(slice, layer, params); + auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params); SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer); #ifdef DETAILED_DEBUG_LOGS @@ -1146,13 +1191,6 @@ void raise_alerts_for_issues(const SupportPoints } } - for (const SupportPoint &sp : support_points) { - if (sp.cause == SupportPointCause::WeakObjectPart) { - alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::WeakObjectPart); - return; - } - } - std::vector ext_supp_points{}; ext_supp_points.reserve(support_points.size()); for (const SupportPoint &sp : support_points) { @@ -1189,8 +1227,15 @@ void raise_alerts_for_issues(const SupportPoints for (const SupportPoint &sp : support_points) { if (sp.cause == SupportPointCause::LongBridge) { - alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::LongBridge); - return; + alert_fn(PrintStateBase::WarningLevel::NON_CRITICAL, SupportPointCause::LongBridge); + break; + } + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::WeakObjectPart) { + alert_fn(PrintStateBase::WarningLevel::NON_CRITICAL, SupportPointCause::WeakObjectPart); + break; } } } diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index bd3301874..23fb5dad0 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -4,6 +4,7 @@ #include "Layer.hpp" #include "Line.hpp" #include "PrintBase.hpp" +#include "PrintConfig.hpp" #include #include #include @@ -14,8 +15,9 @@ namespace SupportSpotsGenerator { struct Params { - Params(const std::vector &filament_types, float max_acceleration, int raft_layers_count) - : max_acceleration(max_acceleration), raft_layers_count(raft_layers_count) + Params( + const std::vector &filament_types, float max_acceleration, int raft_layers_count, BrimType brim_type, float brim_width) + : max_acceleration(max_acceleration), raft_layers_count(raft_layers_count), brim_type(brim_type), brim_width(brim_width) { if (filament_types.size() > 1) { BOOST_LOG_TRIVIAL(warning) @@ -37,6 +39,9 @@ struct Params const int raft_layers_count; std::string filament_type; + BrimType brim_type; + const float brim_width; + const std::pair malformation_distance_factors = std::pair { 0.4, 1.2 }; const float max_curled_height_factor = 10.0f; @@ -53,7 +58,7 @@ struct Params // MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface double get_bed_adhesion_yield_strength() const { if (raft_layers_count > 0) { - return get_support_spots_adhesion_strength(); + return get_support_spots_adhesion_strength() * 2.0; } if (filament_type == "PLA") { From c38bd9adde9d73639b32a09722d568596e7cbc37 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 25 Jan 2023 17:05:13 +0100 Subject: [PATCH 11/22] missing space in description --- src/libslic3r/PrintObject.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 887caa8bf..474e02f58 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -431,16 +431,16 @@ void PrintObject::generate_support_spots() auto alert_fn = [&](PrintStateBase::WarningLevel level, SupportSpotsGenerator::SupportPointCause cause) { switch (cause) { case SupportSpotsGenerator::SupportPointCause::LongBridge: - this->active_step_add_warning(level, L("There are bridges longer than recommended length. Consider adding supports.") + + this->active_step_add_warning(level, L("There are bridges longer than recommended length. Consider adding supports. ") + (L("Object name")) + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: - this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed.") + (L("Object name")) + + this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed. ") + (L("Object name")) + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion: if (level == PrintStateBase::WarningLevel::CRITICAL) { - this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed.") + + this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed. ") + (L("Object name")) + ": " + this->model_object()->name); } else { this->active_step_add_warning(level, L("Some unspported extrusions found. Consider adding supports. ") + @@ -448,15 +448,15 @@ void PrintObject::generate_support_spots() } break; case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: - this->active_step_add_warning(level, L("Object part may break from the bed. Consider adding brim and/or supports.") + + this->active_step_add_warning(level, L("Object part may break from the bed. Consider adding brim and/or supports. ") + (L("Object name")) + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: - this->active_step_add_warning(level, L("Floating object parts detected. Supports are needed.") + (L("Object name")) + + this->active_step_add_warning(level, L("Floating object parts detected. Supports are needed. ") + (L("Object name")) + ": " + this->model_object()->name); break; case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: - this->active_step_add_warning(level, L("Thin parts of the object may break. Consider adding supports.") + + this->active_step_add_warning(level, L("Thin parts of the object may break. Consider adding supports. ") + (L("Object name")) + ": " + this->model_object()->name); break; } From 37ca6e30bdcd0a747742753b2b3f3b9e0ae07ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Miku=C5=A1?= Date: Wed, 25 Jan 2023 17:24:53 +0100 Subject: [PATCH 12/22] Update SupportSpotsGenerator.cpp --- src/libslic3r/SupportSpotsGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 7a77ae767..3820aa4d5 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -38,7 +38,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -#define DEBUG_FILES +// #define DEBUG_FILES #ifdef DEBUG_FILES #include From 70a9520cc3e2caf5f9062eac565cc3a297b881a5 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 25 Jan 2023 17:45:40 +0100 Subject: [PATCH 13/22] App udpater fixes - checks of path, error reporting and translations --- src/slic3r/GUI/DownloaderFileGet.cpp | 4 +- src/slic3r/GUI/UpdateDialogs.cpp | 42 +++++++++++++----- src/slic3r/Utils/AppUpdater.cpp | 66 ++++++++++++++++++++-------- 3 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index 2e591a75a..d81261296 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -119,8 +119,8 @@ void FileGet::priv::get_perform() wxString temp_path_wstring(m_tmp_path.wstring()); - std::cout << "dest_path: " << dest_path.string() << std::endl; - std::cout << "m_tmp_path: " << m_tmp_path.string() << std::endl; + //std::cout << "dest_path: " << dest_path.string() << std::endl; + //std::cout << "m_tmp_path: " << m_tmp_path.string() << std::endl; BOOST_LOG_TRIVIAL(info) << GUI::format("Starting download from %1% to %2%. Temp path is %3%",m_url, dest_path, m_tmp_path); diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 9c4f12eaf..5663262ca 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -167,10 +167,14 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( const Semver& ver_online, boos wxString wxext = boost::nowide::widen(extension); wildcard = GUI::format_wxstr("%1% Files (*.%2%)|*.%2%", wxext.Upper(), wxext); } + boost::system::error_code ec; + boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(GUI::format(txtctrl_path->GetValue())), ec); + if (ec) + dir = GUI::format(txtctrl_path->GetValue()); wxDirDialog save_dlg( this , _L("Select directory:") - , txtctrl_path->GetValue() + , GUI::format_wxstr(dir.string()) /* , filename //boost::nowide::widen(AppUpdater::get_filename_from_url(txtctrl_path->GetValue().ToUTF8().data())) , wildcard @@ -188,35 +192,46 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( const Semver& ver_online, boos if (auto* btn_ok = get_button(wxID_OK); btn_ok != NULL) { btn_ok->SetLabel(_L("Download")); btn_ok->Bind(wxEVT_BUTTON, ([this, path](wxCommandEvent& e){ - boost::filesystem::path path = boost::filesystem::path(txtctrl_path->GetValue().ToUTF8().data()) / GUI::format(filename); boost::system::error_code ec; - if (path.parent_path().string().empty()) { + std::string input = GUI::format(txtctrl_path->GetValue()); + boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(input), ec); + if (ec) + dir = boost::filesystem::path(input); + bool show_change = (dir.string() != input); + boost::filesystem::path path = dir / GUI::format(filename); + ec.clear(); + if (dir.string().empty()) { MessageDialog msgdlg(nullptr, _L("Directory path is empty."), _L("Notice"), wxOK); msgdlg.ShowModal(); return; } ec.clear(); - if (!boost::filesystem::exists(path.parent_path(), ec) || !boost::filesystem::is_directory(path.parent_path(),ec) || ec) { + if (!boost::filesystem::exists(dir, ec) || !boost::filesystem::is_directory(dir,ec) || ec) { ec.clear(); - if (!boost::filesystem::exists(path.parent_path().parent_path(), ec) || !boost::filesystem::is_directory(path.parent_path().parent_path(), ec) || ec) { + if (!boost::filesystem::exists(dir.parent_path(), ec) || !boost::filesystem::is_directory(dir.parent_path(), ec) || ec) { MessageDialog msgdlg(nullptr, _L("Directory path is incorrect."), _L("Notice"), wxOK); msgdlg.ShowModal(); return; } - - MessageDialog msgdlg(nullptr, GUI::format_wxstr(_L("Directory %1% doesn't exists. Do you wish to create it?"), GUI::format_wxstr(path.parent_path().string())), _L("Notice"), wxYES_NO); + show_change = false; + MessageDialog msgdlg(nullptr, GUI::format_wxstr(_L("Directory %1% doesn't exists. Do you wish to create it?"), dir.string()), _L("Notice"), wxYES_NO); if (msgdlg.ShowModal() != wxID_YES) return; ec.clear(); - if(!boost::filesystem::create_directory(path.parent_path(), ec) || ec) - { + if(!boost::filesystem::create_directory(dir, ec) || ec) { MessageDialog msgdlg(nullptr, _L("Failed to create directory."), _L("Notice"), wxOK); msgdlg.ShowModal(); return; } } if (boost::filesystem::exists(path)) { - MessageDialog msgdlg(nullptr, GUI::format_wxstr(_L("File %1% already exists. Do you wish to overwrite it?"), GUI::format_wxstr(path.string())),_L("Notice"), wxYES_NO); + show_change = false; + MessageDialog msgdlg(nullptr, GUI::format_wxstr(_L("File %1% already exists. Do you wish to overwrite it?"), path.string()),_L("Notice"), wxYES_NO); + if (msgdlg.ShowModal() != wxID_YES) + return; + } + if (show_change) { + MessageDialog msgdlg(nullptr, GUI::format_wxstr(_L("Download path is %1%. Do you wish to continue?"), path.string()), _L("Notice"), wxYES_NO); if (msgdlg.ShowModal() != wxID_YES) return; } @@ -241,7 +256,12 @@ bool AppUpdateDownloadDialog::run_after_download() const boost::filesystem::path AppUpdateDownloadDialog::get_download_path() const { - return boost::filesystem::path(txtctrl_path->GetValue().ToUTF8().data()) / GUI::format(filename); + boost::system::error_code ec; + std::string input = GUI::format(txtctrl_path->GetValue()); + boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(input), ec); + if (ec) + dir = boost::filesystem::path(input); + return dir / GUI::format(filename); } // MsgUpdateConfig diff --git a/src/slic3r/Utils/AppUpdater.cpp b/src/slic3r/Utils/AppUpdater.cpp index fc847ec53..d054db4b5 100644 --- a/src/slic3r/Utils/AppUpdater.cpp +++ b/src/slic3r/Utils/AppUpdater.cpp @@ -13,6 +13,7 @@ #include "slic3r/GUI/format.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/I18N.hpp" #include "slic3r/Utils/Http.hpp" #include "libslic3r/Utils.hpp" @@ -36,7 +37,7 @@ namespace { std::string msg; bool res = GUI::create_process(path, std::wstring(), msg); if (!res) { - std::string full_message = GUI::format("Running downloaded instaler of %1% has failed:\n%2%", SLIC3R_APP_NAME, msg); + std::string full_message = GUI::format(_utf8("Running downloaded instaler of %1% has failed:\n%2%"), SLIC3R_APP_NAME, msg); BOOST_LOG_TRIVIAL(error) << full_message; // lm: maybe UI error msg? // dk: bellow. (maybe some general show error evt would be better?) wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); evt->SetString(full_message); @@ -170,8 +171,10 @@ bool AppUpdater::priv::http_get_file(const std::string& url, size_t size_limit, // progress function returns true as success (to continue) cancel = (m_cancel ? true : !progress_fn(std::move(progress))); if (cancel) { - error_message = GUI::format("Error getting: `%1%`: Download was canceled.", //lm:typo //dk: am i blind? :) - url); + // Lets keep error_message empty here - if there is need to show error dialog, the message will be probably shown by whatever caused the cancel. + /* + error_message = GUI::format(_utf8("Error getting: `%1%`: Download was canceled."), url); + */ BOOST_LOG_TRIVIAL(debug) << "AppUpdater::priv::http_get_file message: "<< error_message; } }) @@ -200,7 +203,30 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d assert(!dest_path.empty()); if (dest_path.empty()) { - BOOST_LOG_TRIVIAL(error) << "Download from " << data.url << " could not start. Destination path is empty."; + std::string line1 = GUI::format(_utf8("Internal download error for url %1%:"), data.url); + std::string line2 = _utf8("Destination path is empty."); + std::string message = GUI::format("%1%\n%2%", line1, line2); + BOOST_LOG_TRIVIAL(error) << message; + wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); + evt->SetString(message); + GUI::wxGetApp().QueueEvent(evt); + return boost::filesystem::path(); + } + + boost::filesystem::path tmp_path = dest_path; + tmp_path += format(".%1%%2%", get_current_pid(), ".download"); + FILE* file; + wxString temp_path_wstring(tmp_path.wstring()); + file = fopen(temp_path_wstring.c_str(), "wb"); + assert(file != NULL); + if (file == NULL) { + std::string line1 = GUI::format(_utf8("Download from %1% couldn't start:"), data.url); + std::string line2 = GUI::format(_utf8("Can't create file at %1%."), tmp_path.string()); + std::string message = GUI::format("%1%\n%2%", line1, line2); + BOOST_LOG_TRIVIAL(error) << message; + wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); + evt->SetString(message); + GUI::wxGetApp().QueueEvent(evt); return boost::filesystem::path(); } @@ -217,7 +243,7 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d GUI::wxGetApp().QueueEvent(evt); return false; } else if (progress.dltotal > 0 && progress.dltotal < expected_size) { - //lm:When will this happen? Is that not an error? // dk: It is possible error, but we cannot know until the download is finished. Somehow the total size can grow during the download. + // This is possible error, but we cannot know until the download is finished. Somehow the total size can grow during the download. BOOST_LOG_TRIVIAL(info) << GUI::format("Downloading new %1% has incorrect size. The download will continue. \nExpected size: %2%\nDownload size: %3%", SLIC3R_APP_NAME, expected_size, progress.dltotal); } // progress event @@ -232,27 +258,26 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d return true; } // on_complete - , [dest_path, expected_size](std::string body, std::string& error_message){ + , [&file, dest_path, tmp_path, expected_size](std::string body, std::string& error_message){ // Size check. Does always 1 char == 1 byte? size_t body_size = body.size(); if (body_size != expected_size) { - //lm:UI message? // dk: changed. Now it propagates to UI. - error_message = GUI::format("Downloaded file has wrong size. Expected size: %1% Downloaded size: %2%", expected_size, body_size); + error_message = GUI::format(_utf8("Downloaded file has wrong size. Expected size: %1% Downloaded size: %2%"), expected_size, body_size); + return false; + } + if (file == NULL) { + error_message = GUI::format(_utf8("Can't create file at %1%."), tmp_path.string()); return false; } - boost::filesystem::path tmp_path = dest_path; - tmp_path += format(".%1%%2%", get_current_pid(), ".download"); try { - FILE* file; - file = fopen(tmp_path.string().c_str(), "wb"); fwrite(body.c_str(), 1, body.size(), file); fclose(file); boost::filesystem::rename(tmp_path, dest_path); } catch (const std::exception& e) { - error_message = GUI::format("Failed to write and move %1% to %2%:/n%3%", tmp_path, dest_path, e.what()); + error_message = GUI::format(_utf8("Failed to write to file or to move %1% to %2%:\n%3%"), tmp_path, dest_path, e.what()); return false; } return true; @@ -261,16 +286,19 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d ); if (!res) { - if (m_cancel) - { - BOOST_LOG_TRIVIAL(info) << error_message; //lm:Is this an error? // dk: changed to info + if (m_cancel) { + BOOST_LOG_TRIVIAL(info) << error_message; wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); // FAILED with empty msg only closes progress notification GUI::wxGetApp().QueueEvent(evt); } else { - std::string message = GUI::format("Downloading new %1% has failed:\n%2%", SLIC3R_APP_NAME, error_message); - BOOST_LOG_TRIVIAL(error) << message; + std::string message = (error_message.empty() + ? std::string() + : GUI::format(_utf8("Downloading new %1% has failed:\n%2%"), SLIC3R_APP_NAME, error_message)); wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_APP_DOWNLOAD_FAILED); - evt->SetString(message); + if (!message.empty()) { + BOOST_LOG_TRIVIAL(error) << message; + evt->SetString(message); + } GUI::wxGetApp().QueueEvent(evt); } return boost::filesystem::path(); From 90bd46e30a1d47d7c37fc2b8f3bbd116c2f6842f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 21 Oct 2022 09:49:57 +0200 Subject: [PATCH 14/22] Added 'is_extruder_used' placeholder accessible from Custom Start G-Code --- src/libslic3r/GCode.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ad0d3c43f..4da8af567 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1263,6 +1263,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + + std::vector is_extruder_used(print.config().nozzle_diameter.size(), 0); + for (unsigned int extruder_id : print.extruders()) + is_extruder_used[extruder_id] = true; + m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); } std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); // Set bed temperature if the start G-code does not contain any bed temp control G-codes. From b3664179f62db797d9a29767b497dfc7ff62d1e5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 22 Dec 2022 15:46:25 +0100 Subject: [PATCH 15/22] Wipe tower: remove a move to the wipe tower when not needed --- src/libslic3r/GCode.cpp | 5 +++-- src/libslic3r/GCode/WipeTower.cpp | 35 +++++++++++++++++++++---------- src/libslic3r/GCode/WipeTower.hpp | 1 - 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4da8af567..47bd6df40 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -238,8 +238,9 @@ namespace Slic3r { std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); - if (! tcr.priming) { - // Move over the wipe tower. + if (gcodegen.config().single_extruder_multi_material && ! tcr.priming) { + // Move over the wipe tower. If this is not single-extruder MM, the first wipe tower move following the + // toolchange will travel there anyway. gcode += gcodegen.retract(); gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); gcode += gcodegen.travel_to( diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index f24311a13..87f04f08c 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -71,6 +71,8 @@ public: return *this; } + WipeTowerWriter& set_position(const Vec2f &pos) { m_current_pos = pos; return *this; } + WipeTowerWriter& set_initial_tool(size_t tool) { m_current_tool = tool; return *this; } WipeTowerWriter& set_z(float z) @@ -802,19 +804,24 @@ void WipeTower::toolchange_Unload( { float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width; float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width; - - const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness + + const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm + const Vec2f ramming_start_pos = Vec2f(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f); + writer.append("; CP TOOLCHANGE UNLOAD\n") .change_analyzer_line_width(line_width); unsigned i = 0; // iterates through ramming_speed m_left_to_right = true; // current direction of ramming float remaining = xr - xl ; // keeps track of distance to the next turnaround - float e_done = 0; // measures E move done from each segment + float e_done = 0; // measures E move done from each segment - writer.travel(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f ); // move to starting position + if (m_semm) + writer.travel(ramming_start_pos); // move to starting position + else + writer.set_position(ramming_start_pos); // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower: if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) { @@ -849,7 +856,7 @@ void WipeTower::toolchange_Unload( writer.disable_linear_advance(); // now the ramming itself: - while (i < m_filpar[m_current_tool].ramming_speed.size()) + while (m_semm && i < m_filpar[m_current_tool].ramming_speed.size()) { const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height); const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / filament_area(); // transform volume per sec to E move; @@ -898,7 +905,7 @@ void WipeTower::toolchange_Unload( // Cooling: const int& number_of_moves = m_filpar[m_current_tool].cooling_moves; - if (number_of_moves > 0) { + if (m_semm && number_of_moves > 0) { const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed; const float& final_speed = m_filpar[m_current_tool].cooling_final_speed; @@ -916,14 +923,20 @@ void WipeTower::toolchange_Unload( } } - // let's wait is necessary: - writer.wait(m_filpar[m_current_tool].delay); - // we should be at the beginning of the cooling tube again - let's move to parking position: - writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000); + if (m_semm) { + // let's wait is necessary: + writer.wait(m_filpar[m_current_tool].delay); + // we should be at the beginning of the cooling tube again - let's move to parking position: + writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000); + } // this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start: // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material - writer.travel(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f); + Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width); + if (m_semm) + writer.travel(pos, 2400.f); + else + writer.set_position(pos); writer.resume_preview() .flush_planner_queue(); diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 397b5ab7d..ab3af507d 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -290,7 +290,6 @@ private: // Extruder specific parameters. std::vector m_filpar; - // State of the wipe tower generator. unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics. unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics. From 7fb1bc2c16e880a7e357031185c4cdd9ed225bd9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 6 Jan 2023 01:49:04 +0100 Subject: [PATCH 16/22] Placeholders 'layer_num', 'layer_z' and 'max_layer_z' were not accessible in fil. start gcode when the wipe tower was off --- src/libslic3r/GCode.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 47bd6df40..13b21174a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3192,6 +3192,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) if (! start_filament_gcode.empty()) { // Process the start_filament_gcode for the filament. DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); check_add_eol(gcode); @@ -3263,6 +3266,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) if (! start_filament_gcode.empty()) { // Process the start_filament_gcode for the new filament. DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); check_add_eol(gcode); From 98fea2f6ee64f8f4e0456bcd528208118a2fa65c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 6 Jan 2023 13:25:42 +0100 Subject: [PATCH 17/22] Wipe tower: use GCode::set_extruder, allow ooze prevention: this removes duplicated code and fixes toolchange retraction The ooze prevention part needs further work, now it does not work as advertised (the tall skirt) --- src/libslic3r/GCode.cpp | 49 +++++++++++-------------------- src/libslic3r/GCode/WipeTower.cpp | 5 ++-- src/libslic3r/Print.cpp | 6 ++-- tests/fff_print/test_multi.cpp | 24 +++++++-------- 4 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 13b21174a..7c786cf89 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -260,21 +260,10 @@ namespace Slic3r { } - // Process the end filament gcode. - std::string end_filament_gcode_str; - if (gcodegen.writer().extruder() != nullptr) { - // Process the custom end_filament_gcode in case of single_extruder_multi_material. - unsigned int old_extruder_id = gcodegen.writer().extruder()->id(); - const std::string& end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); - if (gcodegen.writer().extruder() != nullptr && !end_filament_gcode.empty()) { - end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); - check_add_eol(end_filament_gcode_str); - } - } // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. // Otherwise, leave control to the user completely. - std::string toolchange_gcode_str; + /*std::string toolchange_gcode_str; const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; if (! toolchange_gcode.empty()) { DynamicConfig config; @@ -296,29 +285,26 @@ namespace Slic3r { toolchange_gcode_str += toolchange_command; else { // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code. + }*/ + + std::string toolchange_gcode_str; + std::string deretraction_str; + if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) { + if (gcodegen.config().single_extruder_multi_material) + gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines. + toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z + if (gcodegen.config().wipe_tower) + deretraction_str = gcodegen.unretract(); } - gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); - // Process the start filament gcode. - std::string start_filament_gcode_str; - const std::string& start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); - if (!start_filament_gcode.empty()) { - // Process the start_filament_gcode for the active filament only. - DynamicConfig config; - config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(gcodegen.writer().get_position()(2) - gcodegen.m_config.z_offset.value)); - config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z)); - config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); - start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); - check_add_eol(start_filament_gcode_str); - } + - // Insert the end filament, toolchange, and start filament gcode into the generated gcode. + + // Insert the toolchange and deretraction gcode into the generated gcode. DynamicConfig config; - config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); - config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str)); + config.set_key_value("deretraction_from_wipe_tower_generator", new ConfigOptionString(deretraction_str)); std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); gcode += tcr_gcode; @@ -915,7 +901,7 @@ namespace DoExport { skirts.emplace_back(std::move(s)); } ooze_prevention.enable = true; - ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.))); + //ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.))); #if 0 require "Slic3r/SVG.pm"; Slic3r::SVG::output( @@ -3210,8 +3196,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) m_wipe.reset_path(); if (m_writer.extruder() != nullptr) { - // Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower - // so it should not be injected twice. + // Process the custom end_filament_gcode. unsigned int old_extruder_id = m_writer.extruder()->id(); const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); if (! end_filament_gcode.empty()) { diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 87f04f08c..fec2eb86b 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -954,7 +954,7 @@ void WipeTower::toolchange_Change( // This is where we want to place the custom gcodes. We will use placeholders for this. // These will be substituted by the actual gcodes when the gcode is generated. - writer.append("[end_filament_gcode]\n"); + //writer.append("[end_filament_gcode]\n"); writer.append("[toolchange_gcode]\n"); // Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc) @@ -965,11 +965,12 @@ void WipeTower::toolchange_Change( .append(std::string("G1 X") + Slic3r::float_to_string_decimal_point(current_pos.x()) + " Y" + Slic3r::float_to_string_decimal_point(current_pos.y()) + never_skip_tag() + "\n"); + writer.append("[deretraction_from_wipe_tower_generator]"); // The toolchange Tn command will be inserted later, only in case that the user does // not provide a custom toolchange gcode. writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed. - writer.append("[start_filament_gcode]\n"); + //writer.append("[start_filament_gcode]\n"); writer.flush_planner_queue(); m_current_tool = new_tool; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e7305234f..3f0f5c2cd 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -347,7 +347,7 @@ std::vector Print::print_object_ids() const bool Print::has_infinite_skirt() const { - return (m_config.draft_shield == dsEnabled && m_config.skirts > 0) || (m_config.ooze_prevention && this->extruders().size() > 1); + return (m_config.draft_shield == dsEnabled && m_config.skirts > 0)/* || (m_config.ooze_prevention && this->extruders().size() > 1)*/; } bool Print::has_skirt() const @@ -505,8 +505,8 @@ std::string Print::validate(std::string* warning) const return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors."); if (! m_config.use_relative_e_distances) return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); - if (m_config.ooze_prevention) - return L("Ooze prevention is currently not supported with the wipe tower enabled."); + if (m_config.ooze_prevention && m_config.single_extruder_multi_material) + return L("Ooze prevention is only supported with the wipe tower when 'single_extruder_multi_material' is off."); if (m_config.use_volumetric_e) return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); if (m_config.complete_objects && extruders.size() > 1) diff --git a/tests/fff_print/test_multi.cpp b/tests/fff_print/test_multi.cpp index 03c7f644b..90f311c39 100644 --- a/tests/fff_print/test_multi.cpp +++ b/tests/fff_print/test_multi.cpp @@ -108,18 +108,18 @@ SCENARIO("Ooze prevention", "[Multi]") Polygon convex_hull = Geometry::convex_hull(extrusion_points); - THEN("all nozzles are outside skirt at toolchange") { - Points t; - sort_remove_duplicates(toolchange_points); - size_t inside = 0; - for (const auto &point : toolchange_points) - for (const Vec2d &offset : print_config.extruder_offset.values) { - Point p = point + scaled(offset); - if (convex_hull.contains(p)) - ++ inside; - } - REQUIRE(inside == 0); - } + // THEN("all nozzles are outside skirt at toolchange") { + // Points t; + // sort_remove_duplicates(toolchange_points); + // size_t inside = 0; + // for (const auto &point : toolchange_points) + // for (const Vec2d &offset : print_config.extruder_offset.values) { + // Point p = point + scaled(offset); + // if (convex_hull.contains(p)) + // ++ inside; + // } + // REQUIRE(inside == 0); + // } #if 0 require "Slic3r/SVG.pm"; From a067da6d53196727c6bc610bca5134e743c73aff Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 10 Jan 2023 14:26:53 +0100 Subject: [PATCH 18/22] Ooze prevention: - remove the infinite skirt - added 'idle_temperature' in Filament Settings as an optional parameter - the logic is changed: if idle_temp is present, it is used, otherwise it uses the old delta value from Print Settings - TODO: the optional parameter is not well supported in UI --- src/libslic3r/GCode.cpp | 82 +++++++++++------------------------ src/libslic3r/GCode.hpp | 3 +- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 27 ++++++++++-- src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/Tab.cpp | 2 + 7 files changed, 54 insertions(+), 64 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7c786cf89..ffe19a9cb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -113,26 +113,19 @@ namespace Slic3r { { std::string gcode; - // move to the nearest standby point - if (!this->standby_points.empty()) { - // get current position in print coordinates - Vec3d writer_pos = gcodegen.writer().get_position(); - Point pos = Point::new_scale(writer_pos(0), writer_pos(1)); - - // find standby point - Point standby_point = nearest_point(this->standby_points, pos).first; - - /* We don't call gcodegen.travel_to() because we don't need retraction (it was already - triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates - of the destination point must not be transformed by origin nor current extruder offset. */ - gcode += gcodegen.writer().travel_to_xy(unscale(standby_point), - "move to standby position"); - } - - if (gcodegen.config().standby_temperature_delta.value != 0) { - // we assume that heating is always slower than cooling, so no need to block - gcode += gcodegen.writer().set_temperature - (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id()); + unsigned int extruder_id = gcodegen.writer().extruder()->id(); + const ConfigOptionIntsNullable& filament_idle_temp = gcodegen.config().idle_temperature; + if (filament_idle_temp.is_nil(extruder_id)) { + // There is no idle temperature defined in filament settings. + // Use the delta value from print config. + if (gcodegen.config().standby_temperature_delta.value != 0) { + // we assume that heating is always slower than cooling, so no need to block + gcode += gcodegen.writer().set_temperature + (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, extruder_id); + } + } else { + // Use the value from filament settings. That one is absolute, not delta. + gcode += gcodegen.writer().set_temperature(filament_idle_temp.get_at(extruder_id), false, extruder_id); } return gcode; @@ -145,8 +138,7 @@ namespace Slic3r { std::string(); } - int - OozePrevention::_get_temp(GCode& gcodegen) + int OozePrevention::_get_temp(const GCode& gcodegen) const { return (gcodegen.layer() != nullptr && gcodegen.layer()->id() == 0) ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) @@ -885,34 +877,7 @@ namespace DoExport { static void init_ooze_prevention(const Print &print, OozePrevention &ooze_prevention) { - // Calculate wiping points if needed - if (print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material) { - Points skirt_points; - for (const ExtrusionEntity *ee : print.skirt().entities) - for (const ExtrusionPath &path : dynamic_cast(ee)->paths) - append(skirt_points, path.polyline.points); - if (! skirt_points.empty()) { - Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); - Polygons skirts; - for (unsigned int extruder_id : print.extruders()) { - const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id); - Polygon s(outer_skirt); - s.translate(Point::new_scale(-extruder_offset(0), -extruder_offset(1))); - skirts.emplace_back(std::move(s)); - } - ooze_prevention.enable = true; - //ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.))); - #if 0 - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "ooze_prevention.svg", - red_polygons => \@skirts, - polygons => [$outer_skirt], - points => $gcodegen->ooze_prevention->standby_points, - ); - #endif - } - } + ooze_prevention.enable = print.config().ooze_prevention.value && ! print.config().single_extruder_multi_material; } // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. @@ -1274,8 +1239,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Set other general things. file.write(this->preamble()); - // Calculate wiping points if needed + // Enable ooze prevention if configured so. DoExport::init_ooze_prevention(print, m_ooze_prevention); + print.throw_if_canceled(); // Collect custom seam data from all objects. @@ -1806,7 +1772,7 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr m_writer.set_temperature(temp, wait, first_printing_extruder_id); } else { // Custom G-code does not set the extruder temperature. Do it now. - if (print.config().single_extruder_multi_material.value) { + if (print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) { // Set temperature of the first printing extruder only. int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp > 0) @@ -2128,11 +2094,14 @@ LayerResult GCode::process_layer( // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // first_layer_temperature vs. temperature settings. for (const Extruder &extruder : m_writer.extruders()) { - if (print.config().single_extruder_multi_material.value && extruder.id() != m_writer.extruder()->id()) + if (print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) { // In single extruder multi material mode, set the temperature for the current extruder only. - continue; + // The same applies when ooze prevention is enabled. + if (extruder.id() != m_writer.extruder()->id()) + continue; + } int temperature = print.config().temperature.get_at(extruder.id()); - if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id())) + if (temperature > 0 && (temperature != print.config().first_layer_temperature.get_at(extruder.id()))) gcode += m_writer.set_temperature(temperature, false, extruder.id()); } gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id)); @@ -3206,8 +3175,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) } - // If ooze prevention is enabled, park current extruder in the nearest - // standby point and set it to the standby temperature. + // If ooze prevention is enabled, set current extruder to the standby temperature. if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 0741d7e37..ee50aefcc 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -39,14 +39,13 @@ struct PrintInstance; class OozePrevention { public: bool enable; - Points standby_points; OozePrevention() : enable(false) {} std::string pre_toolchange(GCode &gcodegen); std::string post_toolchange(GCode &gcodegen); private: - int _get_temp(GCode &gcodegen); + int _get_temp(const GCode &gcodegen) const; }; class Wipe { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index d2f5256f9..d6a4692f3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -470,7 +470,7 @@ static std::vector s_Preset_filament_options { "extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time", "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower", - "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", + "temperature", "idle_temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode", // Retract overrides diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3f0f5c2cd..055c6f730 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -191,6 +191,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "infill_first" || opt_key == "single_extruder_multi_material" || opt_key == "temperature" + || opt_key == "idle_temperature" || opt_key == "wipe_tower" || opt_key == "wipe_tower_width" || opt_key == "wipe_tower_brim_width" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4da75489c..86e0d8222 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -229,6 +229,11 @@ static void assign_printer_technology_to_unknown(t_optiondef_map &options, Print kvp.second.printer_technology = printer_technology; } +// Maximum extruder temperature, bumped to 1500 to support printing of glass. +namespace { + const int max_temp = 1500; +}; + PrintConfigDef::PrintConfigDef() { this->init_common_params(); @@ -1972,9 +1977,7 @@ void PrintConfigDef::init_fff_params() def = this->add("ooze_prevention", coBool); def->label = L("Enable"); - def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing. " - "It will enable a tall skirt automatically and move extruders outside such " - "skirt when changing temperatures."); + def->tooltip = L("This option will drop the temperature of the inactive extruders to prevent oozing. "); def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); @@ -2475,7 +2478,8 @@ void PrintConfigDef::init_fff_params() def = this->add("standby_temperature_delta", coInt); def->label = L("Temperature variation"); def->tooltip = L("Temperature difference to be applied when an extruder is not active. " - "Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped."); + "The value is not used when 'idle_temperature' in filament settings " + "is defined."); def->sidetext = "∆°C"; def->min = -max_temp; def->max = max_temp; @@ -3731,6 +3735,15 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->set_default_value(new ConfigOptionFloat(0.3)); + def = this->add_nullable("idle_temperature", coInts); + def->label = L("Idle temperature"); + def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups." + "This is only used when 'Ooze prevention is active in Print Settings.'"); + def->sidetext = L("°C"); + //def->min = 0; + //def->max = max_temp; + def->set_default_value(new ConfigOptionIntsNullable { ConfigOptionIntsNullable::nil_value() }); + def = this->add("bottle_volume", coFloat); def->label = L("Bottle volume"); def->tooltip = L("Bottle volume"); @@ -4511,6 +4524,12 @@ std::string validate(const FullPrintConfig &cfg) assert(opt != nullptr); const ConfigOptionDef *optdef = print_config_def.get(opt_key); assert(optdef != nullptr); + + if (opt->nullable() && opt->is_nil()) { + // Do not check nil values + continue; + } + bool out_of_range = false; switch (opt->type()) { case coFloat: diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 42ac9c0e2..6bfc7723d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -769,6 +769,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloatOrPercent, first_layer_height)) ((ConfigOptionFloatOrPercent, first_layer_speed)) ((ConfigOptionInts, first_layer_temperature)) + ((ConfigOptionIntsNullable, idle_temperature)) ((ConfigOptionInts, full_fan_speed_layer)) ((ConfigOptionFloat, infill_acceleration)) ((ConfigOptionBool, infill_first)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 7d91f689d..52d83eff9 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1957,6 +1957,8 @@ void TabFilament::build() line.append_option(optgroup->get_option("temperature")); optgroup->append_line(line); + optgroup->append_single_option_line("idle_temperature"); + line = { L("Bed"), "" }; line.append_option(optgroup->get_option("first_layer_bed_temperature")); line.append_option(optgroup->get_option("bed_temperature")); From 71cedd2eeab6f040bad17f5638a7f24cc0beb48e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 11 Jan 2023 12:29:15 +0100 Subject: [PATCH 19/22] Implemented UI to "Idle temperature" parameter --- src/libslic3r/PrintConfig.cpp | 4 +- src/slic3r/GUI/Field.cpp | 60 ++++++++++++++++--- src/slic3r/GUI/Field.hpp | 18 ++---- src/slic3r/GUI/Tab.cpp | 106 +++++++++++++++++++--------------- src/slic3r/GUI/Tab.hpp | 2 + 5 files changed, 122 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 86e0d8222..1d0446ce2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3740,8 +3740,8 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups." "This is only used when 'Ooze prevention is active in Print Settings.'"); def->sidetext = L("°C"); - //def->min = 0; - //def->max = max_temp; + def->min = 0; + def->max = max_temp; def->set_default_value(new ConfigOptionIntsNullable { ConfigOptionIntsNullable::nil_value() }); def = this->add("bottle_volume", coFloat); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 419d48d5b..4e087c98f 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -762,28 +762,26 @@ void SpinCtrl::BUILD() { if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit); wxString text_value = wxString(""); - int default_value = 0; + int default_value = UNDEF_VALUE; switch (m_opt.type) { case coInt: default_value = m_opt.default_value->getInt(); - text_value = wxString::Format(_T("%i"), default_value); break; case coInts: { - const ConfigOptionInts *vec = m_opt.get_default_value(); - if (vec == nullptr || vec->empty()) break; - for (size_t id = 0; id < vec->size(); ++id) - { - default_value = vec->get_at(id); - text_value += wxString::Format(_T("%i"), default_value); - } + default_value = m_opt.get_default_value()->get_at(m_opt_idx); + if (m_opt.nullable) + m_last_meaningful_value = default_value == ConfigOptionIntsNullable::nil_value() ? static_cast(m_opt.max) : default_value; break; } default: break; } + if (default_value != UNDEF_VALUE) + text_value = wxString::Format(_T("%i"), default_value); + const int min_val = m_opt.min == -FLT_MAX #ifdef __WXOSX__ // We will forcibly set the input value for SpinControl, since the value @@ -882,6 +880,50 @@ void SpinCtrl::BUILD() { window = dynamic_cast(temp); } +void SpinCtrl::set_value(const boost::any& value, bool change_event/* = false*/) +{ + m_disable_change_event = !change_event; + tmp_value = boost::any_cast(value); + m_value = value; + if (m_opt.nullable) { + const bool m_is_na_val = tmp_value == ConfigOptionIntsNullable::nil_value(); + if (m_is_na_val) + dynamic_cast(window)->SetValue(na_value()); + else { + m_last_meaningful_value = value; + dynamic_cast(window)->SetValue(tmp_value); + } + } + else + dynamic_cast(window)->SetValue(tmp_value); + m_disable_change_event = false; +} + +void SpinCtrl::set_last_meaningful_value() +{ + const int val = boost::any_cast(m_last_meaningful_value); + dynamic_cast(window)->SetValue(val); + tmp_value = val; + propagate_value(); +} + +void SpinCtrl::set_na_value() +{ + dynamic_cast(window)->SetValue(na_value()); + m_value = ConfigOptionIntsNullable::nil_value(); + propagate_value(); +} + +boost::any& SpinCtrl::get_value() +{ + wxSpinCtrl* spin = static_cast(window); + if (spin->GetTextValue() == na_value()) + return m_value; + + int value = spin->GetValue(); + return m_value = value; +} + void SpinCtrl::propagate_value() { // check if value was really changed diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index eaa4fe481..73af0ef53 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -330,24 +330,18 @@ public: void BUILD() override; /// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER void propagate_value() ; - +/* void set_value(const std::string& value, bool change_event = false) { m_disable_change_event = !change_event; dynamic_cast(window)->SetValue(value); m_disable_change_event = false; } - void set_value(const boost::any& value, bool change_event = false) override { - m_disable_change_event = !change_event; - tmp_value = boost::any_cast(value); - m_value = value; - dynamic_cast(window)->SetValue(tmp_value); - m_disable_change_event = false; - } +*/ + void set_value(const boost::any& value, bool change_event = false) override; + void set_last_meaningful_value() override; + void set_na_value() override; - boost::any& get_value() override { - int value = static_cast(window)->GetValue(); - return m_value = value; - } + boost::any& get_value() override; void msw_rescale() override; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 52d83eff9..3a69bcee1 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1826,45 +1826,59 @@ static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_co tab->on_value_change(opt_key, value); } +void TabFilament::create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/) +{ + Line line {"",""}; + if (opt_key == "filament_retract_lift_above" || opt_key == "filament_retract_lift_below") { + Option opt = optgroup->get_option(opt_key); + opt.opt.label = opt.opt.full_label; + line = optgroup->create_single_option_line(opt); + } + else + line = optgroup->create_single_option_line(optgroup->get_option(opt_key)); + + line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup), opt_key, opt_index](wxWindow* parent) { + wxCheckBox* check_box = new wxCheckBox(parent, wxID_ANY, ""); + + check_box->Bind(wxEVT_CHECKBOX, [optgroup_wk, opt_key, opt_index](wxCommandEvent& evt) { + const bool is_checked = evt.IsChecked(); + if (auto optgroup_sh = optgroup_wk.lock(); optgroup_sh) { + if (Field *field = optgroup_sh->get_fieldc(opt_key, opt_index); field != nullptr) { + field->toggle(is_checked); + if (is_checked) + field->set_last_meaningful_value(); + else + field->set_na_value(); + } + } + }, check_box->GetId()); + + m_overrides_options[opt_key] = check_box; + return check_box; + }; + + optgroup->append_line(line); +} + +void TabFilament::update_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/, bool is_checked/* = true*/) +{ + if (!m_overrides_options[opt_key]) + return; + m_overrides_options[opt_key]->Enable(is_checked); + + is_checked &= !m_config->option(opt_key)->is_nil(); + m_overrides_options[opt_key]->SetValue(is_checked); + + Field* field = optgroup->get_fieldc(opt_key, opt_index); + if (field != nullptr) + field->toggle(is_checked); +} + void TabFilament::add_filament_overrides_page() { PageShp page = add_options_page(L("Filament Overrides"), "wrench"); ConfigOptionsGroupShp optgroup = page->new_optgroup(L("Retraction")); - auto append_single_option_line = [optgroup, this](const std::string& opt_key, int opt_index) - { - Line line {"",""}; - if (opt_key == "filament_retract_lift_above" || opt_key == "filament_retract_lift_below") { - Option opt = optgroup->get_option(opt_key); - opt.opt.label = opt.opt.full_label; - line = optgroup->create_single_option_line(opt); - } - else - line = optgroup->create_single_option_line(optgroup->get_option(opt_key)); - - line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup), opt_key, opt_index](wxWindow* parent) { - wxCheckBox* check_box = new wxCheckBox(parent, wxID_ANY, ""); - - check_box->Bind(wxEVT_CHECKBOX, [optgroup_wk, opt_key, opt_index](wxCommandEvent& evt) { - const bool is_checked = evt.IsChecked(); - if (auto optgroup_sh = optgroup_wk.lock(); optgroup_sh) { - if (Field *field = optgroup_sh->get_fieldc(opt_key, opt_index); field != nullptr) { - field->toggle(is_checked); - if (is_checked) - field->set_last_meaningful_value(); - else - field->set_na_value(); - } - } - }, check_box->GetId()); - - m_overrides_options[opt_key] = check_box; - return check_box; - }; - - optgroup->append_line(line); - }; - const int extruder_idx = 0; // #ys_FIXME for (const std::string opt_key : { "filament_retract_length", @@ -1879,7 +1893,7 @@ void TabFilament::add_filament_overrides_page() "filament_wipe", "filament_retract_before_wipe" }) - append_single_option_line(opt_key, extruder_idx); + create_line_with_near_label_widget(optgroup, opt_key, extruder_idx); } void TabFilament::update_filament_overrides_page() @@ -1914,14 +1928,7 @@ void TabFilament::update_filament_overrides_page() for (const std::string& opt_key : opt_keys) { bool is_checked = opt_key=="filament_retract_length" ? true : have_retract_length; - m_overrides_options[opt_key]->Enable(is_checked); - - is_checked &= !m_config->option(opt_key)->is_nil(); - m_overrides_options[opt_key]->SetValue(is_checked); - - Field* field = optgroup->get_fieldc(opt_key, extruder_idx); - if (field != nullptr) - field->toggle(is_checked); + update_line_with_near_label_widget(optgroup, opt_key, extruder_idx, is_checked); } } @@ -1952,13 +1959,14 @@ void TabFilament::build() }; optgroup = page->new_optgroup(L("Temperature")); + + create_line_with_near_label_widget(optgroup, "idle_temperature"); + Line line = { L("Nozzle"), "" }; line.append_option(optgroup->get_option("first_layer_temperature")); line.append_option(optgroup->get_option("temperature")); optgroup->append_line(line); - optgroup->append_single_option_line("idle_temperature"); - line = { L("Bed"), "" }; line.append_option(optgroup->get_option("first_layer_bed_temperature")); line.append_option(optgroup->get_option("bed_temperature")); @@ -2144,6 +2152,14 @@ void TabFilament::toggle_options() if (m_active_page->title() == "Filament Overrides") update_filament_overrides_page(); + + if (m_active_page->title() == "Filament") { + Page* page = m_active_page; + + const auto og_it = std::find_if(page->m_optgroups.begin(), page->m_optgroups.end(), [](const ConfigOptionsGroupShp og) { return og->title == "Temperature"; }); + if (og_it != page->m_optgroups.end()) + update_line_with_near_label_widget(*og_it, "idle_temperature"); + } } void TabFilament::update() diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index c060eb7fd..b131761e6 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -436,6 +436,8 @@ private: ogStaticText* m_volumetric_speed_description_line {nullptr}; ogStaticText* m_cooling_description_line {nullptr}; + void create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string &opt_key, int opt_index = 0); + void update_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string &opt_key, int opt_index = 0, bool is_checked = true); void add_filament_overrides_page(); void update_filament_overrides_page(); void update_volumetric_flow_preset_hints(); From ae15032e0f5c973acf60e4590f8739521b72ff00 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Sat, 21 Jan 2023 15:11:20 +0100 Subject: [PATCH 20/22] Wipe tower: fixed missing travels to wipe tower on layers with no toolchanges --- src/libslic3r/GCode.cpp | 49 ++++++++----------------------- src/libslic3r/GCode/WipeTower.cpp | 3 +- 2 files changed, 13 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ffe19a9cb..ffffd9d31 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -230,9 +230,16 @@ namespace Slic3r { std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); - if (gcodegen.config().single_extruder_multi_material && ! tcr.priming) { + double current_z = gcodegen.writer().get_position().z(); + if (z == -1.) // in case no specific z was provided, print at current_z pos + z = current_z; + + const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id); + const bool will_go_down = ! is_approx(z, current_z); + + if (! needs_toolchange || (gcodegen.config().single_extruder_multi_material && ! tcr.priming)) { // Move over the wipe tower. If this is not single-extruder MM, the first wipe tower move following the - // toolchange will travel there anyway. + // toolchange will travel there anyway (if there is a toolchange). gcode += gcodegen.retract(); gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); gcode += gcodegen.travel_to( @@ -241,47 +248,16 @@ namespace Slic3r { "Travel to a Wipe Tower"); gcode += gcodegen.unretract(); } - - double current_z = gcodegen.writer().get_position().z(); - if (z == -1.) // in case no specific z was provided, print at current_z pos - z = current_z; - if (! is_approx(z, current_z)) { + + if (will_go_down) { gcode += gcodegen.writer().retract(); gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); gcode += gcodegen.writer().unretract(); } - - - // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. - // Otherwise, leave control to the user completely. - /*std::string toolchange_gcode_str; - const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; - if (! toolchange_gcode.empty()) { - DynamicConfig config; - int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; - config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); - config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); - config.set_key_value("toolchange_z", new ConfigOptionFloat(z)); - config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z)); - toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); - check_add_eol(toolchange_gcode_str); - } - - std::string toolchange_command; - if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) - toolchange_command = gcodegen.writer().toolchange(new_extruder_id); - if (!custom_gcode_changes_tool(toolchange_gcode_str, gcodegen.writer().toolchange_prefix(), new_extruder_id)) - toolchange_gcode_str += toolchange_command; - else { - // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code. - }*/ - std::string toolchange_gcode_str; std::string deretraction_str; - if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) { + if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) { if (gcodegen.config().single_extruder_multi_material) gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines. toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z @@ -302,7 +278,6 @@ namespace Slic3r { gcode += tcr_gcode; check_add_eol(toolchange_gcode_str); - // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(end_pos.cast()); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index fec2eb86b..04fab3fcd 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -822,9 +822,8 @@ void WipeTower::toolchange_Unload( writer.travel(ramming_start_pos); // move to starting position else writer.set_position(ramming_start_pos); - // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower: - if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) { + if (m_semm && (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion ))) { // this is y of the center of previous sparse infill border float sparse_beginning_y = 0.f; From 24a91d7420b4d723b8fab5483695f2ff490bc7a1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 23 Jan 2023 13:23:50 +0100 Subject: [PATCH 21/22] Fixed is_nil(size_t) when checking out-of-range element --- src/libslic3r/Config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index ef3dc32c8..23be26ee7 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -776,7 +776,7 @@ public: static int nil_value() { return std::numeric_limits::max(); } // A scalar is nil, or all values of a vector are nil. bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } - bool is_nil(size_t idx) const override { return this->values[idx] == nil_value(); } + bool is_nil(size_t idx) const override { return values[idx < this->values.size() ? idx : 0] == nil_value(); } std::string serialize() const override { From 5b0a0897f44b8b122e88fcd81e75f7422231854d Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 26 Jan 2023 09:43:08 +0100 Subject: [PATCH 22/22] followup of 70a9520cc3e2caf5f9062eac565cc3a297b881a5 into_u8 instead of format --- src/slic3r/GUI/UpdateDialogs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 5663262ca..7640f3220 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -193,7 +193,7 @@ AppUpdateDownloadDialog::AppUpdateDownloadDialog( const Semver& ver_online, boos btn_ok->SetLabel(_L("Download")); btn_ok->Bind(wxEVT_BUTTON, ([this, path](wxCommandEvent& e){ boost::system::error_code ec; - std::string input = GUI::format(txtctrl_path->GetValue()); + std::string input = GUI::into_u8(txtctrl_path->GetValue()); boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(input), ec); if (ec) dir = boost::filesystem::path(input);