diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp index c233d81f6..468f47c15 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.cpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp @@ -9,6 +9,9 @@ namespace Slic3r { // (print.config.complete_objects is true). ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder) { + if (object.layers.empty()) + return; + // Initialize the print layers for just a single object. { std::vector zs; @@ -26,7 +29,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(); + this->fill_wipe_tower_partitions(object.print()->config, object.layers.front()->print_z - object.layers.front()->height); } // For the use case when all objects are printed at once. @@ -34,6 +37,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder) { // Initialize the print layers for all objects and all layers. + coordf_t object_bottom_z = 0.; { std::vector zs; for (auto object : print.objects) { @@ -42,6 +46,8 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder) zs.emplace_back(layer->print_z); for (auto layer : object->support_layers) zs.emplace_back(layer->print_z); + if (! object->layers.empty()) + object_bottom_z = object->layers.front()->print_z - object->layers.front()->height; } this->initialize_layers(zs); } @@ -53,7 +59,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder) // Reorder the extruders to minimize tool switches. this->reorder_extruders(first_extruder); - this->fill_wipe_tower_partitions(); + this->fill_wipe_tower_partitions(print.config, object_bottom_z); } unsigned int ToolOrdering::first_extruder() const @@ -221,7 +227,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id) } } -void ToolOrdering::fill_wipe_tower_partitions() +void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z) { if (m_layer_tools.empty()) return; @@ -244,7 +250,50 @@ void ToolOrdering::fill_wipe_tower_partitions() //FIXME this is a hack to get the ball rolling. for (LayerTools < : m_layer_tools) - lt.has_wipe_tower = lt.has_object && lt.wipe_tower_partitions > 0; + lt.has_wipe_tower = (lt.has_object && lt.wipe_tower_partitions > 0) || lt.print_z < object_bottom_z + EPSILON; + + // Test for a raft, insert additional wipe tower layer to fill in the raft separation gap. + double max_layer_height = FLT_MAX; + for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) { + double mlh = config.max_layer_height.values[i]; + if (mlh == 0.) + mlh = 0.75 * config.nozzle_diameter.values[i]; + max_layer_height = std::min(max_layer_height, mlh); + } + for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) { + const LayerTools < = m_layer_tools[i]; + const LayerTools <_next = m_layer_tools[i + 1]; + if (lt.print_z < object_bottom_z + EPSILON && lt_next.print_z >= object_bottom_z + EPSILON) { + // lt is the last raft layer. Find the 1st object layer. + size_t j = i + 1; + for (; j < m_layer_tools.size() && ! m_layer_tools[j].has_wipe_tower; ++ j); + if (j < m_layer_tools.size()) { + const LayerTools <_object = m_layer_tools[j]; + coordf_t gap = lt_object.print_z - lt.print_z; + assert(gap > 0.f); + if (gap > max_layer_height + EPSILON) { + // Insert one additional wipe tower layer between lh.print_z and lt_object.print_z. + LayerTools lt_new(0.5f * (lt.print_z + lt_object.print_z)); + // Find the 1st layer above lt_new. + for (j = i + 1; j < m_layer_tools.size() && m_layer_tools[j].print_z < lt_new.print_z; ++ j); + LayerTools <_extra = (m_layer_tools[j].print_z == lt_new.print_z) ? + m_layer_tools[j] : + *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); + lt_extra.has_wipe_tower = true; + lt_extra.wipe_tower_partitions = lt_object.wipe_tower_partitions; + } + } + break; + } + } + + // Calculate the wipe_tower_layer_height values. + coordf_t wipe_tower_print_z_last = 0.; + for (LayerTools < : m_layer_tools) + if (lt.has_wipe_tower) { + lt.wipe_tower_layer_height = lt.print_z - wipe_tower_print_z_last; + wipe_tower_print_z_last = lt.print_z; + } } } // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp index 81f6e8eaa..77de726ba 100644 --- a/xs/src/libslic3r/GCode/ToolOrdering.hpp +++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp @@ -20,7 +20,8 @@ public: has_object(false), has_support(false), has_wipe_tower(false), - wipe_tower_partitions(0) {} + wipe_tower_partitions(0), + wipe_tower_layer_height(0.) {} bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; } bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } @@ -37,6 +38,7 @@ public: // Number of wipe tower partitions to support the required number of tool switches // and to support the wipe tower partitions above this one. size_t wipe_tower_partitions; + coordf_t wipe_tower_layer_height; }; ToolOrdering() {} @@ -71,7 +73,7 @@ private: void initialize_layers(std::vector &zs); void collect_extruders(const PrintObject &object); void reorder_extruders(unsigned int last_extruder_id); - void fill_wipe_tower_partitions(); + void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); std::vector m_layer_tools; }; diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp index ceb6921f1..db3977246 100644 --- a/xs/src/libslic3r/GCode/WipeTower.hpp +++ b/xs/src/libslic3r/GCode/WipeTower.hpp @@ -75,6 +75,7 @@ public: { // Print heigh of this tool change. float print_z; + float layer_height; // G-code section to be directly included into the output G-code. std::string gcode; // For path preview. diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 199a6210c..60f8a2004 100644 --- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -471,12 +471,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(int tool, bool last_in } ToolChangeResult result; - result.print_z = this->m_z_pos; - result.gcode = writer.gcode(); + result.print_z = this->m_z_pos; + result.layer_height = this->m_layer_height; + result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); - result.extrusions = writer.extrusions(); - result.start_pos = writer.start_pos(); - result.end_pos = writer.pos(); + result.extrusions = writer.extrusions(); + result.start_pos = writer.start_pos(); + result.end_pos = writer.pos(); return result; } @@ -554,12 +555,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, b } ToolChangeResult result; - result.print_z = this->m_z_pos; - result.gcode = writer.gcode(); + result.print_z = this->m_z_pos; + result.layer_height = this->m_layer_height; + result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); - result.extrusions = writer.extrusions(); - result.start_pos = writer.start_pos(); - result.end_pos = writer.pos(); + result.extrusions = writer.extrusions(); + result.start_pos = writer.start_pos(); + result.end_pos = writer.pos(); return result; } @@ -848,12 +850,13 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose) } ToolChangeResult result; - result.print_z = this->m_z_pos; - result.gcode = writer.gcode(); + result.print_z = this->m_z_pos; + result.layer_height = this->m_layer_height; + result.gcode = writer.gcode(); result.elapsed_time = writer.elapsed_time(); - result.extrusions = writer.extrusions(); - result.start_pos = writer.start_pos(); - result.end_pos = writer.pos(); + result.extrusions = writer.extrusions(); + result.start_pos = writer.start_pos(); + result.end_pos = writer.pos(); return result; } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index bbff4fc6d..1fef7e731 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -565,7 +565,7 @@ std::string Print::validate() const return "The Spiral Vase option can only be used when printing single material objects."; } - if (this->config.wipe_tower) { + if (this->config.wipe_tower && ! this->objects.empty()) { for (auto dmr : this->config.nozzle_diameter.values) if (std::abs(dmr - 0.4) > EPSILON) return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter."; @@ -573,16 +573,22 @@ std::string Print::validate() const return "The Wipe Tower is currently only supported for the RepRap (Marlin / Sprinter) G-code flavor."; if (! this->config.use_relative_e_distances) return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."; + SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters(); for (PrintObject *object : this->objects) { - if (std::abs(object->config.first_layer_height - this->objects.front()->config.first_layer_height) > EPSILON || - std::abs(object->config.layer_height - this->objects.front()->config.layer_height ) > EPSILON) + SlicingParameters slicing_params = object->slicing_parameters(); + if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON || + std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON) return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths"; + if (slicing_params.raft_layers() != slicing_params0.raft_layers()) + return "The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers"; + if (object->config.support_material_contact_distance != this->objects.front()->config.support_material_contact_distance) + return "The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance"; + if (! equal_layering(slicing_params, slicing_params0)) + return "The Wipe Tower is only supported for multiple objects if they are sliced equally."; object->update_layer_height_profile(); - if (object->layer_height_profile.size() != 8 || - std::abs(object->layer_height_profile[1] - object->config.first_layer_height) > EPSILON || - std::abs(object->layer_height_profile[3] - object->config.first_layer_height) > EPSILON || - std::abs(object->layer_height_profile[5] - object->config.layer_height) > EPSILON || - std::abs(object->layer_height_profile[7] - object->config.layer_height) > EPSILON) + for (size_t i = 1; i < object->layer_height_profile.size(); i += 2) + if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON && + std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON) return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed."; } } @@ -959,9 +965,7 @@ void Print::_make_wipe_tower() bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0; wipe_tower.set_layer( float(layer_tools.print_z), - float(first_layer ? - this->objects.front()->config.first_layer_height.get_abs_value(this->objects.front()->config.layer_height.value) : - this->objects.front()->config.layer_height.value), + float(layer_tools.wipe_tower_layer_height), layer_tools.wipe_tower_partitions, first_layer, last_layer); diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 3ed520e96..1534e19f5 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -91,6 +91,32 @@ struct SlicingParameters coordf_t object_print_z_max; }; +// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action. +inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2) +{ + return sp1.base_raft_layers == sp2.base_raft_layers && + sp1.interface_raft_layers == sp2.interface_raft_layers && + sp1.base_raft_layer_height == sp2.base_raft_layer_height && + sp1.interface_raft_layer_height == sp2.interface_raft_layer_height && + sp1.contact_raft_layer_height == sp2.contact_raft_layer_height && + sp1.contact_raft_layer_height_bridging == sp2.contact_raft_layer_height_bridging && + sp1.layer_height == sp2.layer_height && + sp1.min_layer_height == sp2.min_layer_height && + sp1.max_layer_height == sp2.max_layer_height && + sp1.max_suport_layer_height == sp2.max_suport_layer_height && + sp1.first_print_layer_height == sp2.first_print_layer_height && + sp1.first_object_layer_height == sp2.first_object_layer_height && + sp1.first_object_layer_bridging == sp2.first_object_layer_bridging && + sp1.soluble_interface == sp2.soluble_interface && + sp1.gap_raft_object == sp2.gap_raft_object && + sp1.gap_object_support == sp2.gap_object_support && + sp1.gap_support_object == sp2.gap_support_object && + sp1.raft_base_top_z == sp2.raft_base_top_z && + sp1.raft_interface_top_z == sp2.raft_interface_top_z && + sp1.raft_contact_top_z == sp2.raft_contact_top_z && + sp1.object_print_z_min == sp2.object_print_z_min; +} + typedef std::pair t_layer_height_range; typedef std::map t_layer_height_ranges; diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 6c11a50bc..018d804de 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1016,9 +1016,6 @@ void _3DScene::_load_wipe_tower_toolpaths( } for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { const std::vector &layer = ctxt.print->m_wipe_tower_tool_changes[idx_layer]; - coordf_t layer_height = (idx_layer == 0) ? - ctxt.print->objects.front()->config.first_layer_height.get_abs_value(ctxt.print->objects.front()->config.layer_height.value) : - ctxt.print->objects.front()->config.layer_height.value; for (size_t i = 0; i < vols.size(); ++ i) { GLVolume &vol = *vols[i]; if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { @@ -1045,7 +1042,7 @@ void _3DScene::_load_wipe_tower_toolpaths( std::vector heights; lines.reserve(n_lines); widths.reserve(n_lines); - heights.assign(n_lines, layer_height); + heights.assign(n_lines, extrusions.layer_height); for (; i < j; ++ i) { const WipeTower::Extrusion &e = extrusions.extrusions[i]; assert(e.width > 0.f);