mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Initial integration of the Prusa MultiMatrial Wipe Tower.
This commit is contained in:
		
							parent
							
								
									74346efccb
								
							
						
					
					
						commit
						c22b6edeeb
					
				
					 11 changed files with 922 additions and 439 deletions
				
			
		|  | @ -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; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| #include "ExtrusionEntity.hpp" | ||||
| #include "EdgeGrid.hpp" | ||||
| #include "Geometry.hpp" | ||||
| #include "GCode/WipeTowerPrusaMM.hpp" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <cstdlib> | ||||
|  | @ -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<ToolOrdering::LayerTools> 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<ToolOrdering::LayerTools> 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<LayerToPrint>,
 | ||||
|             // 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<LayerToPrint> &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<unsigned int> 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<size_t, size_t>(0, print.config.skirts.value); | ||||
|             skirt_loops_per_extruder[first_extruder_id] = std::pair<size_t, size_t>(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<unsigned int> 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<std::unique_ptr<EdgeGrid::Grid>> 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<std::string, WipeTower::xy> 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 | ||||
| { | ||||
|  |  | |||
|  | @ -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 <memory> | ||||
|  | @ -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<LayerToPrint> &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<CoolingBuffer>      m_cooling_buffer; | ||||
|     std::unique_ptr<SpiralVase>         m_spiral_vase; | ||||
|     std::unique_ptr<PressureEqualizer>  m_pressure_equalizer; | ||||
|     std::unique_ptr<WipeTower>          m_wipe_tower; | ||||
| 
 | ||||
|     // Heights at which the skirt has already been extruded.
 | ||||
|     std::vector<coordf_t>               m_skirt_done; | ||||
|  |  | |||
							
								
								
									
										194
									
								
								xs/src/libslic3r/GCode/ToolOrdering.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								xs/src/libslic3r/GCode/ToolOrdering.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<LayerTools> &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<const ExtrusionEntityCollection*>(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<LayerTools> &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<LayerTools> &layers) | ||||
| { | ||||
|     if (layers.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     // Count the minimum number of tool changes per layer.
 | ||||
|     for (LayerTools < : layers) | ||||
|         lt.wipe_tower_partitions = std::max<int>(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<LayerTools> tool_ordering(PrintObject &object) | ||||
| { | ||||
|     // Initialize the print layers for just a single object.
 | ||||
|     std::vector<LayerTools> layers; | ||||
|     { | ||||
|         std::vector<coordf_t> 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<LayerTools> tool_ordering(const Print &print) | ||||
| { | ||||
|     // Initialize the print layers for all objects and all layers. 
 | ||||
|     std::vector<LayerTools> layers; | ||||
|     { | ||||
|         std::vector<coordf_t> 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
 | ||||
							
								
								
									
										38
									
								
								xs/src/libslic3r/GCode/ToolOrdering.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								xs/src/libslic3r/GCode/ToolOrdering.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<unsigned int> 	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<LayerTools> tool_ordering(PrintObject &object); | ||||
| 
 | ||||
| // For the use case when all objects are printed at once.
 | ||||
| // (print.config.complete_objects is false).
 | ||||
| extern std::vector<LayerTools> tool_ordering(const Print &print); | ||||
| 
 | ||||
| } // namespace ToolOrdering
 | ||||
| } // namespace SLic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_ToolOrdering_hpp_ */ | ||||
							
								
								
									
										59
									
								
								xs/src/libslic3r/GCode/WipeTower.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								xs/src/libslic3r/GCode/WipeTower.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| #ifndef slic3r_WipeTower_hpp_ | ||||
| #define slic3r_WipeTower_hpp_ | ||||
| 
 | ||||
| #include <utility> | ||||
| #include <string> | ||||
| 
 | ||||
| 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<std::string, xy> 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<std::string, xy> close_layer() = 0; | ||||
| }; | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_WipeTower_hpp_ */ | ||||
|  | @ -1,4 +1,4 @@ | |||
| #include "WipeTower.hpp" | ||||
| #include "WipeTowerPrusaMM.hpp" | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <math.h> | ||||
|  | @ -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<std::string, WipeTower::xy> 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<std::string, WipeTower::xy> 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<std::string, WipeTower::xy> 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<std::string, xy>(writer.gcode(), writer.pos()); | ||||
| } | ||||
| 
 | ||||
| std::pair<std::string, WipeTower::xy> 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<std::string, xy>(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<std::string, WipeTower::xy> 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<std::string, xy>(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
 | ||||
							
								
								
									
										217
									
								
								xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,217 @@ | |||
| #ifndef WipeTowerPrusaMM_hpp_ | ||||
| #define WipeTowerPrusaMM_hpp_ | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| 
 | ||||
| #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<std::string, xy> 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<std::string, xy> 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<std::string, WipeTower::xy> 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_ */ | ||||
|  | @ -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"; | ||||
|  |  | |||
|  | @ -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<GCodeFlavor>::get_enum_values() { | ||||
|     t_config_enum_values keys_map; | ||||
|     keys_map["reprap"]          = gcfRepRap; | ||||
|  | @ -91,6 +95,20 @@ template<> inline t_config_enum_values ConfigOptionEnum<SeamPosition>::get_enum_ | |||
|     return keys_map; | ||||
| } | ||||
| 
 | ||||
| template<> inline t_config_enum_values ConfigOptionEnum<FilamentType>::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
 | ||||
|  |  | |||
|  | @ -1,210 +0,0 @@ | |||
| #ifndef PrusaSingleExtruderMM_WipeTower_hpp_ | ||||
| #define PrusaSingleExtruderMM_WipeTower_hpp_ | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| 
 | ||||
| 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<std::string, WipeTower::xy> 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_ */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bubnikv
						bubnikv