diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 3c44322b2e..b87249501a 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -558,6 +558,7 @@ sub build { top_infill_extrusion_width support_material_extrusion_width infill_overlap bridge_flow_ratio clip_multipart_objects xy_size_compensation threads resolution + wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe )); $self->{config}->set('print_settings_id', ''); @@ -720,6 +721,14 @@ sub build { $optgroup->append_single_option_line('ooze_prevention'); $optgroup->append_single_option_line('standby_temperature_delta'); } + { + my $optgroup = $page->new_optgroup('Wipe tower'); + $optgroup->append_single_option_line('wipe_tower'); + $optgroup->append_single_option_line('wipe_tower_x'); + $optgroup->append_single_option_line('wipe_tower_y'); + $optgroup->append_single_option_line('wipe_tower_width'); + $optgroup->append_single_option_line('wipe_tower_per_color_wipe'); + } { my $optgroup = $page->new_optgroup('Advanced'); $optgroup->append_single_option_line('interface_shells'); @@ -955,6 +964,10 @@ sub _update { my $have_ooze_prevention = $config->ooze_prevention; $self->get_field($_)->toggle($have_ooze_prevention) for qw(standby_temperature_delta); + + my $have_wipe_tower = $config->wipe_tower; + $self->get_field($_)->toggle($have_wipe_tower) + for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe); } sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () } @@ -969,7 +982,7 @@ sub build { my $self = shift; $self->init_config_options(qw( - filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost + filament_colour filament_diameter filament_type filament_soluble filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost 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 @@ -984,6 +997,8 @@ sub build { $optgroup->append_single_option_line('filament_colour', 0); $optgroup->append_single_option_line('filament_diameter', 0); $optgroup->append_single_option_line('extrusion_multiplier', 0); + $optgroup->append_single_option_line('filament_type', 0); + $optgroup->append_single_option_line('filament_soluble', 0); $optgroup->append_single_option_line('filament_density', 0); $optgroup->append_single_option_line('filament_cost', 0); } @@ -1137,7 +1152,7 @@ sub build { octoprint_host octoprint_apikey use_firmware_retraction use_volumetric_e variable_layer_height - start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode + single_extruder_multi_material start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode nozzle_diameter extruder_offset retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_length_toolchange retract_restart_extra_toolchange @@ -1198,6 +1213,7 @@ sub build { min => 1, ); $optgroup->append_single_option_line($option); + $optgroup->append_single_option_line('single_extruder_multi_material'); } $optgroup->on_change(sub { my ($opt_id) = @_; @@ -1538,6 +1554,7 @@ sub _update { my $have_multiple_extruders = $self->{extruders_count} > 1; $self->get_field('toolchange_gcode')->toggle($have_multiple_extruders); + $self->get_field('single_extruder_multi_material')->toggle($have_multiple_extruders); for my $i (0 .. ($self->{extruders_count}-1)) { my $have_retract_length = $config->get_at('retract_length', $i) > 0; diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index ce0be2ec88..7b3def024f 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -2,6 +2,7 @@ #include "ExtrusionEntity.hpp" #include "EdgeGrid.hpp" #include "Geometry.hpp" +#include "GCode/WipeTowerPrusaMM.hpp" #include #include @@ -384,6 +385,8 @@ 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; @@ -401,7 +404,9 @@ bool GCode::do_export(FILE *file, Print &print) -- idx_object_layer; } } - this->process_layer(file, print, layers_to_print, © - object->_shifted_copies.data()); + 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()); } write(file, this->filter(m_cooling_buffer->flush(), true)); ++ finished_objects; @@ -437,21 +442,63 @@ 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) { + // Initialize the wipe tower. + auto *wipe_tower = new WipeTowerPrusaMM( + float(print.config.wipe_tower_x.value), float(print.config.wipe_tower_y.value), + float(print.config.wipe_tower_width.value), float(print.config.wipe_tower_per_color_wipe.value)); + //wipe_tower->set_retract(); + //wipe_tower->set_zhop(); + //wipe_tower->set_zhop(); + // Set the extruder & material properties at the wipe tower object. + for (size_t i = 0; i < 4; ++ i) + wipe_tower->set_extruder( + i, + WipeTowerPrusaMM::parse_material(print.config.filament_type.get_at(i).c_str()), + print.config.temperature.get_at(i), + print.config.first_layer_temperature.get_at(i)); + m_wipe_tower.reset(wipe_tower); + } // Extrude the layers. - for (auto &layer : layers) + for (auto &layer : layers) { // layer.second is of type std::vector, // wher the objects are sorted by their sorted order given by object_indices. - this->process_layer(file, print, layer.second); + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), ToolOrdering::LayerTools(layer.first)); + assert(it_layer_tools != tool_ordering.end() && layer.first); + if (m_wipe_tower) { + bool first_layer = layer.first == layers.begin()->first; + auto it_layer_tools_next = it_layer_tools; + ++ it_layer_tools_next; + m_wipe_tower->set_layer( + layer.first, + first_layer ? + print.objects.front()->config.first_layer_height.get_abs_value(print.objects.front()->config.layer_height.value) : + print.objects.front()->config.layer_height.value, + it_layer_tools->wipe_tower_partitions, + first_layer, + it_layer_tools->wipe_tower_partitions == 0 || (it_layer_tools_next == tool_ordering.end() || it_layer_tools_next->wipe_tower_partitions == 0)); + } + this->process_layer(file, print, layer.second, *it_layer_tools, size_t(-1)); + } write(file, this->filter(m_cooling_buffer->flush(), true)); } // write end commands to file - write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully + if (m_wipe_tower) { + // Unload the current filament over the purge tower. + write(file, this->wipe_tower_tool_change(-1)); + m_wipe_tower.release(); + } else + write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully write(file, m_writer.set_fan(false)); writeln(file, m_placeholder_parser.process(print.config.end_gcode)); write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% write(file, m_writer.postamble()); - + // get filament stats print.filament_stats.clear(); print.total_used_filament = 0.; @@ -543,6 +590,7 @@ void GCode::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, + const ToolOrdering::LayerTools &layer_tools, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx) @@ -562,7 +610,8 @@ void GCode::process_layer( } const Layer &layer = (object_layer != nullptr) ? *object_layer : *support_layer; coordf_t print_z = layer.print_z; - bool first_layer = print_z < m_config.first_layer_height.get_abs_value(m_config.layer_height.value) + EPSILON; + bool first_layer = layer.id() == 0; + unsigned int first_extruder_id = layer_tools.extruders.empty() ? 0 : layer_tools.extruders.front(); // Initialize config with the 1st object to be printed at this layer. m_config.apply(layer.object()->config, true); @@ -620,7 +669,7 @@ void GCode::process_layer( 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(print.regions.front()->config.perimeter_extruder.value - 1); + gcode += this->set_extruder(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. @@ -643,10 +692,10 @@ void GCode::process_layer( std::vector extruder_ids = m_writer.extruder_ids(); // Reorder the extruders, so that the last used extruder is at the front. for (size_t i = 1; i < extruder_ids.size(); ++ i) - if (extruder_ids[i] == m_writer.extruder()->id) { + if (extruder_ids[i] == first_extruder_id) { // Move the last extruder to the front. memmove(extruder_ids.data() + 1, extruder_ids.data(), i * sizeof(unsigned int)); - extruder_ids.front() = m_writer.extruder()->id; + extruder_ids.front() = first_extruder_id; break; } size_t n_loops = print.skirt.entities.size(); @@ -668,7 +717,7 @@ void GCode::process_layer( } } else // Extrude all skirts with the current extruder. - skirt_loops_per_extruder[m_writer.extruder()->id] = std::pair(0, print.config.skirts.value); + skirt_loops_per_extruder[first_extruder_id] = std::pair(0, print.config.skirts.value); } // Group extrusions by an extruder, then by an object, an island and a region. @@ -684,12 +733,12 @@ void GCode::process_layer( // Don't change extruder if the extruder is set to 0. Use the current extruder instead. bool single_extruder = (object.config.support_material_extruder.value == object.config.support_material_interface_extruder.value || - (object.config.support_material_extruder.value == int(m_writer.extruder()->id) && object.config.support_material_interface_extruder.value == 0) || - (object.config.support_material_interface_extruder.value == int(m_writer.extruder()->id) && object.config.support_material_extruder.value == 0)); + (object.config.support_material_extruder.value == int(first_extruder_id) && object.config.support_material_interface_extruder.value == 0) || + (object.config.support_material_interface_extruder.value == int(first_extruder_id) && object.config.support_material_extruder.value == 0)); // Assign an extruder to the base. ObjectByExtruder &obj = object_by_extruder( by_extruder, - (object.config.support_material_extruder == 0) ? m_writer.extruder()->id : (object.config.support_material_extruder - 1), + (object.config.support_material_extruder == 0) ? first_extruder_id : (object.config.support_material_extruder - 1), &layer_to_print - layers.data(), layers.size()); obj.support = &support_layer.support_fills; @@ -697,7 +746,7 @@ void GCode::process_layer( if (! single_extruder) { ObjectByExtruder &obj_interface = object_by_extruder( by_extruder, - (object.config.support_material_interface_extruder == 0) ? m_writer.extruder()->id : (object.config.support_material_interface_extruder - 1), + (object.config.support_material_interface_extruder == 0) ? first_extruder_id : (object.config.support_material_interface_extruder - 1), &layer_to_print - layers.data(), layers.size()); obj_interface.support = &support_layer.support_fills; @@ -793,31 +842,14 @@ void GCode::process_layer( } } // for objects - // Tweak extruder ordering to save toolchanges. - std::vector extruders; - extruders.reserve(by_extruder.size()); - for (const auto &ex : by_extruder) - extruders.push_back(ex.first); - if (extrude_skirt) { - // Merge with the skirt extruders. - for (const auto &ex : skirt_loops_per_extruder) - extruders.push_back(ex.first); - sort_remove_duplicates(extruders); - } - // Reorder the extruders, so that the last used extruder is at the front. - for (size_t i = 1; i < extruders.size(); ++ i) - if (extruders[i] == m_writer.extruder()->id) { - // Move the last extruder to the front. - memmove(extruders.data() + 1, extruders.data(), i * sizeof(unsigned int)); - extruders.front() = m_writer.extruder()->id; - break; - } // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. std::vector> lower_layer_edge_grids(layers.size()); - for (unsigned int extruder_id : extruders) + for (unsigned int extruder_id : layer_tools.extruders) { gcode += this->set_extruder(extruder_id); - //FIXME here will come the priming tower call. + if (m_wipe_tower && ! m_wipe_tower->finished() && extruder_id == layer_tools.extruders.back()) + // Last extruder change on the layer or no extruder change at all. + m_wipe_tower->close_layer(); if (extrude_skirt) { auto loops_it = skirt_loops_per_extruder.find(extruder_id); @@ -1675,7 +1707,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, this->set_last_pos(path.last_point()); if (m_config.cooling) - m_elapsed_time += path_length / F * 60; + m_elapsed_time += path_length / F * 60.f; return gcode; } @@ -1814,21 +1846,39 @@ std::string GCode::set_extruder(unsigned int extruder_id) gcode += pp.process(m_config.toolchange_gcode.value) + '\n'; } - // if ooze prevention is enabled, park current extruder in the nearest - // standby point and set it to the standby temperature - if (m_ooze_prevention.enable && m_writer.extruder() != NULL) - gcode += m_ooze_prevention.pre_toolchange(*this); - - // append the toolchange command - gcode += m_writer.toolchange(extruder_id); - - // set the new extruder to the operating temperature - if (m_ooze_prevention.enable) - gcode += m_ooze_prevention.post_toolchange(*this); + if (m_wipe_tower) { + assert(! m_wipe_tower->finished()); + if (! m_wipe_tower->finished()) + gcode += this->wipe_tower_tool_change(extruder_id); + } else { + // if ooze prevention is enabled, park current extruder in the nearest + // standby point and set it to the standby temperature + if (m_ooze_prevention.enable && m_writer.extruder() != NULL) + gcode += m_ooze_prevention.pre_toolchange(*this); + // append the toolchange command + gcode += m_writer.toolchange(extruder_id); + // set the new extruder to the operating temperature + if (m_ooze_prevention.enable) + gcode += m_ooze_prevention.post_toolchange(*this); + } return gcode; } +std::string GCode::wipe_tower_tool_change(int extruder_id) +{ + // Move over the wipe tower. + std::string gcode = m_writer.travel_to_xy(Pointf3(m_wipe_tower->position().x, m_wipe_tower->position().y)); + gcode += m_writer.unlift(); + // Let the tool change be executed by the wipe tower class. + std::pair code_and_pos = m_wipe_tower->tool_change(extruder_id); + // Inform the G-code writer about the changes done behind its back. + gcode += code_and_pos.first; + // A phony move to the end position at the wipe tower. + m_writer.travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y)); + return gcode; +} + // convert a model-space scaled point into G-code coordinates Pointf GCode::point_to_gcode(const Point &point) const { diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 6601961299..4bba6866f7 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -13,6 +13,8 @@ #include "GCode/CoolingBuffer.hpp" #include "GCode/PressureEqualizer.hpp" #include "GCode/SpiralVase.hpp" +#include "GCode/ToolOrdering.hpp" +#include "GCode/WipeTower.hpp" #include "EdgeGrid.hpp" #include @@ -129,6 +131,7 @@ private: const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, + const ToolOrdering::LayerTools &layer_tools, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx = size_t(-1)); @@ -172,6 +175,7 @@ private: std::string retract(bool toolchange = false); std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id); + std::string wipe_tower_tool_change(int extruder_id); /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() @@ -218,6 +222,7 @@ private: std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; std::unique_ptr m_pressure_equalizer; + std::unique_ptr m_wipe_tower; // Heights at which the skirt has already been extruded. std::vector m_skirt_done; diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp new file mode 100644 index 0000000000..b9240dabc3 --- /dev/null +++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp @@ -0,0 +1,194 @@ +#include "ToolOrdering.hpp" + +namespace Slic3r { +namespace ToolOrdering { + +// Collect extruders reuqired to print layers. +static void collect_extruders(const PrintObject &object, std::vector &layers) +{ + // Collect the support extruders. + for (auto support_layer : object.support_layers) { + auto it_layer = std::find(layers.begin(), layers.end(), LayerTools(support_layer->print_z)); + assert(it_layer != layers.end()); + ExtrusionRole role = support_layer->support_fills.role(); + bool has_support = role == erMixed || role == erSupportMaterial; + bool has_interface = role == erMixed || role == erSupportMaterialInterface; + unsigned int extruder_support = object.config.support_material_extruder.value; + unsigned int extruder_interface = object.config.support_material_interface_extruder.value; + if (has_support && has_interface) { + // If both base and interface supports are to be extruded and one of them will be extruded with a "don't care" extruder, + // print both with the same extruder to minimize extruder switches. + if (extruder_support == 0) + extruder_support = extruder_interface; + else if (extruder_interface == 0) + extruder_interface = extruder_support; + } + if (has_support) + it_layer->extruders.push_back(extruder_support); + if (has_interface) + it_layer->extruders.push_back(extruder_interface); + } + // Collect the object extruders. + for (auto layer : object.layers) { + auto it_layer = std::find(layers.begin(), layers.end(), LayerTools(layer->print_z)); + assert(it_layer != layers.end()); + // What extruders are required to print this object layer? + for (size_t region_id = 0; region_id < object.print()->regions.size(); ++ region_id) { + const LayerRegion *layerm = layer->regions[region_id]; + if (layerm == nullptr) + continue; + const PrintRegion ®ion = *object.print()->regions[region_id]; + if (! layerm->perimeters.entities.empty()) + it_layer->extruders.push_back(region.config.perimeter_extruder.value); + bool has_infill = false; + bool has_solid_infill = false; + for (const ExtrusionEntity *ee : layerm->fills.entities) { + // fill represents infill extrusions of a single island. + const auto *fill = dynamic_cast(ee); + ExtrusionRole role = fill->entities.empty() ? erNone : fill->entities.front()->role(); + if (is_solid_infill(role)) + has_solid_infill = true; + else if (role != erNone) + has_infill = true; + } + if (has_solid_infill) + it_layer->extruders.push_back(region.config.solid_infill_extruder); + if (has_infill) + it_layer->extruders.push_back(region.config.infill_extruder); + } + } + + // Sort and remove duplicates + for (LayerTools < : layers) + sort_remove_duplicates(lt.extruders); +} + +// Reorder extruders to minimize layer changes. +static void reorder_extruders(std::vector &layers) +{ + 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; + + for (LayerTools < : layers) { + if (lt.extruders.empty()) + continue; + if (lt.extruders.size() == 1 && lt.extruders.front() == 0) + lt.extruders.front() = last_extruder_id; + else { + if (lt.extruders.front() == 0) + // Pop the "don't care" extruder, the "don't care" region will be merged with the next one. + lt.extruders.erase(lt.extruders.begin()); + // Reorder the extruders to start with the last one. + for (size_t i = 1; i < lt.extruders.size(); ++ i) + if (lt.extruders[i] == last_extruder_id) { + // Move the last extruder to the front. + memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int)); + lt.extruders.front() = last_extruder_id; + break; + } + } + last_extruder_id = lt.extruders.back(); + } + + // Reindex the extruders, so they are zero based, not 1 based. + for (LayerTools < : layers) + for (unsigned int &extruder_id : lt.extruders) { + assert(extruder_id > 0); + -- extruder_id; + } +} + +static void fill_wipe_tower_partitions(std::vector &layers) +{ + if (layers.empty()) + 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; + + // 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) + layers[i].wipe_tower_partitions = std::max(layers[i + 1].wipe_tower_partitions, layers[i].wipe_tower_partitions); +} + +// For the use case when each object is printed separately +// (print.config.complete_objects is true). +std::vector tool_ordering(PrintObject &object) +{ + // Initialize the print layers for just a single object. + std::vector layers; + { + std::vector zs; + zs.reserve(zs.size() + object.layers.size() + object.support_layers.size()); + for (auto layer : object.layers) + zs.emplace_back(layer->print_z); + for (auto layer : object.support_layers) + zs.emplace_back(layer->print_z); + sort_remove_duplicates(zs); + for (coordf_t z : zs) + layers.emplace_back(LayerTools(z)); + } + + // Collect extruders reuqired to print the layers. + collect_extruders(object, layers); + + // Reorder the extruders to minimize tool switches. + reorder_extruders(layers); + + fill_wipe_tower_partitions(layers); + return layers; +} + +// For the use case when all objects are printed at once. +// (print.config.complete_objects is false). +std::vector tool_ordering(const Print &print) +{ + // Initialize the print layers for all objects and all layers. + std::vector layers; + { + std::vector zs; + for (auto object : print.objects) { + zs.reserve(zs.size() + object->layers.size() + object->support_layers.size()); + for (auto layer : object->layers) + zs.emplace_back(layer->print_z); + for (auto layer : object->support_layers) + zs.emplace_back(layer->print_z); + } + sort_remove_duplicates(zs); + for (coordf_t z : zs) + layers.emplace_back(LayerTools(z)); + } + + // Collect extruders reuqired to print the layers. + for (auto object : print.objects) + collect_extruders(*object, layers); + + // Reorder the extruders to minimize tool switches. + reorder_extruders(layers); + + fill_wipe_tower_partitions(layers); + return layers; +} + +} // namespace ToolOrdering +} // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp new file mode 100644 index 0000000000..0556f7e2ef --- /dev/null +++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp @@ -0,0 +1,38 @@ +// Ordering of the tools to minimize tool switches. + +#ifndef slic3r_ToolOrdering_hpp_ +#define slic3r_ToolOrdering_hpp_ + +#include "libslic3r.h" +#include "Print.hpp" + +namespace Slic3r { +namespace ToolOrdering { + +struct LayerTools +{ + LayerTools(const coordf_t z) : print_z(z), wipe_tower_partitions(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; } + + coordf_t print_z; + // Zero based extruder IDs, ordered to minimize tool switches. + std::vector extruders; + // 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; +}; + +// For the use case when each object is printed separately +// (print.config.complete_objects is true). +extern std::vector tool_ordering(PrintObject &object); + +// 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); + +} // namespace ToolOrdering +} // namespace SLic3r + +#endif /* slic3r_ToolOrdering_hpp_ */ diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp new file mode 100644 index 0000000000..37a339cd31 --- /dev/null +++ b/xs/src/libslic3r/GCode/WipeTower.hpp @@ -0,0 +1,59 @@ +#ifndef slic3r_WipeTower_hpp_ +#define slic3r_WipeTower_hpp_ + +#include +#include + +namespace Slic3r +{ + +// A pure virtual WipeTower definition. +class WipeTower +{ +public: + // Internal point class, to make the wipe tower independent from other slic3r modules. + // This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r. + struct xy + { + xy(float x = 0.f, float y = 0.f) : x(x), y(y) {} + xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; } + xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; } + xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; } + xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; } + float x; + float y; + }; + + WipeTower() {} + virtual ~WipeTower() {} + + // Return the wipe tower position. + virtual const xy& position() const = 0; + + // The wipe tower is finished, there should be no more tool changes or wipe tower prints. + virtual bool finished() const = 0; + + // Switch to a next layer. + virtual void set_layer( + // Print height of this layer. + float print_z, + // Layer height, used to calculate extrusion the rate. + float layer_height, + // Maximum number of tool changes on this layer or the layers below. + size_t max_tool_changes, + // Is this the first layer of the print? In that case print the brim first. + bool is_first_layer, + // Is this the last layer of the wipe tower? + bool is_last_layer) = 0; + + // Returns gcode for toolchange and the end position. + // if new_tool == -1, just unload the current filament over the wipe tower. + virtual std::pair tool_change(int new_tool) = 0; + + // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. + virtual std::pair close_layer() = 0; +}; + +}; // namespace Slic3r + +#endif /* slic3r_WipeTower_hpp_ */ diff --git a/xs/src/libslic3r/WipeTower.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp similarity index 61% rename from xs/src/libslic3r/WipeTower.cpp rename to xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index b00263acf5..bce5e02479 100644 --- a/xs/src/libslic3r/WipeTower.cpp +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -1,4 +1,4 @@ -#include "WipeTower.hpp" +#include "WipeTowerPrusaMM.hpp" #include #include @@ -13,9 +13,11 @@ #define strcasecmp _stricmp #endif -namespace PrusaSingleExtruderMM +namespace Slic3r { +namespace PrusaMultiMaterial { + class Writer { public: @@ -171,18 +173,18 @@ public: return *this; }; - Writer& comment_material(WipeTower::material_type material) + Writer& comment_material(WipeTowerPrusaMM::material_type material) { m_gcode += "; material : "; switch (material) { - case WipeTower::PVA: + case WipeTowerPrusaMM::PVA: m_gcode += "#8 (PVA)"; break; - case WipeTower::SCAFF: + case WipeTowerPrusaMM::SCAFF: m_gcode += "#5 (Scaffold)"; break; - case WipeTower::FLEX: + case WipeTowerPrusaMM::FLEX: m_gcode += "#4 (Flex)"; break; default: @@ -236,6 +238,8 @@ private: } }; +} // namespace PrusaMultiMaterial + static inline int randi(int lo, int hi) { int n = hi - lo + 1; @@ -244,7 +248,7 @@ static inline int randi(int lo, int hi) return lo + i; } -WipeTower::material_type WipeTower::parse_material(const char *name) +WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name) { if (strcasecmp(name, "PLA") == 0) return PLA; @@ -267,116 +271,58 @@ WipeTower::material_type WipeTower::parse_material(const char *name) return INVALID; } -std::string WipeTower::FirstLayer(bool sideOnly, float y_offset) +std::pair WipeTowerPrusaMM::tool_change(int tool) { - const box_coordinates wipeTower_box( - m_wipe_tower_pos, - m_wipe_tower_width, - m_wipe_area * float(m_color_changes) - perimeterWidth / 2); + // Either it is the last tool unload, + // or there must be a nonzero wipe tower partitions available. + assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0); - Writer writer; - writer.set_extrusion_flow(extrusion_flow * 1.1f) - // Let the writer know the current Z position as a base for Z-hop. - .set_z(m_z_pos) - .append( - ";-------------------------------------\n" - "; CP WIPE TOWER FIRST LAYER BRIM START\n"); + if (m_layer_change_in_layer == size_t(-1)) + // First layer, prime the extruder. + return toolchange_Brim(tool); - // Move with Z hop and prime the extruder 10*perimeterWidth left along the vertical edge of the wipe tower. - writer.z_hop(zHop, 7200) - .travel(wipeTower_box.lu - xy(perimeterWidth * 10.f, 0), 6000) - .z_hop(0, 7200) - .extrude_explicit(wipeTower_box.ld - xy(perimeterWidth * 10.f, 0), retract, 2400) - .feedrate(2100); - - if (sideOnly) { - float x_offset = 0.f; - for (size_t i = 0; i < 4; ++ i, x_offset += perimeterWidth) - writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset)) - .extrude(wipeTower_box.lu + xy(- x_offset, - y_offset)); - writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000) - .feedrate(2100); - x_offset = 0.f; - for (size_t i = 0; i < 4; ++ i, x_offset += perimeterWidth) - writer.travel (wipeTower_box.rd + xy(x_offset, y_offset)) - .extrude(wipeTower_box.ru + xy(x_offset, - y_offset)); - } else { - // Extrude 4 rounds of a brim around the future wipe tower. - box_coordinates box(wipeTower_box); - box.ld += xy(- perimeterWidth / 2, 0); - box.lu += xy(- perimeterWidth / 2, perimeterWidth); - box.rd += xy( perimeterWidth / 2, 0); - box.ru += xy( perimeterWidth / 2, perimeterWidth); - for (size_t i = 0; i < 4; ++ i) { - writer.travel(box.ld) - .extrude(box.lu) .extrude(box.ru) - .extrude(box.rd) .extrude(box.ld); - box.expand(perimeterWidth); - } - } - - // Move to the front left corner and wipe along the front edge. - writer.travel(wipeTower_box.ld, 7000) - .travel(wipeTower_box.rd) - .travel(wipeTower_box.ld) - .append("; CP WIPE TOWER FIRST LAYER BRIM END\n" - ";-----------------------------------\n"); - - return writer.gcode(); -} - -std::pair WipeTower::Toolchange( - const int tool, - const material_type current_material, - const material_type new_material, - const int temperature, - const wipe_shape shape, - const int count, - const float spaceAvailable, - const float wipeStartY, - const bool lastInFile, - const bool colorInit) -{ box_coordinates cleaning_box( m_wipe_tower_pos.x, - m_wipe_tower_pos.y + wipeStartY, + m_wipe_tower_pos.y + m_current_wipe_start_y, m_wipe_tower_width, - spaceAvailable - perimeterWidth / 2); + m_wipe_area - m_perimeter_width / 2); - Writer writer; - writer.set_extrusion_flow(extrusion_flow) + PrusaMultiMaterial::Writer writer; + writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .append(";--------------------\n" "; CP TOOLCHANGE START\n") - .comment_with_value(" toolchange #", count) - .comment_material(current_material) + .comment_with_value(" toolchange #", m_layer_change_total) + .comment_material(m_current_material) .append(";--------------------\n") .speed_override(100) // Lift for a Z hop. - .z_hop(zHop, 7200) + .z_hop(m_zhop, 7200) // additional retract on move to tower - .retract(retract/2, 3600) - .travel(((shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.rd) + xy(perimeterWidth, shape * perimeterWidth), 7200) + .retract(m_retract/2, 3600) + .travel(((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.rd) + xy(m_perimeter_width, m_current_shape * m_perimeter_width), 7200) // Unlift for a Z hop. .z_hop(0, 7200) // Additional retract on move to tower. - .deretract(retract/2, 3600) - .deretract(retract, 1500) + .deretract(m_retract/2, 3600) + .deretract(m_retract, 1500) // Increase extruder current for ramming. .set_extruder_trimpot(750) .flush_planner_queue(); // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. - toolchange_Unload(writer, cleaning_box, current_material, shape, temperature); + toolchange_Unload(writer, cleaning_box, m_current_material, m_current_shape, + m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]); - if (! lastInFile) { + if (tool >= 0) { + // This is not the last change. // Change the tool, set a speed override for solube and flex materials. - toolchange_Change(writer, tool, current_material, new_material); - toolchange_Load(writer, cleaning_box, current_material, shape, colorInit); + toolchange_Change(writer, tool, m_current_material, m_material[tool]); + toolchange_Load(writer, cleaning_box); // Wipe the newly loaded filament until the end of the assigned wipe area. - toolchange_Wipe(writer, cleaning_box, current_material, shape); + toolchange_Wipe(writer, cleaning_box, m_current_material); // Draw a perimeter around cleaning_box and wipe. - toolchange_Done(writer, cleaning_box, current_material, shape); + toolchange_Done(writer, cleaning_box); } // Reset the extruder current to a normal value. @@ -387,20 +333,83 @@ std::pair WipeTower::Toolchange( ";------------------\n" "\n\n"); + ++ m_layer_change_in_layer; + m_current_wipe_start_y += m_wipe_area; + m_current_material = m_material[tool]; + return std::pair(writer.gcode(), writer.pos()); +} + +std::pair WipeTowerPrusaMM::toolchange_Brim(size_t tool, bool sideOnly, float y_offset) +{ + const box_coordinates wipeTower_box( + m_wipe_tower_pos, + m_wipe_tower_width, + m_wipe_area * float(m_max_color_changes) - m_perimeter_width / 2); + + PrusaMultiMaterial::Writer writer; + writer.set_extrusion_flow(m_extrusion_flow * 1.1f) + // Let the writer know the current Z position as a base for Z-hop. + .set_z(m_z_pos) + .append( + ";-------------------------------------\n" + "; CP WIPE TOWER FIRST LAYER BRIM START\n"); + + // Move with Z hop and prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower. + writer.z_hop(m_zhop, 7200) + .travel(wipeTower_box.lu - xy(m_perimeter_width * 10.f, 0), 6000) + .z_hop(0, 7200) + .extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 10.f, 0), m_retract, 2400) + .feedrate(2100); + + toolchange_Change(writer, tool, m_current_material, m_material[tool]); + + if (sideOnly) { + float x_offset = 0.f; + for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width) + writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset)) + .extrude(wipeTower_box.lu + xy(- x_offset, - y_offset)); + writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000) + .feedrate(2100); + x_offset = 0.f; + for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width) + writer.travel (wipeTower_box.rd + xy(x_offset, y_offset)) + .extrude(wipeTower_box.ru + xy(x_offset, - y_offset)); + } else { + // Extrude 4 rounds of a brim around the future wipe tower. + box_coordinates box(wipeTower_box); + box.ld += xy(- m_perimeter_width / 2, 0); + box.lu += xy(- m_perimeter_width / 2, m_perimeter_width); + box.rd += xy( m_perimeter_width / 2, 0); + box.ru += xy( m_perimeter_width / 2, m_perimeter_width); + for (size_t i = 0; i < 4; ++ i) { + writer.travel(box.ld) + .extrude(box.lu) .extrude(box.ru) + .extrude(box.rd) .extrude(box.ld); + box.expand(m_perimeter_width); + } + } + + // Move to the front left corner and wipe along the front edge. + writer.travel(wipeTower_box.ld, 7000) + .travel(wipeTower_box.rd) + .travel(wipeTower_box.ld) + .append("; CP WIPE TOWER FIRST LAYER BRIM END\n" + ";-----------------------------------\n"); + return std::pair(writer.gcode(), writer.pos()); } // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. -void WipeTower::toolchange_Unload( - Writer &writer, +void WipeTowerPrusaMM::toolchange_Unload( + PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box, const material_type material, const wipe_shape shape, const int temperature) { - float xl = cleaning_box.ld.x + (perimeterWidth / 2); - float xr = cleaning_box.rd.x - (perimeterWidth / 2); - float y_step = shape * perimeterWidth; + float xl = cleaning_box.ld.x + (m_perimeter_width / 2); + float xr = cleaning_box.rd.x - (m_perimeter_width / 2); + float y_step = shape * m_perimeter_width; writer.append("; CP TOOLCHANGE UNLOAD"); @@ -409,20 +418,20 @@ void WipeTower::toolchange_Unload( { case PVA: // ramming start end y increment amount feedrate - writer.ram(xl + perimeterWidth * 2, xr - perimeterWidth, y_step * 1.2f, 3, 4000) - .ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 1.5f, 3, 4500) - .ram(xl + perimeterWidth * 2, xr - perimeterWidth * 2, y_step * 1.5f, 3, 4800) - .ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 1.5f, 3, 5000); + writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 1.2f, 3, 4000) + .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.5f, 3, 4500) + .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.5f, 3, 4800) + .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.5f, 3, 5000); break; case SCAFF: - writer.ram(xl + perimeterWidth * 2, xr - perimeterWidth, y_step * 3.f, 3, 4000) - .ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 3.f, 4, 4600) - .ram(xl + perimeterWidth * 2, xr - perimeterWidth * 2, y_step * 3.f, 4.5, 5200); + writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 3.f, 3, 4000) + .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 3.f, 4, 4600) + .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 3.f, 4.5, 5200); break; default: - writer.ram(xl + perimeterWidth * 2, xr - perimeterWidth, y_step * 1.2f, 1.6f, 4000) - .ram(xr - perimeterWidth, xl + perimeterWidth, y_step * 1.2f, 1.65f, 4600) - .ram(xl + perimeterWidth * 2, xr - perimeterWidth * 2, y_step * 1.2f, 1.74f, 5200); + writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 1.2f, 1.6f, 4000) + .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.2f, 1.65f, 4600) + .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, 1.74f, 5200); } // Pull the filament end into a cooling tube. @@ -462,8 +471,8 @@ void WipeTower::toolchange_Unload( } // Change the tool, set a speed override for solube and flex materials. -void WipeTower::toolchange_Change( - Writer &writer, +void WipeTowerPrusaMM::toolchange_Change( + PrusaMultiMaterial::Writer &writer, const int tool, material_type /* current_material */, material_type new_material) @@ -481,15 +490,12 @@ void WipeTower::toolchange_Change( .flush_planner_queue(); } -void WipeTower::toolchange_Load( - Writer &writer, - const box_coordinates &cleaning_box, - const material_type /* material */, - const wipe_shape shape, - const bool colorInit) +void WipeTowerPrusaMM::toolchange_Load( + PrusaMultiMaterial::Writer &writer, + const box_coordinates &cleaning_box) { - float xl = cleaning_box.ld.x + perimeterWidth; - float xr = cleaning_box.rd.x - perimeterWidth; + float xl = cleaning_box.ld.x + m_perimeter_width; + float xr = cleaning_box.rd.x - m_perimeter_width; writer.append("; CP TOOLCHANGE LOAD\n") // Load the filament while moving left / right, @@ -501,11 +507,12 @@ void WipeTower::toolchange_Load( // Extrude first five lines (just three lines if colorInit is set). writer.extrude(xr, writer.y(), 1600); + bool colorInit = false; size_t pass = colorInit ? 1 : 2; for (int i = 0; i < pass; ++ i) - writer.travel (xr, writer.y() + shape * perimeterWidth * 0.85f, 2200) + writer.travel (xr, writer.y() + m_current_shape * m_perimeter_width * 0.85f, 2200) .extrude(xl, writer.y()) - .travel (xl, writer.y() + shape * perimeterWidth * 0.85f) + .travel (xl, writer.y() + m_current_shape * m_perimeter_width * 0.85f) .extrude(xr, writer.y()); // Reset the extruder current to the normal value. @@ -513,52 +520,49 @@ void WipeTower::toolchange_Load( } // Wipe the newly loaded filament until the end of the assigned wipe area. -void WipeTower::toolchange_Wipe( - Writer &writer, +void WipeTowerPrusaMM::toolchange_Wipe( + PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box, - const material_type material, - const wipe_shape shape) + const material_type material) { // Increase flow on first layer, slow down print. - writer.set_extrusion_flow(extrusion_flow * (is_first_layer() ? 1.18f : 1.f)) + writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f)) .append("; CP TOOLCHANGE WIPE\n"); - float wipe_coeff = is_first_layer() ? 0.5f : 1.f; - float xl = cleaning_box.ld.x + 2.f * perimeterWidth; - float xr = cleaning_box.rd.x - 2.f * perimeterWidth; + float wipe_coeff = m_is_first_layer ? 0.5f : 1.f; + float xl = cleaning_box.ld.x + 2.f * m_perimeter_width; + float xr = cleaning_box.rd.x - 2.f * m_perimeter_width; // Wipe speed will increase up to 4800. float wipe_speed = 4200; // Y increment per wipe line. - float dy = shape * perimeterWidth * 0.7f; + float dy = m_current_shape * m_perimeter_width * 0.7f; for (bool p = true; ; p = ! p) { writer.feedrate((wipe_speed = std::min(4800.f, wipe_speed + 50.f)) * wipe_coeff); if (p) - writer.extrude(xl - perimeterWidth/2, writer.y() + dy) - .extrude(xr + perimeterWidth, writer.y()); + writer.extrude(xl - m_perimeter_width/2, writer.y() + dy) + .extrude(xr + m_perimeter_width, writer.y()); else - writer.extrude(xl - perimeterWidth, writer.y() + dy) - .extrude(xr + perimeterWidth*2, writer.y()); + writer.extrude(xl - m_perimeter_width, writer.y() + dy) + .extrude(xr + m_perimeter_width*2, writer.y()); writer.feedrate((wipe_speed = std::min(4800.f, wipe_speed + 50.f)) * wipe_coeff) - .extrude(xr + perimeterWidth, writer.y() + dy) - .extrude(xl - perimeterWidth, writer.y()); - if ((shape == SHAPE_NORMAL) ? - (writer.y() > cleaning_box.lu.y - perimeterWidth) : - (writer.y() < cleaning_box.ld.y + perimeterWidth)) + .extrude(xr + m_perimeter_width, writer.y() + dy) + .extrude(xl - m_perimeter_width, writer.y()); + if ((m_current_shape == SHAPE_NORMAL) ? + (writer.y() > cleaning_box.lu.y - m_perimeter_width) : + (writer.y() < cleaning_box.ld.y + m_perimeter_width)) // Next wipe line does not fit the cleaning box. break; } // Reset the extrusion flow. - writer.set_extrusion_flow(extrusion_flow); + writer.set_extrusion_flow(m_extrusion_flow); } // Draw a perimeter around cleaning_box and wipe. -void WipeTower::toolchange_Done( - Writer &writer, - const box_coordinates &cleaning_box, - const material_type /* material */, - const wipe_shape shape) +void WipeTowerPrusaMM::toolchange_Done( + PrusaMultiMaterial::Writer &writer, + const box_coordinates &cleaning_box) { box_coordinates box = cleaning_box; - if (shape == SHAPE_REVERSED) { + if (m_current_shape == SHAPE_REVERSED) { std::swap(box.lu, box.ld); std::swap(box.ru, box.rd); } @@ -572,82 +576,86 @@ void WipeTower::toolchange_Done( .feedrate(6000); } -std::string WipeTower::Perimeter(int order, int total, int Layer, bool afterToolchange, int firstLayerOffset) +std::pair WipeTowerPrusaMM::close_layer() { - Writer writer; - writer.set_extrusion_flow(extrusion_flow) + PrusaMultiMaterial::Writer writer; + writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .append(";--------------------\n" "; CP EMPTY GRID START\n") - .comment_with_value(" layer #", Layer); + .comment_with_value(" layer #", m_layer_change_total ++); // Slow down on the 1st layer. - float speed_factor = is_first_layer() ? 0.5f : 1.f; + float speed_factor = m_is_first_layer ? 0.5f : 1.f; - box_coordinates _p = _boxForColor(order); + box_coordinates _p = _boxForColor(m_layer_change_in_layer); { - box_coordinates _to = _boxForColor(total); + box_coordinates _to = _boxForColor(m_max_color_changes); + float firstLayerOffset = 0.f; _p.ld.y += firstLayerOffset; _p.rd.y += firstLayerOffset; _p.lu = _to.lu; _p.ru = _to.ru; } - if (! afterToolchange) + if (m_layer_change_in_layer == 0) + // There were no tool changes at all in this layer. // Jump with retract to _p.ld + a random shift in +x. - writer.retract(retract * 1.5f, 3600) - .z_hop(zHop, 7200) + writer.retract(m_retract * 1.5f, 3600) + .z_hop(m_zhop, 7200) .travel(_p.ld.x + randi(5, 20), _p.ld.y, 7000) .z_hop(0, 7200) - .extrude_explicit(_p.ld, retract * 1.5f, 3600); + .extrude_explicit(_p.ld, m_retract * 1.5f, 3600); box_coordinates box = _p; writer.extrude(box.lu, 2400 * speed_factor) .extrude(box.ru) .extrude(box.rd) - .extrude(box.ld + xy(perimeterWidth / 2, 0)); + .extrude(box.ld + xy(m_perimeter_width / 2, 0)); - box.expand(- perimeterWidth / 2); + box.expand(- m_perimeter_width / 2); writer.extrude(box.lu, 3200 * speed_factor) .extrude(box.ru) .extrude(box.rd) - .extrude(box.ld + xy(perimeterWidth / 2, 0)) - .extrude(box.ld + xy(perimeterWidth / 2, perimeterWidth / 2)); + .extrude(box.ld + xy(m_perimeter_width / 2, 0)) + .extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2)); - writer.extrude(_p.ld + xy(perimeterWidth * 3, perimeterWidth), 2900 * speed_factor) - .extrude(_p.lu + xy(perimeterWidth * 3, - perimeterWidth)) - .extrude(_p.lu + xy(perimeterWidth * 6, - perimeterWidth)) - .extrude(_p.ld + xy(perimeterWidth * 6, perimeterWidth)); + writer.extrude(_p.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor) + .extrude(_p.lu + xy(m_perimeter_width * 3, - m_perimeter_width)) + .extrude(_p.lu + xy(m_perimeter_width * 6, - m_perimeter_width)) + .extrude(_p.ld + xy(m_perimeter_width * 6, m_perimeter_width)); if (_p.lu.y - _p.ld.y > 4) { // Extrude three zig-zags. writer.feedrate(3200 * speed_factor); - float step = (m_wipe_tower_width - perimeterWidth * 12.f) / 12.f; + float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f; for (size_t i = 0; i < 3; ++ i) { - writer.extrude(writer.x() + step, _p.ld.y + perimeterWidth * 8); - writer.extrude(writer.x() , _p.lu.y - perimeterWidth * 8); - writer.extrude(writer.x() + step, _p.lu.y - perimeterWidth ); - writer.extrude(writer.x() + step, _p.lu.y - perimeterWidth * 8); - writer.extrude(writer.x() , _p.ld.y + perimeterWidth * 8); - writer.extrude(writer.x() + step, _p.ld.y + perimeterWidth ); + writer.extrude(writer.x() + step, _p.ld.y + m_perimeter_width * 8); + writer.extrude(writer.x() , _p.lu.y - m_perimeter_width * 8); + writer.extrude(writer.x() + step, _p.lu.y - m_perimeter_width ); + writer.extrude(writer.x() + step, _p.lu.y - m_perimeter_width * 8); + writer.extrude(writer.x() , _p.ld.y + m_perimeter_width * 8); + writer.extrude(writer.x() + step, _p.ld.y + m_perimeter_width ); } } - writer.extrude(_p.ru + xy(- perimeterWidth * 6, - perimeterWidth), 2900 * speed_factor) - .extrude(_p.ru + xy(- perimeterWidth * 3, - perimeterWidth)) - .extrude(_p.rd + xy(- perimeterWidth * 3, perimeterWidth)) - .extrude(_p.rd + xy(- perimeterWidth, perimeterWidth)) + // Extrude the perimeter. + writer.extrude(_p.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor) + .extrude(_p.ru + xy(- m_perimeter_width * 3, - m_perimeter_width)) + .extrude(_p.rd + xy(- m_perimeter_width * 3, m_perimeter_width)) + .extrude(_p.rd + xy(- m_perimeter_width, m_perimeter_width)) // Wipe along the front side of the current wiping box. - .travel(_p.ld + xy( perimeterWidth, perimeterWidth / 2), 7200) - .travel(_p.rd + xy(- perimeterWidth, perimeterWidth / 2)) + .travel(_p.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200) + .travel(_p.rd + xy(- m_perimeter_width, m_perimeter_width / 2)) .append("; CP EMPTY GRID END\n" ";------------------\n\n\n\n\n\n\n"); - return writer.gcode(); + m_current_shape = wipe_shape(- m_current_shape); + return std::pair(writer.gcode(), writer.pos()); } -WipeTower::box_coordinates WipeTower::_boxForColor(int order) const +WipeTowerPrusaMM::box_coordinates WipeTowerPrusaMM::_boxForColor(int order) const { - return box_coordinates(m_wipe_tower_pos.x, m_wipe_tower_pos.y + m_wipe_area * order - perimeterWidth / 2, m_wipe_tower_width, perimeterWidth); + return box_coordinates(m_wipe_tower_pos.x, m_wipe_tower_pos.y + m_wipe_area * order - m_perimeter_width / 2, m_wipe_tower_width, m_perimeter_width); } -}; // namespace PrusaSingleExtruderMM +}; // namespace Slic3r diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp new file mode 100644 index 0000000000..6e804a725e --- /dev/null +++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -0,0 +1,217 @@ +#ifndef WipeTowerPrusaMM_hpp_ +#define WipeTowerPrusaMM_hpp_ + +#include +#include +#include + +#include "WipeTower.hpp" + +namespace Slic3r +{ + +namespace PrusaMultiMaterial { + class Writer; +}; + +class WipeTowerPrusaMM : public WipeTower +{ +public: + enum material_type + { + INVALID = -1, + PLA = 0, // E:210C B:55C + ABS = 1, // E:255C B:100C + PET = 2, // E:240C B:90C + HIPS = 3, // E:220C B:100C + FLEX = 4, // E:245C B:80C + SCAFF = 5, // E:215C B:55C + EDGE = 6, // E:240C B:80C + NGEN = 7, // E:230C B:80C + PVA = 8 // E:210C B:80C + }; + + // Parse material name into material_type. + static material_type parse_material(const char *name); + + // x -- x coordinates of wipe tower in mm ( left bottom corner ) + // y -- y coordinates of wipe tower in mm ( left bottom corner ) + // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) + // wipe_area -- space available for one toolchange in mm + WipeTowerPrusaMM(float x, float y, float width, float wipe_area) : + m_wipe_tower_pos(x, y), + m_wipe_tower_width(width), + m_wipe_area(wipe_area), + m_z_pos(0.f) { + for (size_t i = 0; i < 4; ++ i) { + // Extruder specific parameters. + m_material[i] = PLA; + m_temperature[i] = 0; + m_first_layer_temperature[i] = 0; + } + } + virtual ~WipeTowerPrusaMM() {} + + // _retract - retract value in mm + void set_retract(float retract) { m_retract = retract; } + + // _zHop - z hop value in mm + void set_zhop(float zhop) { m_zhop = zhop; } + + // Set the extruder properties. + void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp) + { + m_material[idx] = material; + m_temperature[idx] = temp; + m_first_layer_temperature[idx] = first_layer_temp; + } + + // Switch to a next layer. + virtual void set_layer( + // Print height of this layer. + float print_z, + // Layer height, used to calculate extrusion the rate. + float layer_height, + // Maximum number of tool changes on this layer or the layers below. + size_t max_tool_changes, + // Is this the first layer of the print? In that case print the brim first. + bool is_first_layer, + // Is this the last layer of the waste tower? + bool is_last_layer) + { + m_z_pos = print_z; + m_max_color_changes = max_tool_changes; + m_is_first_layer = is_first_layer; + m_is_last_layer = is_last_layer; + // Start counting the color changes from zero. + m_layer_change_in_layer = is_first_layer ? size_t(-1) : 0; + m_current_wipe_start_y = 0.f; + + int layer_idx = int(floor(layer_height * 100) + 0.5f); + switch (layer_idx) + { + case 15: + m_extrusion_flow = (float)0.024; + break; + case 20: + default: + m_extrusion_flow = (float)0.029; + break; + } + } + + // Return the wipe tower position. + virtual const xy& position() const { return m_wipe_tower_pos; } + // The wipe tower is finished, there should be no more tool changes or wipe tower prints. + virtual bool finished() const { return m_max_color_changes == 0; } + + // Returns gcode for toolchange + virtual std::pair tool_change(int new_tool); + + // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. + virtual std::pair close_layer(); + +private: + WipeTowerPrusaMM(); + + enum wipe_shape + { + SHAPE_NORMAL = 1, + SHAPE_REVERSED = -1 + }; + + // Left front corner of the wipe tower in mm. + xy m_wipe_tower_pos; + // Width of the wipe tower. + float m_wipe_tower_width; + // Per color Y span. + float m_wipe_area; + // Current Z position. + float m_z_pos = 0.f; + // Maximum number of color changes per layer. + size_t m_max_color_changes = 0; + // Is this the 1st layer of the print? If so, print the brim around the waste tower. + bool m_is_first_layer = false; + // Is this the last layer of this waste tower? + bool m_is_last_layer = false; + + // G-code generator parameters. + float m_zhop = 0.5f; + float m_retract = 4.f; + float m_perimeter_width = 0.5f; + float m_extrusion_flow = 0.029f; + + // Extruder specific parameters. + material_type m_material[4]; + int m_temperature[4]; + int m_first_layer_temperature[4]; + + // State of the wiper tower generator. + // Layer change counter for the output statistics. + unsigned int m_layer_change_total = 0; + // Layer change counter in this layer. Counting up to m_max_color_changes. + unsigned int m_layer_change_in_layer = 0; + wipe_shape m_current_shape = SHAPE_NORMAL; + material_type m_current_material = PLA; + // Current y position at the wipe tower. + float m_current_wipe_start_y = 0.f; + + struct box_coordinates + { + box_coordinates(float left, float bottom, float width, float height) : + ld(left , bottom ), + lu(left , bottom + height), + rd(left + width, bottom ), + ru(left + width, bottom + height) {} + box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {} + void expand(const float offset) { + ld += xy(- offset, - offset); + lu += xy(- offset, offset); + rd += xy( offset, - offset); + ru += xy( offset, offset); + } + xy ld; // left down + xy lu; // left upper + xy ru; // right upper + xy rd; // right lower + }; + + // Returns gcode for wipe tower brim + // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower + // offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower + std::pair toolchange_Brim(size_t tool, bool sideOnly = false, float y_offset = 0.f); + + void toolchange_Unload( + PrusaMultiMaterial::Writer &writer, + const box_coordinates &cleaning_box, + const material_type material, + const wipe_shape shape, + const int temperature); + + void toolchange_Change( + PrusaMultiMaterial::Writer &writer, + int tool, + material_type current_material, + material_type new_material); + + void toolchange_Load( + PrusaMultiMaterial::Writer &writer, + const box_coordinates &cleaning_box); + + void toolchange_Wipe( + PrusaMultiMaterial::Writer &writer, + const box_coordinates &cleaning_box, + const material_type material); + + void toolchange_Done( + PrusaMultiMaterial::Writer &writer, + const box_coordinates &cleaning_box); + + void toolchange_Perimeter(); + + box_coordinates _boxForColor(int order) const; +}; + +}; // namespace Slic3r + +#endif /* WipeTowerPrusaMM_hpp_ */ diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index b73390ab1f..a7b88c9928 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -354,6 +354,37 @@ PrintConfigDef::PrintConfigDef() def->default_value = opt; } + def = this->add("filament_type", coStrings); + def->label = "Filament type"; + def->tooltip = "If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Slic3r config settings by reading environment variables."; + def->cli = "filament_type=s@"; + def->gui_type = "f_enum_open"; + def->gui_flags = "show_value"; + def->enum_values.push_back("PLA"); + def->enum_values.push_back("ABS"); + def->enum_values.push_back("PET"); + def->enum_values.push_back("HIPS"); + def->enum_values.push_back("FLEX"); + def->enum_values.push_back("SCAFF"); + def->enum_values.push_back("EDGE"); + def->enum_values.push_back("NGEN"); + def->enum_values.push_back("PVA"); + { + ConfigOptionStrings* opt = new ConfigOptionStrings(); + opt->values.push_back("PLA"); + def->default_value = opt; + } + + def = this->add("filament_soluble", coBools); + def->label = "Soluble material"; + def->tooltip = "Soluble material is most likely used for a soluble support."; + def->cli = "filament-soluble!"; + { + ConfigOptionBools* opt = new ConfigOptionBools(); + opt->values.push_back(false); + def->default_value = opt; + } + def = this->add("filament_cost", coFloats); def->label = "Cost"; def->tooltip = "Enter your filament cost per kg here. This is only for statistical information."; @@ -1168,6 +1199,12 @@ PrintConfigDef::PrintConfigDef() def->height = 120; def->default_value = new ConfigOptionString("G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"); + def = this->add("single_extruder_multi_material", coBool); + def->label = "Single Extruder Multi Material"; + def->tooltip = "The printer multiplexes filaments into a single hot end."; + def->cli = "single-extruder-multi-material!"; + def->default_value = new ConfigOptionBool(false); + def = this->add("support_material", coBool); def->label = "Generate support material"; def->category = "Support material"; @@ -1454,6 +1491,40 @@ PrintConfigDef::PrintConfigDef() def->default_value = opt; } + def = this->add("wipe_tower", coBool); + def->label = "Enable"; + def->tooltip = "Multi material printers may need to prime or purge extruders on tool changes. Extrude the excess material into the wipe tower."; + def->cli = "wipe-tower!"; + def->default_value = new ConfigOptionBool(false); + + def = this->add("wipe_tower_x", coFloat); + def->label = "Position X"; + def->tooltip = "X coordinate of the left front corner of a wipe tower"; + def->sidetext = "mm"; + def->cli = "wipe-tower-x=f"; + def->default_value = new ConfigOptionFloat(180.); + + def = this->add("wipe_tower_y", coFloat); + def->label = "Position Y"; + def->tooltip = "Y coordinate of the left front corner of a wipe tower"; + def->sidetext = "mm"; + def->cli = "wipe-tower-y=f"; + def->default_value = new ConfigOptionFloat(140.); + + def = this->add("wipe_tower_width", coFloat); + def->label = "Width"; + def->tooltip = "Width of a wipe tower"; + def->sidetext = "mm"; + def->cli = "wipe-tower-width=f"; + def->default_value = new ConfigOptionFloat(60.); + + def = this->add("wipe_tower_per_color_wipe", coFloat); + def->label = "Per color change depth"; + def->tooltip = "Depth of a wipe color per color change. For N colors, there will be maximum (N-1) tool switches performed, therefore the total depth of the wipe tower will be (N-1) times this value."; + def->sidetext = "mm"; + def->cli = "wipe-tower-per-color-wipe=f"; + def->default_value = new ConfigOptionFloat(15.); + def = this->add("xy_size_compensation", coFloat); def->label = "XY Size Compensation"; def->category = "Advanced"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 770d2664dd..7a826d7e8f 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -42,6 +42,10 @@ enum SeamPosition { spRandom, spNearest, spAligned, spRear }; +enum FilamentType { + ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA +}; + template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { t_config_enum_values keys_map; keys_map["reprap"] = gcfRepRap; @@ -91,6 +95,20 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum_ return keys_map; } +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["PLA"] = ftPLA; + keys_map["ABS"] = ftABS; + keys_map["PET"] = ftPET; + keys_map["HIPS"] = ftHIPS; + keys_map["FLEX"] = ftFLEX; + keys_map["SCAFF"] = ftSCAFF; + keys_map["EDGE"] = ftEDGE; + keys_map["NGEN"] = ftNGEN; + keys_map["PVA"] = ftPVA; + return keys_map; +} + // Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs. // Does not store the actual values, but defines default values. class PrintConfigDef : public ConfigDef @@ -297,13 +315,15 @@ class PrintRegionConfig : public virtual StaticPrintConfig // This object is mapped to Perl as Slic3r::Config::GCode. class GCodeConfig : public virtual StaticPrintConfig { - public: +public: ConfigOptionString before_layer_gcode; ConfigOptionString end_gcode; ConfigOptionString extrusion_axis; ConfigOptionFloats extrusion_multiplier; ConfigOptionFloats filament_diameter; ConfigOptionFloats filament_density; + ConfigOptionStrings filament_type; + ConfigOptionBools filament_soluble; ConfigOptionFloats filament_cost; ConfigOptionFloats filament_max_volumetric_speed; ConfigOptionBool gcode_comments; @@ -322,6 +342,7 @@ class GCodeConfig : public virtual StaticPrintConfig ConfigOptionFloats retract_restart_extra_toolchange; ConfigOptionFloats retract_speed; ConfigOptionString start_gcode; + ConfigOptionBool single_extruder_multi_material; ConfigOptionString toolchange_gcode; ConfigOptionFloat travel_speed; ConfigOptionBool use_firmware_retraction; @@ -341,6 +362,8 @@ class GCodeConfig : public virtual StaticPrintConfig OPT_PTR(extrusion_multiplier); OPT_PTR(filament_diameter); OPT_PTR(filament_density); + OPT_PTR(filament_type); + OPT_PTR(filament_soluble); OPT_PTR(filament_cost); OPT_PTR(filament_max_volumetric_speed); OPT_PTR(gcode_comments); @@ -358,6 +381,7 @@ class GCodeConfig : public virtual StaticPrintConfig OPT_PTR(retract_restart_extra); OPT_PTR(retract_restart_extra_toolchange); OPT_PTR(retract_speed); + OPT_PTR(single_extruder_multi_material); OPT_PTR(start_gcode); OPT_PTR(toolchange_gcode); OPT_PTR(travel_speed); @@ -435,6 +459,11 @@ class PrintConfig : public GCodeConfig ConfigOptionInts temperature; ConfigOptionInt threads; ConfigOptionBools wipe; + ConfigOptionBool wipe_tower; + ConfigOptionFloat wipe_tower_x; + ConfigOptionFloat wipe_tower_y; + ConfigOptionFloat wipe_tower_width; + ConfigOptionFloat wipe_tower_per_color_wipe; ConfigOptionFloat z_offset; PrintConfig(bool initialize = true) : GCodeConfig(false) { @@ -494,6 +523,11 @@ class PrintConfig : public GCodeConfig OPT_PTR(temperature); OPT_PTR(threads); OPT_PTR(wipe); + OPT_PTR(wipe_tower); + OPT_PTR(wipe_tower_x); + OPT_PTR(wipe_tower_y); + OPT_PTR(wipe_tower_width); + OPT_PTR(wipe_tower_per_color_wipe); OPT_PTR(z_offset); // look in parent class diff --git a/xs/src/libslic3r/WipeTower.hpp b/xs/src/libslic3r/WipeTower.hpp deleted file mode 100644 index 38d8203c95..0000000000 --- a/xs/src/libslic3r/WipeTower.hpp +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef PrusaSingleExtruderMM_WipeTower_hpp_ -#define PrusaSingleExtruderMM_WipeTower_hpp_ - -#include -#include -#include - -namespace PrusaSingleExtruderMM -{ - -class Writer; - -class WipeTower -{ -public: - enum material_type - { - INVALID = -1, - PLA = 0, // E:210C B:55C - ABS = 1, // E:255C B:100C - PET = 2, // E:240C B:90C - HIPS = 3, // E:220C B:100C - FLEX = 4, // E:245C B:80C - SCAFF = 5, // E:215C B:55C - EDGE = 6, // E:240C B:80C - NGEN = 7, // E:230C B:80C - PVA = 8 // E:210C B:80C - }; - - enum wipe_shape - { - SHAPE_NORMAL = 1, - SHAPE_REVERSED = -1 - }; - - struct xy - { - xy(float x = 0.f, float y = 0.f) : x(x), y(y) {} - xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; } - xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; } - xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; } - xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; } - float x; - float y; - }; - - // Parse material name into material_type. - static material_type parse_material(const char *name); - - // x -- x coordinates of wipe tower in mm ( left bottom corner ) - // y -- y coordinates of wipe tower in mm ( left bottom corner ) - // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) - // wipe_area -- space available for one toolchange in mm - // colors -- maximum colors for object - WipeTower(float x, float y, float width, float wipe_area, int color_changes) : - m_wipe_tower_pos(x, y), - m_wipe_tower_width(width), - m_wipe_area(wipe_area), - m_color_changes(color_changes), - m_z_pos(0.f) {} - - // colors -- maximum color changes for layer - void setColors(int colors) { m_color_changes = colors; } - - // Z height -- mm - void setZ(float z) { m_z_pos = z; } - bool is_first_layer() const { return m_z_pos < 0.205f; } - - // _retract - retract value in mm - void setRetract(float _retract) { retract = _retract; } - - // _zHop - z hop value in mm - void setZHop(float _zhop) { zHop = _zhop; } - - void setExtrusion(int layerHeight) - { - // set extrusion coefficient for layer height - // layerHeight -- mm * 100 - - switch (layerHeight) - { - case 15: - extrusion_flow = (float)0.024; - break; - case 20: - extrusion_flow = (float)0.029; - break; - default: - break; - } - } - - /* - Returns gcode for wipe tower brim - - sideOnly -- set to false -- experimental, draw brim on sides of wipe tower - offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower - */ - std::string FirstLayer(bool sideOnly = false, float y_offset = 0.f); - - // Returns gcode for toolchange - std::pair Toolchange( - // extruder # 0 - 3 - const int tool, - // filament type currently used to print and loaded in nozzle -- see enum material_type - const material_type current_material, - // filament type that will be loaded in to the nozzle -- see enum material_type - const material_type new_material, - // temperature in Celsius for new filament that will be loaded into the nozzle - const int temperature, - // orientation of purge / wipe shape (NORMAL / REVERSED) - const wipe_shape shape, - // total toolchanges done counter ( comment in header of toolchange only ) - const int count, - // space available for toolchange ( purge / load / wipe ) - in mm - const float spaceAvailable, - // experimental, don't use, set to 0 - const float wipeStartY, - // for last toolchange in object set to true to unload filament into cooling tube, for all other set to false - const bool lastInFile, - // experimental, set to false - const bool colorInit = false); - - /* - Returns gcode to draw empty pattern in place of a toolchange -> in case there are less toolchanges atm then what is required later - - order -- total toolchanges done for current layer - total -- total colors in current z layer including empty ones - afterToolchange -- true - ignore some not neccesary moves | false - do whole move from object to wipe tower - firstLayerOffset -- experimental , set to 0 - */ - std::string Perimeter(int order, int total, int Layer, bool afterToolchange, int firstLayerOffset = 0); - -private: - WipeTower(); - - // Left front corner of the wipe tower in mm. - xy m_wipe_tower_pos; - // Width of the wipe tower. - float m_wipe_tower_width; - // Per color Y span. - float m_wipe_area; - // Current Z position. - float m_z_pos; - // Maximum number of color changes per layer. - int m_color_changes; - - float zHop = 0.5f; - float retract = 4.f; - float perimeterWidth = 0.5f; - float extrusion_flow = 0.029f; - - struct box_coordinates - { - box_coordinates(float left, float bottom, float width, float height) : - ld(left , bottom ), - lu(left , bottom + height), - rd(left + width, bottom ), - ru(left + width, bottom + height) {} - box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {} - void expand(const float offset) { - ld += xy(- offset, - offset); - lu += xy(- offset, offset); - rd += xy( offset, - offset); - ru += xy( offset, offset); - } - xy ld; // left down - xy lu; // left upper - xy ru; // right upper - xy rd; // right lower - }; - - void toolchange_Unload( - Writer &writer, - const box_coordinates &cleaning_box, - const material_type material, - const wipe_shape shape, - const int temperature); - - void toolchange_Change( - Writer &writer, - int tool, - material_type current_material, - material_type new_material); - - void toolchange_Load( - Writer &writer, - const box_coordinates &cleaning_box, - const material_type material, - const wipe_shape shape, - const bool colorInit); - - void toolchange_Wipe( - Writer &writer, - const box_coordinates &cleaning_box, - const material_type material, - const wipe_shape shape); - - void toolchange_Done( - Writer &writer, - const box_coordinates &cleaning_box, - const material_type material, - const wipe_shape shape); - - box_coordinates _boxForColor(int order) const; -}; - -}; // namespace PrusaSingleExtruderMM - -#endif /* PrusaSingleExtruderMM_WipeTower_hpp_ */