From 4bd1de2943670d6b121e527e269fba31da92e5cd Mon Sep 17 00:00:00 2001 From: bene Date: Sun, 10 Sep 2023 16:46:02 +0200 Subject: [PATCH] issue-7932: add option to repeat last layer while reducing extrusion to get a flush last layer in vase mode closes #7932 --- src/libslic3r/GCode.cpp | 7 +- src/libslic3r/GCode/SpiralVase.cpp | 115 ++++++++++++++++++-------- src/libslic3r/GCode/SpiralVase.hpp | 28 ++++--- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 7 ++ src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/ConfigManipulation.cpp | 3 + src/slic3r/GUI/Tab.cpp | 1 + 8 files changed, 116 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 74a40afd9fd..3d46cb65024 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1578,11 +1578,16 @@ void GCode::process_layers( } }); const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [spiral_vase = this->m_spiral_vase.get()](LayerResult in) -> LayerResult { + [spiral_vase = this->m_spiral_vase.get(), &layers_to_print](LayerResult in) -> LayerResult { if (in.nop_layer_result) return in; spiral_vase->enable(in.spiral_vase_enable); + + if (in.layer_id == layers_to_print.size() - 1) { + spiral_vase->setLastLayer(); + } + return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; }); const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, diff --git a/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp index 4fb52443ac5..4351383082d 100644 --- a/src/libslic3r/GCode/SpiralVase.cpp +++ b/src/libslic3r/GCode/SpiralVase.cpp @@ -21,52 +21,59 @@ std::string SpiralVase::process_layer(const std::string &gcode) at the beginning - each layer is composed by suitable geometry (i.e. a single complete loop) - loops were not clipped before calling this method */ - + // If we're not going to modify G-code, just feed it to the reader // in order to update positions. - if (! m_enabled) { + if (!m_enabled) { m_reader.parse_buffer(gcode); return gcode; } - + // Get total XY length for this layer by summing all extrusion moves. float total_layer_length = 0; - float layer_height = 0; - float z = 0.f; - + float layer_height = 0; + float layer_z = 0.f; + { - //FIXME Performance warning: This copies the GCodeConfig of the reader. - GCodeReader r = m_reader; // clone - bool set_z = false; - r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z] - (GCodeReader &reader, const GCodeReader::GCodeLine &line) { - if (line.cmd_is("G1")) { - if (line.extruding(reader)) { - total_layer_length += line.dist_XY(reader); - } else if (line.has(Z)) { - layer_height += line.dist_Z(reader); - if (!set_z) { - z = line.new_Z(reader); - set_z = true; - } - } - } - }); + // FIXME Performance warning: This copies the GCodeConfig of the reader. + GCodeReader r = m_reader; // clone + bool set_z = false; + r.parse_buffer(gcode, + [&total_layer_length, &layer_height, &layer_z, &set_z](GCodeReader &reader, const GCodeReader::GCodeLine &line) { + if (line.cmd_is("G1")) { + if (line.extruding(reader)) { + total_layer_length += line.dist_XY(reader); + } else if (line.has(Z)) { + layer_height += line.dist_Z(reader); + if (!set_z) { + layer_z = line.new_Z(reader); + set_z = true; + } + } + } + }); + } + + // get a copy of the reader in case a last_layer_transition should be done + bool last_layer_transition = m_config.spiral_vase_flush_finish && m_last_layer && m_config.use_relative_e_distances.value; + GCodeReader last_layer_reader; + if (last_layer_transition) { + last_layer_reader = m_reader; // clone } - - // Remove layer height from initial Z. - z -= layer_height; - + + // Remove layer height from initial Z. + float z = layer_z - layer_height; + std::string new_gcode; - //FIXME Tapering of the transition layer only works reliably with relative extruder distances. + // FIXME Tapering of the transition layer only works reliably with relative extruder distances. // For absolute extruder distances it will be switched off. // Tapering the absolute extruder distances requires to process every extrusion value after the first transition // layer. - bool transition = m_transition_layer && m_config.use_relative_e_distances.value; + bool transition = m_transition_layer && m_config.use_relative_e_distances.value; float layer_height_factor = layer_height / total_layer_length; - float len = 0.f; - m_reader.parse_buffer(gcode, [&new_gcode, &z, total_layer_length, layer_height_factor, transition, &len] - (GCodeReader &reader, GCodeReader::GCodeLine line) { + float len = 0.f; + m_reader.parse_buffer(gcode, [&new_gcode, &z, total_layer_length, layer_height_factor, transition, &len](GCodeReader &reader, + GCodeReader::GCodeLine line) { if (line.cmd_is("G1")) { if (line.has_z()) { // If this is the initial Z move of the layer, replace it with a @@ -87,7 +94,7 @@ std::string SpiralVase::process_layer(const std::string &gcode) new_gcode += line.raw() + '\n'; } return; - + /* Skip travel moves: the move to first perimeter point will cause a visible seam when loops are not aligned in XY; by skipping it we blend the first loop move in the XY plane (although the smoothness @@ -98,8 +105,46 @@ std::string SpiralVase::process_layer(const std::string &gcode) } new_gcode += line.raw() + '\n'; }); - + + if (last_layer_transition) { + // Repeat last layer on final height while reducing extrusion to zero to get a flush last layer. + len = 0.f; + + last_layer_reader.parse_buffer(gcode, [&new_gcode, &layer_z, total_layer_length, layer_height_factor, transition, + last_layer_transition, &len](GCodeReader &reader, GCodeReader::GCodeLine line) { + if (line.cmd_is("G1")) { + if (line.has_z()) { + // Set z to final layer_z. + // We should actually already be at this height. + line.set(reader, Z, layer_z); + new_gcode += line.raw() + '\n'; + return; + } else { + float dist_XY = line.dist_XY(reader); + if (dist_XY > 0) { + if (line.extruding(reader)) { + len += dist_XY; + if (line.has(E)) + // Transition layer, modulate the amount of extrusion to zero. + line.set(reader, E, line.value(E) * (1.f - len / total_layer_length)); + new_gcode += line.raw() + '\n'; + } + return; + } + } + } else { + // We need to remove the layer change comment before the last_layer_transition, + // as otherwise the gcode preview will not work properly. The transition layer will be part of the last layer. + // Note: we need to keep the other layer change commands though - e.g. resetting the relative E value. + if (last_layer_transition && line.comment() == GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change)) { + return; + } + new_gcode += line.raw() + '\n'; + } + }); + } + return new_gcode; } -} +} // namespace Slic3r diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index 19bc3f72e58..130b5a83da2 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -14,30 +14,36 @@ namespace Slic3r { -class SpiralVase { +class SpiralVase +{ public: SpiralVase(const PrintConfig &config) : m_config(config) { - m_reader.z() = (float)m_config.z_offset; + m_reader.z() = (float) m_config.z_offset; m_reader.apply_config(m_config); }; - void enable(bool en) { - m_transition_layer = en && ! m_enabled; - m_enabled = en; + void enable(bool en) + { + m_transition_layer = en && !m_enabled; + m_enabled = en; } + void setLastLayer() { m_last_layer = true; } + std::string process_layer(const std::string &gcode); - + private: - const PrintConfig &m_config; - GCodeReader m_reader; + const PrintConfig &m_config; + GCodeReader m_reader; - bool m_enabled = false; + bool m_enabled = false; // First spiral vase layer. Layer height has to be ramped up from zero to the target layer height. - bool m_transition_layer = false; + bool m_transition_layer = false; + // Last spiral vase layer. Repeat last layer on final height while reducing extrusion till zero. + bool m_last_layer = false; }; -} +} // namespace Slic3r #endif // slic3r_SpiralVase_hpp_ diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a75517f3780..509a0866827 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -433,7 +433,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) } static std::vector s_Preset_print_options { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode", + "layer_height", "first_layer_height", "perimeters", "spiral_vase", "spiral_vase_flush_finish", "slice_closing_radius", "slicing_mode", "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0d925d729f4..702005f1025 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2532,6 +2532,13 @@ void PrintConfigDef::init_fff_params() "It won't work when printing more than one single object."); def->set_default_value(new ConfigOptionBool(false)); + def = this->add("spiral_vase_flush_finish", coBool); + def->label = L("Spiral vase flush finish"); + def->tooltip = L("End a spiral vase mode print with a flush layer. " + "Disable to restore the old behavior, which will end the print with a normal " + "vase mode layer - i.e. a slightly tilted layer."); + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("standby_temperature_delta", coInt); def->label = L("Temperature variation"); // TRN PrintSettings : "Ooze prevention" > "Temperature variation" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index db6a538126e..ca6bca1313e 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -840,6 +840,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInts, slowdown_below_layer_time)) ((ConfigOptionFloat, solid_infill_acceleration)) ((ConfigOptionBool, spiral_vase)) + ((ConfigOptionBool, spiral_vase_flush_finish)) ((ConfigOptionInt, standby_temperature_delta)) ((ConfigOptionInts, temperature)) ((ConfigOptionInt, threads)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index e6abeda550d..9513cb1fca2 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -237,6 +237,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("infill_anchor", has_infill_anchors); bool has_spiral_vase = config->opt_bool("spiral_vase"); + bool have_spiral_vase_flush_finish = config->opt_bool("spiral_vase_flush_finish"); + toggle_field("spiral_vase_flush_finish", has_spiral_vase); + bool has_top_solid_infill = config->opt_int("top_solid_layers") > 0; bool has_bottom_solid_infill = config->opt_int("bottom_solid_layers") > 0; bool has_solid_infill = has_top_solid_infill || has_bottom_solid_infill; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ec8ae1139cf..aab1c9a00be 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1429,6 +1429,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Vertical shells")); optgroup->append_single_option_line("perimeters", category_path + "perimeters"); optgroup->append_single_option_line("spiral_vase", category_path + "spiral-vase"); + optgroup->append_single_option_line("spiral_vase_flush_finish"); Line line { "", "" }; line.full_width = 1;