From a85a0a8191265bdcc35463f52e62c392aaa0022e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 24 Jan 2024 11:25:02 +0100 Subject: [PATCH 1/5] SPE-2098: Show color_change_extruder in Custom G-code editor. --- src/libslic3r/PrintConfig.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f7ead0b0da..e72e9bff92 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5397,6 +5397,8 @@ static std::map s_CustomGcodeSpecificP {"before_layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, {"layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, {"toolchange_gcode", {"layer_num", "layer_z", "max_layer_z", "previous_extruder", "next_extruder", "toolchange_z"}}, + {"color_change_gcode", {"color_change_extruder"}}, + {"pause_print_gcode", {"color_change_extruder"}}, }; const std::map& custom_gcode_specific_placeholders() @@ -5435,6 +5437,10 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() def = this->add("toolchange_z", coFloat); def->label = L("Toolchange Z"); def->tooltip = L("Height above the print bed when the toolchange takes place. Usually the same as layer_z, but can be different."); + + def = this->add("color_change_extruder", coInt); + def->label = L("Color change extruder"); + def->tooltip = L("Index of the extruder for which color change will be performed. The index is zero based (first extruder has index 0)."); } const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; From ff3612840c2d84e01ddc9b3fd72c11232fcefff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 25 Jan 2024 16:12:01 +0100 Subject: [PATCH 2/5] SPE-2098: Pass color_change_extruder also into Color Change G-code. Some minor refactoring of ProcessLayer::emit_custom_gcode_per_print_z(); --- src/libslic3r/GCode.cpp | 65 ++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 583281dddd..769cfe37f6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1916,66 +1916,59 @@ namespace ProcessLayer // Extruder switches are processed by LayerTools, they should be filtered out. assert(custom_gcode->type != CustomGCode::ToolChange); - CustomGCode::Type gcode_type = custom_gcode->type; - bool color_change = gcode_type == CustomGCode::ColorChange; - bool tool_change = gcode_type == CustomGCode::ToolChange; + CustomGCode::Type gcode_type = custom_gcode->type; + const bool color_change = gcode_type == CustomGCode::ColorChange; + const bool tool_change = gcode_type == CustomGCode::ToolChange; // Tool Change is applied as Color Change for a single extruder printer only. - assert(! tool_change || single_extruder_printer); - - std::string pause_print_msg; - int m600_extruder_before_layer = -1; - if (color_change && custom_gcode->extruder > 0) - m600_extruder_before_layer = custom_gcode->extruder - 1; - else if (gcode_type == CustomGCode::PausePrint) - pause_print_msg = custom_gcode->extra; + assert(!tool_change || single_extruder_printer); // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count - if (color_change || tool_change) - { - assert(m600_extruder_before_layer >= 0); + if (color_change || tool_change) { + int color_change_extruder = -1; + if (color_change && custom_gcode->extruder > 0) + color_change_extruder = custom_gcode->extruder - 1; + + assert(color_change_extruder >= 0); // Color Change or Tool Change as Color Change. // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "," + custom_gcode->color + "\n"; + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(color_change_extruder) + "," + custom_gcode->color + "\n"; - if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer - // && !MMU1 - ) { + DynamicConfig cfg; + cfg.set_key_value("color_change_extruder", new ConfigOptionInt(color_change_extruder)); + if (!single_extruder_printer && color_change_extruder >= 0 && first_extruder_id != unsigned(color_change_extruder)) { //! FIXME_in_fw show message during print pause - // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? Why is that not - // passed to color_change_gcode below? - DynamicConfig cfg; - cfg.set_key_value("color_change_extruder", new ConfigOptionInt(m600_extruder_before_layer)); + // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); gcode += "\n"; - gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n"; - } - else { - gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id); + gcode += "M117 Change filament for Extruder " + std::to_string(color_change_extruder) + "\n"; + } else { + gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id, &cfg); gcode += "\n"; //FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after // return from M600. Thus the G-code generated by the following line is ignored. // see GH issue #6362 gcodegen.writer().unretract(); } - } - else { - if (gcode_type == CustomGCode::PausePrint) // Pause print - { + } else { + if (gcode_type == CustomGCode::PausePrint) { // Pause print + const std::string pause_print_msg = custom_gcode->extra; + // add tag for processor gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; //! FIXME_in_fw show message during print pause - if (!pause_print_msg.empty()) - gcode += "M117 " + pause_print_msg + "\n"; - gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id); - } - else { + if (!pause_print_msg.empty()) + gcode += "M117 " + pause_print_msg + "\n"; + + DynamicConfig cfg; + cfg.set_key_value("color_change_extruder", new ConfigOptionInt(int(current_extruder_id))); + gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); + } else { // add tag for processor gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; if (gcode_type == CustomGCode::Template) // Template Custom Gcode gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id); else // custom Gcode gcode += custom_gcode->extra; - } gcode += "\n"; } From 145fb92e078fb4bb1db975f0e6ce6c4cacc2148f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 25 Jan 2024 16:36:22 +0100 Subject: [PATCH 3/5] SPE-2098: Emit M600 instead of M601 for multi-tool printers. --- src/libslic3r/GCode.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 769cfe37f6..e50fd3b82b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1924,6 +1924,9 @@ namespace ProcessLayer // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count if (color_change || tool_change) { + const bool single_extruder_multi_material = config.single_extruder_multi_material; + const bool single_extruder_printer = config.nozzle_diameter.size() == 1; + int color_change_extruder = -1; if (color_change && custom_gcode->extruder > 0) color_change_extruder = custom_gcode->extruder - 1; @@ -1935,7 +1938,7 @@ namespace ProcessLayer DynamicConfig cfg; cfg.set_key_value("color_change_extruder", new ConfigOptionInt(color_change_extruder)); - if (!single_extruder_printer && color_change_extruder >= 0 && first_extruder_id != unsigned(color_change_extruder)) { + if (single_extruder_multi_material && !single_extruder_printer && color_change_extruder >= 0 && first_extruder_id != unsigned(color_change_extruder)) { //! FIXME_in_fw show message during print pause // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); From 1071645967dd8411f7aa4a1d798a1bb13c4237e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 25 Jan 2024 17:04:38 +0100 Subject: [PATCH 4/5] Separate color change handing from emit_custom_color_change_gcode_per_print_z() into emit_custom_color_change_gcode_per_print_z(). --- src/libslic3r/GCode.cpp | 132 ++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 60 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e50fd3b82b..994fb3ac17 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1901,80 +1901,92 @@ std::vector GCodeGenerator::sort_print_object_i namespace ProcessLayer { +static std::string emit_custom_color_change_gcode_per_print_z( + GCodeGenerator &gcodegen, + const CustomGCode::Item &custom_gcode, + unsigned int current_extruder_id, + unsigned int first_extruder_id, // ID of the first extruder printing this layer. + const PrintConfig &config +) { + const bool single_extruder_multi_material = config.single_extruder_multi_material; + const bool single_extruder_printer = config.nozzle_diameter.size() == 1; + const bool color_change = custom_gcode.type == CustomGCode::ColorChange; + + std::string gcode; + + int color_change_extruder = -1; + if (color_change && custom_gcode.extruder > 0) + color_change_extruder = custom_gcode.extruder - 1; + + assert(color_change_extruder >= 0); + // Color Change or Tool Change as Color Change. + // add tag for processor + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(color_change_extruder) + "," + custom_gcode.color + "\n"; + + DynamicConfig cfg; + cfg.set_key_value("color_change_extruder", new ConfigOptionInt(color_change_extruder)); + if (single_extruder_multi_material && !single_extruder_printer && color_change_extruder >= 0 && first_extruder_id != unsigned(color_change_extruder)) { + //! FIXME_in_fw show message during print pause + // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? + gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); + gcode += "\n"; + gcode += "M117 Change filament for Extruder " + std::to_string(color_change_extruder) + "\n"; + } else { + gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id, &cfg); + gcode += "\n"; + //FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after + // return from M600. Thus the G-code generated by the following line is ignored. + // see GH issue #6362 + gcodegen.writer().unretract(); + } + + return gcode; +} + static std::string emit_custom_gcode_per_print_z( GCodeGenerator &gcodegen, - const CustomGCode::Item *custom_gcode, + const CustomGCode::Item &custom_gcode, unsigned int current_extruder_id, // ID of the first extruder printing this layer. unsigned int first_extruder_id, const PrintConfig &config) { std::string gcode; - bool single_extruder_printer = config.nozzle_diameter.size() == 1; - if (custom_gcode != nullptr) { - // Extruder switches are processed by LayerTools, they should be filtered out. - assert(custom_gcode->type != CustomGCode::ToolChange); + // Extruder switches are processed by LayerTools, they should be filtered out. + assert(custom_gcode.type != CustomGCode::ToolChange); - CustomGCode::Type gcode_type = custom_gcode->type; - const bool color_change = gcode_type == CustomGCode::ColorChange; - const bool tool_change = gcode_type == CustomGCode::ToolChange; - // Tool Change is applied as Color Change for a single extruder printer only. - assert(!tool_change || single_extruder_printer); + CustomGCode::Type gcode_type = custom_gcode.type; + const bool color_change = gcode_type == CustomGCode::ColorChange; + const bool tool_change = gcode_type == CustomGCode::ToolChange; + // Tool Change is applied as Color Change for a single extruder printer only. + assert(!tool_change || config.nozzle_diameter.size() == 1); - // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count - if (color_change || tool_change) { - const bool single_extruder_multi_material = config.single_extruder_multi_material; - const bool single_extruder_printer = config.nozzle_diameter.size() == 1; + // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count + if (color_change || tool_change) { + gcode += emit_custom_color_change_gcode_per_print_z(gcodegen, custom_gcode, current_extruder_id, first_extruder_id, config); + } else { + if (gcode_type == CustomGCode::PausePrint) { // Pause print + const std::string pause_print_msg = custom_gcode.extra; - int color_change_extruder = -1; - if (color_change && custom_gcode->extruder > 0) - color_change_extruder = custom_gcode->extruder - 1; - - assert(color_change_extruder >= 0); - // Color Change or Tool Change as Color Change. // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(color_change_extruder) + "," + custom_gcode->color + "\n"; + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; + //! FIXME_in_fw show message during print pause + if (!pause_print_msg.empty()) + gcode += "M117 " + pause_print_msg + "\n"; DynamicConfig cfg; - cfg.set_key_value("color_change_extruder", new ConfigOptionInt(color_change_extruder)); - if (single_extruder_multi_material && !single_extruder_printer && color_change_extruder >= 0 && first_extruder_id != unsigned(color_change_extruder)) { - //! FIXME_in_fw show message during print pause - // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? - gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); - gcode += "\n"; - gcode += "M117 Change filament for Extruder " + std::to_string(color_change_extruder) + "\n"; - } else { - gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id, &cfg); - gcode += "\n"; - //FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after - // return from M600. Thus the G-code generated by the following line is ignored. - // see GH issue #6362 - gcodegen.writer().unretract(); - } + cfg.set_key_value("color_change_extruder", new ConfigOptionInt(int(current_extruder_id))); + gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); } else { - if (gcode_type == CustomGCode::PausePrint) { // Pause print - const std::string pause_print_msg = custom_gcode->extra; - - // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; - //! FIXME_in_fw show message during print pause - if (!pause_print_msg.empty()) - gcode += "M117 " + pause_print_msg + "\n"; - - DynamicConfig cfg; - cfg.set_key_value("color_change_extruder", new ConfigOptionInt(int(current_extruder_id))); - gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); - } else { - // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; - if (gcode_type == CustomGCode::Template) // Template Custom Gcode - gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id); - else // custom Gcode - gcode += custom_gcode->extra; - } - gcode += "\n"; + // add tag for processor + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; + if (gcode_type == CustomGCode::Template) // Template Custom Gcode + gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id); + else // custom Gcode + gcode += custom_gcode.extra; } + gcode += "\n"; } return gcode; @@ -2243,10 +2255,10 @@ LayerResult GCodeGenerator::process_layer( // Map from extruder ID to index of skirt loops to be extruded with that extruder. std::map> skirt_loops_per_extruder; - if (single_object_instance_idx == size_t(-1)) { + if (single_object_instance_idx == size_t(-1) && layer_tools.custom_gcode != nullptr) { // Normal (non-sequential) print. - std::string custom_gcode = ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); - if (layer_tools.custom_gcode != nullptr && layer_tools.custom_gcode->type == CustomGCode::ColorChange) { + std::string custom_gcode = ProcessLayer::emit_custom_gcode_per_print_z(*this, *layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); + if (layer_tools.custom_gcode->type == CustomGCode::ColorChange) { // We have a color change to do on this layer, but we want to do it immediately before the first extrusion instead of now, in order to fix GH #2672 m_pending_pre_extrusion_gcode = custom_gcode; } else { From 8ce089c6ff1aa6b46ddd6cafc28f31f2118b61f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 25 Jan 2024 19:42:39 +0100 Subject: [PATCH 5/5] SPE-2098: Pick a required extruder before a color change for multi-extruder printers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we need to perform a color change on an extruder that isn’t used within the layer, we perform a tool change to select the correct extruder. Then, we perform the color change, and finally, we perform another tool change to switch back to the original extruder. --- src/libslic3r/CustomGCode.cpp | 16 +++++++++ src/libslic3r/CustomGCode.hpp | 4 +++ src/libslic3r/GCode.cpp | 34 ++++++++++++------ src/libslic3r/GCode/ToolOrdering.cpp | 43 +++++++++++++++++----- src/libslic3r/GCode/ToolOrdering.hpp | 5 ++- src/libslic3r/PrintApply.cpp | 54 +++++++++++++++++----------- 6 files changed, 115 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index 7b950b8d7c..92fc8c0d9d 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -72,6 +72,22 @@ std::vector> custom_tool_changes(const Info& cus return custom_tool_changes; } +// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. +// Where print_z corresponds to the layer on which we perform a color change for the specified extruder. +std::vector> custom_color_changes(const Info& custom_gcode_per_print_z, size_t num_extruders) +{ + std::vector> custom_color_changes; + for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes) + if (custom_gcode.type == ColorChange) { + // If extruder count in PrinterSettings was changed, ignore custom g-codes for extruder ids bigger than num_extruders. + assert(custom_gcode.extruder >= 0); + if (size_t(custom_gcode.extruder) <= num_extruders) { + custom_color_changes.emplace_back(custom_gcode.print_z, static_cast(custom_gcode.extruder)); + } + } + return custom_color_changes; +} + } // namespace CustomGCode } // namespace Slic3r diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp index 3563084cb6..939f8bdd51 100644 --- a/src/libslic3r/CustomGCode.hpp +++ b/src/libslic3r/CustomGCode.hpp @@ -91,6 +91,10 @@ extern void check_mode_for_custom_gcode_per_print_z(Info& info); // print_z corresponds to the first layer printed with the new extruder. std::vector> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders); +// Return pairs of sorted by increasing print_z from custom_gcode_per_print_z. +// Where print_z corresponds to the layer on which we perform a color change for the specified extruder. +std::vector> custom_color_changes(const Info& custom_gcode_per_print_z, size_t num_extruders); + } // namespace CustomGCode } // namespace Slic3r diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 994fb3ac17..04770d1083 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2255,16 +2255,6 @@ LayerResult GCodeGenerator::process_layer( // Map from extruder ID to index of skirt loops to be extruded with that extruder. std::map> skirt_loops_per_extruder; - if (single_object_instance_idx == size_t(-1) && layer_tools.custom_gcode != nullptr) { - // Normal (non-sequential) print. - std::string custom_gcode = ProcessLayer::emit_custom_gcode_per_print_z(*this, *layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); - if (layer_tools.custom_gcode->type == CustomGCode::ColorChange) { - // We have a color change to do on this layer, but we want to do it immediately before the first extrusion instead of now, in order to fix GH #2672 - m_pending_pre_extrusion_gcode = custom_gcode; - } else { - gcode += custom_gcode; - } - } // Extrude skirt at the print_z of the raft layers and normal object layers // not at the print_z of the interlaced support material layers. skirt_loops_per_extruder = first_layer ? @@ -2283,6 +2273,23 @@ LayerResult GCodeGenerator::process_layer( } } + const bool has_custom_gcode_to_emit = single_object_instance_idx == size_t(-1) && layer_tools.custom_gcode != nullptr; + const int extruder_id_for_custom_gcode = int(layer_tools.extruder_needed_for_color_changer) - 1; + + if (has_custom_gcode_to_emit && extruder_id_for_custom_gcode == -1) { + // Normal (non-sequential) print with some custom code without picking a specific extruder before it. + // If we don't need to pick a specific extruder before the color change, we can just emit a custom g-code. + // Otherwise, we will emit the g-code after picking the specific extruder. + + std::string custom_gcode = ProcessLayer::emit_custom_gcode_per_print_z(*this, *layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); + if (layer_tools.custom_gcode->type == CustomGCode::ColorChange) { + // We have a color change to do on this layer, but we want to do it immediately before the first extrusion instead of now, in order to fix GH #2672. + m_pending_pre_extrusion_gcode = custom_gcode; + } else { + gcode += custom_gcode; + } + } + // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { @@ -2294,6 +2301,13 @@ LayerResult GCodeGenerator::process_layer( if (layer_tools.has_wipe_tower && m_wipe_tower) m_last_processor_extrusion_role = GCodeExtrusionRole::WipeTower; + if (has_custom_gcode_to_emit && extruder_id_for_custom_gcode == int(extruder_id)) { + assert(m_writer.extruder()->id() == extruder_id_for_custom_gcode); + assert(m_pending_pre_extrusion_gcode.empty()); + // Now we have picked the right extruder, so we can emit the custom g-code. + gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, *layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); + } + if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { const std::pair loops = loops_it->second; this->set_origin(0., 0.); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 2eba21de8c..1bcb076dd3 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -110,7 +110,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude double max_layer_height = calc_max_layer_height(object.print()->config(), object.config().layer_height); // Collect extruders required to print the layers. - this->collect_extruders(object, std::vector>()); + this->collect_extruders(object, std::vector>(), std::vector>()); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -156,17 +156,24 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool // Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object. // Do it only if all the objects were configured to be printed with a single extruder. std::vector> per_layer_extruder_switches; - if (auto num_extruders = unsigned(print.config().nozzle_diameter.size()); - num_extruders > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle + auto num_extruders = unsigned(print.config().nozzle_diameter.size()); + if (num_extruders > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle print.model().custom_gcode_per_print_z.mode == CustomGCode::MultiAsSingle) { // Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material). // There may be custom per-layer tool changes available at the model. per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_extruders); } - // Collect extruders reuqired to print the layers. + // Color changes for each layer to determine which extruder needs to be picked before color change. + // This is done just for multi-extruder printers without enabled Single Extruder Multi Material (tool changer printers). + std::vector> per_layer_color_changes; + if (num_extruders > 1 && print.model().custom_gcode_per_print_z.mode == CustomGCode::MultiExtruder && !print.config().single_extruder_multi_material) { + per_layer_color_changes = custom_color_changes(print.model().custom_gcode_per_print_z, num_extruders); + } + + // Collect extruders required to print the layers. for (auto object : print.objects()) - this->collect_extruders(*object, per_layer_extruder_switches); + this->collect_extruders(*object, per_layer_extruder_switches, per_layer_color_changes); // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); @@ -219,8 +226,11 @@ void ToolOrdering::initialize_layers(std::vector &zs) } // Collect extruders reuqired to print layers. -void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches) -{ +void ToolOrdering::collect_extruders( + const PrintObject &object, + const std::vector> &per_layer_extruder_switches, + const std::vector> &per_layer_color_changes +) { // Collect the support extruders. for (auto support_layer : object.support_layers()) { LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z); @@ -238,10 +248,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto } // Extruder overrides are ordered by print_z. - std::vector>::const_iterator it_per_layer_extruder_override; - it_per_layer_extruder_override = per_layer_extruder_switches.begin(); + std::vector>::const_iterator it_per_layer_extruder_override = per_layer_extruder_switches.begin(); unsigned int extruder_override = 0; + std::vector>::const_iterator it_per_layer_color_changes = per_layer_color_changes.begin(); + // Collect the object extruders. for (auto layer : object.layers()) { LayerTools &layer_tools = this->tools_for_layer(layer->print_z); @@ -253,6 +264,15 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto // Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it. layer_tools.extruder_override = extruder_override; + // Append the extruder needed to be picked before performing the color change. + for (; it_per_layer_color_changes != per_layer_color_changes.end() && it_per_layer_color_changes->first < layer->print_z + EPSILON; ++it_per_layer_color_changes) { + if (std::abs(it_per_layer_color_changes->first - layer->print_z) < EPSILON) { + assert(layer_tools.extruder_needed_for_color_changer == 0); // Just on color change per layer is allowed. + layer_tools.extruder_needed_for_color_changer = it_per_layer_color_changes->second; + layer_tools.extruders.emplace_back(it_per_layer_color_changes->second); + } + } + // What extruders are required to print this object layer? for (const LayerRegion *layerm : layer->regions()) { const PrintRegion ®ion = layerm->region(); @@ -370,6 +390,11 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) std::swap(lt.extruders[i], lt.extruders.front()); break; } + } else if (lt.extruder_needed_for_color_changer != 0) { + // Put the extruder needed for performing the color change at the beginning. + auto it = std::find(lt.extruders.begin(), lt.extruders.end(), lt.extruder_needed_for_color_changer); + assert(it != lt.extruders.end()); + std::rotate(lt.extruders.begin(), it, it + 1); } } last_extruder_id = lt.extruders.back(); diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index 028663660b..aa8f228b24 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -98,6 +98,9 @@ public: // If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with. // If not overriden, it is set to 0. unsigned int extruder_override = 0; + // For multi-extruder printers, when there is a color change, this contains an extruder (1 based) on which the color change will be performed. + // Otherwise, it is set to 0. + unsigned int extruder_needed_for_color_changer = 0; // Should a skirt be printed at this layer? // Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed. bool has_skirt = false; @@ -169,7 +172,7 @@ public: private: void initialize_layers(std::vector &zs); - void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches); + void collect_extruders(const PrintObject &object, const std::vector> &per_layer_extruder_switches, const std::vector> &per_layer_color_changes); void reorder_extruders(unsigned int last_extruder_id); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height); bool insert_wipe_tower_extruder(); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 7baaa75a8d..1d9fdbcc8c 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1,7 +1,7 @@ -///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Roman Beránek @zavorka -///|/ -///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher -///|/ +///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Roman Beránek @zavorka +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Model.hpp" #include "Print.hpp" @@ -169,34 +169,37 @@ static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_ return true; } -// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored. -static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector &va, const std::vector &vb) -{ +// Returns true if va == vb when all CustomGCode items that are not the specified type (not_ignore_type) are ignored. +static bool custom_per_printz_gcodes_tool_changes_differ( + const std::vector &va, + const std::vector &vb, + CustomGCode::Type not_ignore_type +) { auto it_a = va.begin(); auto it_b = vb.begin(); while (it_a != va.end() || it_b != vb.end()) { - if (it_a != va.end() && it_a->type != CustomGCode::ToolChange) { - // Skip any CustomGCode items, which are not tool changes. + if (it_a != va.end() && it_a->type != not_ignore_type) { + // Skip any CustomGCode items, which are not equal to not_ignore_type. ++ it_a; continue; } - if (it_b != vb.end() && it_b->type != CustomGCode::ToolChange) { - // Skip any CustomGCode items, which are not tool changes. + if (it_b != vb.end() && it_b->type != not_ignore_type) { + // Skip any CustomGCode items, which are not equal to not_ignore_type. ++ it_b; continue; } if (it_a == va.end() || it_b == vb.end()) - // va or vb contains more Tool Changes than the other. + // va or vb contains more items of not_ignore_type than the other. return true; - assert(it_a->type == CustomGCode::ToolChange); - assert(it_b->type == CustomGCode::ToolChange); + assert(it_a->type == not_ignore_type); + assert(it_b->type == not_ignore_type); if (*it_a != *it_b) - // The two Tool Changes differ. + // The two items of not_ignore_type differ. return true; ++ it_a; ++ it_b; } - // There is no change in custom Tool Changes. + // There is no change in specified not_ignore_type items. return false; } @@ -1059,11 +1062,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ model_object_status_db.add(*model_object, ModelObjectStatus::New); } else { if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { - update_apply_status(num_extruders_changed || - // Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering. - //FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable - // to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same. - (num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ? + const CustomGCode::Mode current_mode = m_model.custom_gcode_per_print_z.mode; + const CustomGCode::Mode next_mode = model.custom_gcode_per_print_z.mode; + + const bool multi_extruder_differ = (current_mode == next_mode) && (current_mode == CustomGCode::MultiExtruder || next_mode == CustomGCode::MultiExtruder); + // Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering. + // FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable + // to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same. + const bool tool_change_differ = num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes, CustomGCode::ToolChange); + // For multi-extruder printers, we perform a tool change before a color change. + // So, in that case, we must invalidate tool ordering and wipe tower even if custom color change g-codes differ. + const bool color_change_differ = num_extruders > 1 && (next_mode == CustomGCode::MultiExtruder) && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes, CustomGCode::ColorChange); + + update_apply_status( + (num_extruders_changed || tool_change_differ || multi_extruder_differ || color_change_differ) ? // The Tool Ordering and the Wipe Tower are no more valid. this->invalidate_steps({ psWipeTower, psGCodeExport }) : // There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.