diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 7b3def024f..111f9a4713 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -292,9 +292,29 @@ bool GCode::do_export(FILE *file, Print &print) boost::ifind_first(print.config.start_gcode.value, std::string("M140")).empty() && boost::ifind_first(print.config.start_gcode.value, std::string("M190")).empty()) write(file, m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.value, true)); - + + // Get optimal tool ordering to minimize tool switches of a multi-exruder print. + // For a print by objects, find the 1st printing object. + std::vector tool_ordering; + unsigned int initial_extruder_id = (unsigned int)-1; + size_t initial_print_object_id = 0; + if (print.config.complete_objects.value) { + tool_ordering = ToolOrdering::tool_ordering(print, initial_extruder_id); + initial_extruder_id = ToolOrdering::first_extruder(tool_ordering); + } else { + for (; initial_print_object_id < print.objects.size() && initial_extruder_id == (unsigned int)-1; ++ initial_print_object_id) { + tool_ordering = ToolOrdering::tool_ordering(*print.objects[initial_print_object_id], initial_extruder_id); + initial_extruder_id = ToolOrdering::first_extruder(tool_ordering); + } + } + if (initial_extruder_id == (unsigned int)-1) + // Nothing to print! + initial_extruder_id = 0; + // Set extruder(s) temperature before and after start G-code. this->_print_first_layer_extruder_temperatures(file, print, false); + // Let the start-up script prime the 1st printing tool. + m_placeholder_parser.set("initial_tool", initial_extruder_id); fprintf(file, "%s\n", m_placeholder_parser.process(print.config.start_gcode.value).c_str()); this->_print_first_layer_extruder_temperatures(file, print, true); @@ -355,8 +375,8 @@ bool GCode::do_export(FILE *file, Print &print) } // Set initial extruder only after custom start G-code. - write(file, this->set_extruder(print.extruders().front())); - + write(file, this->set_extruder(initial_extruder_id)); + // Do all objects for each layer. if (print.config.complete_objects.value) { // Print objects from the smallest to the tallest to avoid collisions @@ -364,8 +384,19 @@ bool GCode::do_export(FILE *file, Print &print) std::vector objects(print.objects); std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; }); size_t finished_objects = 0; - for (PrintObject *object : objects) { - for (const Point © : object->_shifted_copies) { + for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) { + const PrintObject &object = *print.objects[object_id]; + for (const Point © : object._shifted_copies) { + // Get optimal tool ordering to minimize tool switches of a multi-exruder print. + if (object_id != initial_print_object_id || © != object._shifted_copies.data()) { + // Don't initialize for the first object and first copy. + tool_ordering = ToolOrdering::tool_ordering(object, initial_extruder_id); + unsigned int new_extruder_id = ToolOrdering::first_extruder(tool_ordering); + if (new_extruder_id == (unsigned int)-1) + // Skip this object. + continue; + initial_extruder_id = new_extruder_id; + } this->set_origin(unscale(copy.x), unscale(copy.y)); if (finished_objects > 0) { // Move to the origin position for the copy we're going to print. @@ -385,16 +416,14 @@ bool GCode::do_export(FILE *file, Print &print) // Set first layer extruder. this->_print_first_layer_extruder_temperatures(file, print, false); } - // Get optimal tool ordering to minimize tool switches of a multi-exruder print. - std::vector tool_ordering = ToolOrdering::tool_ordering(*object); // Pair the object layers with the support layers by z, extrude them. size_t idx_object_layer = 0; size_t idx_support_layer = 0; std::vector layers_to_print(1, LayerToPrint()); LayerToPrint &layer_to_print = layers_to_print.front(); - while (idx_object_layer < object->layers.size() || idx_support_layer < object->support_layers.size()) { - layer_to_print.object_layer = (idx_object_layer < object->layers.size()) ? object->layers[idx_object_layer ++] : nullptr; - layer_to_print.support_layer = (idx_support_layer < object->support_layers.size()) ? object->support_layers[idx_support_layer ++] : nullptr; + while (idx_object_layer < object.layers.size() || idx_support_layer < object.support_layers.size()) { + layer_to_print.object_layer = (idx_object_layer < object.layers.size()) ? object.layers[idx_object_layer ++] : nullptr; + layer_to_print.support_layer = (idx_support_layer < object.support_layers.size()) ? object.support_layers[idx_support_layer ++] : nullptr; if (layer_to_print.object_layer && layer_to_print.support_layer) { if (layer_to_print.object_layer->print_z < layer_to_print.support_layer->print_z) { layer_to_print.support_layer = nullptr; @@ -406,7 +435,7 @@ bool GCode::do_export(FILE *file, Print &print) } auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), ToolOrdering::LayerTools(layer_to_print.layer()->print_z)); assert(it_layer_tools != tool_ordering.end() && it_layer_tools->print_z == layer_to_print.layer()->print_z); - this->process_layer(file, print, layers_to_print, *it_layer_tools, © - object->_shifted_copies.data()); + this->process_layer(file, print, layers_to_print, *it_layer_tools, © - object._shifted_copies.data()); } write(file, this->filter(m_cooling_buffer->flush(), true)); ++ finished_objects; @@ -442,8 +471,6 @@ bool GCode::do_export(FILE *file, Print &print) } ++ object_order; } - // Get optimal tool ordering to minimize tool switches of a multi-exruder print. - std::vector tool_ordering = ToolOrdering::tool_ordering(print); // Prusa Multi-Material wipe tower. if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value && ! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0) { @@ -596,9 +623,14 @@ void GCode::process_layer( const size_t single_object_idx) { assert(! layers.empty()); + assert(! layer_tools.extruders.empty()); // Either printing all copies of all objects, or just a single copy of a single object. assert(single_object_idx == size_t(-1) || layers.size() == 1); + if (layer_tools.extruders.empty()) + // Nothing to extrude. + return; + // Extract 1st object_layer and support_layer of this set of layers with an equal print_z. const Layer *object_layer = nullptr; const SupportLayer *support_layer = nullptr; @@ -666,11 +698,10 @@ void GCode::process_layer( gcode += pp.process(print.config.layer_gcode.value) + "\n"; } - if (! m_brim_done) - // Switch the extruder to the extruder of the perimeters, so the perimeters extruder will be primed - // by the skirt before the brim is extruded with the same extruder. - gcode += this->set_extruder(layer_tools.extruders.front()); - + if (m_wipe_tower && ! m_wipe_tower->finished() && layer.id() == 0 && m_writer.extruder()->id == layer_tools.extruders.front()) + // Trigger the tool change explicitely to draw the wipe tower brim always. + gcode += this->wipe_tower_tool_change(layer_tools.extruders.front()); + // 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. bool extrude_skirt = diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp index b9240dabc3..9838f65c93 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.cpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp @@ -64,23 +64,29 @@ static void collect_extruders(const PrintObject &object, std::vector } // Reorder extruders to minimize layer changes. -static void reorder_extruders(std::vector &layers) +static void reorder_extruders(std::vector &layers, unsigned int last_extruder_id) { if (layers.empty()) return; - // Initialize the last_extruder_id with the first non-zero extruder id used for the print. - unsigned int last_extruder_id = 0; - for (size_t i = 0; i < layers.size() && last_extruder_id == 0; ++ i) { - const LayerTools < = layers[i]; - for (unsigned int extruder_id : lt.extruders) - if (extruder_id > 0) { - last_extruder_id = extruder_id; - break; - } - } - if (last_extruder_id == 0) - last_extruder_id = 1; + if (last_extruder_id == (unsigned int)-1) { + // The initial print extruder has not been decided yet. + // Initialize the last_extruder_id with the first non-zero extruder id used for the print. + last_extruder_id = 0; + for (size_t i = 0; i < layers.size() && last_extruder_id == 0; ++ i) { + const LayerTools < = layers[i]; + for (unsigned int extruder_id : lt.extruders) + if (extruder_id > 0) { + last_extruder_id = extruder_id; + break; + } + } + if (last_extruder_id == 0) + // Nothing to extrude. + return; + } else + // 1 based index + ++ last_extruder_id; for (LayerTools < : layers) { if (lt.extruders.empty()) @@ -117,14 +123,16 @@ static void fill_wipe_tower_partitions(std::vector &layers) return; // Count the minimum number of tool changes per layer. - for (LayerTools < : layers) - lt.wipe_tower_partitions = std::max(0, int(layers.front().extruders.size()) - 1); - - // In case a distinct set of tools are used between two layers, there will be an additional tool change at the start of a layer. - //FIXME this does not minimize the number of tool changes in worst case. - for (size_t i = 1; i < layers.size(); ++ i) - if (layers[i-1].extruders.back() != layers[i].extruders.front()) - ++ layers[i].wipe_tower_partitions; + size_t last_extruder = size_t(-1); + for (LayerTools < : layers) { + lt.wipe_tower_partitions = layers.front().extruders.size(); + if (! lt.extruders.empty()) { + if (last_extruder == size_t(-1) || last_extruder == lt.extruders.front()) + // The first extruder on this layer is equal to the current one, no need to do an initial tool change. + -- lt.wipe_tower_partitions; + last_extruder = lt.extruders.back(); + } + } // Propagate the wipe tower partitions down to support the upper partitions by the lower partitions. for (int i = int(layers.size()) - 2; i >= 0; -- i) @@ -133,7 +141,7 @@ static void fill_wipe_tower_partitions(std::vector &layers) // For the use case when each object is printed separately // (print.config.complete_objects is true). -std::vector tool_ordering(PrintObject &object) +std::vector tool_ordering(const PrintObject &object, unsigned int first_extruder) { // Initialize the print layers for just a single object. std::vector layers; @@ -153,7 +161,7 @@ std::vector tool_ordering(PrintObject &object) collect_extruders(object, layers); // Reorder the extruders to minimize tool switches. - reorder_extruders(layers); + reorder_extruders(layers, first_extruder); fill_wipe_tower_partitions(layers); return layers; @@ -161,7 +169,7 @@ std::vector tool_ordering(PrintObject &object) // For the use case when all objects are printed at once. // (print.config.complete_objects is false). -std::vector tool_ordering(const Print &print) +std::vector tool_ordering(const Print &print, unsigned int first_extruder) { // Initialize the print layers for all objects and all layers. std::vector layers; @@ -184,11 +192,27 @@ std::vector tool_ordering(const Print &print) collect_extruders(*object, layers); // Reorder the extruders to minimize tool switches. - reorder_extruders(layers); + reorder_extruders(layers, first_extruder); fill_wipe_tower_partitions(layers); return layers; } +unsigned int first_extruder(const std::vector &layer_tools) +{ + for (const auto < : layer_tools) + if (! lt.extruders.empty()) + return lt.extruders.front(); + return (unsigned int)-1; +} + +unsigned int last_extruder(const std::vector &layer_tools) +{ + for (auto lt_it = layer_tools.rend(); lt_it != layer_tools.rbegin(); ++ lt_it) + if (! lt_it->extruders.empty()) + return lt_it->extruders.back(); + return (unsigned int)-1; +} + } // namespace ToolOrdering } // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp index 0556f7e2ef..1f649b7e0a 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.hpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp @@ -26,11 +26,17 @@ struct LayerTools // For the use case when each object is printed separately // (print.config.complete_objects is true). -extern std::vector tool_ordering(PrintObject &object); +extern std::vector tool_ordering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1); // For the use case when all objects are printed at once. // (print.config.complete_objects is false). -extern std::vector tool_ordering(const Print &print); +extern std::vector tool_ordering(const Print &print, unsigned int first_extruder = (unsigned int)-1); + +// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed. +extern unsigned int first_extruder(const std::vector &layer_tools); + +// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed. +extern unsigned int last_extruder(const std::vector &layer_tools); } // namespace ToolOrdering } // namespace SLic3r